craft-native 0.0.76 → 0.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2222,41 +2222,33 @@ struct CraftWebView: UIViewRepresentable {
2222
2222
  _progressCallbacks: [],
2223
2223
  _statusCallbacks: [],
2224
2224
 
2225
+ // OTA is not implemented natively. These five used to post
2226
+ // to actions the switch below does not handle, and register
2227
+ // a resolve/reject pair that nothing would ever call — they
2228
+ // also bypassed _createCallback, which is what owns the 30s
2229
+ // timeout, so the promises hung forever rather than
2230
+ // rejecting. An immediate rejection is the honest answer:
2231
+ // an app can catch it, where it could never catch a hang.
2232
+ _unavailable: function(name) {
2233
+ return Promise.reject(new Error(
2234
+ 'craft.ota.' + name + ' is not implemented on this platform'
2235
+ ));
2236
+ },
2225
2237
  configure: function(options) {
2226
2238
  this._config = options;
2227
- window.webkit.messageHandlers.craft.postMessage({action: 'otaConfigure', config: options});
2239
+ console.warn('[craft] ota.configure stored locally; OTA is not implemented natively');
2228
2240
  },
2229
2241
  checkForUpdate: function() {
2230
- var self = window.craft;
2231
- var id = 'cb_' + (++self._callbackId);
2232
- window.webkit.messageHandlers.craft.postMessage({action: 'otaCheckForUpdate', callbackId: id});
2233
- return new Promise(function(resolve, reject) {
2234
- self._callbacks[id] = {resolve: resolve, reject: reject};
2235
- });
2242
+ return window.craft.ota._unavailable('checkForUpdate');
2236
2243
  },
2237
2244
  downloadUpdate: function(options) {
2238
- var self = window.craft;
2239
- var id = 'cb_' + (++self._callbackId);
2240
- window.webkit.messageHandlers.craft.postMessage({action: 'otaDownloadUpdate', options: options || {}, callbackId: id});
2241
- return new Promise(function(resolve, reject) {
2242
- self._callbacks[id] = {resolve: resolve, reject: reject};
2243
- });
2245
+ return window.craft.ota._unavailable('downloadUpdate');
2244
2246
  },
2245
2247
  applyUpdate: function() {
2246
- var self = window.craft;
2247
- var id = 'cb_' + (++self._callbackId);
2248
- window.webkit.messageHandlers.craft.postMessage({action: 'otaApplyUpdate', callbackId: id});
2249
- return new Promise(function(resolve, reject) {
2250
- self._callbacks[id] = {resolve: resolve, reject: reject};
2251
- });
2248
+ return window.craft.ota._unavailable('applyUpdate');
2252
2249
  },
2253
2250
  rollback: function() {
2254
- var self = window.craft;
2255
- var id = 'cb_' + (++self._callbackId);
2256
- window.webkit.messageHandlers.craft.postMessage({action: 'otaRollback', callbackId: id});
2257
- return new Promise(function(resolve, reject) {
2258
- self._callbacks[id] = {resolve: resolve, reject: reject};
2259
- });
2251
+ return window.craft.ota._unavailable('rollback');
2260
2252
  },
2261
2253
  getCurrentBundle: function() {
2262
2254
  // This returns synchronously from stored data
package/dist/package.d.ts CHANGED
@@ -74,6 +74,20 @@ export interface PackageConfig {
74
74
  * the helper lands next to `process.execPath`.
75
75
  */
76
76
  additionalExecutables?: string[];
77
+ /**
78
+ * URL schemes the app registers as a handler for — `['myapp']` makes
79
+ * `myapp://…` open it.
80
+ *
81
+ * Emitted as `CFBundleURLTypes`. Without this key macOS never dispatches
82
+ * a URL to the app at all, so Craft's receive path (the `kAEGetURL`
83
+ * AppleEvent handler behind `craft.deepLink`) can be fully working and
84
+ * still never hear anything.
85
+ *
86
+ * Schemes are matched case-insensitively by LaunchServices and are
87
+ * global to the machine: pick something specific to the app, not `app`
88
+ * or `open`.
89
+ */
90
+ urlSchemes?: string[];
77
91
  };
78
92
  /** Windows-specific options */
79
93
  windows?: {
@@ -138,7 +152,18 @@ export interface MacOSBundleMetadata {
138
152
  minimumSystemVersion?: string;
139
153
  /** Sets `LSUIElement` — no Dock icon, no app switcher entry */
140
154
  menuBarOnly?: boolean;
155
+ /** URL schemes the app handles, emitted as `CFBundleURLTypes` */
156
+ urlSchemes?: string[];
141
157
  }
158
+ /**
159
+ * `CFBundleURLTypes` for the schemes an app handles, or `null` if it handles
160
+ * none.
161
+ *
162
+ * All the schemes go in one URL type. They are alternatives for reaching the
163
+ * same app, not different kinds of document, so splitting them across several
164
+ * dicts would only invent distinctions LaunchServices does not care about.
165
+ */
166
+ export declare function urlTypesEntry(bundleId: string, schemes: readonly string[]): string | null;
142
167
  /**
143
168
  * Render `Contents/Info.plist` for a macOS app bundle.
144
169
  *
@@ -178,6 +203,50 @@ export declare function dmgCreateArguments(opts: {
178
203
  volumeName: string;
179
204
  }, contentBytes: number): string[];
180
205
  export declare function shouldRetryHdiutil(error: string, attempt: number, maxAttempts?: number): boolean;
206
+ /**
207
+ * Helper: Create PKG from app bundle
208
+ *
209
+ * Two flavours share this entry point:
210
+ * - a plain `pkgbuild` installer for direct distribution, and
211
+ * - a signed `productbuild` submission package for the Mac App Store, which is
212
+ * the only form App Store Connect accepts.
213
+ */
214
+ /**
215
+ * The component property list `pkgbuild` is given for the app bundle.
216
+ *
217
+ * Written out rather than left to `pkgbuild`'s inference, because its default
218
+ * for a bundle is `BundleIsRelocatable = true`, which puts this in the package:
219
+ *
220
+ * <relocate><bundle id="dev.example.app"/></relocate>
221
+ *
222
+ * That directive tells `installer` the payload path is only a suggestion: it
223
+ * looks the bundle identifier up on the target volume and, if the system has a
224
+ * copy of that bundle registered anywhere, writes the payload over *that* copy
225
+ * instead — and exits 0. The user sees "The install was successful" and finds
226
+ * nothing at /Applications.
227
+ *
228
+ * It is intermittent by nature, since it turns on whether the system has
229
+ * indexed some other copy yet, which is exactly how it behaved: the native
230
+ * lifecycle workflow's macOS legs failed only on slow runs, on both
231
+ * architectures, with `installer` reporting success and the app absent.
232
+ *
233
+ * `BundleIsVersionChecked` is off for a related reason — with it on, installing
234
+ * an *older* version over a newer one is skipped, which silently turns a
235
+ * rollback into a no-op.
236
+ *
237
+ * The `pkg-info` attribute `relocatable="false"` that appears either way is not
238
+ * this setting and does not govern it; only the `<relocate>` element does.
239
+ */
240
+ export declare function pkgbuildComponentPlist(rootRelativeBundlePath: string): string;
241
+ /** Build the `pkgbuild` argument list for a non-App-Store package. */
242
+ export declare function pkgbuildArguments(opts: {
243
+ root: string;
244
+ componentPlistPath: string;
245
+ identifier: string;
246
+ version: string;
247
+ outputPath: string;
248
+ installerIdentity?: string;
249
+ }): string[];
181
250
  export declare function windowsArchitecture(architecture: string): 'x86' | 'x64' | 'arm64';
182
251
  export declare function renderWixSource(opts: Pick<MSIOptions, 'name' | 'version' | 'manufacturer' | 'architecture'>, sourceName: string): string;
183
252
  export declare function windowsExecutableName(name: string): string;
package/dist/types.d.ts CHANGED
@@ -55,6 +55,55 @@ export interface WindowOptions {
55
55
  * Window title
56
56
  */
57
57
  title?: string;
58
+ /**
59
+ * Build the window without ever putting it on screen (macOS).
60
+ *
61
+ * The page loads and runs JavaScript exactly as it would visibly, and it
62
+ * remains capturable — a snapshot taken after script has mutated the DOM
63
+ * reflects the mutation.
64
+ *
65
+ * **It does not animate.** An unshown window does not drive the compositor,
66
+ * so `requestAnimationFrame` stops after one frame and page timers fall to
67
+ * roughly 1Hz. Anything that advances itself — CSS or canvas animation, a
68
+ * charting library redrawing on rAF, "wait until the spinner stops" — sees
69
+ * the first frame forever. Drive the page with explicit calls and take
70
+ * readiness from load completion, not from a polling loop inside the page.
71
+ *
72
+ * Contradicts `menubarOnly`, which has no window to hide; passing both is an
73
+ * error rather than a silent no-op.
74
+ */
75
+ headless?: boolean;
76
+ /**
77
+ * Keep the process running after the last window closes (macOS).
78
+ *
79
+ * Craft decides this from the shape of the app when it is not set: an app
80
+ * with a tray icon or in menubar-only mode stays running, because outliving
81
+ * its window is what those are for; an ordinary windowed app quits.
82
+ *
83
+ * Set it to `true` for a windowed app that should stay resident and be
84
+ * brought back by clicking the Dock icon, or `false` for a tray app that
85
+ * should genuinely go away when its window closes.
86
+ *
87
+ * Before this existed the answer was AppKit's default, `false`, for every
88
+ * app: closing the last window left a process with no window and no way to
89
+ * get one back.
90
+ */
91
+ keepRunning?: boolean;
92
+ /**
93
+ * Remember this window's size and position across launches, under this name
94
+ * (macOS).
95
+ *
96
+ * `width`/`height`/`x`/`y` become **first-launch defaults**: they are what
97
+ * the window opens at until there is a saved frame to restore, and the saved
98
+ * frame wins from then on. Without this the window forgets its geometry
99
+ * every launch, and an app that wants the standard behaviour has to wire
100
+ * `onResize`/`onMove` to its own storage and reimplement what AppKit does in
101
+ * one call.
102
+ *
103
+ * The name is the key AppKit stores under, so it must be stable across
104
+ * launches and distinct per window.
105
+ */
106
+ frameAutosave?: string;
58
107
  /**
59
108
  * Window width in pixels
60
109
  * @default 800
@@ -454,6 +503,19 @@ export interface AppConfig {
454
503
  * Window options
455
504
  */
456
505
  window?: WindowOptions;
506
+ /**
507
+ * The name macOS shows for the app: the App menu title, and the
508
+ * "About X" / "Hide X" / "Quit X" items.
509
+ *
510
+ * Without it those read the executable's name, so every app launched
511
+ * through the shared `craft` binary calls itself "craft" in the menu bar.
512
+ * A packaged `.app` gets this from `CFBundleName` instead; this is what
513
+ * gives a dev-mode app its identity back without a packaging step.
514
+ *
515
+ * Not the name `ps` and Activity Monitor show — that comes from the
516
+ * executable and cannot be changed from inside the process.
517
+ */
518
+ appName?: string;
457
519
  /**
458
520
  * Path to Craft binary (auto-detected if not provided)
459
521
  */
@@ -627,11 +689,46 @@ export interface AppInfo {
627
689
  /**
628
690
  * Notification options
629
691
  */
692
+ /**
693
+ * A button on a notification banner.
694
+ *
695
+ * macOS shows two directly on a banner and puts any others behind an
696
+ * "Options" disclosure, so craft accepts at most four — past that they are
697
+ * a menu the user has to go looking for rather than a choice they can see.
698
+ *
699
+ * Two spellings are accepted. `{ id, label }` is the one to use. `{ action,
700
+ * title }` is what this type has said since before anything implemented it,
701
+ * and it keeps working rather than being deleted out from under whoever wrote
702
+ * against it — the field was published, it just never did anything.
703
+ */
704
+ export type NotificationAction = {
705
+ /** Comes back as `actionId` in `craft.notifications.onAction`. */
706
+ id: string;
707
+ /** The text on the button. */
708
+ label: string;
709
+ } | {
710
+ /** @deprecated Use `id`. */
711
+ action: string;
712
+ /** @deprecated Use `label`. */
713
+ title: string;
714
+ };
630
715
  export interface NotificationOptions {
631
716
  /**
632
717
  * Notification title (required)
633
718
  */
634
719
  title: string;
720
+ /**
721
+ * Buttons on the banner.
722
+ *
723
+ * Pressing one brings the app forward and fires
724
+ * `craft.notifications.onAction` with `{ notificationId, actionId }` —
725
+ * which is what lets a prompt be answered without switching to the app
726
+ * first.
727
+ *
728
+ * Two buttons cannot share an `id`: the response names the button by id,
729
+ * and for an Approve/Deny prompt that name is the entire answer.
730
+ */
731
+ actions?: NotificationAction[];
635
732
  /**
636
733
  * Notification body text
637
734
  */
@@ -648,13 +745,6 @@ export interface NotificationOptions {
648
745
  * - Or any system sound name
649
746
  */
650
747
  sound?: string;
651
- /**
652
- * Action buttons (platform dependent)
653
- */
654
- actions?: Array<{
655
- action: string;
656
- title: string;
657
- }>;
658
748
  /**
659
749
  * Notification tag (for grouping/replacing)
660
750
  */
@@ -870,7 +960,83 @@ export interface CraftMobileAPI {
870
960
  * defines the CraftBridge interface with additional mobile-only features
871
961
  * such as AR, ML, deep links, OTA updates, widgets, and auth persistence.
872
962
  */
963
+ /**
964
+ * Where the platform drew this window's window buttons — the macOS traffic
965
+ * lights, and their equivalents elsewhere.
966
+ *
967
+ * The buttons are the window server's on every desktop window Craft opens that
968
+ * is not frameless: real, correctly styled, wired to the keyboard and to
969
+ * accessibility. A page must never draw replicas beside them, and this is what
970
+ * makes that unnecessary — it says where they are, so a layout can leave room
971
+ * instead of inventing its own.
972
+ *
973
+ * Every number is measured from the live window and re-sent when it changes: a
974
+ * resize, a fullscreen transition, a new document. Listen for the
975
+ * `craft:windowcontrols` event on `window` for layout that CSS cannot express;
976
+ * everything else is better served by the four CSS variables, which the host
977
+ * sets before the document is parsed.
978
+ *
979
+ * The same facts reach CSS as `--craft-window-controls-width` / `-height` /
980
+ * `-inset-x` / `-inset-y` / `-replicas`, and the document as
981
+ * `<html data-craft-window-controls="...">`.
982
+ */
983
+ export interface CraftWindowControls {
984
+ /**
985
+ * `titlebar` — real buttons, in a titlebar above the page. Nothing to do.
986
+ * `overlay` — real buttons, over the page's own top-left corner. Leave room.
987
+ * `custom` — a frameless window: no buttons, and the page's own are the only
988
+ * ones there can be.
989
+ * `none` — no window chrome in this environment at all (iOS, Android).
990
+ */
991
+ style: 'titlebar' | 'overlay' | 'custom' | 'none';
992
+ /** The platform drew real buttons for this window. */
993
+ native: boolean;
994
+ /**
995
+ * ...and they are on screen right now. False in fullscreen, where macOS
996
+ * takes them into an auto-hiding titlebar — which is why a layout should
997
+ * reserve `reserveWidth` rather than a remembered constant.
998
+ */
999
+ visible: boolean;
1000
+ /**
1001
+ * The block's true position, in CSS px from the top-left of the web
1002
+ * viewport. Negative where the buttons are not over the page at all: above
1003
+ * it in a plain titlebar window, to its left in a window whose web content
1004
+ * starts after a native sidebar.
1005
+ */
1006
+ x: number;
1007
+ y: number;
1008
+ width: number;
1009
+ height: number;
1010
+ /**
1011
+ * The room to leave inside the page — the block's far edge, or zero when it
1012
+ * does not reach into the page. `--craft-window-controls-width` / `-height`.
1013
+ */
1014
+ reserveWidth: number;
1015
+ reserveHeight: number;
1016
+ /**
1017
+ * Where the block starts inside the page, zero unless it overlaps.
1018
+ * `--craft-window-controls-inset-x` / `-inset-y`.
1019
+ */
1020
+ insetX: number;
1021
+ insetY: number;
1022
+ /**
1023
+ * The `display` a replica should take: `'none'` wherever real buttons exist
1024
+ * and wherever there is no window to control, and `null` in a frameless
1025
+ * window, where the page's own controls are the real ones. Published as
1026
+ * `--craft-window-controls-replicas`, which is *removed* rather than set when
1027
+ * this is null, so the page's own fallback applies.
1028
+ */
1029
+ replicas: 'none' | null;
1030
+ }
873
1031
  export interface CraftBridgeAPI {
1032
+ /**
1033
+ * Where the platform drew this window's close/minimise/zoom buttons.
1034
+ *
1035
+ * Present in every Craft window, so a UI that has to lay out around them can
1036
+ * ask instead of guessing — and so a UI shared with the browser can tell the
1037
+ * two apart. See `CraftWindowControls`.
1038
+ */
1039
+ windowControls?: CraftWindowControls;
874
1040
  /**
875
1041
  * Trackpad gesture phases (desktop only).
876
1042
  *
@@ -923,6 +1089,259 @@ export interface CraftBridgeAPI {
923
1089
  * Screen-sharing and screen-recording detection (macOS)
924
1090
  */
925
1091
  screenSharing?: CraftScreenSharingAPI;
1092
+ /**
1093
+ * System-wide hotkeys (macOS).
1094
+ *
1095
+ * Absent in effect on Linux and Windows: the calls exist, and every
1096
+ * registration is refused, because Craft has no implementation there and a
1097
+ * shortcut that can never fire is worse than one that was never accepted.
1098
+ */
1099
+ shortcuts?: CraftGlobalShortcutsAPI;
1100
+ /**
1101
+ * A small scalar preference store (macOS: CFPreferences).
1102
+ */
1103
+ prefs?: CraftPreferencesAPI;
1104
+ /**
1105
+ * The Cmd+, convention: where the App menu's Settings… item arrives.
1106
+ */
1107
+ settings?: CraftSettingsAPI;
1108
+ /**
1109
+ * What the native side actually serves.
1110
+ *
1111
+ * Always present: it is how you find out whether anything else here is.
1112
+ */
1113
+ capabilities?: () => Promise<CraftCapabilities>;
1114
+ /** The last capabilities answer, or null if nothing has asked yet. */
1115
+ capabilitiesSync?: () => CraftCapabilities | null;
1116
+ /**
1117
+ * Whether one surface is known to work — `craft.supports('tray.destroy')`.
1118
+ *
1119
+ * Fails **open**: returns true before capabilities have been fetched, and
1120
+ * true for a namespace craft has not audited. A feature-detection mechanism
1121
+ * that breaks working code when it cannot see itself would be worse than the
1122
+ * gaps it describes.
1123
+ */
1124
+ supports?: (path: string) => boolean;
1125
+ }
1126
+ /**
1127
+ * How much craft is willing to claim about a namespace.
1128
+ *
1129
+ * - `declared` — audited: the action list below is complete and enforced by a
1130
+ * conformance test against the dispatch chain.
1131
+ * - `undeclared` — routed, but not audited. Craft claims nothing either way;
1132
+ * treat it as "try it and handle failure", not as "missing".
1133
+ * - `unavailable` — reachable and known not to work. `reason` says why.
1134
+ * - `unrouted` — implemented natively but absent from the dispatcher, so no
1135
+ * message can reach it.
1136
+ */
1137
+ export type CapabilityNamespaceStatus = 'declared' | 'undeclared' | 'unavailable' | 'unrouted';
1138
+ export interface CapabilityAction {
1139
+ status: 'live' | 'unavailable';
1140
+ /** Whether native sends a reply the caller is waiting on. */
1141
+ reply: 'none' | 'result';
1142
+ /** Present when the action is unavailable. */
1143
+ reason?: string;
1144
+ }
1145
+ export interface CapabilityNamespace {
1146
+ status: CapabilityNamespaceStatus;
1147
+ reason?: string;
1148
+ /** Present only for `declared` namespaces. */
1149
+ actions?: Record<string, CapabilityAction>;
1150
+ }
1151
+ /**
1152
+ * What the native binary behind this page actually serves.
1153
+ *
1154
+ * The injected bridge script is one blob, the same in every build, so the
1155
+ * presence of `window.craft.x` has never been evidence that anything is behind
1156
+ * it. This is the evidence.
1157
+ */
1158
+ export interface CraftCapabilities {
1159
+ namespaces: Record<string, CapabilityNamespace>;
1160
+ /**
1161
+ * Every `craft:*` event channel, and what craft can say about it.
1162
+ *
1163
+ * `'live'` means something in this build took out a permit to emit on it.
1164
+ * `'unknown'` means craft cannot prove it either way — subscribe and see, and
1165
+ * do **not** disable a feature over it.
1166
+ *
1167
+ * There is deliberately no `'dead'`. Craft cannot establish absence: the
1168
+ * `craft:window:*` names are composed in JavaScript from
1169
+ * `__craftDeliverWindowEvent('focus')`, so no source scan finds the literal
1170
+ * even though the emitter is right there.
1171
+ */
1172
+ channels: Record<string, 'live' | 'unknown'>;
1173
+ }
1174
+ /**
1175
+ * The only value types `craft.prefs` stores.
1176
+ *
1177
+ * Anything else is refused in the page, before it can reach native. That is
1178
+ * deliberate rather than a limitation dodged: the preferences API raises an
1179
+ * Objective-C exception for a value that is not a property-list type, and Zig
1180
+ * cannot catch one — so refusing containers here is what makes that crash
1181
+ * unreachable. Serialise structure yourself:
1182
+ * `prefs.set(k, JSON.stringify(v))`.
1183
+ */
1184
+ export type PreferenceValue = string | number | boolean;
1185
+ export interface CraftPreferencesInfo {
1186
+ /**
1187
+ * The preferences domain in use — the bundle identifier inside a packaged
1188
+ * `.app`, the executable's name otherwise.
1189
+ */
1190
+ domain: string;
1191
+ /** The key prefix craft namespaces its own preferences under. */
1192
+ prefix: string;
1193
+ /** How many keys the app currently has stored. */
1194
+ count: number;
1195
+ /** A copy-pasteable command that prints the domain. */
1196
+ readCommand: string;
1197
+ }
1198
+ /**
1199
+ * A small preference store over the platform's own mechanism, so `defaults
1200
+ * read` and a native settings pane both see what the app wrote.
1201
+ *
1202
+ * macOS only. Values are stored as native property-list types under a reserved
1203
+ * key prefix, so clearing craft's preferences cannot disturb the AppKit and
1204
+ * WebKit keys that share the same domain.
1205
+ */
1206
+ export interface CraftPreferencesAPI {
1207
+ /**
1208
+ * Read a preference.
1209
+ *
1210
+ * `fallback` is returned when the key is absent — and also when what is
1211
+ * stored is of a different type from the fallback, which is how a value left
1212
+ * behind by an older build of the app degrades to the default rather than to
1213
+ * a surprise. Call `get(key)` with no fallback to read whatever is stored.
1214
+ *
1215
+ * Rejects with `code: 'PREFS_FOREIGN_VALUE'` if something outside craft
1216
+ * wrote a value craft cannot represent, rather than coercing it away.
1217
+ */
1218
+ get: <T extends PreferenceValue>(key: string, fallback?: T) => Promise<T | undefined>;
1219
+ /**
1220
+ * Write a preference. Resolves once the value is on disk, not merely once
1221
+ * the message was posted.
1222
+ *
1223
+ * Rejects with `code: 'PREFS_UNSUPPORTED_VALUE'` for anything that is not a
1224
+ * string, number or boolean; `'PREFS_NON_FINITE'` for NaN or Infinity;
1225
+ * `'PREFS_VALUE_TOO_LARGE'` past 8 KiB; `'PREFS_BAD_KEY'` for a key outside
1226
+ * `/^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/`.
1227
+ */
1228
+ set: (key: string, value: PreferenceValue) => Promise<void>;
1229
+ /** Remove a preference. Resolves to whether it was there to begin with. */
1230
+ delete: (key: string) => Promise<boolean>;
1231
+ /**
1232
+ * Remove every preference this app has set, and nothing else. Resolves to
1233
+ * how many were removed.
1234
+ */
1235
+ clear: () => Promise<number>;
1236
+ /** Every key this app has set, sorted. */
1237
+ keys: () => Promise<string[]>;
1238
+ /**
1239
+ * Which domain the preferences are actually landing in.
1240
+ *
1241
+ * Worth having: an unbundled dev-mode binary writes to a domain named after
1242
+ * the executable while a packaged `.app` writes to its bundle identifier, so
1243
+ * preferences set during development can appear to vanish once the app is
1244
+ * packaged.
1245
+ */
1246
+ info: () => Promise<CraftPreferencesInfo>;
1247
+ }
1248
+ /**
1249
+ * The Cmd+, convention.
1250
+ *
1251
+ * craft's default App menu ships a `Settings…` item, and this is where its
1252
+ * click arrives. An app that replaces the whole menu bar with
1253
+ * `craft.menu.set()` loses the default item, and can declare
1254
+ * `{ id: 'settings', role: 'settings', label: 'Settings…', shortcut: 'cmd+,' }`
1255
+ * to get it back — the same handler answers either way.
1256
+ */
1257
+ export interface CraftSettingsAPI {
1258
+ /** Subscribe to Settings being opened. Returns an unsubscribe function. */
1259
+ onOpen: (handler: (event: {
1260
+ source: string;
1261
+ }) => void) => () => void;
1262
+ /**
1263
+ * Open settings from the app's own UI — a gear button, say — so it lands in
1264
+ * the same handler as Cmd+, rather than needing a second code path. Purely
1265
+ * page-local; nothing crosses the bridge.
1266
+ */
1267
+ open: (source?: string) => Promise<void>;
1268
+ }
1269
+ /** A global hotkey, as `craft.shortcuts.list()` reports it. */
1270
+ export interface GlobalShortcut {
1271
+ /** The id the app registered it under. */
1272
+ id: string;
1273
+ /**
1274
+ * Craft's canonical spelling of the combination — `Cmd+Delete`, whatever
1275
+ * the app originally wrote — so it can be passed straight back to
1276
+ * `register()`.
1277
+ */
1278
+ accelerator: string;
1279
+ /** The key alone, canonically named. */
1280
+ key: string;
1281
+ /** False while `disable()` has the key released back to the system. */
1282
+ enabled: boolean;
1283
+ }
1284
+ /** Why a registration was refused. */
1285
+ export interface GlobalShortcutError {
1286
+ /** The id from the payload that failed, or `''` if it had none. */
1287
+ id: string;
1288
+ /** Craft's error code, e.g. `NATIVE_CALL_FAILED`, `INVALID_PARAMETER`. */
1289
+ code: string;
1290
+ message: string;
1291
+ }
1292
+ /**
1293
+ * System-wide hotkeys: they fire whether or not the app is frontmost.
1294
+ *
1295
+ * Accelerators are `+`-separated and case-insensitive — `'Cmd+Shift+H'`. The
1296
+ * last component is the key, everything before it a modifier (`Cmd`/`Command`/
1297
+ * `Meta`, `Ctrl`/`Control`, `Alt`/`Option`, `Shift`, or `CmdOrCtrl` for the
1298
+ * platform's own). Keys are named by position on the keyboard, not by the
1299
+ * character they produce, so a binding survives a layout change.
1300
+ *
1301
+ * At least one of Command, Control or Option is required, except on the
1302
+ * function keys: a bare global hotkey on `H` would mean no application on the
1303
+ * system ever saw the user type an h again.
1304
+ */
1305
+ export interface CraftGlobalShortcutsAPI {
1306
+ /**
1307
+ * Reserve a combination system-wide. Registering an `id` twice replaces the
1308
+ * first binding.
1309
+ *
1310
+ * The promise resolves once the message is posted, **not** once the key is
1311
+ * reserved — so a combination that belongs to another app or to the system
1312
+ * resolves here and reports on {@link CraftGlobalShortcutsAPI.onError}.
1313
+ */
1314
+ register: (id: string, accelerator: string) => Promise<void>;
1315
+ /** Give the key back to the system and forget the shortcut. */
1316
+ unregister: (id: string) => Promise<void>;
1317
+ /** The same, for every shortcut this app holds. */
1318
+ unregisterAll: () => Promise<void>;
1319
+ /**
1320
+ * Stop firing *and release the key*, so other apps can use it again. The
1321
+ * shortcut stays listed. Holding a reservation while dropping the event
1322
+ * would make the combination dead in every other app for as long as Craft
1323
+ * ran.
1324
+ */
1325
+ disable: (id: string) => Promise<void>;
1326
+ /**
1327
+ * Take the key back. Can fail if something else claimed it while it was
1328
+ * released, which reports on {@link CraftGlobalShortcutsAPI.onError}.
1329
+ */
1330
+ enable: (id: string) => Promise<void>;
1331
+ /** Whether the id is known — enabled or not. */
1332
+ isRegistered: (id: string) => Promise<boolean>;
1333
+ list: () => Promise<GlobalShortcut[]>;
1334
+ /** Every press of a registered, enabled shortcut. Returns an unsubscribe. */
1335
+ on: (handler: (event: {
1336
+ id: string;
1337
+ accelerator: string;
1338
+ }) => void) => () => void;
1339
+ /**
1340
+ * Where every fire-and-forget call reports its failure — `register`,
1341
+ * `enable`, `disable`, `unregister`. (`isRegistered` and `list` are
1342
+ * requests and reject their own promise instead.) Returns an unsubscribe.
1343
+ */
1344
+ onError: (handler: (error: GlobalShortcutError) => void) => () => void;
926
1345
  }
927
1346
  /**
928
1347
  * Focus / Do Not Disturb authorization state, mirroring
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "craft-native",
3
- "version": "0.0.76",
3
+ "version": "0.0.78",
4
4
  "type": "module",
5
5
  "description": "Build desktop apps with web languages - TypeScript SDK for Craft",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",