@stacksjs/desktop 0.2.20 → 0.2.24

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 (56) hide show
  1. package/dist/_bridge.d.ts +105 -0
  2. package/dist/app-info.d.ts +22 -0
  3. package/dist/apple-script.d.ts +8 -0
  4. package/dist/audio.d.ts +17 -0
  5. package/dist/battery.d.ts +15 -0
  6. package/dist/biometric.d.ts +14 -0
  7. package/dist/bluetooth.d.ts +52 -0
  8. package/dist/bonjour.d.ts +13 -0
  9. package/dist/capabilities.d.ts +31 -0
  10. package/dist/clipboard.d.ts +11 -0
  11. package/dist/continuity-camera.d.ts +10 -0
  12. package/dist/coreml.d.ts +6 -0
  13. package/dist/crash-reporter.d.ts +46 -0
  14. package/dist/deep-link.d.ts +10 -0
  15. package/dist/drag-out.d.ts +18 -0
  16. package/dist/file-associations.d.ts +5 -0
  17. package/dist/fs.d.ts +62 -0
  18. package/dist/global-shortcuts.d.ts +19 -0
  19. package/dist/handoff.d.ts +24 -0
  20. package/dist/hotkeys.d.ts +10 -8
  21. package/dist/iap.d.ts +79 -0
  22. package/dist/index.d.ts +322 -0
  23. package/dist/index.js +2505 -20
  24. package/dist/index.js.map +55 -7
  25. package/dist/keychain.d.ts +7 -0
  26. package/dist/live-activities.d.ts +11 -0
  27. package/dist/local-server.d.ts +23 -0
  28. package/dist/location.d.ts +29 -0
  29. package/dist/log.d.ts +7 -0
  30. package/dist/menu.d.ts +29 -0
  31. package/dist/midi.d.ts +17 -0
  32. package/dist/native-autolaunch.d.ts +6 -0
  33. package/dist/network.d.ts +33 -0
  34. package/dist/notifications.d.ts +56 -0
  35. package/dist/pdf.d.ts +5 -0
  36. package/dist/permissions.d.ts +21 -0
  37. package/dist/printing.d.ts +9 -0
  38. package/dist/screen-capture.d.ts +11 -0
  39. package/dist/screen.d.ts +18 -0
  40. package/dist/serial.d.ts +15 -0
  41. package/dist/service-menu.d.ts +10 -0
  42. package/dist/shell.d.ts +28 -0
  43. package/dist/speech-recognition.d.ts +12 -0
  44. package/dist/speech.d.ts +21 -0
  45. package/dist/spotlight.d.ts +12 -0
  46. package/dist/system.d.ts +16 -0
  47. package/dist/tags.d.ts +6 -0
  48. package/dist/test-utils.d.ts +17 -0
  49. package/dist/test-utils.js +108 -0
  50. package/dist/test-utils.js.map +10 -0
  51. package/dist/theme.d.ts +10 -0
  52. package/dist/touchbar.d.ts +29 -0
  53. package/dist/updater.d.ts +37 -0
  54. package/dist/vision.d.ts +17 -0
  55. package/dist/window-events.d.ts +20 -0
  56. package/package.json +5 -1
package/dist/index.js CHANGED
@@ -2772,14 +2772,19 @@ async function createWindow(url, options = {}) {
2772
2772
  }
