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.
- package/dist/api/process.d.ts +9 -3
- package/dist/api/window.d.ts +17 -11
- package/dist/cli.js +94 -18
- package/dist/index.cjs +104 -19
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +104 -19
- package/dist/ios/templates/CraftApp.swift +17 -25
- package/dist/package.d.ts +69 -0
- package/dist/types.d.ts +426 -7
- package/package.json +1 -1
- package/dist/bridge/android.d.ts +0 -573
- package/dist/bridge/ios.d.ts +0 -527
|
@@ -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
|
-
|
|
2239
|
+
console.warn('[craft] ota.configure stored locally; OTA is not implemented natively');
|
|
2228
2240
|
},
|
|
2229
2241
|
checkForUpdate: function() {
|
|
2230
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|