2773
2773
  async function openDevWindow(port, options = {}) {
2774
2774
  const url = `http://localhost:${port}/`;
2775
+ const isTestEnv = process3.env.NODE_ENV === "test" || process3.env.BUN_TEST;
2776
+ if (isTestEnv) {
2777
+ console.warn("\u26A0 Skipping native window in test environment");
2778
+ console.log("(Skipping browser fallback in test environment)");
2779
+ return false;
2780
+ }
2775
2781
  try {
2776
2782
  const { createApp } = await import("ts-craft");
2777
- console.log("\u26A1 Opening native window...");
2778
- const craftPath = getCraftBinaryPath();
2783
+ console.log("\u26A1 Opening native window via ts-craft\u2026");
2779
2784
  const useSystemTray = !options.nativeSidebar;
2780
2785
  const app = createApp({
2781
2786
  url,
2782
- craftPath,
2787
+ craftPath: getCraftBinaryPath(),
2783
2788
  window: {
2784
2789
  title: options.title || "stx Development",
2785
2790
  width: options.width || 1400,
@@ -2798,30 +2803,51 @@ async function openDevWindow(port, options = {}) {
2798
2803
  activeWindows.set(id, { app, url, options });
2799
2804
  await app.show();
2800
2805
  console.log(`\u2713 Native window opened at ${url}`);
2801
- console.log(`\uD83D\uDCCC Look for the "stx Development" icon in your menubar`);
2802
2806
  return true;
2803
- } catch (error) {
2804
- console.warn("\u26A0 Could not open native window:", error.message);
2807
+ } catch (tsCraftErr) {
2808
+ const craftPath = getCraftBinaryPath();
2809
+ if (craftPath) {
2810
+ try {
2811
+ console.log(`\u26A1 Opening native window via craft binary (${craftPath})\u2026`);
2812
+ const { spawn } = await import("child_process");
2813
+ const args = [
2814
+ "--url",
2815
+ url,
2816
+ "--title",
2817
+ options.title || "stx Development",
2818
+ "--width",
2819
+ String(options.width || 1400),
2820
+ "--height",
2821
+ String(options.height || 900)
2822
+ ];
2823
+ if (options.darkMode)
2824
+ args.push("--dark");
2825
+ if (options.hotReload ?? true)
2826
+ args.push("--hot-reload");
2827
+ const child = spawn(craftPath, args, { stdio: "inherit" });
2828
+ child.on("exit", () => process3.exit(0));
2829
+ child.on("error", (err) => console.warn("craft child error:", err.message));
2830
+ const id = `dev-window-${port}`;
2831
+ activeWindows.set(id, { app: { close: () => child.kill() }, url, options });
2832
+ console.log(`\u2713 Native window opened at ${url}`);
2833
+ return true;
2834
+ } catch (binaryErr) {
2835
+ console.warn("\u26A0 Could not spawn craft binary:", binaryErr.message);
2836
+ }
2837
+ } else {
2838
+ console.warn("\u26A0 ts-craft not installed and no craft binary found");
2839
+ console.warn(` (ts-craft error: ${tsCraftErr.message})`);
2840
+ }
2805
2841
  if (process3.env.NODE_ENV === "test" || process3.env.BUN_TEST) {
2806
2842
  console.log("(Skipping browser fallback in test environment)");
2807
2843
  return false;
2808
2844
  }
2809
- console.log("\uD83D\uDCF1 Opening in browser instead...");
2845
+ console.log("\uD83D\uDCF1 Opening in browser instead\u2026");
2810
2846
  try {
2811
2847
  const { spawn } = await import("child_process");
2812
2848
  const platform = process3.platform;
2813
- let command;
2814
- let args;
2815
- if (platform === "darwin") {
2816
- command = "open";
2817
- args = [url];
2818
- } else if (platform === "win32") {
2819
- command = "cmd";
2820
- args = ["/c", "start", url];
2821
- } else {
2822
- command = "xdg-open";
2823
- args = [url];
2824
- }
2849
+ const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
2850
+ const args = platform === "win32" ? ["/c", "start", url] : [url];
2825
2851
  spawn(command, args, { detached: true, stdio: "ignore" }).unref();
2826
2852
  console.log(`\u2713 Browser opened at ${url}`);
2827
2853
  return true;
@@ -3760,10 +3786,2428 @@ function formatCompact(ms) {
3760
3786
  }
3761
3787
  return `${seconds}s`;
3762
3788
  }
3789
+ // src/_bridge.ts
3790
+ function hasBridge(ns) {
3791
+ if (typeof window === "undefined")
3792
+ return false;
3793
+ const c = window.craft;
3794
+ return !!(c && c[ns]);
3795
+ }
3796
+ function requireBridge(ns) {
3797
+ if (!hasBridge(ns)) {
3798
+ throw new Error(`craft.${ns} is not available \u2014 this API requires a Craft native window`);
3799
+ }
3800
+ return window.craft[ns];
3801
+ }
3802
+ function onCraftEvent(name, cb) {
3803
+ if (typeof window === "undefined")
3804
+ return () => {};
3805
+ const h = (e) => cb(e.detail ?? {});
3806
+ window.addEventListener(name, h);
3807
+ return () => window.removeEventListener(name, h);
3808
+ }
3809
+
3810
+ // src/clipboard.ts
3811
+ var clipboard = {
3812
+ async writeText(text) {
3813
+ if (hasBridge("clipboard")) {
3814
+ await window.craft.clipboard.writeText(text);
3815
+ return;
3816
+ }
3817
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
3818
+ await navigator.clipboard.writeText(text);
3819
+ }
3820
+ },
3821
+ async readText() {
3822
+ if (hasBridge("clipboard")) {
3823
+ const v = await window.craft.clipboard.readText();
3824
+ return typeof v === "string" ? v : "";
3825
+ }
3826
+ if (typeof navigator !== "undefined" && navigator.clipboard?.readText) {
3827
+ try {
3828
+ return await navigator.clipboard.readText();
3829
+ } catch {
3830
+ return "";
3831
+ }
3832
+ }
3833
+ return "";
3834
+ },
3835
+ async writeHTML(html) {
3836
+ if (hasBridge("clipboard")) {
3837
+ await window.craft.clipboard.writeHTML(html);
3838
+ return;
3839
+ }
3840
+ if (typeof navigator !== "undefined" && navigator.clipboard?.write) {
3841
+ try {
3842
+ const item = new window.ClipboardItem({
3843
+ "text/html": new Blob([html], { type: "text/html" }),
3844
+ "text/plain": new Blob([stripHtml(html)], { type: "text/plain" })
3845
+ });
3846
+ await navigator.clipboard.write([item]);
3847
+ } catch {}
3848
+ }
3849
+ },
3850
+ async readHTML() {
3851
+ if (hasBridge("clipboard")) {
3852
+ const v = await window.craft.clipboard.readHTML();
3853
+ return typeof v === "string" ? v : "";
3854
+ }
3855
+ if (typeof navigator !== "undefined" && navigator.clipboard?.read) {
3856
+ try {
3857
+ const items = await navigator.clipboard.read();
3858
+ for (const item of items) {
3859
+ if (item.types.includes("text/html")) {
3860
+ const blob = await item.getType("text/html");
3861
+ return await blob.text();
3862
+ }
3863
+ }
3864
+ } catch {}
3865
+ }
3866
+ return "";
3867
+ },
3868
+ async clear() {
3869
+ if (hasBridge("clipboard")) {
3870
+ await window.craft.clipboard.clear();
3871
+ return;
3872
+ }
3873
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
3874
+ try {
3875
+ await navigator.clipboard.writeText("");
3876
+ } catch {}
3877
+ }
3878
+ },
3879
+ async hasText() {
3880
+ if (hasBridge("clipboard")) {
3881
+ return await window.craft.clipboard.hasText();
3882
+ }
3883
+ return (await this.readText()).length > 0;
3884
+ },
3885
+ async hasHTML() {
3886
+ if (hasBridge("clipboard")) {
3887
+ return await window.craft.clipboard.hasHTML();
3888
+ }
3889
+ return (await this.readHTML()).length > 0;
3890
+ },
3891
+ async hasImage() {
3892
+ if (hasBridge("clipboard")) {
3893
+ return await window.craft.clipboard.hasImage();
3894
+ }
3895
+ return false;
3896
+ }
3897
+ };
3898
+ function stripHtml(html) {
3899
+ return html.replace(/<(script|style)[^>]*>[\s\S]*?<\/\1>/gi, "").replace(/<\/?(br|p|div|li|h[1-6])\b[^>]*>/gi, `
3900
+ `).replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/\n{3,}/g, `
3901
+
3902
+ `).trim();
3903
+ }
3904
+ // src/notifications.ts
3905
+ var notifications = {
3906
+ async show(options) {
3907
+ if (!options.title)
3908
+ throw new Error("notification title is required");
3909
+ if (hasBridge("notifications")) {
3910
+ await window.craft.notifications.show(options);
3911
+ return;
3912
+ }
3913
+ if (typeof window !== "undefined" && "Notification" in window) {
3914
+ const N = window.Notification;
3915
+ if (N.permission === "granted") {
3916
+ new N(options.title, { body: options.body, icon: options.icon });
3917
+ } else if (N.permission === "default") {
3918
+ const granted = await N.requestPermission() === "granted";
3919
+ if (granted)
3920
+ new N(options.title, { body: options.body, icon: options.icon });
3921
+ }
3922
+ }
3923
+ },
3924
+ async schedule(options) {
3925
+ if (hasBridge("notifications")) {
3926
+ const o = { ...options };
3927
+ if (o.triggerAt instanceof Date)
3928
+ o.triggerAt = o.triggerAt.toISOString();
3929
+ await window.craft.notifications.schedule(o);
3930
+ return;
3931
+ }
3932
+ const fireAt = toEpochMs(options.triggerAt);
3933
+ const delay2 = Math.max(0, fireAt - Date.now());
3934
+ setTimeout(() => {
3935
+ this.show(options).catch(() => {});
3936
+ }, delay2);
3937
+ },
3938
+ async cancel(id) {
3939
+ if (hasBridge("notifications")) {
3940
+ await window.craft.notifications.cancel(id);
3941
+ }
3942
+ },
3943
+ async cancelAll() {
3944
+ if (hasBridge("notifications")) {
3945
+ await window.craft.notifications.cancelAll();
3946
+ }
3947
+ },
3948
+ async setBadge(n) {
3949
+ const safe = Math.max(0, Math.round(Number.isFinite(n) ? n : 0));
3950
+ if (hasBridge("notifications")) {
3951
+ await window.craft.notifications.setBadge(safe);
3952
+ return;
3953
+ }
3954
+ if (typeof navigator !== "undefined" && navigator.setAppBadge) {
3955
+ try {
3956
+ await navigator.setAppBadge(safe);
3957
+ } catch {}
3958
+ }
3959
+ },
3960
+ async clearBadge() {
3961
+ if (hasBridge("notifications")) {
3962
+ await window.craft.notifications.clearBadge();
3963
+ return;
3964
+ }
3965
+ if (typeof navigator !== "undefined" && navigator.clearAppBadge) {
3966
+ try {
3967
+ await navigator.clearAppBadge();
3968
+ } catch {}
3969
+ }
3970
+ },
3971
+ async requestPermission() {
3972
+ if (hasBridge("notifications")) {
3973
+ return await window.craft.notifications.requestPermission();
3974
+ }
3975
+ if (typeof window !== "undefined" && "Notification" in window) {
3976
+ const N = window.Notification;
3977
+ if (N.permission === "granted")
3978
+ return true;
3979
+ if (N.permission === "denied")
3980
+ return false;
3981
+ const result = await N.requestPermission();
3982
+ return result === "granted";
3983
+ }
3984
+ return false;
3985
+ },
3986
+ async registerCategories(categories) {
3987
+ if (!Array.isArray(categories) || categories.length === 0)
3988
+ return;
3989
+ if (hasBridge("notifications")) {
3990
+ const fn = window.craft.notifications.registerCategories;
3991
+ if (typeof fn === "function")
3992
+ await fn(categories);
3993
+ }
3994
+ },
3995
+ onActionClicked(cb) {
3996
+ return onCraftEvent("craft:notification:actionClicked", cb);
3997
+ },
3998
+ onReply(cb) {
3999
+ return onCraftEvent("craft:notification:reply", cb);
4000
+ }
4001
+ };
4002
+ function toEpochMs(t) {
4003
+ if (t == null)
4004
+ return Date.now();
4005
+ if (t instanceof Date)
4006
+ return t.getTime();
4007
+ if (typeof t === "number")
4008
+ return t;
4009
+ const parsed = Date.parse(t);
4010
+ return Number.isNaN(parsed) ? Date.now() : parsed;
4011
+ }
4012
+ // src/fs.ts
4013
+ var fs = {
4014
+ async readFile(path) {
4015
+ const r = await requireBridge("fs").readFile(path);
4016
+ return r && r.data || "";
4017
+ },
4018
+ async readBuffer(path) {
4019
+ const r = await requireBridge("fs").readFile(path);
4020
+ const text = r && r.data || "";
4021
+ if (r && r.base64 === true) {
4022
+ return base64ToBytes(text);
4023
+ }
4024
+ return new TextEncoder().encode(text);
4025
+ },
4026
+ async writeFile(path, data) {
4027
+ await requireBridge("fs").writeFile(path, data);
4028
+ },
4029
+ async writeBuffer(path, data) {
4030
+ const b64 = bytesToBase64(data);
4031
+ const bridge = requireBridge("fs");
4032
+ if (typeof bridge.writeFileBytes === "function") {
4033
+ await bridge.writeFileBytes(path, b64);
4034
+ } else {
4035
+ await bridge.writeFile(path, b64);
4036
+ }
4037
+ },
4038
+ async copy(from, to) {
4039
+ if (from === to)
4040
+ throw new Error("fs.copy: source and destination must differ");
4041
+ await requireBridge("fs").copy(from, to);
4042
+ },
4043
+ async move(from, to) {
4044
+ if (from === to)
4045
+ throw new Error("fs.move: source and destination must differ");
4046
+ await requireBridge("fs").move(from, to);
4047
+ },
4048
+ async appendFile(path, data) {
4049
+ await requireBridge("fs").appendFile(path, data);
4050
+ },
4051
+ async deleteFile(path) {
4052
+ await requireBridge("fs").deleteFile(path);
4053
+ },
4054
+ async exists(path) {
4055
+ return await requireBridge("fs").exists(path);
4056
+ },
4057
+ async stat(path) {
4058
+ return normalizeStat(await requireBridge("fs").stat(path));
4059
+ },
4060
+ async readDir(path) {
4061
+ const r = await requireBridge("fs").readDir(path);
4062
+ const raw = r && r.entries || [];
4063
+ const base = path.endsWith("/") ? path.slice(0, -1) : path;
4064
+ return raw.map((e) => ({
4065
+ name: e.name,
4066
+ path: `${base}/${e.name}`,
4067
+ isDirectory: !!e.isDirectory
4068
+ }));
4069
+ },
4070
+ async mkdir(path, opts) {
4071
+ await requireBridge("fs").mkdir(path, opts);
4072
+ },
4073
+ async rmdir(path, opts) {
4074
+ await requireBridge("fs").rmdir(path, opts);
4075
+ },
4076
+ async watch(path, id) {
4077
+ await requireBridge("fs").watch(path, id);
4078
+ },
4079
+ async unwatch(id) {
4080
+ await requireBridge("fs").unwatch(id);
4081
+ },
4082
+ onChange(cb) {
4083
+ return onCraftEvent("craft:fs:change", cb);
4084
+ },
4085
+ async watchTree(path, options, cb) {
4086
+ const id = `watch-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
4087
+ const bridge = requireBridge("fs");
4088
+ await bridge.watch(path, id, { recursive: !!options.recursive });
4089
+ const allowed = options.kinds && options.kinds.length > 0 ? new Set(options.kinds) : null;
4090
+ const coalesceMs = Math.max(0, Math.floor(options.coalesceMs ?? 0));
4091
+ let buffer = [];
4092
+ let flushTimer = null;
4093
+ let stopped = false;
4094
+ const flush = () => {
4095
+ flushTimer = null;
4096
+ if (buffer.length === 0)
4097
+ return;
4098
+ const batch = buffer;
4099
+ buffer = [];
4100
+ try {
4101
+ cb(batch);
4102
+ } catch {}
4103
+ };
4104
+ const off = onCraftEvent("craft:fs:change", (e) => {
4105
+ if (stopped)
4106
+ return;
4107
+ if (e.id !== id)
4108
+ return;
4109
+ if (allowed && e.kind && !allowed.has(e.kind))
4110
+ return;
4111
+ buffer.push(e);
4112
+ if (coalesceMs === 0) {
4113
+ flush();
4114
+ return;
4115
+ }
4116
+ if (flushTimer == null) {
4117
+ flushTimer = setTimeout(flush, coalesceMs);
4118
+ }
4119
+ });
4120
+ return {
4121
+ id,
4122
+ async stop() {
4123
+ if (stopped)
4124
+ return;
4125
+ stopped = true;
4126
+ off();
4127
+ if (flushTimer) {
4128
+ clearTimeout(flushTimer);
4129
+ flushTimer = null;
4130
+ }
4131
+ if (buffer.length > 0)
4132
+ flush();
4133
+ try {
4134
+ await bridge.unwatch(id);
4135
+ } catch {}
4136
+ }
4137
+ };
4138
+ },
4139
+ async homeDir() {
4140
+ return await requireBridge("fs").homeDir();
4141
+ },
4142
+ async tempDir() {
4143
+ return await requireBridge("fs").tempDir();
4144
+ },
4145
+ async appDataDir() {
4146
+ return await requireBridge("fs").appDataDir();
4147
+ }
4148
+ };
4149
+ function bytesToBase64(bytes) {
4150
+ let s = "";
4151
+ for (let i = 0;i < bytes.length; i++)
4152
+ s += String.fromCharCode(bytes[i]);
4153
+ return typeof btoa === "function" ? btoa(s) : Buffer.from(bytes).toString("base64");
4154
+ }
4155
+ function base64ToBytes(b64) {
4156
+ const bin = typeof atob === "function" ? atob(b64) : Buffer.from(b64, "base64").toString("binary");
4157
+ const out = new Uint8Array(bin.length);
4158
+ for (let i = 0;i < bin.length; i++)
4159
+ out[i] = bin.charCodeAt(i);
4160
+ return out;
4161
+ }
4162
+ function normalizeStat(raw) {
4163
+ return {
4164
+ isFile: !!raw.isFile,
4165
+ isDirectory: !!raw.isDirectory,
4166
+ isSymlink: !!raw.isSymlink,
4167
+ size: Number(raw.size) || 0,
4168
+ modifiedAt: raw.modifiedAt != null ? raw.modifiedAt < 1000000000000 ? raw.modifiedAt * 1000 : raw.modifiedAt : 0
4169
+ };
4170
+ }
4171
+ // src/shell.ts
4172
+ var activeSpawnIds = new Set;
4173
+ var exitListenerAttached = false;
4174
+ function ensureExitListener() {
4175
+ if (exitListenerAttached)
4176
+ return;
4177
+ if (typeof window === "undefined" || typeof window.addEventListener !== "function")
4178
+ return;
4179
+ window.addEventListener("craft:shell:exit", (e) => {
4180
+ const detail = e.detail;
4181
+ if (detail?.id)
4182
+ activeSpawnIds.delete(detail.id);
4183
+ });
4184
+ exitListenerAttached = true;
4185
+ }
4186
+ var BLOCKED_SCHEMES = new Set(["javascript:", "data:", "file:", "vbscript:"]);
4187
+ var shell = {
4188
+ async openExternal(url) {
4189
+ const lc = url.trim().toLowerCase();
4190
+ for (const s of BLOCKED_SCHEMES) {
4191
+ if (lc.startsWith(s)) {
4192
+ throw new Error(`shell.openExternal: ${s} URLs are blocked for safety`);
4193
+ }
4194
+ }
4195
+ if (hasBridge("shell")) {
4196
+ await window.craft.shell.openExternal(url);
4197
+ return;
4198
+ }
4199
+ if (typeof window !== "undefined" && typeof window.open === "function") {
4200
+ window.open(url, "_blank", "noopener,noreferrer");
4201
+ }
4202
+ },
4203
+ async openPath(path) {
4204
+ await requireBridge("shell").openPath(path);
4205
+ },
4206
+ async showInFinder(path) {
4207
+ await requireBridge("shell").showInFinder(path);
4208
+ },
4209
+ async spawn(id, command, args = [], opts = {}) {
4210
+ ensureExitListener();
4211
+ if (!id || typeof id !== "string")
4212
+ throw new Error("shell.spawn: id must be a non-empty string");
4213
+ if (!command || typeof command !== "string")
4214
+ throw new Error("shell.spawn: command must be a non-empty string");
4215
+ if (!Array.isArray(args))
4216
+ throw new Error("shell.spawn: args must be an array");
4217
+ for (const a of args) {
4218
+ if (typeof a !== "string")
4219
+ throw new Error("shell.spawn: args entries must be strings");
4220
+ }
4221
+ if (activeSpawnIds.has(id)) {
4222
+ throw new Error(`shell.spawn: id "${id}" is already in use \u2014 call kill(id) first or pick a different id`);
4223
+ }
4224
+ activeSpawnIds.add(id);
4225
+ try {
4226
+ await requireBridge("shell").spawn(id, command, args, opts);
4227
+ } catch (e) {
4228
+ activeSpawnIds.delete(id);
4229
+ throw e;
4230
+ }
4231
+ },
4232
+ async kill(id) {
4233
+ await requireBridge("shell").kill(id);
4234
+ activeSpawnIds.delete(id);
4235
+ },
4236
+ async getEnv(name) {
4237
+ if (hasBridge("shell")) {
4238
+ const v = await window.craft.shell.getEnv(name);
4239
+ return v == null ? undefined : String(v);
4240
+ }
4241
+ return;
4242
+ },
4243
+ async setEnv(name, value) {
4244
+ await requireBridge("shell").setEnv(name, value);
4245
+ },
4246
+ onStdout(cb) {
4247
+ return onCraftEvent("craft:shell:stdout", cb);
4248
+ },
4249
+ onStderr(cb) {
4250
+ return onCraftEvent("craft:shell:stderr", cb);
4251
+ },
4252
+ onExit(cb) {
4253
+ return onCraftEvent("craft:shell:exit", cb);
4254
+ }
4255
+ };
4256
+ // src/global-shortcuts.ts
4257
+ var globalShortcuts = {
4258
+ async register(id, accelerator, opts) {
4259
+ if (!hasBridge("shortcuts"))
4260
+ return;
4261
+ await window.craft.shortcuts.register(id, accelerator, opts);
4262
+ },
4263
+ async unregister(id) {
4264
+ if (!hasBridge("shortcuts"))
4265
+ return;
4266
+ await window.craft.shortcuts.unregister(id);
4267
+ },
4268
+ async unregisterAll() {
4269
+ if (!hasBridge("shortcuts"))
4270
+ return;
4271
+ await window.craft.shortcuts.unregisterAll();
4272
+ },
4273
+ async enable(id) {
4274
+ if (!hasBridge("shortcuts"))
4275
+ return;
4276
+ await window.craft.shortcuts.enable(id);
4277
+ },
4278
+ async disable(id) {
4279
+ if (!hasBridge("shortcuts"))
4280
+ return;
4281
+ await window.craft.shortcuts.disable(id);
4282
+ },
4283
+ async isRegistered(id) {
4284
+ if (!hasBridge("shortcuts"))
4285
+ return false;
4286
+ return await window.craft.shortcuts.isRegistered(id);
4287
+ },
4288
+ async list() {
4289
+ if (!hasBridge("shortcuts"))
4290
+ return [];
4291
+ return await window.craft.shortcuts.list();
4292
+ },
4293
+ on(cb) {
4294
+ return onCraftEvent("craft:shortcut", cb);
4295
+ }
4296
+ };
4297
+ // src/theme.ts
4298
+ var theme = {
4299
+ get() {
4300
+ if (hasBridge("theme")) {
4301
+ try {
4302
+ return window.craft.theme.get();
4303
+ } catch {}
4304
+ }
4305
+ return { appearance: detectWebAppearance() };
4306
+ },
4307
+ onChange(cb) {
4308
+ cb(this.get());
4309
+ if (hasBridge("theme")) {
4310
+ return onCraftEvent("craft:theme", cb);
4311
+ }
4312
+ if (typeof window !== "undefined" && window.matchMedia) {
4313
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
4314
+ const handler = () => cb({ appearance: mq.matches ? "dark" : "light" });
4315
+ mq.addEventListener("change", handler);
4316
+ return () => mq.removeEventListener("change", handler);
4317
+ }
4318
+ return () => {};
4319
+ },
4320
+ async current() {
4321
+ return this.get();
4322
+ }
4323
+ };
4324
+ function detectWebAppearance() {
4325
+ if (typeof window === "undefined" || !window.matchMedia)
4326
+ return "light";
4327
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
4328
+ }
4329
+ // src/drag-out.ts
4330
+ async function dragOut(paths, options = {}) {
4331
+ if (!hasBridge("dragOut")) {
4332
+ throw new Error("dragOut requires a Craft native window");
4333
+ }
4334
+ const arr = Array.isArray(paths) ? paths : [paths];
4335
+ if (arr.length === 0)
4336
+ throw new Error("dragOut: at least one path required");
4337
+ await window.craft.dragOut.start(arr, options);
4338
+ }
4339
+ function isDragOutAvailable() {
4340
+ return hasBridge("dragOut");
4341
+ }
4342
+ // src/deep-link.ts
4343
+ var deepLinks = {
4344
+ onUrl(cb) {
4345
+ if (!hasBridge("deepLink"))
4346
+ return () => {};
4347
+ return onCraftEvent("craft:deepLink", cb);
4348
+ },
4349
+ getInitialUrl() {
4350
+ if (typeof window === "undefined")
4351
+ return null;
4352
+ if (hasBridge("deepLink")) {
4353
+ try {
4354
+ return window.craft.deepLink.getInitialUrl();
4355
+ } catch {
4356
+ return null;
4357
+ }
4358
+ }
4359
+ return window.__craftPendingDeepLink || null;
4360
+ },
4361
+ consumeInitialUrl() {
4362
+ const url = this.getInitialUrl();
4363
+ if (typeof window !== "undefined")
4364
+ window.__craftPendingDeepLink = undefined;
4365
+ return url;
4366
+ },
4367
+ isAvailable() {
4368
+ return hasBridge("deepLink");
4369
+ }
4370
+ };
4371
+ // src/battery.ts
4372
+ var battery = {
4373
+ async isCharging() {
4374
+ if (hasBridge("power"))
4375
+ return await window.craft.power.isCharging();
4376
+ const b = await getWebBatteryManager();
4377
+ return b ? !!b.charging : false;
4378
+ },
4379
+ async isPluggedIn() {
4380
+ if (hasBridge("power"))
4381
+ return await window.craft.power.isPluggedIn();
4382
+ const b = await getWebBatteryManager();
4383
+ return b ? !!b.charging || b.level >= 0.999 : false;
4384
+ },
4385
+ async isLowPowerMode() {
4386
+ if (hasBridge("power"))
4387
+ return await window.craft.power.isLowPowerMode();
4388
+ return false;
4389
+ },
4390
+ async level() {
4391
+ if (hasBridge("power")) {
4392
+ const v = await window.craft.power.batteryLevel();
4393
+ return typeof v === "number" ? v : null;
4394
+ }
4395
+ const b = await getWebBatteryManager();
4396
+ return b ? b.level : null;
4397
+ },
4398
+ async timeRemaining() {
4399
+ if (hasBridge("power")) {
4400
+ const r = await window.craft.power.timeRemaining();
4401
+ return typeof r === "number" ? r : null;
4402
+ }
4403
+ const b = await getWebBatteryManager();
4404
+ if (!b)
4405
+ return null;
4406
+ const sec = b.charging ? b.chargingTime : b.dischargingTime;
4407
+ return Number.isFinite(sec) ? Math.round(sec / 60) : null;
4408
+ },
4409
+ async thermalState() {
4410
+ if (hasBridge("power")) {
4411
+ const s = await window.craft.power.thermalState();
4412
+ return s || "unknown";
4413
+ }
4414
+ return "unknown";
4415
+ },
4416
+ async uptimeSeconds() {
4417
+ if (hasBridge("power"))
4418
+ return await window.craft.power.uptimeSeconds();
4419
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
4420
+ return Math.round(performance.now() / 1000);
4421
+ }
4422
+ return 0;
4423
+ },
4424
+ async preventSleep(reason = "app is busy") {
4425
+ if (hasBridge("power")) {
4426
+ await window.craft.power.preventSleep(reason);
4427
+ return;
4428
+ }
4429
+ if (typeof navigator === "undefined" || !navigator.wakeLock)
4430
+ return;
4431
+ const w = window;
4432
+ if (w.__craftWebWakeLock?.release) {
4433
+ try {
4434
+ await w.__craftWebWakeLock.release();
4435
+ } catch {}
4436
+ w.__craftWebWakeLock = null;
4437
+ }
4438
+ try {
4439
+ const sentinel = await navigator.wakeLock.request("screen");
4440
+ w.__craftWebWakeLock = sentinel;
4441
+ } catch {}
4442
+ },
4443
+ async allowSleep() {
4444
+ if (hasBridge("power")) {
4445
+ await window.craft.power.allowSleep();
4446
+ return;
4447
+ }
4448
+ const s = window.__craftWebWakeLock;
4449
+ if (s && typeof s.release === "function") {
4450
+ try {
4451
+ await s.release();
4452
+ } catch {}
4453
+ window.__craftWebWakeLock = null;
4454
+ }
4455
+ },
4456
+ onSleep(cb) {
4457
+ return onCraftEvent("craft:powerSleep", cb);
4458
+ },
4459
+ onWake(cb) {
4460
+ return onCraftEvent("craft:powerWake", cb);
4461
+ }
4462
+ };
4463
+ async function getWebBatteryManager() {
4464
+ if (typeof navigator === "undefined")
4465
+ return null;
4466
+ const nav = navigator;
4467
+ if (typeof nav.getBattery !== "function")
4468
+ return null;
4469
+ try {
4470
+ return await nav.getBattery();
4471
+ } catch {
4472
+ return null;
4473
+ }
4474
+ }
4475
+ // src/network.ts
4476
+ var network = {
4477
+ async connectionType() {
4478
+ if (hasBridge("network"))
4479
+ return await window.craft.network.connectionType();
4480
+ return webConnectionType();
4481
+ },
4482
+ async wifiSSID() {
4483
+ if (hasBridge("network")) {
4484
+ const v = await window.craft.network.wifiSSID();
4485
+ return v || undefined;
4486
+ }
4487
+ return;
4488
+ },
4489
+ async wifiSignalStrength() {
4490
+ if (hasBridge("network")) {
4491
+ const v = await window.craft.network.wifiSignalStrength();
4492
+ return typeof v === "number" ? v : undefined;
4493
+ }
4494
+ return;
4495
+ },
4496
+ async ipAddress() {
4497
+ if (hasBridge("network"))
4498
+ return await window.craft.network.ipAddress();
4499
+ return "";
4500
+ },
4501
+ async macAddress() {
4502
+ if (hasBridge("network"))
4503
+ return await window.craft.network.macAddress();
4504
+ return "";
4505
+ },
4506
+ async interfaces() {
4507
+ if (hasBridge("network"))
4508
+ return await window.craft.network.interfaces();
4509
+ return [];
4510
+ },
4511
+ async isVPNConnected() {
4512
+ if (hasBridge("network"))
4513
+ return await window.craft.network.isVPNConnected();
4514
+ return false;
4515
+ },
4516
+ async proxySettings() {
4517
+ if (hasBridge("network")) {
4518
+ const r = await window.craft.network.proxySettings();
4519
+ return r || {};
4520
+ }
4521
+ return {};
4522
+ },
4523
+ async openPreferences() {
4524
+ if (hasBridge("network"))
4525
+ await window.craft.network.openPreferences();
4526
+ },
4527
+ onChange(cb) {
4528
+ if (hasBridge("network")) {
4529
+ return onCraftEvent("craft:networkChange", cb);
4530
+ }
4531
+ if (typeof window === "undefined")
4532
+ return () => {};
4533
+ const onlineH = () => cb({ type: webConnectionType(), online: true });
4534
+ const offlineH = () => cb({ type: "none", online: false });
4535
+ window.addEventListener("online", onlineH);
4536
+ window.addEventListener("offline", offlineH);
4537
+ return () => {
4538
+ window.removeEventListener("online", onlineH);
4539
+ window.removeEventListener("offline", offlineH);
4540
+ };
4541
+ }
4542
+ };
4543
+ function webConnectionType() {
4544
+ if (typeof navigator === "undefined")
4545
+ return "unknown";
4546
+ if (navigator.onLine === false)
4547
+ return "none";
4548
+ const conn = navigator.connection;
4549
+ if (!conn)
4550
+ return "unknown";
4551
+ const t = String(conn.type || conn.effectiveType || "unknown").toLowerCase();
4552
+ if (t === "wifi" || t === "cellular" || t === "ethernet" || t === "bluetooth" || t === "none")
4553
+ return t;
4554
+ return "unknown";
4555
+ }
4556
+ // src/updater.ts
4557
+ var updater = {
4558
+ async checkForUpdates() {
4559
+ if (!hasBridge("updater"))
4560
+ return;
4561
+ await window.craft.updater.checkForUpdates();
4562
+ },
4563
+ async checkInBackground() {
4564
+ if (!hasBridge("updater"))
4565
+ return;
4566
+ await window.craft.updater.checkInBackground();
4567
+ },
4568
+ async setAutomaticChecks(on) {
4569
+ if (!hasBridge("updater"))
4570
+ return;
4571
+ await window.craft.updater.setAutomaticChecks(on);
4572
+ },
4573
+ async setCheckInterval(seconds) {
4574
+ if (!hasBridge("updater"))
4575
+ return;
4576
+ if (!Number.isFinite(seconds)) {
4577
+ throw new Error("setCheckInterval: must be a finite number");
4578
+ }
4579
+ const safe = seconds <= 0 ? 0 : Math.max(60, Math.round(seconds));
4580
+ await window.craft.updater.setCheckInterval(safe);
4581
+ },
4582
+ async setFeedURL(url) {
4583
+ if (!hasBridge("updater"))
4584
+ return;
4585
+ await window.craft.updater.setFeedURL(url);
4586
+ },
4587
+ async getLastUpdateCheckDate() {
4588
+ if (!hasBridge("updater"))
4589
+ return null;
4590
+ const v = await window.craft.updater.getLastUpdateCheckDate();
4591
+ return v || null;
4592
+ },
4593
+ async getUpdateInfo() {
4594
+ if (!hasBridge("updater"))
4595
+ return null;
4596
+ const v = await window.craft.updater.getUpdateInfo();
4597
+ if (!v || typeof v.version !== "string" || v.version.length === 0)
4598
+ return null;
4599
+ return v;
4600
+ },
4601
+ onAvailable(cb) {
4602
+ return onCraftEvent("craft:updateAvailable", cb);
4603
+ },
4604
+ onDownloaded(cb) {
4605
+ return onCraftEvent("craft:updateDownloaded", cb);
4606
+ },
4607
+ async verifySignature({ payload, signatureB64, publicKeyB64 }) {
4608
+ const data = toArrayBufferBytes(payload);
4609
+ let publicKey;
4610
+ try {
4611
+ publicKey = await crypto.subtle.importKey("raw", base64ToBytes2(publicKeyB64), { name: "Ed25519" }, false, ["verify"]);
4612
+ } catch {
4613
+ return false;
4614
+ }
4615
+ try {
4616
+ return await crypto.subtle.verify("Ed25519", publicKey, base64ToBytes2(signatureB64), data);
4617
+ } catch {
4618
+ return false;
4619
+ }
4620
+ },
4621
+ async verifyDownload({ url, signatureB64, publicKeyB64, fetchInit }) {
4622
+ let response;
4623
+ try {
4624
+ response = await fetch(url, fetchInit);
4625
+ } catch {
4626
+ return { ok: false, reason: "fetch-failed" };
4627
+ }
4628
+ if (!response.ok) {
4629
+ return { ok: false, reason: "http-error", status: response.status };
4630
+ }
4631
+ const buffer = new Uint8Array(await response.arrayBuffer());
4632
+ let publicKey;
4633
+ try {
4634
+ publicKey = await crypto.subtle.importKey("raw", base64ToBytes2(publicKeyB64), { name: "Ed25519" }, false, ["verify"]);
4635
+ } catch {
4636
+ return { ok: false, reason: "bad-key" };
4637
+ }
4638
+ let valid = false;
4639
+ try {
4640
+ valid = await crypto.subtle.verify("Ed25519", publicKey, base64ToBytes2(signatureB64), toArrayBufferBytes(buffer));
4641
+ } catch {
4642
+ valid = false;
4643
+ }
4644
+ if (!valid)
4645
+ return { ok: false, reason: "bad-signature" };
4646
+ return { ok: true, payload: buffer };
4647
+ }
4648
+ };
4649
+ function toArrayBufferBytes(input) {
4650
+ if (input instanceof ArrayBuffer)
4651
+ return new Uint8Array(input);
4652
+ const out = new ArrayBuffer(input.byteLength);
4653
+ const view = new Uint8Array(out);
4654
+ view.set(input);
4655
+ return view;
4656
+ }
4657
+ function base64ToBytes2(b64) {
4658
+ if (typeof atob === "function") {
4659
+ const bin = atob(b64);
4660
+ const buffer = new ArrayBuffer(bin.length);
4661
+ const out = new Uint8Array(buffer);
4662
+ for (let i = 0;i < bin.length; i++)
4663
+ out[i] = bin.charCodeAt(i);
4664
+ return out;
4665
+ }
4666
+ const node = Buffer.from(b64, "base64");
4667
+ const buf = new ArrayBuffer(node.length);
4668
+ new Uint8Array(buf).set(node);
4669
+ return new Uint8Array(buf);
4670
+ }
4671
+ // src/window-events.ts
4672
+ var windowEvents = {
4673
+ onFocus(cb) {
4674
+ if (hasBridge("window"))
4675
+ return onCraftEvent("craft:window:focus", () => cb());
4676
+ return webEvent("focus", cb);
4677
+ },
4678
+ onBlur(cb) {
4679
+ if (hasBridge("window"))
4680
+ return onCraftEvent("craft:window:blur", () => cb());
4681
+ return webEvent("blur", cb);
4682
+ },
4683
+ onResize(cb) {
4684
+ if (hasBridge("window"))
4685
+ return onCraftEvent("craft:window:resize", cb);
4686
+ if (typeof window === "undefined")
4687
+ return () => {};
4688
+ const h = () => cb({ width: window.innerWidth, height: window.innerHeight });
4689
+ window.addEventListener("resize", h);
4690
+ return () => window.removeEventListener("resize", h);
4691
+ },
4692
+ onMove(cb) {
4693
+ if (hasBridge("window"))
4694
+ return onCraftEvent("craft:window:move", cb);
4695
+ return () => {};
4696
+ },
4697
+ onClose(cb) {
4698
+ if (hasBridge("window"))
4699
+ return onCraftEvent("craft:window:close", () => cb());
4700
+ return webEvent("beforeunload", cb);
4701
+ },
4702
+ onMinimize(cb) {
4703
+ if (hasBridge("window"))
4704
+ return onCraftEvent("craft:window:minimize", () => cb());
4705
+ if (typeof document === "undefined")
4706
+ return () => {};
4707
+ const h = () => {
4708
+ if (document.visibilityState === "hidden")
4709
+ cb();
4710
+ };
4711
+ document.addEventListener("visibilitychange", h);
4712
+ return () => document.removeEventListener("visibilitychange", h);
4713
+ },
4714
+ onRestore(cb) {
4715
+ if (hasBridge("window"))
4716
+ return onCraftEvent("craft:window:restore", () => cb());
4717
+ if (typeof document === "undefined")
4718
+ return () => {};
4719
+ const h = () => {
4720
+ if (document.visibilityState === "visible")
4721
+ cb();
4722
+ };
4723
+ document.addEventListener("visibilitychange", h);
4724
+ return () => document.removeEventListener("visibilitychange", h);
4725
+ }
4726
+ };
4727
+ function webEvent(name, cb) {
4728
+ if (typeof window === "undefined")
4729
+ return () => {};
4730
+ const h = () => cb();
4731
+ window.addEventListener(name, h);
4732
+ return () => window.removeEventListener(name, h);
4733
+ }
4734
+ // src/app-info.ts
4735
+ var DEFAULT_INFO = { name: "", version: "0.0.0" };
4736
+ var app = {
4737
+ async hideDockIcon() {
4738
+ if (hasBridge("app"))
4739
+ await window.craft.app.hideDockIcon();
4740
+ },
4741
+ async showDockIcon() {
4742
+ if (hasBridge("app"))
4743
+ await window.craft.app.showDockIcon();
4744
+ },
4745
+ async quit() {
4746
+ if (hasBridge("app"))
4747
+ await window.craft.app.quit();
4748
+ },
4749
+ async getInfo() {
4750
+ if (!hasBridge("app"))
4751
+ return DEFAULT_INFO;
4752
+ const r = await window.craft.app.getInfo();
4753
+ return { ...DEFAULT_INFO, ...r || {} };
4754
+ },
4755
+ async notify(options) {
4756
+ if (!options.title)
4757
+ throw new Error("notify: title is required");
4758
+ if (hasBridge("app"))
4759
+ await window.craft.app.notify(options);
4760
+ },
4761
+ async setBadge(count) {
4762
+ if (hasBridge("app"))
4763
+ await window.craft.app.setBadge(count);
4764
+ },
4765
+ async bounce(type = "informational") {
4766
+ if (hasBridge("app"))
4767
+ await window.craft.app.bounce(type);
4768
+ }
4769
+ };
4770
+ // src/menu.ts
4771
+ var menu = {
4772
+ async set(items) {
4773
+ if (hasBridge("menu"))
4774
+ await window.craft.menu.set(items);
4775
+ },
4776
+ async setDock(items) {
4777
+ if (hasBridge("menu"))
4778
+ await window.craft.menu.setDock(items);
4779
+ },
4780
+ async addItem(parent, item) {
4781
+ if (hasBridge("menu"))
4782
+ await window.craft.menu.addItem(parent, item);
4783
+ },
4784
+ async removeItem(id) {
4785
+ if (hasBridge("menu"))
4786
+ await window.craft.menu.removeItem(id);
4787
+ },
4788
+ async enableItem(id) {
4789
+ if (hasBridge("menu"))
4790
+ await window.craft.menu.enableItem(id);
4791
+ },
4792
+ async disableItem(id) {
4793
+ if (hasBridge("menu"))
4794
+ await window.craft.menu.disableItem(id);
4795
+ },
4796
+ async checkItem(id) {
4797
+ if (hasBridge("menu"))
4798
+ await window.craft.menu.checkItem(id);
4799
+ },
4800
+ async uncheckItem(id) {
4801
+ if (hasBridge("menu"))
4802
+ await window.craft.menu.uncheckItem(id);
4803
+ },
4804
+ async setItemLabel(id, lbl) {
4805
+ if (hasBridge("menu"))
4806
+ await window.craft.menu.setItemLabel(id, lbl);
4807
+ },
4808
+ async clearDock() {
4809
+ if (hasBridge("menu"))
4810
+ await window.craft.menu.clearDock();
4811
+ },
4812
+ onAction(cb) {
4813
+ return onCraftEvent("craft:menu:action", cb);
4814
+ }
4815
+ };
4816
+ // src/system.ts
4817
+ var system = {
4818
+ accentColor: () => bridgeOr("system", "accentColor", () => ""),
4819
+ highlightColor: () => bridgeOr("system", "highlightColor", () => ""),
4820
+ language: () => bridgeOr("system", "language", () => {
4821
+ if (typeof navigator === "undefined")
4822
+ return "";
4823
+ return navigator.language?.split("-")[0] || "";
4824
+ }),
4825
+ locale: () => bridgeOr("system", "locale", () => {
4826
+ if (typeof navigator === "undefined")
4827
+ return "";
4828
+ return navigator.language || "";
4829
+ }),
4830
+ timezone: () => bridgeOr("system", "timezone", () => {
4831
+ try {
4832
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "";
4833
+ } catch {
4834
+ return "";
4835
+ }
4836
+ }),
4837
+ is24HourTime: () => bridgeOr("system", "is24HourTime", () => {
4838
+ try {
4839
+ const opts = new Intl.DateTimeFormat([], { hour: "numeric" }).resolvedOptions();
4840
+ return opts.hourCycle === "h23" || opts.hourCycle === "h24";
4841
+ } catch {
4842
+ return false;
4843
+ }
4844
+ }),
4845
+ reduceMotion: () => bridgeOr("system", "reduceMotion", () => mediaMatches("(prefers-reduced-motion: reduce)")),
4846
+ reduceTransparency: () => bridgeOr("system", "reduceTransparency", () => mediaMatches("(prefers-reduced-transparency: reduce)")),
4847
+ increaseContrast: () => bridgeOr("system", "increaseContrast", () => mediaMatches("(prefers-contrast: more)")),
4848
+ systemVersion: () => bridgeOr("system", "systemVersion", () => ""),
4849
+ hostname: () => bridgeOr("system", "hostname", () => ""),
4850
+ username: () => bridgeOr("system", "username", () => ""),
4851
+ async openPreferences() {
4852
+ if (hasBridge("system"))
4853
+ await window.craft.system.openPreferences();
4854
+ }
4855
+ };
4856
+ async function bridgeOr(ns, method, fallback) {
4857
+ if (hasBridge(ns))
4858
+ return await window.craft[ns][method]();
4859
+ return await fallback();
4860
+ }
4861
+ function mediaMatches(query) {
4862
+ if (typeof window === "undefined" || !window.matchMedia)
4863
+ return false;
4864
+ return window.matchMedia(query).matches;
4865
+ }
4866
+ // src/screen.ts
4867
+ var screen = {
4868
+ async getDisplays() {
4869
+ if (hasBridge("screen"))
4870
+ return await window.craft.screen.getDisplays();
4871
+ return webDisplays();
4872
+ },
4873
+ async getPrimary() {
4874
+ if (hasBridge("screen")) {
4875
+ const r = await window.craft.screen.getPrimary();
4876
+ return r && typeof r.width === "number" ? r : null;
4877
+ }
4878
+ return webDisplays()[0] ?? null;
4879
+ },
4880
+ onChange(cb) {
4881
+ if (hasBridge("screen"))
4882
+ return onCraftEvent("craft:screen:change", () => cb());
4883
+ if (typeof window === "undefined")
4884
+ return () => {};
4885
+ const h = () => cb();
4886
+ window.addEventListener("resize", h);
4887
+ return () => window.removeEventListener("resize", h);
4888
+ }
4889
+ };
4890
+ function webDisplays() {
4891
+ if (typeof window === "undefined" || !window.screen)
4892
+ return [];
4893
+ const s = window.screen;
4894
+ return [{
4895
+ id: 0,
4896
+ x: s.left ?? 0,
4897
+ y: s.top ?? 0,
4898
+ width: s.width || 0,
4899
+ height: s.height || 0,
4900
+ workX: s.availLeft ?? 0,
4901
+ workY: s.availTop ?? 0,
4902
+ workWidth: s.availWidth ?? s.width ?? 0,
4903
+ workHeight: s.availHeight ?? s.height ?? 0,
4904
+ scaleFactor: window.devicePixelRatio || 1
4905
+ }];
4906
+ }
4907
+ // src/keychain.ts
4908
+ var keychain = {
4909
+ async set(service, account, password) {
4910
+ if (!service)
4911
+ throw new Error("keychain.set: service is required");
4912
+ if (!account)
4913
+ throw new Error("keychain.set: account is required");
4914
+ await requireBridge("keychain").set(service, account, password);
4915
+ },
4916
+ async get(service, account) {
4917
+ if (!service)
4918
+ throw new Error("keychain.get: service is required");
4919
+ if (!account)
4920
+ throw new Error("keychain.get: account is required");
4921
+ const v = await requireBridge("keychain").get(service, account);
4922
+ return typeof v === "string" ? v : null;
4923
+ },
4924
+ async delete(service, account) {
4925
+ if (!service)
4926
+ throw new Error("keychain.delete: service is required");
4927
+ if (!account)
4928
+ throw new Error("keychain.delete: account is required");
4929
+ await requireBridge("keychain").delete(service, account);
4930
+ },
4931
+ async has(service, account) {
4932
+ if (!service)
4933
+ throw new Error("keychain.has: service is required");
4934
+ if (!account)
4935
+ throw new Error("keychain.has: account is required");
4936
+ return await requireBridge("keychain").has(service, account);
4937
+ }
4938
+ };
4939
+ // src/permissions.ts
4940
+ var permissions = {
4941
+ async check(name) {
4942
+ if (hasBridge("permissions"))
4943
+ return await window.craft.permissions.check(name);
4944
+ return await webCheck(name);
4945
+ },
4946
+ async request(name) {
4947
+ if (hasBridge("permissions"))
4948
+ return await window.craft.permissions.request(name);
4949
+ return await webRequest(name);
4950
+ },
4951
+ async openSettings(name) {
4952
+ if (hasBridge("permissions"))
4953
+ await window.craft.permissions.openSettings(name);
4954
+ }
4955
+ };
4956
+ async function webCheck(name) {
4957
+ if (typeof navigator === "undefined" || !navigator.permissions?.query)
4958
+ return "not-supported";
4959
+ try {
4960
+ const result = await navigator.permissions.query({ name });
4961
+ return mapWebState(result.state);
4962
+ } catch {
4963
+ return "not-supported";
4964
+ }
4965
+ }
4966
+ async function webRequest(name) {
4967
+ if (name === "notifications" && typeof window !== "undefined" && "Notification" in window) {
4968
+ const r = await window.Notification.requestPermission();
4969
+ return r === "granted" ? "granted" : r === "denied" ? "denied" : "undetermined";
4970
+ }
4971
+ return await webCheck(name);
4972
+ }
4973
+ function mapWebState(s) {
4974
+ if (s === "granted")
4975
+ return "granted";
4976
+ if (s === "denied")
4977
+ return "denied";
4978
+ if (s === "prompt")
4979
+ return "undetermined";
4980
+ return "undetermined";
4981
+ }
4982
+ // src/printing.ts
4983
+ var printing = {
4984
+ async print() {
4985
+ if (hasBridge("printing")) {
4986
+ await window.craft.printing.print();
4987
+ return;
4988
+ }
4989
+ if (typeof window !== "undefined" && typeof window.print === "function") {
4990
+ window.print();
4991
+ }
4992
+ },
4993
+ async printToPDF(path) {
4994
+ if (!path)
4995
+ throw new Error("printToPDF: path is required");
4996
+ const isPosixAbs = path.startsWith("/");
4997
+ const isWinAbs = /^[a-zA-Z]:[\\/]/.test(path) || path.startsWith("\\\\");
4998
+ if (!isPosixAbs && !isWinAbs) {
4999
+ throw new Error("printToPDF: path must be absolute");
5000
+ }
5001
+ const r = await requireBridge("printing").printToPDF(path);
5002
+ return { ok: !!(r && r.ok), path: r?.path };
5003
+ }
5004
+ };
5005
+ // src/native-autolaunch.ts
5006
+ var nativeAutoLaunch = {
5007
+ async enable() {
5008
+ if (!hasBridge("autoLaunch"))
5009
+ return false;
5010
+ return await window.craft.autoLaunch.enable();
5011
+ },
5012
+ async disable() {
5013
+ if (!hasBridge("autoLaunch"))
5014
+ return false;
5015
+ return await window.craft.autoLaunch.disable();
5016
+ },
5017
+ async isEnabled() {
5018
+ if (!hasBridge("autoLaunch"))
5019
+ return false;
5020
+ return await window.craft.autoLaunch.isEnabled();
5021
+ }
5022
+ };
5023
+ // src/touchbar.ts
5024
+ var touchbar = {
5025
+ async addItem(item) {
5026
+ if (hasBridge("touchbar"))
5027
+ await window.craft.touchbar.addItem(item);
5028
+ },
5029
+ async removeItem(id) {
5030
+ if (hasBridge("touchbar"))
5031
+ await window.craft.touchbar.removeItem(id);
5032
+ },
5033
+ async updateItem(id, props) {
5034
+ if (hasBridge("touchbar"))
5035
+ await window.craft.touchbar.updateItem(id, props);
5036
+ },
5037
+ async setLabel(id, label) {
5038
+ if (hasBridge("touchbar"))
5039
+ await window.craft.touchbar.setLabel(id, label);
5040
+ },
5041
+ async setIcon(id, icon) {
5042
+ if (hasBridge("touchbar"))
5043
+ await window.craft.touchbar.setIcon(id, icon);
5044
+ },
5045
+ async setEnabled(id, enabled) {
5046
+ if (hasBridge("touchbar"))
5047
+ await window.craft.touchbar.setEnabled(id, enabled);
5048
+ },
5049
+ async setSliderValue(id, value) {
5050
+ if (hasBridge("touchbar"))
5051
+ await window.craft.touchbar.setSliderValue(id, value);
5052
+ },
5053
+ async clear() {
5054
+ if (hasBridge("touchbar"))
5055
+ await window.craft.touchbar.clear();
5056
+ },
5057
+ async show() {
5058
+ if (hasBridge("touchbar"))
5059
+ await window.craft.touchbar.show();
5060
+ },
5061
+ async hide() {
5062
+ if (hasBridge("touchbar"))
5063
+ await window.craft.touchbar.hide();
5064
+ },
5065
+ onAction(cb) {
5066
+ return onCraftEvent("craft:touchbar:action", cb);
5067
+ }
5068
+ };
5069
+ // src/bluetooth.ts
5070
+ var HEX_RE = /^[\da-f]*$/i;
5071
+ function assertHex(label, hex) {
5072
+ if (typeof hex !== "string" || !HEX_RE.test(hex) || hex.length % 2 !== 0) {
5073
+ throw new Error(`${label}: must be a hex string with even length, got ${JSON.stringify(hex)}`);
5074
+ }
5075
+ }
5076
+ var bluetooth = {
5077
+ async isEnabled() {
5078
+ return hasBridge("bluetooth") ? await window.craft.bluetooth.isEnabled() : false;
5079
+ },
5080
+ async powerState() {
5081
+ return hasBridge("bluetooth") ? await window.craft.bluetooth.powerState() : "unknown";
5082
+ },
5083
+ async connectedDevices() {
5084
+ return hasBridge("bluetooth") ? await window.craft.bluetooth.connectedDevices() : [];
5085
+ },
5086
+ async pairedDevices() {
5087
+ return hasBridge("bluetooth") ? await window.craft.bluetooth.pairedDevices() : [];
5088
+ },
5089
+ async startDiscovery() {
5090
+ if (hasBridge("bluetooth"))
5091
+ await window.craft.bluetooth.startDiscovery();
5092
+ },
5093
+ async stopDiscovery() {
5094
+ if (hasBridge("bluetooth"))
5095
+ await window.craft.bluetooth.stopDiscovery();
5096
+ },
5097
+ async isDiscovering() {
5098
+ return hasBridge("bluetooth") ? await window.craft.bluetooth.isDiscovering() : false;
5099
+ },
5100
+ async connect(id) {
5101
+ if (hasBridge("bluetooth"))
5102
+ await window.craft.bluetooth.connect(id);
5103
+ },
5104
+ async disconnect(id) {
5105
+ if (hasBridge("bluetooth"))
5106
+ await window.craft.bluetooth.disconnect(id);
5107
+ },
5108
+ async openPreferences() {
5109
+ if (hasBridge("bluetooth"))
5110
+ await window.craft.bluetooth.openPreferences();
5111
+ },
5112
+ async discoverServices(deviceId) {
5113
+ if (!deviceId)
5114
+ throw new Error("bluetooth.discoverServices: deviceId is required");
5115
+ if (!hasBridge("bluetooth"))
5116
+ return [];
5117
+ const r = await window.craft.bluetooth.discoverServices(deviceId);
5118
+ return Array.isArray(r) ? r : [];
5119
+ },
5120
+ async discoverCharacteristics(deviceId, serviceUuid) {
5121
+ if (!deviceId || !serviceUuid)
5122
+ throw new Error("bluetooth.discoverCharacteristics: deviceId and serviceUuid are required");
5123
+ if (!hasBridge("bluetooth"))
5124
+ return [];
5125
+ const r = await window.craft.bluetooth.discoverCharacteristics(deviceId, serviceUuid);
5126
+ return Array.isArray(r) ? r : [];
5127
+ },
5128
+ async readCharacteristic(deviceId, serviceUuid, characteristicUuid) {
5129
+ if (!deviceId || !serviceUuid || !characteristicUuid) {
5130
+ throw new Error("bluetooth.readCharacteristic: deviceId, serviceUuid, characteristicUuid are required");
5131
+ }
5132
+ if (!hasBridge("bluetooth"))
5133
+ return { ok: false, reason: "bridge unavailable" };
5134
+ return await window.craft.bluetooth.readCharacteristic(deviceId, serviceUuid, characteristicUuid);
5135
+ },
5136
+ async writeCharacteristic(deviceId, serviceUuid, characteristicUuid, valueHex, mode = "with-response") {
5137
+ if (!deviceId || !serviceUuid || !characteristicUuid) {
5138
+ throw new Error("bluetooth.writeCharacteristic: deviceId, serviceUuid, characteristicUuid are required");
5139
+ }
5140
+ assertHex("bluetooth.writeCharacteristic.valueHex", valueHex);
5141
+ if (!hasBridge("bluetooth"))
5142
+ return { ok: false, reason: "bridge unavailable" };
5143
+ return await window.craft.bluetooth.writeCharacteristic(deviceId, serviceUuid, characteristicUuid, valueHex, mode);
5144
+ },
5145
+ async setCharacteristicNotify(deviceId, serviceUuid, characteristicUuid, on) {
5146
+ if (!deviceId || !serviceUuid || !characteristicUuid) {
5147
+ throw new Error("bluetooth.setCharacteristicNotify: deviceId, serviceUuid, characteristicUuid are required");
5148
+ }
5149
+ if (!hasBridge("bluetooth"))
5150
+ return { ok: false, reason: "bridge unavailable" };
5151
+ return await window.craft.bluetooth.setCharacteristicNotify(deviceId, serviceUuid, characteristicUuid, on);
5152
+ },
5153
+ onDeviceFound(cb) {
5154
+ return onCraftEvent("craft:bluetooth:deviceFound", cb);
5155
+ },
5156
+ onDeviceConnected(cb) {
5157
+ return onCraftEvent("craft:bluetooth:deviceConnected", cb);
5158
+ },
5159
+ onDeviceDisconnected(cb) {
5160
+ return onCraftEvent("craft:bluetooth:deviceDisconnected", cb);
5161
+ },
5162
+ onCharacteristicValue(cb) {
5163
+ return onCraftEvent("craft:bluetooth:characteristicValue", cb);
5164
+ }
5165
+ };
5166
+ // src/speech.ts
5167
+ var speech = {
5168
+ async speak(text, options) {
5169
+ if (!text)
5170
+ throw new Error("speech.speak: text is required");
5171
+ if (hasBridge("speech")) {
5172
+ await window.craft.speech.speak(text, options);
5173
+ return;
5174
+ }
5175
+ if (typeof window !== "undefined" && window.speechSynthesis) {
5176
+ const u = new window.SpeechSynthesisUtterance(text);
5177
+ if (options) {
5178
+ if (options.rate != null)
5179
+ u.rate = options.rate;
5180
+ if (options.pitch != null)
5181
+ u.pitch = options.pitch;
5182
+ if (options.volume != null)
5183
+ u.volume = options.volume;
5184
+ if (options.voice) {
5185
+ const voices = window.speechSynthesis.getVoices();
5186
+ const match = voices.find((v) => v.voiceURI === options.voice || v.name === options.voice || v.lang === options.voice);
5187
+ if (match)
5188
+ u.voice = match;
5189
+ }
5190
+ }
5191
+ window.speechSynthesis.speak(u);
5192
+ }
5193
+ },
5194
+ async stop() {
5195
+ if (hasBridge("speech")) {
5196
+ await window.craft.speech.stop();
5197
+ return;
5198
+ }
5199
+ if (typeof window !== "undefined" && window.speechSynthesis) {
5200
+ window.speechSynthesis.cancel();
5201
+ }
5202
+ },
5203
+ async pause() {
5204
+ if (hasBridge("speech")) {
5205
+ await window.craft.speech.pause();
5206
+ return;
5207
+ }
5208
+ if (typeof window !== "undefined" && window.speechSynthesis) {
5209
+ window.speechSynthesis.pause();
5210
+ }
5211
+ },
5212
+ async resume() {
5213
+ if (hasBridge("speech")) {
5214
+ await window.craft.speech.resume();
5215
+ return;
5216
+ }
5217
+ if (typeof window !== "undefined" && window.speechSynthesis) {
5218
+ window.speechSynthesis.resume();
5219
+ }
5220
+ },
5221
+ async isSpeaking() {
5222
+ if (hasBridge("speech"))
5223
+ return await window.craft.speech.isSpeaking();
5224
+ if (typeof window !== "undefined" && window.speechSynthesis) {
5225
+ return !!window.speechSynthesis.speaking;
5226
+ }
5227
+ return false;
5228
+ },
5229
+ async getVoices() {
5230
+ if (hasBridge("speech"))
5231
+ return await window.craft.speech.getVoices();
5232
+ if (typeof window !== "undefined" && window.speechSynthesis) {
5233
+ const raw = window.speechSynthesis.getVoices();
5234
+ return raw.map((v) => ({
5235
+ id: v.voiceURI || v.name,
5236
+ name: v.name,
5237
+ language: v.lang || "",
5238
+ quality: "default"
5239
+ }));
5240
+ }
5241
+ return [];
5242
+ }
5243
+ };
5244
+ // src/crash-reporter.ts
5245
+ var jsQueue = [];
5246
+ var jsEnabled = true;
5247
+ var jsUserId;
5248
+ var jsAppVersion;
5249
+ var crashReporter = {
5250
+ async report(entry) {
5251
+ if (hasBridge("crashReporter")) {
5252
+ await window.craft.crashReporter.report(entry);
5253
+ return;
5254
+ }
5255
+ if (!jsEnabled)
5256
+ return;
5257
+ const normalized = entry instanceof Error ? {
5258
+ timestamp: Date.now(),
5259
+ severity: "error",
5260
+ message: entry.message,
5261
+ source: "js",
5262
+ stack: entry.stack || "",
5263
+ userId: jsUserId,
5264
+ appVersion: jsAppVersion
5265
+ } : {
5266
+ timestamp: Date.now(),
5267
+ severity: entry.severity || "error",
5268
+ message: entry.message || "",
5269
+ source: entry.source || "js",
5270
+ stack: entry.stack || "",
5271
+ userId: jsUserId,
5272
+ appVersion: jsAppVersion
5273
+ };
5274
+ if (jsQueue.length >= 64)
5275
+ jsQueue.shift();
5276
+ jsQueue.push(normalized);
5277
+ },
5278
+ async flush() {
5279
+ if (hasBridge("crashReporter"))
5280
+ return await window.craft.crashReporter.flush();
5281
+ return [...jsQueue];
5282
+ },
5283
+ async clear() {
5284
+ if (hasBridge("crashReporter")) {
5285
+ await window.craft.crashReporter.clear();
5286
+ return;
5287
+ }
5288
+ jsQueue.length = 0;
5289
+ },
5290
+ async setEnabled(on) {
5291
+ if (hasBridge("crashReporter")) {
5292
+ await window.craft.crashReporter.setEnabled(on);
5293
+ return;
5294
+ }
5295
+ jsEnabled = on;
5296
+ },
5297
+ async isEnabled() {
5298
+ if (hasBridge("crashReporter"))
5299
+ return await window.craft.crashReporter.isEnabled();
5300
+ return jsEnabled;
5301
+ },
5302
+ async setUser(id) {
5303
+ if (hasBridge("crashReporter")) {
5304
+ await window.craft.crashReporter.setUser(id);
5305
+ return;
5306
+ }
5307
+ jsUserId = id || undefined;
5308
+ },
5309
+ async setAppVersion(version) {
5310
+ if (hasBridge("crashReporter")) {
5311
+ await window.craft.crashReporter.setAppVersion(version);
5312
+ return;
5313
+ }
5314
+ jsAppVersion = version || undefined;
5315
+ },
5316
+ attachGlobalHandlers() {
5317
+ if (hasBridge("crashReporter") && window.craft.crashReporter.attachGlobalHandlers) {
5318
+ return window.craft.crashReporter.attachGlobalHandlers();
5319
+ }
5320
+ if (typeof window === "undefined")
5321
+ return () => {};
5322
+ const errorH = (e) => {
5323
+ crashReporter.report({
5324
+ severity: "error",
5325
+ message: e.message,
5326
+ source: "js",
5327
+ stack: e.error?.stack || `${e.message}
5328
+ at ${e.filename}:${e.lineno}:${e.colno}`
5329
+ }).catch(() => {});
5330
+ };
5331
+ const rejectH = (e) => {
5332
+ const r = e.reason;
5333
+ crashReporter.report({
5334
+ severity: "error",
5335
+ message: r?.message || String(r),
5336
+ source: "js",
5337
+ stack: r?.stack || ""
5338
+ }).catch(() => {});
5339
+ };
5340
+ window.addEventListener("error", errorH);
5341
+ window.addEventListener("unhandledrejection", rejectH);
5342
+ return () => {
5343
+ window.removeEventListener("error", errorH);
5344
+ window.removeEventListener("unhandledrejection", rejectH);
5345
+ };
5346
+ },
5347
+ forwardTo(options) {
5348
+ return startForwarder(options);
5349
+ }
5350
+ };
5351
+ var DEFAULT_PERSIST_KEY = "craft:crashReporter:pending";
5352
+ var EMAIL_RE = /[\w.+-]+@[\w-]+\.[\w.-]+/g;
5353
+ var IPV4_RE = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
5354
+ var HOME_PATH_RE = /\/(?:Users|home)\/[^\s/'"`]+/g;
5355
+ function redactPII(entry) {
5356
+ const scrub = (s) => s.replace(EMAIL_RE, "<email>").replace(IPV4_RE, "<ip>").replace(HOME_PATH_RE, "/<home>");
5357
+ return {
5358
+ ...entry,
5359
+ message: scrub(entry.message),
5360
+ stack: scrub(entry.stack)
5361
+ };
5362
+ }
5363
+ async function signPayload(secret, body) {
5364
+ const enc = new TextEncoder;
5365
+ const key = await crypto.subtle.importKey("raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
5366
+ const sig = await crypto.subtle.sign("HMAC", key, enc.encode(body));
5367
+ return [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, "0")).join("");
5368
+ }
5369
+ function loadPersisted(key) {
5370
+ if (!key || typeof localStorage === "undefined")
5371
+ return [];
5372
+ try {
5373
+ const raw = localStorage.getItem(key);
5374
+ if (!raw)
5375
+ return [];
5376
+ const parsed = JSON.parse(raw);
5377
+ return Array.isArray(parsed) ? parsed : [];
5378
+ } catch {
5379
+ return [];
5380
+ }
5381
+ }
5382
+ function savePersisted(key, entries) {
5383
+ if (!key || typeof localStorage === "undefined")
5384
+ return;
5385
+ try {
5386
+ if (entries.length === 0)
5387
+ localStorage.removeItem(key);
5388
+ else
5389
+ localStorage.setItem(key, JSON.stringify(entries));
5390
+ } catch {}
5391
+ }
5392
+ function startForwarder(options) {
5393
+ const {
5394
+ endpoint,
5395
+ intervalMs = 60000,
5396
+ signingSecret,
5397
+ redact = true,
5398
+ headers = {},
5399
+ maxRetries = 5,
5400
+ persistKey
5401
+ } = options;
5402
+ const storageKey = persistKey === null ? null : persistKey ?? DEFAULT_PERSIST_KEY;
5403
+ let pending = loadPersisted(storageKey);
5404
+ let stopped = false;
5405
+ let timer = null;
5406
+ let inFlight = null;
5407
+ const redactor = typeof redact === "function" ? redact : redact === false ? (e) => e : redactPII;
5408
+ async function postBatch(batch) {
5409
+ const body = JSON.stringify({ entries: batch });
5410
+ const requestHeaders = {
5411
+ "Content-Type": "application/json",
5412
+ ...headers
5413
+ };
5414
+ if (signingSecret) {
5415
+ requestHeaders["X-Craft-Signature"] = await signPayload(signingSecret, body);
5416
+ }
5417
+ let attempt = 0;
5418
+ let delay2 = 1000;
5419
+ while (!stopped) {
5420
+ try {
5421
+ const res = await fetch(endpoint, { method: "POST", headers: requestHeaders, body });
5422
+ if (res.ok)
5423
+ return;
5424
+ if (res.status >= 400 && res.status < 500)
5425
+ return;
5426
+ throw new Error(`HTTP ${res.status}`);
5427
+ } catch (err) {
5428
+ attempt += 1;
5429
+ if (attempt > maxRetries) {
5430
+ pending = [...batch, ...pending];
5431
+ savePersisted(storageKey, pending);
5432
+ throw err;
5433
+ }
5434
+ await new Promise((r) => {
5435
+ setTimeout(r, delay2);
5436
+ });
5437
+ delay2 = Math.min(delay2 * 2, 60000);
5438
+ }
5439
+ }
5440
+ }
5441
+ async function drain() {
5442
+ if (stopped || inFlight)
5443
+ return inFlight ?? undefined;
5444
+ const fresh = await crashReporter.flush();
5445
+ if (fresh.length > 0)
5446
+ await crashReporter.clear();
5447
+ pending = [...pending, ...fresh.map((e) => redactor(e))];
5448
+ if (pending.length === 0)
5449
+ return;
5450
+ const batch = pending;
5451
+ pending = [];
5452
+ savePersisted(storageKey, pending);
5453
+ inFlight = postBatch(batch).catch(() => {}).finally(() => {
5454
+ inFlight = null;
5455
+ });
5456
+ return inFlight;
5457
+ }
5458
+ if (intervalMs > 0) {
5459
+ timer = setInterval(() => {
5460
+ drain().catch(() => {});
5461
+ }, intervalMs);
5462
+ }
5463
+ return {
5464
+ async flushNow() {
5465
+ await drain();
5466
+ },
5467
+ stop() {
5468
+ stopped = true;
5469
+ if (timer)
5470
+ clearInterval(timer);
5471
+ timer = null;
5472
+ },
5473
+ pending() {
5474
+ return [...pending];
5475
+ }
5476
+ };
5477
+ }
5478
+ // src/iap.ts
5479
+ var iap = {
5480
+ async isAvailable() {
5481
+ if (!hasBridge("iap"))
5482
+ return false;
5483
+ return await window.craft.iap.isAvailable();
5484
+ },
5485
+ async getProducts(ids) {
5486
+ if (!hasBridge("iap"))
5487
+ return [];
5488
+ const arr = Array.isArray(ids) ? ids : [String(ids)];
5489
+ return await window.craft.iap.getProducts(arr);
5490
+ },
5491
+ async purchase(productId) {
5492
+ if (!hasBridge("iap"))
5493
+ return { queued: false, productId, reason: "IAP bridge not available" };
5494
+ const r = await window.craft.iap.purchase(productId);
5495
+ return { queued: !!(r && r.queued), productId: r?.productId, reason: r?.reason };
5496
+ },
5497
+ async restorePurchases() {
5498
+ if (!hasBridge("iap"))
5499
+ return { ok: false };
5500
+ const r = await window.craft.iap.restorePurchases();
5501
+ return { ok: !!(r && r.ok) };
5502
+ },
5503
+ async finishTransaction(transactionId) {
5504
+ if (!hasBridge("iap"))
5505
+ return;
5506
+ await window.craft.iap.finishTransaction(transactionId);
5507
+ },
5508
+ async getReceiptData() {
5509
+ if (!hasBridge("iap"))
5510
+ return null;
5511
+ const r = await window.craft.iap.getReceiptData();
5512
+ return r ? String(r) : null;
5513
+ },
5514
+ onPurchased(cb) {
5515
+ return onCraftEvent("craft:iap:purchased", cb);
5516
+ },
5517
+ onFailed(cb) {
5518
+ return onCraftEvent("craft:iap:failed", cb);
5519
+ },
5520
+ onRestored(cb) {
5521
+ return onCraftEvent("craft:iap:restored", cb);
5522
+ },
5523
+ onProductsLoaded(cb) {
5524
+ return onCraftEvent("craft:iap:productsLoaded", (e) => cb(e.products || []));
5525
+ },
5526
+ onRefunded(cb) {
5527
+ return onCraftEvent("craft:iap:refunded", cb);
5528
+ },
5529
+ onSubscriptionStatusChanged(cb) {
5530
+ return onCraftEvent("craft:iap:subscriptionStatusChanged", cb);
5531
+ },
5532
+ async getActiveSubscriptions() {
5533
+ if (!hasBridge("iap"))
5534
+ return [];
5535
+ const fn = window.craft.iap.getActiveSubscriptions;
5536
+ if (typeof fn !== "function")
5537
+ return [];
5538
+ const r = await fn();
5539
+ return Array.isArray(r) ? r : [];
5540
+ },
5541
+ async isEligibleForIntroOffer(productId) {
5542
+ if (!hasBridge("iap"))
5543
+ return false;
5544
+ const fn = window.craft.iap.isEligibleForIntroOffer;
5545
+ if (typeof fn !== "function")
5546
+ return false;
5547
+ return !!await fn(productId);
5548
+ }
5549
+ };
5550
+ // src/handoff.ts
5551
+ var handoff = {
5552
+ async startActivity(type, options) {
5553
+ if (!type)
5554
+ throw new Error("handoff.startActivity: type is required");
5555
+ if (!hasBridge("handoff"))
5556
+ return false;
5557
+ const r = await window.craft.handoff.startActivity(type, options);
5558
+ return typeof r === "boolean" ? r : !!(r && r.ok);
5559
+ },
5560
+ async updateActivity(options) {
5561
+ if (!hasBridge("handoff"))
5562
+ return false;
5563
+ const r = await window.craft.handoff.updateActivity(options);
5564
+ return typeof r === "boolean" ? r : !!(r && r.ok);
5565
+ },
5566
+ async stopActivity() {
5567
+ if (!hasBridge("handoff"))
5568
+ return;
5569
+ await window.craft.handoff.stopActivity();
5570
+ },
5571
+ async getCurrentActivity() {
5572
+ if (!hasBridge("handoff"))
5573
+ return null;
5574
+ const r = await window.craft.handoff.getCurrentActivity();
5575
+ return r && typeof r.type === "string" ? r : null;
5576
+ },
5577
+ onIncoming(cb) {
5578
+ return onCraftEvent("craft:handoff:incoming", cb);
5579
+ }
5580
+ };
5581
+ // src/live-activities.ts
5582
+ var liveActivities = {
5583
+ async start(type, state) {
5584
+ return handoff.startActivity(type, {
5585
+ title: state?.title,
5586
+ webpageURL: state?.webpageURL,
5587
+ userInfo: state?.state
5588
+ });
5589
+ },
5590
+ async update(state) {
5591
+ return handoff.updateActivity({
5592
+ title: state.title,
5593
+ webpageURL: state.webpageURL,
5594
+ userInfo: state.state
5595
+ });
5596
+ },
5597
+ async stop() {
5598
+ await handoff.stopActivity();
5599
+ }
5600
+ };
5601
+ // src/location.ts
5602
+ var location = {
5603
+ async requestPermission(mode = "whenInUse") {
5604
+ if (hasBridge("location"))
5605
+ return await window.craft.location.requestPermission(mode);
5606
+ if (typeof navigator !== "undefined" && navigator.geolocation) {
5607
+ return "undetermined";
5608
+ }
5609
+ return "not-supported";
5610
+ },
5611
+ async getAuthorization() {
5612
+ if (hasBridge("location"))
5613
+ return await window.craft.location.getAuthorization();
5614
+ return "unknown";
5615
+ },
5616
+ async getCurrentLocation() {
5617
+ if (hasBridge("location"))
5618
+ return await window.craft.location.getCurrentLocation();
5619
+ if (typeof navigator !== "undefined" && navigator.geolocation) {
5620
+ navigator.geolocation.getCurrentPosition((pos) => {
5621
+ window.dispatchEvent(new CustomEvent("craft:location:update", {
5622
+ detail: {
5623
+ latitude: pos.coords.latitude,
5624
+ longitude: pos.coords.longitude,
5625
+ altitude: pos.coords.altitude,
5626
+ horizontalAccuracy: pos.coords.accuracy,
5627
+ verticalAccuracy: pos.coords.altitudeAccuracy,
5628
+ speed: pos.coords.speed
5629
+ }
5630
+ }));
5631
+ }, (err) => {
5632
+ window.dispatchEvent(new CustomEvent("craft:location:error", {
5633
+ detail: { message: err.message || String(err) }
5634
+ }));
5635
+ });
5636
+ return { requested: true };
5637
+ }
5638
+ return { requested: false };
5639
+ },
5640
+ async startWatching(options) {
5641
+ if (hasBridge("location"))
5642
+ return await window.craft.location.startWatching(options);
5643
+ if (typeof navigator !== "undefined" && navigator.geolocation) {
5644
+ const watchId = navigator.geolocation.watchPosition((pos) => {
5645
+ window.dispatchEvent(new CustomEvent("craft:location:update", {
5646
+ detail: {
5647
+ latitude: pos.coords.latitude,
5648
+ longitude: pos.coords.longitude,
5649
+ altitude: pos.coords.altitude,
5650
+ horizontalAccuracy: pos.coords.accuracy,
5651
+ verticalAccuracy: pos.coords.altitudeAccuracy,
5652
+ speed: pos.coords.speed
5653
+ }
5654
+ }));
5655
+ });
5656
+ window.__craftWebLocationWatchId = watchId;
5657
+ return true;
5658
+ }
5659
+ return false;
5660
+ },
5661
+ async stopWatching() {
5662
+ if (hasBridge("location")) {
5663
+ await window.craft.location.stopWatching();
5664
+ return;
5665
+ }
5666
+ if (typeof navigator !== "undefined" && navigator.geolocation) {
5667
+ const id = window.__craftWebLocationWatchId;
5668
+ if (id != null) {
5669
+ navigator.geolocation.clearWatch(id);
5670
+ window.__craftWebLocationWatchId = null;
5671
+ }
5672
+ }
5673
+ },
5674
+ onUpdate(cb) {
5675
+ return onCraftEvent("craft:location:update", cb);
5676
+ },
5677
+ onError(cb) {
5678
+ return onCraftEvent("craft:location:error", cb);
5679
+ },
5680
+ onAuthChanged(cb) {
5681
+ return onCraftEvent("craft:location:authChanged", cb);
5682
+ }
5683
+ };
5684
+ // src/screen-capture.ts
5685
+ var screenCapture = {
5686
+ async captureScreen() {
5687
+ if (!hasBridge("screenCapture"))
5688
+ return null;
5689
+ const r = await window.craft.screenCapture.captureScreen();
5690
+ return r ? String(r) : null;
5691
+ },
5692
+ async captureWindow(id) {
5693
+ if (!hasBridge("screenCapture"))
5694
+ return null;
5695
+ if (!Number.isFinite(id) || id <= 0)
5696
+ throw new Error("captureWindow: id must be a positive number");
5697
+ const r = await window.craft.screenCapture.captureWindow(id);
5698
+ return r ? String(r) : null;
5699
+ },
5700
+ async listWindows() {
5701
+ if (!hasBridge("screenCapture"))
5702
+ return [];
5703
+ return await window.craft.screenCapture.listWindows();
5704
+ }
5705
+ };
5706
+ // src/local-server.ts
5707
+ var localServer = {
5708
+ async start(port = 0, host = "127.0.0.1") {
5709
+ if (!hasBridge("localServer"))
5710
+ return { port: 0, started: false, reason: "bridge unavailable" };
5711
+ return await window.craft.localServer.start(port, host);
5712
+ },
5713
+ async stop() {
5714
+ if (!hasBridge("localServer"))
5715
+ return;
5716
+ await window.craft.localServer.stop();
5717
+ },
5718
+ async respond(options) {
5719
+ if (!hasBridge("localServer"))
5720
+ return;
5721
+ await window.craft.localServer.respond(options || { status: 200, body: "OK" });
5722
+ },
5723
+ onRequest(cb) {
5724
+ return onCraftEvent("craft:localServer:request", cb);
5725
+ },
5726
+ async awaitOAuthCallback(options = {}) {
5727
+ requireBridge("localServer");
5728
+ const { port: requestedPort = 0, host = "127.0.0.1", timeoutMs = 5 * 60 * 1000, successHTML } = options;
5729
+ const start = await this.start(requestedPort, host);
5730
+ if (!start.started)
5731
+ throw new Error(`localServer: start failed${start.reason ? ` \u2014 ${start.reason}` : ""}`);
5732
+ return new Promise((resolve, reject) => {
5733
+ const timer = setTimeout(() => {
5734
+ off();
5735
+ this.stop().catch(() => {});
5736
+ reject(new Error("localServer: OAuth callback timed out"));
5737
+ }, timeoutMs);
5738
+ const off = this.onRequest(({ url }) => {
5739
+ clearTimeout(timer);
5740
+ off();
5741
+ const body = successHTML ?? `<!doctype html><meta charset="utf-8"><title>Done</title>
5742
+ <style>body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#f5f5f7}</style>
5743
+ <div><h1>You can close this tab.</h1><p>Returning to the app\u2026</p></div>
5744
+ <script>setTimeout(()=>window.close(),1500)</script>`;
5745
+ this.respond({ status: 200, body, contentType: "text/html; charset=utf-8" }).catch(() => {}).finally(() => this.stop().catch(() => {}));
5746
+ resolve({ url, port: start.port });
5747
+ });
5748
+ });
5749
+ }
5750
+ };
5751
+ // src/biometric.ts
5752
+ var biometric = {
5753
+ async isAvailable() {
5754
+ if (!hasBridge("biometric"))
5755
+ return false;
5756
+ return await window.craft.biometric.isAvailable();
5757
+ },
5758
+ async getBiometryType() {
5759
+ if (!hasBridge("biometric"))
5760
+ return "none";
5761
+ return await window.craft.biometric.getBiometryType();
5762
+ },
5763
+ async evaluate(reason, options) {
5764
+ if (!reason)
5765
+ throw new Error("biometric.evaluate: reason is required");
5766
+ if (!hasBridge("biometric"))
5767
+ return { success: false, errorCode: -1 };
5768
+ return await window.craft.biometric.evaluate(reason, options);
5769
+ }
5770
+ };
5771
+ // src/audio.ts
5772
+ var webAudio = null;
5773
+ var audio = {
5774
+ async play(path, options) {
5775
+ if (hasBridge("audio")) {
5776
+ return await window.craft.audio.play(path, options);
5777
+ }
5778
+ if (typeof window === "undefined" || typeof Audio === "undefined")
5779
+ return false;
5780
+ if (webAudio) {
5781
+ webAudio.pause();
5782
+ webAudio = null;
5783
+ }
5784
+ webAudio = new Audio(path);
5785
+ if (options?.volume != null)
5786
+ webAudio.volume = options.volume;
5787
+ if (options?.loops)
5788
+ webAudio.loop = true;
5789
+ try {
5790
+ await webAudio.play();
5791
+ return true;
5792
+ } catch {
5793
+ return false;
5794
+ }
5795
+ },
5796
+ async playSystemSound(name) {
5797
+ if (hasBridge("audio"))
5798
+ return await window.craft.audio.playSystemSound(name);
5799
+ if (typeof window === "undefined" || typeof Audio === "undefined")
5800
+ return false;
5801
+ try {
5802
+ webAudio = new Audio(`/System/Library/Sounds/${name}.aiff`);
5803
+ await webAudio.play();
5804
+ return true;
5805
+ } catch {
5806
+ return false;
5807
+ }
5808
+ },
5809
+ async stop() {
5810
+ if (hasBridge("audio")) {
5811
+ await window.craft.audio.stop();
5812
+ return;
5813
+ }
5814
+ if (webAudio) {
5815
+ webAudio.pause();
5816
+ webAudio.currentTime = 0;
5817
+ webAudio = null;
5818
+ }
5819
+ },
5820
+ async isPlaying() {
5821
+ if (hasBridge("audio"))
5822
+ return await window.craft.audio.isPlaying();
5823
+ return !!(webAudio && !webAudio.paused);
5824
+ },
5825
+ async startRecording(path, options) {
5826
+ if (hasBridge("audio"))
5827
+ return await window.craft.audio.startRecording(path, options);
5828
+ return false;
5829
+ },
5830
+ async stopRecording() {
5831
+ if (hasBridge("audio"))
5832
+ await window.craft.audio.stopRecording();
5833
+ },
5834
+ async isRecording() {
5835
+ if (hasBridge("audio"))
5836
+ return await window.craft.audio.isRecording();
5837
+ return false;
5838
+ }
5839
+ };
5840
+ // src/apple-script.ts
5841
+ var appleScript = {
5842
+ async execute(source) {
5843
+ if (!source)
5844
+ throw new Error("appleScript.execute: source is required");
5845
+ if (!hasBridge("appleScript"))
5846
+ return { ok: false };
5847
+ return await window.craft.appleScript.execute(source);
5848
+ }
5849
+ };
5850
+ // src/file-associations.ts
5851
+ var fileAssociations = {
5852
+ async getDefault(uti) {
5853
+ if (!uti)
5854
+ throw new Error("fileAssociations.getDefault: uti is required");
5855
+ if (!hasBridge("fileAssociations"))
5856
+ return null;
5857
+ const v = await window.craft.fileAssociations.getDefault(uti);
5858
+ return v ? String(v) : null;
5859
+ },
5860
+ async setDefault(uti, bundleId) {
5861
+ if (!uti || !bundleId)
5862
+ throw new Error("fileAssociations.setDefault: uti and bundleId are required");
5863
+ if (!hasBridge("fileAssociations"))
5864
+ return false;
5865
+ return await window.craft.fileAssociations.setDefault(uti, bundleId);
5866
+ }
5867
+ };
5868
+ // src/tags.ts
5869
+ var tags = {
5870
+ async get(path) {
5871
+ if (!path)
5872
+ throw new Error("tags.get: path is required");
5873
+ if (!hasBridge("tags"))
5874
+ return [];
5875
+ return await window.craft.tags.get(path);
5876
+ },
5877
+ async set(path, t) {
5878
+ if (!path)
5879
+ throw new Error("tags.set: path is required");
5880
+ if (!hasBridge("tags"))
5881
+ return false;
5882
+ const arr = Array.isArray(t) ? t : [String(t)];
5883
+ return await window.craft.tags.set(path, arr);
5884
+ },
5885
+ async clear(path) {
5886
+ if (!path)
5887
+ throw new Error("tags.clear: path is required");
5888
+ if (!hasBridge("tags"))
5889
+ return false;
5890
+ return await window.craft.tags.clear(path);
5891
+ }
5892
+ };
5893
+ // src/pdf.ts
5894
+ var pdf = {
5895
+ async countPages(path) {
5896
+ if (!path)
5897
+ throw new Error("pdf.countPages: path is required");
5898
+ if (!hasBridge("pdf"))
5899
+ return 0;
5900
+ return await window.craft.pdf.countPages(path);
5901
+ },
5902
+ async extractText(path) {
5903
+ if (!path)
5904
+ throw new Error("pdf.extractText: path is required");
5905
+ if (!hasBridge("pdf"))
5906
+ return "";
5907
+ return await window.craft.pdf.extractText(path);
5908
+ }
5909
+ };
5910
+ // src/log.ts
5911
+ var log = {
5912
+ async debug(m) {
5913
+ if (hasBridge("log"))
5914
+ await window.craft.log.debug(m);
5915
+ else
5916
+ console.debug(m);
5917
+ },
5918
+ async info(m) {
5919
+ if (hasBridge("log"))
5920
+ await window.craft.log.info(m);
5921
+ else
5922
+ console.info(m);
5923
+ },
5924
+ async warn(m) {
5925
+ if (hasBridge("log"))
5926
+ await window.craft.log.warn(m);
5927
+ else
5928
+ console.warn(m);
5929
+ },
5930
+ async error(m) {
5931
+ if (hasBridge("log"))
5932
+ await window.craft.log.error(m);
5933
+ else
5934
+ console.error(m);
5935
+ }
5936
+ };
5937
+ // src/bonjour.ts
5938
+ var bonjour = {
5939
+ async browse(serviceType) {
5940
+ if (!hasBridge("bonjour"))
5941
+ return { started: false, reason: "bridge unavailable" };
5942
+ return await window.craft.bonjour.browse(serviceType);
5943
+ },
5944
+ async stop() {
5945
+ if (hasBridge("bonjour"))
5946
+ await window.craft.bonjour.stop();
5947
+ },
5948
+ onFound(cb) {
5949
+ return onCraftEvent("craft:bonjour:found", cb);
5950
+ },
5951
+ onLost(cb) {
5952
+ return onCraftEvent("craft:bonjour:lost", cb);
5953
+ }
5954
+ };
5955
+ // src/spotlight.ts
5956
+ var spotlight = {
5957
+ async index(items) {
5958
+ if (!hasBridge("spotlight"))
5959
+ return { ok: false, reason: "bridge unavailable" };
5960
+ return await window.craft.spotlight.index(items);
5961
+ },
5962
+ async remove(ids) {
5963
+ if (!hasBridge("spotlight"))
5964
+ return { ok: false };
5965
+ return await window.craft.spotlight.remove(ids);
5966
+ },
5967
+ async removeAll() {
5968
+ if (!hasBridge("spotlight"))
5969
+ return { ok: false };
5970
+ return await window.craft.spotlight.removeAll();
5971
+ }
5972
+ };
5973
+ // src/speech-recognition.ts
5974
+ var speechRecognition = {
5975
+ async isAvailable() {
5976
+ if (!hasBridge("speechRecognition"))
5977
+ return false;
5978
+ return await window.craft.speechRecognition.isAvailable();
5979
+ },
5980
+ async start(opts) {
5981
+ if (!hasBridge("speechRecognition"))
5982
+ return { started: false, reason: "bridge unavailable" };
5983
+ return await window.craft.speechRecognition.start(opts);
5984
+ },
5985
+ async stop() {
5986
+ if (hasBridge("speechRecognition"))
5987
+ await window.craft.speechRecognition.stop();
5988
+ },
5989
+ onPartial(cb) {
5990
+ return onCraftEvent("craft:speechRecognition:partial", cb);
5991
+ },
5992
+ onFinal(cb) {
5993
+ return onCraftEvent("craft:speechRecognition:final", cb);
5994
+ }
5995
+ };
5996
+ // src/vision.ts
5997
+ var vision = {
5998
+ async recognizeText(path) {
5999
+ if (!path)
6000
+ throw new Error("vision.recognizeText: path is required");
6001
+ if (!hasBridge("vision"))
6002
+ return [];
6003
+ return await window.craft.vision.recognizeText(path);
6004
+ },
6005
+ async detectFaces(path) {
6006
+ if (!path)
6007
+ throw new Error("vision.detectFaces: path is required");
6008
+ if (!hasBridge("vision"))
6009
+ return [];
6010
+ return await window.craft.vision.detectFaces(path);
6011
+ },
6012
+ async detectBarcodes(path) {
6013
+ if (!path)
6014
+ throw new Error("vision.detectBarcodes: path is required");
6015
+ if (!hasBridge("vision"))
6016
+ return [];
6017
+ return await window.craft.vision.detectBarcodes(path);
6018
+ }
6019
+ };
6020
+ // src/midi.ts
6021
+ var midi = {
6022
+ async listSources() {
6023
+ if (!hasBridge("midi"))
6024
+ return [];
6025
+ return await window.craft.midi.listSources();
6026
+ },
6027
+ async listDestinations() {
6028
+ if (!hasBridge("midi"))
6029
+ return [];
6030
+ return await window.craft.midi.listDestinations();
6031
+ },
6032
+ async send(destinationIndex, data) {
6033
+ if (!hasBridge("midi"))
6034
+ return { ok: false, reason: "bridge unavailable" };
6035
+ return await window.craft.midi.send(destinationIndex, data);
6036
+ },
6037
+ async subscribe(sourceIndex) {
6038
+ if (!hasBridge("midi"))
6039
+ return { ok: false, reason: "bridge unavailable" };
6040
+ return await window.craft.midi.subscribe(sourceIndex);
6041
+ },
6042
+ async unsubscribe(sourceIndex) {
6043
+ if (!hasBridge("midi"))
6044
+ return { ok: false };
6045
+ return await window.craft.midi.unsubscribe(sourceIndex);
6046
+ },
6047
+ onMessage(cb) {
6048
+ return onCraftEvent("craft:midi:message", cb);
6049
+ }
6050
+ };
6051
+ // src/coreml.ts
6052
+ var coreml = {
6053
+ async loadModel(id, path) {
6054
+ if (!id || !path)
6055
+ throw new Error("coreml.loadModel: id and path are required");
6056
+ if (!hasBridge("coreml"))
6057
+ return false;
6058
+ return await window.craft.coreml.loadModel(id, path);
6059
+ },
6060
+ async unloadModel(id) {
6061
+ if (!hasBridge("coreml"))
6062
+ return;
6063
+ await window.craft.coreml.unloadModel(id);
6064
+ },
6065
+ async predict(id, input) {
6066
+ if (!id)
6067
+ throw new Error("coreml.predict: id is required");
6068
+ if (!hasBridge("coreml"))
6069
+ return null;
6070
+ return await window.craft.coreml.predict(id, input);
6071
+ }
6072
+ };
6073
+ // src/continuity-camera.ts
6074
+ var continuityCamera = {
6075
+ async listCameras() {
6076
+ if (!hasBridge("continuityCamera"))
6077
+ return [];
6078
+ return await window.craft.continuityCamera.listCameras();
6079
+ }
6080
+ };
6081
+ // src/service-menu.ts
6082
+ var serviceMenu = {
6083
+ async register(name) {
6084
+ if (!name)
6085
+ throw new Error("serviceMenu.register: name is required");
6086
+ if (!hasBridge("serviceMenu"))
6087
+ return { ok: false, reason: "bridge unavailable" };
6088
+ return await window.craft.serviceMenu.register(name);
6089
+ },
6090
+ async unregister(name) {
6091
+ if (!name)
6092
+ throw new Error("serviceMenu.unregister: name is required");
6093
+ if (!hasBridge("serviceMenu"))
6094
+ return;
6095
+ await window.craft.serviceMenu.unregister(name);
6096
+ },
6097
+ onInvoked(cb) {
6098
+ return onCraftEvent("craft:serviceMenu:invoked", cb);
6099
+ }
6100
+ };
6101
+ // src/serial.ts
6102
+ var serial = {
6103
+ async list() {
6104
+ if (!hasBridge("serial"))
6105
+ return [];
6106
+ return await window.craft.serial.list();
6107
+ },
6108
+ async open(path, baud = 9600) {
6109
+ if (!path)
6110
+ throw new Error("serial.open: path is required");
6111
+ if (!hasBridge("serial"))
6112
+ return { ok: false, reason: "bridge unavailable" };
6113
+ return await window.craft.serial.open(path, baud);
6114
+ },
6115
+ async write(id, data) {
6116
+ if (!id)
6117
+ throw new Error("serial.write: id is required");
6118
+ if (!hasBridge("serial"))
6119
+ return { ok: false, reason: "bridge unavailable" };
6120
+ return await window.craft.serial.write(id, data);
6121
+ },
6122
+ async close(id) {
6123
+ if (!hasBridge("serial"))
6124
+ return;
6125
+ await window.craft.serial.close(id);
6126
+ },
6127
+ onData(cb) {
6128
+ return onCraftEvent("craft:serial:data", cb);
6129
+ }
6130
+ };
6131
+ // src/capabilities.ts
6132
+ var BRIDGE_INDEX = [
6133
+ { name: "fs", support: "native" },
6134
+ { name: "shell", support: "native" },
6135
+ { name: "system", support: "all" },
6136
+ { name: "clipboard", support: "all" },
6137
+ { name: "dialog", support: "all" },
6138
+ { name: "window", support: "native" },
6139
+ { name: "tray", support: "native" },
6140
+ { name: "menu", support: "native" },
6141
+ { name: "theme", support: "all" },
6142
+ { name: "screen", support: "all" },
6143
+ { name: "network", support: "all" },
6144
+ { name: "power", support: "all" },
6145
+ { name: "battery", support: "all" },
6146
+ { name: "notifications", support: "all" },
6147
+ { name: "globalShortcuts", support: "native" },
6148
+ { name: "autolaunch", support: "native" },
6149
+ { name: "appInfo", support: "all" },
6150
+ { name: "localServer", support: "native" },
6151
+ { name: "bluetooth", support: "native" },
6152
+ { name: "crashReporter", support: "all" },
6153
+ { name: "updater", support: "native" },
6154
+ { name: "iap", support: "macos" },
6155
+ { name: "keychain", support: "native" },
6156
+ { name: "log", support: "all" },
6157
+ { name: "biometric", support: "macos" },
6158
+ { name: "location", support: "macos" },
6159
+ { name: "audio", support: "macos" },
6160
+ { name: "deepLink", support: "native" },
6161
+ { name: "handoff", support: "macos" },
6162
+ { name: "liveActivities", support: "macos" },
6163
+ { name: "touchbar", support: "macos" },
6164
+ { name: "dragOut", support: "macos" },
6165
+ { name: "appleScript", support: "macos" },
6166
+ { name: "fileAssociations", support: "native" },
6167
+ { name: "tags", support: "macos" },
6168
+ { name: "pdf", support: "macos" },
6169
+ { name: "bonjour", support: "macos" },
6170
+ { name: "spotlight", support: "macos" },
6171
+ { name: "speechRecognition", support: "macos" },
6172
+ { name: "vision", support: "macos" },
6173
+ { name: "midi", support: "macos" },
6174
+ { name: "coreml", support: "macos" },
6175
+ { name: "continuityCamera", support: "macos" },
6176
+ { name: "serviceMenu", support: "macos" },
6177
+ { name: "serial", support: "native" }
6178
+ ];
6179
+ function getCapabilities() {
6180
+ return BRIDGE_INDEX.map(({ name, support }) => ({
6181
+ name,
6182
+ support,
6183
+ available: hasBridge(name)
6184
+ }));
6185
+ }
6186
+ function getCapability(name) {
6187
+ const entry = BRIDGE_INDEX.find((b) => b.name === name);
6188
+ if (!entry)
6189
+ return;
6190
+ return { ...entry, available: hasBridge(name) };
6191
+ }
6192
+ function isAvailable(name) {
6193
+ const cap = getCapability(name);
6194
+ return !!cap && cap.available;
6195
+ }
3763
6196
  export {
6197
+ windowEvents,
6198
+ vision,
6199
+ updater,
3764
6200
  unregisterHotkey,
3765
6201
  unregisterAllHotkeys,
3766
6202
  triggerTrayAction,
6203
+ touchbar,
6204
+ theme,
6205
+ tags,
6206
+ system,
6207
+ spotlight,
6208
+ speechRecognition,
6209
+ speech,
6210
+ signPayload,
3767
6211
  showWarningToast,
3768
6212
  showWarningModal,
3769
6213
  showWarningDialog,
@@ -3784,18 +6228,42 @@ export {
3784
6228
  showColorPicker,
3785
6229
  showAlertDialog,
3786
6230
  showAlert,
6231
+ shell,
3787
6232
  setDesktopConfig,
3788
6233
  setAutoLaunch,
6234
+ serviceMenu,
6235
+ serial,
6236
+ screenCapture,
6237
+ screen,
3789
6238
  resetDesktopConfig,
3790
6239
  requestNotificationPermission,
3791
6240
  registerHotkey,
6241
+ redactPII,
3792
6242
  prompt2 as prompt,
6243
+ printing,
6244
+ permissions,
6245
+ pdf,
3793
6246
  parseShortcut,
3794
6247
  openDevWindow,
3795
6248
  notify,
6249
+ notifications,
6250
+ network,
6251
+ nativeAutoLaunch,
6252
+ midi,
6253
+ menu,
6254
+ log,
6255
+ location,
6256
+ localServer,
6257
+ liveActivities,
6258
+ keychain,
3796
6259
  isWebviewAvailable,
6260
+ isDragOutAvailable,
3797
6261
  isCaffeinated,
6262
+ isAvailable,
3798
6263
  isAutoLaunchEnabled,
6264
+ iap,
6265
+ handoff,
6266
+ globalShortcuts,
3799
6267
  getWindowBridgeScript,
3800
6268
  getWindow,
3801
6269
  getTrayInstance,
@@ -3804,19 +6272,25 @@ export {
3804
6272
  getRegisteredHotkeys,
3805
6273
  getDialogBridgeScript,
3806
6274
  getDesktopConfig,
6275
+ getCapability,
6276
+ getCapabilities,
3807
6277
  getCaffeinateStatus,
3808
6278
  getActiveWindowIds,
3809
6279
  getActiveTrayInstances,
3810
6280
  getActiveModalCount,
3811
6281
  getActiveAlertCount,
6282
+ fs,
3812
6283
  formatTime,
3813
6284
  formatShortcut,
3814
6285
  formatRemainingTime,
3815
6286
  formatDuration,
3816
6287
  formatCompact,
6288
+ fileAssociations,
6289
+ dragOut,
3817
6290
  dismissAllAlerts,
3818
6291
  dismissAlertById,
3819
6292
  delay,
6293
+ deepLinks,
3820
6294
  decaffeinate,
3821
6295
  createWindowWithHTML,
3822
6296
  createWindow,
@@ -3859,10 +6333,21 @@ export {
3859
6333
  createAvatar,
3860
6334
  createAutocomplete,
3861
6335
  createAccordion,
6336
+ crashReporter,
6337
+ coreml,
6338
+ continuityCamera,
3862
6339
  confirm2 as confirm,
3863
6340
  closeAllWindows,
3864
6341
  closeAllModals,
6342
+ clipboard,
3865
6343
  caffeinate,
6344
+ bonjour,
6345
+ bluetooth,
6346
+ biometric,
6347
+ battery,
6348
+ audio,
6349
+ appleScript,
6350
+ app as appInfo,
3866
6351
  alert2 as alert,
3867
6352
  TRAY_MENU_STYLES,
3868
6353
  TOAST_STYLES,
@@ -3871,4 +6356,4 @@ export {
3871
6356
  AVAILABLE_COMPONENTS
3872
6357
  };
3873
6358
 
3874
- //# debugId=20FC12EC72514C4464756E2164756E21
6359
+ //# debugId=EABDD0C2E7CA346F64756E2164756E21