@moldable-ai/ui 0.2.17 → 0.2.19
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/components/chat/chat-panel.d.ts +18 -12
- package/dist/components/chat/chat-panel.d.ts.map +1 -1
- package/dist/components/chat/chat-panel.js +73 -19
- package/dist/components/chat/index.d.ts +2 -1
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +2 -1
- package/dist/components/chat/model-effort-power-effects.d.ts +7 -0
- package/dist/components/chat/model-effort-power-effects.d.ts.map +1 -0
- package/dist/components/chat/model-effort-power-effects.js +54 -0
- package/dist/components/chat/model-effort-selector.d.ts +26 -0
- package/dist/components/chat/model-effort-selector.d.ts.map +1 -0
- package/dist/components/chat/model-effort-selector.js +179 -0
- package/dist/components/chat/model-effort-ultra-effects.d.ts +5 -0
- package/dist/components/chat/model-effort-ultra-effects.d.ts.map +1 -0
- package/dist/components/chat/model-effort-ultra-effects.js +56 -0
- package/dist/components/chat/tool-handlers.d.ts.map +1 -1
- package/dist/components/hardware/bluetooth-panel-helpers.d.ts +9 -0
- package/dist/components/hardware/bluetooth-panel-helpers.d.ts.map +1 -0
- package/dist/components/hardware/bluetooth-panel-helpers.js +37 -0
- package/dist/components/hardware/bluetooth-panel.d.ts +56 -0
- package/dist/components/hardware/bluetooth-panel.d.ts.map +1 -0
- package/dist/components/hardware/bluetooth-panel.js +470 -0
- package/dist/components/hardware/camera-preview.d.ts +39 -0
- package/dist/components/hardware/camera-preview.d.ts.map +1 -0
- package/dist/components/hardware/camera-preview.js +228 -0
- package/dist/components/hardware/capability-badge.d.ts +23 -0
- package/dist/components/hardware/capability-badge.d.ts.map +1 -0
- package/dist/components/hardware/capability-badge.js +39 -0
- package/dist/components/hardware/capability-matrix.d.ts +14 -0
- package/dist/components/hardware/capability-matrix.d.ts.map +1 -0
- package/dist/components/hardware/capability-matrix.js +97 -0
- package/dist/components/hardware/clipboard-button.d.ts +26 -0
- package/dist/components/hardware/clipboard-button.d.ts.map +1 -0
- package/dist/components/hardware/clipboard-button.js +114 -0
- package/dist/components/hardware/device-io-controls.d.ts +4 -0
- package/dist/components/hardware/device-io-controls.d.ts.map +1 -0
- package/dist/components/hardware/device-io-controls.js +28 -0
- package/dist/components/hardware/device-list.d.ts +20 -0
- package/dist/components/hardware/device-list.d.ts.map +1 -0
- package/dist/components/hardware/device-list.js +14 -0
- package/dist/components/hardware/display-map.d.ts +23 -0
- package/dist/components/hardware/display-map.d.ts.map +1 -0
- package/dist/components/hardware/display-map.js +114 -0
- package/dist/components/hardware/haptic-button.d.ts +26 -0
- package/dist/components/hardware/haptic-button.d.ts.map +1 -0
- package/dist/components/hardware/haptic-button.js +94 -0
- package/dist/components/hardware/hardware-panel.d.ts +11 -0
- package/dist/components/hardware/hardware-panel.d.ts.map +1 -0
- package/dist/components/hardware/hardware-panel.js +28 -0
- package/dist/components/hardware/hid-panel.d.ts +40 -0
- package/dist/components/hardware/hid-panel.d.ts.map +1 -0
- package/dist/components/hardware/hid-panel.js +373 -0
- package/dist/components/hardware/index.d.ts +27 -0
- package/dist/components/hardware/index.d.ts.map +1 -0
- package/dist/components/hardware/index.js +26 -0
- package/dist/components/hardware/live-indicator.d.ts +15 -0
- package/dist/components/hardware/live-indicator.d.ts.map +1 -0
- package/dist/components/hardware/live-indicator.js +26 -0
- package/dist/components/hardware/local-auth-button.d.ts +33 -0
- package/dist/components/hardware/local-auth-button.d.ts.map +1 -0
- package/dist/components/hardware/local-auth-button.js +115 -0
- package/dist/components/hardware/location-panel.d.ts +29 -0
- package/dist/components/hardware/location-panel.d.ts.map +1 -0
- package/dist/components/hardware/location-panel.js +154 -0
- package/dist/components/hardware/microphone-meter.d.ts +35 -0
- package/dist/components/hardware/microphone-meter.d.ts.map +1 -0
- package/dist/components/hardware/microphone-meter.js +244 -0
- package/dist/components/hardware/midi-monitor.d.ts +54 -0
- package/dist/components/hardware/midi-monitor.d.ts.map +1 -0
- package/dist/components/hardware/midi-monitor.js +534 -0
- package/dist/components/hardware/notification-panel.d.ts +24 -0
- package/dist/components/hardware/notification-panel.d.ts.map +1 -0
- package/dist/components/hardware/notification-panel.js +124 -0
- package/dist/components/hardware/power-blocker-controller.d.ts +19 -0
- package/dist/components/hardware/power-blocker-controller.d.ts.map +1 -0
- package/dist/components/hardware/power-blocker-controller.js +83 -0
- package/dist/components/hardware/power-panel.d.ts +67 -0
- package/dist/components/hardware/power-panel.d.ts.map +1 -0
- package/dist/components/hardware/power-panel.js +309 -0
- package/dist/components/hardware/readout.d.ts +10 -0
- package/dist/components/hardware/readout.d.ts.map +1 -0
- package/dist/components/hardware/readout.js +11 -0
- package/dist/components/hardware/screen-share-preview.d.ts +36 -0
- package/dist/components/hardware/screen-share-preview.d.ts.map +1 -0
- package/dist/components/hardware/screen-share-preview.js +191 -0
- package/dist/components/hardware/secure-storage-panel.d.ts +36 -0
- package/dist/components/hardware/secure-storage-panel.d.ts.map +1 -0
- package/dist/components/hardware/secure-storage-panel.js +193 -0
- package/dist/components/hardware/serial-console.d.ts +64 -0
- package/dist/components/hardware/serial-console.d.ts.map +1 -0
- package/dist/components/hardware/serial-console.js +399 -0
- package/dist/components/hardware/shortcut-recorder.d.ts +35 -0
- package/dist/components/hardware/shortcut-recorder.d.ts.map +1 -0
- package/dist/components/hardware/shortcut-recorder.js +217 -0
- package/dist/components/hardware/stream-log.d.ts +35 -0
- package/dist/components/hardware/stream-log.d.ts.map +1 -0
- package/dist/components/hardware/stream-log.js +65 -0
- package/dist/components/hardware/system-audio-monitor.d.ts +34 -0
- package/dist/components/hardware/system-audio-monitor.d.ts.map +1 -0
- package/dist/components/hardware/system-audio-monitor.js +275 -0
- package/dist/components/hardware/usb-panel.d.ts +54 -0
- package/dist/components/hardware/usb-panel.d.ts.map +1 -0
- package/dist/components/hardware/usb-panel.js +363 -0
- package/dist/components/hardware/use-capability.d.ts +20 -0
- package/dist/components/hardware/use-capability.d.ts.map +1 -0
- package/dist/components/hardware/use-capability.js +61 -0
- package/dist/components/ui/input-group.d.ts +1 -1
- package/dist/components/ui/slider.d.ts +4 -1
- package/dist/components/ui/slider.d.ts.map +1 -1
- package/dist/components/ui/slider.js +2 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/lib/commands.d.ts +59 -0
- package/dist/lib/commands.d.ts.map +1 -1
- package/dist/lib/commands.js +97 -0
- package/dist/lib/native-hardware/bluetooth-access.d.ts +14 -0
- package/dist/lib/native-hardware/bluetooth-access.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-access.js +40 -0
- package/dist/lib/native-hardware/bluetooth-connection.d.ts +19 -0
- package/dist/lib/native-hardware/bluetooth-connection.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-connection.js +88 -0
- package/dist/lib/native-hardware/bluetooth-gatt.d.ts +7 -0
- package/dist/lib/native-hardware/bluetooth-gatt.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-gatt.js +77 -0
- package/dist/lib/native-hardware/bluetooth-internal.d.ts +10 -0
- package/dist/lib/native-hardware/bluetooth-internal.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-internal.js +105 -0
- package/dist/lib/native-hardware/bluetooth-native-fallback.d.ts +33 -0
- package/dist/lib/native-hardware/bluetooth-native-fallback.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-native-fallback.js +214 -0
- package/dist/lib/native-hardware/bluetooth-types.d.ts +85 -0
- package/dist/lib/native-hardware/bluetooth-types.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-types.js +2 -0
- package/dist/lib/native-hardware/bluetooth.d.ts +7 -0
- package/dist/lib/native-hardware/bluetooth.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth.js +4 -0
- package/dist/lib/native-hardware/bridge.d.ts +23 -0
- package/dist/lib/native-hardware/bridge.d.ts.map +1 -0
- package/dist/lib/native-hardware/bridge.js +266 -0
- package/dist/lib/native-hardware/capabilities.d.ts +7 -0
- package/dist/lib/native-hardware/capabilities.d.ts.map +1 -0
- package/dist/lib/native-hardware/capabilities.js +95 -0
- package/dist/lib/native-hardware/clipboard.d.ts +11 -0
- package/dist/lib/native-hardware/clipboard.d.ts.map +1 -0
- package/dist/lib/native-hardware/clipboard.js +55 -0
- package/dist/lib/native-hardware/device-io-errors.d.ts +11 -0
- package/dist/lib/native-hardware/device-io-errors.d.ts.map +1 -0
- package/dist/lib/native-hardware/device-io-errors.js +82 -0
- package/dist/lib/native-hardware/displays.d.ts +38 -0
- package/dist/lib/native-hardware/displays.d.ts.map +1 -0
- package/dist/lib/native-hardware/displays.js +12 -0
- package/dist/lib/native-hardware/global-shortcuts.d.ts +86 -0
- package/dist/lib/native-hardware/global-shortcuts.d.ts.map +1 -0
- package/dist/lib/native-hardware/global-shortcuts.js +351 -0
- package/dist/lib/native-hardware/haptics.d.ts +25 -0
- package/dist/lib/native-hardware/haptics.d.ts.map +1 -0
- package/dist/lib/native-hardware/haptics.js +18 -0
- package/dist/lib/native-hardware/hid-native.d.ts +26 -0
- package/dist/lib/native-hardware/hid-native.d.ts.map +1 -0
- package/dist/lib/native-hardware/hid-native.js +229 -0
- package/dist/lib/native-hardware/hid.d.ts +52 -0
- package/dist/lib/native-hardware/hid.d.ts.map +1 -0
- package/dist/lib/native-hardware/hid.js +160 -0
- package/dist/lib/native-hardware/index.d.ts +38 -0
- package/dist/lib/native-hardware/index.d.ts.map +1 -0
- package/dist/lib/native-hardware/index.js +20 -0
- package/dist/lib/native-hardware/local-authentication.d.ts +32 -0
- package/dist/lib/native-hardware/local-authentication.d.ts.map +1 -0
- package/dist/lib/native-hardware/local-authentication.js +46 -0
- package/dist/lib/native-hardware/location.d.ts +49 -0
- package/dist/lib/native-hardware/location.d.ts.map +1 -0
- package/dist/lib/native-hardware/location.js +114 -0
- package/dist/lib/native-hardware/media-permissions.d.ts +28 -0
- package/dist/lib/native-hardware/media-permissions.d.ts.map +1 -0
- package/dist/lib/native-hardware/media-permissions.js +37 -0
- package/dist/lib/native-hardware/media.d.ts +17 -0
- package/dist/lib/native-hardware/media.d.ts.map +1 -0
- package/dist/lib/native-hardware/media.js +99 -0
- package/dist/lib/native-hardware/midi-access.d.ts +13 -0
- package/dist/lib/native-hardware/midi-access.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi-access.js +66 -0
- package/dist/lib/native-hardware/midi-internal.d.ts +12 -0
- package/dist/lib/native-hardware/midi-internal.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi-internal.js +88 -0
- package/dist/lib/native-hardware/midi-native-fallback.d.ts +27 -0
- package/dist/lib/native-hardware/midi-native-fallback.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi-native-fallback.js +127 -0
- package/dist/lib/native-hardware/midi-ports.d.ts +6 -0
- package/dist/lib/native-hardware/midi-ports.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi-ports.js +115 -0
- package/dist/lib/native-hardware/midi-types.d.ts +25 -0
- package/dist/lib/native-hardware/midi-types.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi-types.js +1 -0
- package/dist/lib/native-hardware/midi.d.ts +6 -0
- package/dist/lib/native-hardware/midi.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi.js +3 -0
- package/dist/lib/native-hardware/native-device-io.d.ts +28 -0
- package/dist/lib/native-hardware/native-device-io.d.ts.map +1 -0
- package/dist/lib/native-hardware/native-device-io.js +91 -0
- package/dist/lib/native-hardware/native-device-lifecycle.d.ts +13 -0
- package/dist/lib/native-hardware/native-device-lifecycle.d.ts.map +1 -0
- package/dist/lib/native-hardware/native-device-lifecycle.js +40 -0
- package/dist/lib/native-hardware/notifications.d.ts +38 -0
- package/dist/lib/native-hardware/notifications.d.ts.map +1 -0
- package/dist/lib/native-hardware/notifications.js +50 -0
- package/dist/lib/native-hardware/power-session.d.ts +113 -0
- package/dist/lib/native-hardware/power-session.d.ts.map +1 -0
- package/dist/lib/native-hardware/power-session.js +264 -0
- package/dist/lib/native-hardware/secure-storage.d.ts +36 -0
- package/dist/lib/native-hardware/secure-storage.d.ts.map +1 -0
- package/dist/lib/native-hardware/secure-storage.js +64 -0
- package/dist/lib/native-hardware/serial-native.d.ts +23 -0
- package/dist/lib/native-hardware/serial-native.d.ts.map +1 -0
- package/dist/lib/native-hardware/serial-native.js +181 -0
- package/dist/lib/native-hardware/serial.d.ts +76 -0
- package/dist/lib/native-hardware/serial.d.ts.map +1 -0
- package/dist/lib/native-hardware/serial.js +212 -0
- package/dist/lib/native-hardware/system-audio.d.ts +127 -0
- package/dist/lib/native-hardware/system-audio.d.ts.map +1 -0
- package/dist/lib/native-hardware/system-audio.js +330 -0
- package/dist/lib/native-hardware/types.d.ts +62 -0
- package/dist/lib/native-hardware/types.d.ts.map +1 -0
- package/dist/lib/native-hardware/types.js +27 -0
- package/dist/lib/native-hardware/usb-native.d.ts +39 -0
- package/dist/lib/native-hardware/usb-native.d.ts.map +1 -0
- package/dist/lib/native-hardware/usb-native.js +217 -0
- package/dist/lib/native-hardware/usb.d.ts +85 -0
- package/dist/lib/native-hardware/usb.d.ts.map +1 -0
- package/dist/lib/native-hardware/usb.js +222 -0
- package/package.json +1 -1
- package/src/styles/index.css +377 -0
package/dist/lib/commands.js
CHANGED
|
@@ -211,6 +211,103 @@ export function callMoldableApp(targetAppId, method, params, options = {}) {
|
|
|
211
211
|
method,
|
|
212
212
|
params,
|
|
213
213
|
scopes: options.scopes ?? [method],
|
|
214
|
+
timeoutMs: options.timeoutMs,
|
|
215
|
+
requestAccess: options.requestAccess ?? true,
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Ask the Moldable desktop host to enter, exit, or toggle native window fullscreen.
|
|
221
|
+
*
|
|
222
|
+
* This avoids iframe Fullscreen API permissions policy issues inside the desktop
|
|
223
|
+
* shell. Outside Moldable, it falls back to the browser Fullscreen API.
|
|
224
|
+
*/
|
|
225
|
+
export async function setMoldableWindowFullscreen(fullscreen = true, options = {}) {
|
|
226
|
+
if (!isInMoldable()) {
|
|
227
|
+
const requested = fullscreen === 'toggle' ? !document.fullscreenElement : fullscreen;
|
|
228
|
+
if (requested) {
|
|
229
|
+
await document.documentElement.requestFullscreen();
|
|
230
|
+
}
|
|
231
|
+
else if (document.fullscreenElement && document.exitFullscreen) {
|
|
232
|
+
await document.exitFullscreen();
|
|
233
|
+
}
|
|
234
|
+
return { fullscreen: Boolean(document.fullscreenElement) };
|
|
235
|
+
}
|
|
236
|
+
return new Promise((resolve, reject) => {
|
|
237
|
+
const requestId = makeRequestId('window-fullscreen');
|
|
238
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
239
|
+
const timeout = window.setTimeout(() => {
|
|
240
|
+
window.removeEventListener('message', handleResponse);
|
|
241
|
+
reject(new Error('Fullscreen request timed out.'));
|
|
242
|
+
}, timeoutMs);
|
|
243
|
+
function handleResponse(event) {
|
|
244
|
+
if (event.data?.type !== 'moldable:set-window-fullscreen-result')
|
|
245
|
+
return;
|
|
246
|
+
if (event.data?.requestId !== requestId)
|
|
247
|
+
return;
|
|
248
|
+
window.clearTimeout(timeout);
|
|
249
|
+
window.removeEventListener('message', handleResponse);
|
|
250
|
+
const response = event.data;
|
|
251
|
+
if (response.ok && response.result) {
|
|
252
|
+
resolve(response.result);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
reject(new Error(response.error?.message ||
|
|
256
|
+
response.error?.code ||
|
|
257
|
+
'Fullscreen request failed.'));
|
|
258
|
+
}
|
|
259
|
+
window.addEventListener('message', handleResponse);
|
|
260
|
+
sendToMoldable({
|
|
261
|
+
type: 'moldable:set-window-fullscreen',
|
|
262
|
+
requestId,
|
|
263
|
+
fullscreen,
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Publish a static artifact through the Moldable desktop host.
|
|
269
|
+
*
|
|
270
|
+
* Apps provide source file paths; Moldable owns the publishing key and turns the bundle
|
|
271
|
+
* into signed manifest, file upload, and commit requests. The returned URL is
|
|
272
|
+
* public but unlisted.
|
|
273
|
+
*/
|
|
274
|
+
export async function publishMoldableArtifact(options) {
|
|
275
|
+
if (!isInMoldable()) {
|
|
276
|
+
throw new Error('Artifact publishing is only available inside Moldable.');
|
|
277
|
+
}
|
|
278
|
+
return new Promise((resolve, reject) => {
|
|
279
|
+
const requestId = makeRequestId('artifact-publish');
|
|
280
|
+
const timeoutMs = options.timeoutMs ?? 300_000;
|
|
281
|
+
const timeout = window.setTimeout(() => {
|
|
282
|
+
window.removeEventListener('message', handleResponse);
|
|
283
|
+
reject(new Error('Artifact publish timed out.'));
|
|
284
|
+
}, timeoutMs);
|
|
285
|
+
function handleResponse(event) {
|
|
286
|
+
if (event.data?.type !== 'moldable:artifact-publish-result')
|
|
287
|
+
return;
|
|
288
|
+
if (event.data?.requestId !== requestId)
|
|
289
|
+
return;
|
|
290
|
+
window.clearTimeout(timeout);
|
|
291
|
+
window.removeEventListener('message', handleResponse);
|
|
292
|
+
if (event.data.ok) {
|
|
293
|
+
resolve(event.data.result);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
reject(new Error(event.data.error?.message || 'Artifact publish failed.'));
|
|
297
|
+
}
|
|
298
|
+
window.addEventListener('message', handleResponse);
|
|
299
|
+
sendToMoldable({
|
|
300
|
+
type: 'moldable:artifact-publish',
|
|
301
|
+
requestId,
|
|
302
|
+
kind: options.kind,
|
|
303
|
+
title: options.title,
|
|
304
|
+
entrypoint: options.entrypoint,
|
|
305
|
+
metadata: options.metadata ?? {},
|
|
306
|
+
files: options.files.map((file) => ({
|
|
307
|
+
path: file.path,
|
|
308
|
+
contentType: file.contentType,
|
|
309
|
+
sourcePath: file.sourcePath,
|
|
310
|
+
})),
|
|
214
311
|
requestAccess: options.requestAccess ?? true,
|
|
215
312
|
});
|
|
216
313
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MoldableBluetoothAvailability, MoldableBluetoothDevice, MoldableBluetoothOperationOptions, MoldableBluetoothRequestDeviceOptions, MoldableBluetoothRequestOptions } from './bluetooth-types';
|
|
2
|
+
/** Checks only for the standards-based Web Bluetooth entry point. */
|
|
3
|
+
export declare function isMoldableBluetoothSupported(): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Returns adapter availability when the runtime exposes it. `unknown` means the
|
|
6
|
+
* API exists but cannot report adapter state, not that Bluetooth is available.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getMoldableBluetoothAvailability(options?: MoldableBluetoothOperationOptions): Promise<MoldableBluetoothAvailability>;
|
|
9
|
+
/**
|
|
10
|
+
* Opens the browser's device chooser. This must be called directly from a
|
|
11
|
+
* trusted user action; it never silently scans for or selects devices.
|
|
12
|
+
*/
|
|
13
|
+
export declare function requestMoldableBluetoothDevice(deviceOptions: MoldableBluetoothRequestDeviceOptions, options?: MoldableBluetoothRequestOptions): Promise<MoldableBluetoothDevice>;
|
|
14
|
+
//# sourceMappingURL=bluetooth-access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bluetooth-access.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/bluetooth-access.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,6BAA6B,EAC7B,uBAAuB,EACvB,iCAAiC,EACjC,qCAAqC,EACrC,+BAA+B,EAChC,MAAM,mBAAmB,CAAA;AAE1B,qEAAqE;AACrE,wBAAgB,4BAA4B,IAAI,OAAO,CAGtD;AAED;;;GAGG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,6BAA6B,CAAC,CAexC;AAED;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,aAAa,EAAE,qCAAqC,EACpD,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,uBAAuB,CAAC,CAelC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { abortableBluetooth, assertBluetoothUserGesture, bluetoothError, getBluetoothAPI, getBluetoothNavigator, validateBluetoothRequestOptions, } from './bluetooth-internal';
|
|
2
|
+
/** Checks only for the standards-based Web Bluetooth entry point. */
|
|
3
|
+
export function isMoldableBluetoothSupported() {
|
|
4
|
+
const api = getBluetoothNavigator()?.bluetooth;
|
|
5
|
+
return Boolean(api && typeof api.requestDevice === 'function');
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Returns adapter availability when the runtime exposes it. `unknown` means the
|
|
9
|
+
* API exists but cannot report adapter state, not that Bluetooth is available.
|
|
10
|
+
*/
|
|
11
|
+
export async function getMoldableBluetoothAvailability(options = {}) {
|
|
12
|
+
const operation = 'check Bluetooth availability';
|
|
13
|
+
const api = getBluetoothAPI(operation);
|
|
14
|
+
if (typeof api.getAvailability !== 'function')
|
|
15
|
+
return 'unknown';
|
|
16
|
+
try {
|
|
17
|
+
return (await abortableBluetooth(api.getAvailability(), options.signal, operation))
|
|
18
|
+
? 'available'
|
|
19
|
+
: 'unavailable';
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
throw bluetoothError(error, operation);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Opens the browser's device chooser. This must be called directly from a
|
|
27
|
+
* trusted user action; it never silently scans for or selects devices.
|
|
28
|
+
*/
|
|
29
|
+
export async function requestMoldableBluetoothDevice(deviceOptions, options = {}) {
|
|
30
|
+
const operation = 'request a Bluetooth device';
|
|
31
|
+
const api = getBluetoothAPI(operation);
|
|
32
|
+
validateBluetoothRequestOptions(deviceOptions);
|
|
33
|
+
assertBluetoothUserGesture();
|
|
34
|
+
try {
|
|
35
|
+
return await abortableBluetooth(api.requestDevice(deviceOptions), options.signal, operation, (device) => device.gatt?.disconnect());
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw bluetoothError(error, operation);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MoldableBluetoothConnectionOptions, MoldableBluetoothDevice, MoldableBluetoothOperationOptions, MoldableBluetoothRemoteGATTServer, MoldableBluetoothRemoteGATTService, MoldableBluetoothUUID } from './bluetooth-types';
|
|
2
|
+
export declare class MoldableBluetoothConnection {
|
|
3
|
+
readonly device: MoldableBluetoothDevice;
|
|
4
|
+
readonly server: MoldableBluetoothRemoteGATTServer;
|
|
5
|
+
private readonly abortSignal?;
|
|
6
|
+
private readonly disconnectListeners;
|
|
7
|
+
private isClosed;
|
|
8
|
+
constructor(device: MoldableBluetoothDevice, server: MoldableBluetoothRemoteGATTServer, options?: MoldableBluetoothConnectionOptions);
|
|
9
|
+
get connected(): boolean;
|
|
10
|
+
onDisconnected(listener: () => void, signal?: AbortSignal): () => void;
|
|
11
|
+
getPrimaryService(service: MoldableBluetoothUUID, options?: MoldableBluetoothOperationOptions): Promise<MoldableBluetoothRemoteGATTService>;
|
|
12
|
+
disconnect(): void;
|
|
13
|
+
private readonly handleAbort;
|
|
14
|
+
private readonly handleDisconnected;
|
|
15
|
+
private finish;
|
|
16
|
+
}
|
|
17
|
+
/** Connects a previously user-selected device and owns disconnect cleanup. */
|
|
18
|
+
export declare function connectMoldableBluetoothDevice(device: MoldableBluetoothDevice, options?: MoldableBluetoothConnectionOptions): Promise<MoldableBluetoothConnection>;
|
|
19
|
+
//# sourceMappingURL=bluetooth-connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bluetooth-connection.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/bluetooth-connection.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,kCAAkC,EAClC,uBAAuB,EACvB,iCAAiC,EACjC,iCAAiC,EACjC,kCAAkC,EAClC,qBAAqB,EACtB,MAAM,mBAAmB,CAAA;AAE1B,qBAAa,2BAA2B;IACtC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAA;IACxC,QAAQ,CAAC,MAAM,EAAE,iCAAiC,CAAA;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAa;IAC1C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAwB;IAC5D,OAAO,CAAC,QAAQ,CAAQ;gBAGtB,MAAM,EAAE,uBAAuB,EAC/B,MAAM,EAAE,iCAAiC,EACzC,OAAO,GAAE,kCAAuC;IASlD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,cAAc,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,IAAI;IAWhE,iBAAiB,CACrB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,kCAAkC,CAAC;IAmB9C,UAAU,IAAI,IAAI;IAMlB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IACtD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA0B;IAE7D,OAAO,CAAC,MAAM;CAqBf;AAED,8EAA8E;AAC9E,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,uBAAuB,EAC/B,OAAO,GAAE,kCAAuC,GAC/C,OAAO,CAAC,2BAA2B,CAAC,CA0BtC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { abortableBluetooth, bluetoothAborted, bluetoothError, } from './bluetooth-internal';
|
|
2
|
+
export class MoldableBluetoothConnection {
|
|
3
|
+
device;
|
|
4
|
+
server;
|
|
5
|
+
abortSignal;
|
|
6
|
+
disconnectListeners = new Set();
|
|
7
|
+
isClosed = false;
|
|
8
|
+
constructor(device, server, options = {}) {
|
|
9
|
+
this.device = device;
|
|
10
|
+
this.server = server;
|
|
11
|
+
this.abortSignal = options.signal;
|
|
12
|
+
device.addEventListener('gattserverdisconnected', this.handleDisconnected);
|
|
13
|
+
options.signal?.addEventListener('abort', this.handleAbort, { once: true });
|
|
14
|
+
}
|
|
15
|
+
get connected() {
|
|
16
|
+
return !this.isClosed && this.server.connected;
|
|
17
|
+
}
|
|
18
|
+
onDisconnected(listener, signal) {
|
|
19
|
+
if (signal?.aborted || this.isClosed)
|
|
20
|
+
return () => undefined;
|
|
21
|
+
this.disconnectListeners.add(listener);
|
|
22
|
+
const cleanup = () => {
|
|
23
|
+
this.disconnectListeners.delete(listener);
|
|
24
|
+
signal?.removeEventListener('abort', cleanup);
|
|
25
|
+
};
|
|
26
|
+
signal?.addEventListener('abort', cleanup, { once: true });
|
|
27
|
+
return cleanup;
|
|
28
|
+
}
|
|
29
|
+
async getPrimaryService(service, options = {}) {
|
|
30
|
+
const operation = `get Bluetooth service ${String(service)}`;
|
|
31
|
+
if (!this.connected) {
|
|
32
|
+
throw bluetoothError(new DOMException('Bluetooth device is disconnected', 'NetworkError'), operation);
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
return await abortableBluetooth(this.server.getPrimaryService(service), options.signal, operation);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw bluetoothError(error, operation);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
disconnect() {
|
|
42
|
+
if (this.isClosed)
|
|
43
|
+
return;
|
|
44
|
+
this.finish(false);
|
|
45
|
+
if (this.server.connected)
|
|
46
|
+
this.server.disconnect();
|
|
47
|
+
}
|
|
48
|
+
handleAbort = () => this.disconnect();
|
|
49
|
+
handleDisconnected = () => this.finish(true);
|
|
50
|
+
finish(notify) {
|
|
51
|
+
if (this.isClosed)
|
|
52
|
+
return;
|
|
53
|
+
this.isClosed = true;
|
|
54
|
+
this.device.removeEventListener('gattserverdisconnected', this.handleDisconnected);
|
|
55
|
+
this.abortSignal?.removeEventListener('abort', this.handleAbort);
|
|
56
|
+
if (notify) {
|
|
57
|
+
const listeners = [...this.disconnectListeners];
|
|
58
|
+
this.disconnectListeners.clear();
|
|
59
|
+
for (const listener of listeners) {
|
|
60
|
+
try {
|
|
61
|
+
listener();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// A consumer callback must not prevent connection cleanup.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
this.disconnectListeners.clear();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Connects a previously user-selected device and owns disconnect cleanup. */
|
|
72
|
+
export async function connectMoldableBluetoothDevice(device, options = {}) {
|
|
73
|
+
const operation = `connect to Bluetooth device ${device.id}`;
|
|
74
|
+
if (!device.gatt) {
|
|
75
|
+
throw bluetoothError(new DOMException('Device does not expose a GATT server', 'NotSupportedError'), operation);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const server = await abortableBluetooth(device.gatt.connect(), options.signal, operation, (lateServer) => lateServer.disconnect());
|
|
79
|
+
if (options.signal?.aborted) {
|
|
80
|
+
server.disconnect();
|
|
81
|
+
throw bluetoothAborted(operation);
|
|
82
|
+
}
|
|
83
|
+
return new MoldableBluetoothConnection(device, server, options);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
throw bluetoothError(error, operation);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MoldableBluetoothOperationOptions, MoldableBluetoothRemoteGATTCharacteristic, MoldableBluetoothRemoteGATTService, MoldableBluetoothUUID, MoldableBluetoothWriteMode } from './bluetooth-types';
|
|
2
|
+
export declare function getMoldableBluetoothCharacteristic(service: MoldableBluetoothRemoteGATTService, characteristic: MoldableBluetoothUUID, options?: MoldableBluetoothOperationOptions): Promise<MoldableBluetoothRemoteGATTCharacteristic>;
|
|
3
|
+
export declare function readMoldableBluetoothValue(characteristic: MoldableBluetoothRemoteGATTCharacteristic, options?: MoldableBluetoothOperationOptions): Promise<DataView>;
|
|
4
|
+
/** Starts notifications and returns async, idempotent subscription cleanup. */
|
|
5
|
+
export declare function observeMoldableBluetoothNotifications(characteristic: MoldableBluetoothRemoteGATTCharacteristic, listener: (event: Event) => void, options?: MoldableBluetoothOperationOptions): Promise<() => Promise<void>>;
|
|
6
|
+
export declare function writeMoldableBluetoothValue(characteristic: MoldableBluetoothRemoteGATTCharacteristic, value: BufferSource, mode?: MoldableBluetoothWriteMode, options?: MoldableBluetoothOperationOptions): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=bluetooth-gatt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bluetooth-gatt.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/bluetooth-gatt.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,iCAAiC,EACjC,yCAAyC,EACzC,kCAAkC,EAClC,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,mBAAmB,CAAA;AAE1B,wBAAsB,kCAAkC,CACtD,OAAO,EAAE,kCAAkC,EAC3C,cAAc,EAAE,qBAAqB,EACrC,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,yCAAyC,CAAC,CAWpD;AAED,wBAAsB,0BAA0B,CAC9C,cAAc,EAAE,yCAAyC,EACzD,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,QAAQ,CAAC,CAWnB;AAED,+EAA+E;AAC/E,wBAAsB,qCAAqC,CACzD,cAAc,EAAE,yCAAyC,EACzD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,EAChC,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAmC9B;AAED,wBAAsB,2BAA2B,CAC/C,cAAc,EAAE,yCAAyC,EACzD,KAAK,EAAE,YAAY,EACnB,IAAI,GAAE,0BAAwC,EAC9C,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { abortableBluetooth, bluetoothAborted, bluetoothError, } from './bluetooth-internal';
|
|
2
|
+
export async function getMoldableBluetoothCharacteristic(service, characteristic, options = {}) {
|
|
3
|
+
const operation = `get Bluetooth characteristic ${String(characteristic)}`;
|
|
4
|
+
try {
|
|
5
|
+
return await abortableBluetooth(service.getCharacteristic(characteristic), options.signal, operation);
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
throw bluetoothError(error, operation);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export async function readMoldableBluetoothValue(characteristic, options = {}) {
|
|
12
|
+
const operation = `read Bluetooth characteristic ${characteristic.uuid}`;
|
|
13
|
+
try {
|
|
14
|
+
return await abortableBluetooth(characteristic.readValue(), options.signal, operation);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
throw bluetoothError(error, operation);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Starts notifications and returns async, idempotent subscription cleanup. */
|
|
21
|
+
export async function observeMoldableBluetoothNotifications(characteristic, listener, options = {}) {
|
|
22
|
+
const operation = `observe Bluetooth characteristic ${characteristic.uuid}`;
|
|
23
|
+
try {
|
|
24
|
+
await abortableBluetooth(characteristic.startNotifications(), options.signal, operation, (lateCharacteristic) => {
|
|
25
|
+
void lateCharacteristic.stopNotifications();
|
|
26
|
+
});
|
|
27
|
+
if (options.signal?.aborted) {
|
|
28
|
+
await characteristic.stopNotifications();
|
|
29
|
+
throw bluetoothAborted(operation);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw bluetoothError(error, operation);
|
|
34
|
+
}
|
|
35
|
+
let active = true;
|
|
36
|
+
const cleanup = async () => {
|
|
37
|
+
if (!active)
|
|
38
|
+
return;
|
|
39
|
+
active = false;
|
|
40
|
+
characteristic.removeEventListener('characteristicvaluechanged', listener);
|
|
41
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
42
|
+
try {
|
|
43
|
+
await characteristic.stopNotifications();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw bluetoothError(error, `stop ${operation}`);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const onAbort = () => void cleanup().catch(() => undefined);
|
|
50
|
+
characteristic.addEventListener('characteristicvaluechanged', listener);
|
|
51
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
52
|
+
return cleanup;
|
|
53
|
+
}
|
|
54
|
+
export async function writeMoldableBluetoothValue(characteristic, value, mode = 'automatic', options = {}) {
|
|
55
|
+
const operation = `write Bluetooth characteristic ${characteristic.uuid}`;
|
|
56
|
+
let write;
|
|
57
|
+
if (mode === 'with-response') {
|
|
58
|
+
write = characteristic.writeValueWithResponse?.bind(characteristic);
|
|
59
|
+
}
|
|
60
|
+
else if (mode === 'without-response') {
|
|
61
|
+
write = characteristic.writeValueWithoutResponse?.bind(characteristic);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
write =
|
|
65
|
+
characteristic.writeValueWithResponse?.bind(characteristic) ??
|
|
66
|
+
characteristic.writeValue.bind(characteristic);
|
|
67
|
+
}
|
|
68
|
+
if (!write) {
|
|
69
|
+
throw bluetoothError(new DOMException(`Bluetooth write mode ${mode} is unavailable`, 'NotSupportedError'), operation);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await abortableBluetooth(write(value), options.signal, operation);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw bluetoothError(error, operation);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MoldableBluetoothAPI, MoldableBluetoothNavigator, MoldableBluetoothRequestDeviceOptions } from './bluetooth-types';
|
|
2
|
+
import { NativeHardwareBridgeError } from './bridge';
|
|
3
|
+
export declare function bluetoothError(error: unknown, operation: string): NativeHardwareBridgeError;
|
|
4
|
+
export declare function bluetoothAborted(operation: string): NativeHardwareBridgeError;
|
|
5
|
+
export declare function getBluetoothNavigator(): MoldableBluetoothNavigator | undefined;
|
|
6
|
+
export declare function getBluetoothAPI(operation: string): MoldableBluetoothAPI;
|
|
7
|
+
export declare function assertBluetoothUserGesture(): void;
|
|
8
|
+
export declare function validateBluetoothRequestOptions(options: MoldableBluetoothRequestDeviceOptions): void;
|
|
9
|
+
export declare function abortableBluetooth<T>(promise: Promise<T>, signal: AbortSignal | undefined, operation: string, onLateResolution?: (value: T) => void): Promise<T>;
|
|
10
|
+
//# sourceMappingURL=bluetooth-internal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bluetooth-internal.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/bluetooth-internal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,0BAA0B,EAC1B,qCAAqC,EACtC,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,yBAAyB,EAE1B,MAAM,UAAU,CAAA;AAGjB,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,GAChB,yBAAyB,CAsC3B;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,CAK7E;AAED,wBAAgB,qBAAqB,IACjC,0BAA0B,GAC1B,SAAS,CAIZ;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,oBAAoB,CASvE;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAWjD;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,qCAAqC,GAC7C,IAAI,CAiBN;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,EACjB,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GACpC,OAAO,CAAC,CAAC,CAAC,CA+BZ"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { NativeHardwareBridgeError, toNativeHardwareBridgeError, } from './bridge';
|
|
2
|
+
export function bluetoothError(error, operation) {
|
|
3
|
+
if (error instanceof NativeHardwareBridgeError)
|
|
4
|
+
return error;
|
|
5
|
+
const name = error instanceof DOMException ? error.name : undefined;
|
|
6
|
+
let code = 'host_error';
|
|
7
|
+
let retryable = false;
|
|
8
|
+
if (name === 'NotSupportedError')
|
|
9
|
+
code = 'unsupported';
|
|
10
|
+
else if (name === 'NotAllowedError' || name === 'SecurityError') {
|
|
11
|
+
code = 'permission_denied';
|
|
12
|
+
}
|
|
13
|
+
else if (name === 'AbortError')
|
|
14
|
+
code = 'aborted';
|
|
15
|
+
else if (name === 'NotFoundError')
|
|
16
|
+
code = 'cancelled';
|
|
17
|
+
else if (name === 'NetworkError' ||
|
|
18
|
+
name === 'InvalidStateError' ||
|
|
19
|
+
name === 'OperationError') {
|
|
20
|
+
code = 'unavailable';
|
|
21
|
+
retryable = true;
|
|
22
|
+
}
|
|
23
|
+
else if (error instanceof TypeError)
|
|
24
|
+
code = 'invalid_request';
|
|
25
|
+
const fallbackMessage = `Unable to ${operation}`;
|
|
26
|
+
return toNativeHardwareBridgeError({
|
|
27
|
+
code,
|
|
28
|
+
message: error instanceof Error
|
|
29
|
+
? error.message || fallbackMessage
|
|
30
|
+
: fallbackMessage,
|
|
31
|
+
details: {
|
|
32
|
+
capability: 'bluetooth',
|
|
33
|
+
operation,
|
|
34
|
+
...(name ? { cause: name } : {}),
|
|
35
|
+
},
|
|
36
|
+
retryable,
|
|
37
|
+
}, fallbackMessage);
|
|
38
|
+
}
|
|
39
|
+
export function bluetoothAborted(operation) {
|
|
40
|
+
return bluetoothError(new DOMException(`Bluetooth ${operation} was aborted`, 'AbortError'), operation);
|
|
41
|
+
}
|
|
42
|
+
export function getBluetoothNavigator() {
|
|
43
|
+
return typeof navigator === 'undefined'
|
|
44
|
+
? undefined
|
|
45
|
+
: navigator;
|
|
46
|
+
}
|
|
47
|
+
export function getBluetoothAPI(operation) {
|
|
48
|
+
const api = getBluetoothNavigator()?.bluetooth;
|
|
49
|
+
if (!api || typeof api.requestDevice !== 'function') {
|
|
50
|
+
throw bluetoothError(new DOMException('Web Bluetooth is unavailable', 'NotSupportedError'), operation);
|
|
51
|
+
}
|
|
52
|
+
return api;
|
|
53
|
+
}
|
|
54
|
+
export function assertBluetoothUserGesture() {
|
|
55
|
+
const activation = getBluetoothNavigator()?.userActivation;
|
|
56
|
+
if (activation && !activation.isActive) {
|
|
57
|
+
throw toNativeHardwareBridgeError({
|
|
58
|
+
code: 'invalid_request',
|
|
59
|
+
message: 'Bluetooth device selection must be requested from a user-initiated action.',
|
|
60
|
+
details: { capability: 'bluetooth', cause: 'user_gesture_required' },
|
|
61
|
+
retryable: true,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function validateBluetoothRequestOptions(options) {
|
|
66
|
+
const hasFilters = 'filters' in options && options.filters !== undefined;
|
|
67
|
+
const acceptsAll = options.acceptAllDevices === true;
|
|
68
|
+
if (hasFilters === acceptsAll) {
|
|
69
|
+
throw bluetoothError(new TypeError('Specify either a non-empty filters array or acceptAllDevices: true'), 'request a Bluetooth device');
|
|
70
|
+
}
|
|
71
|
+
if (hasFilters && options.filters.length === 0) {
|
|
72
|
+
throw bluetoothError(new TypeError('Bluetooth filters must not be empty'), 'request a Bluetooth device');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export function abortableBluetooth(promise, signal, operation, onLateResolution) {
|
|
76
|
+
if (!signal)
|
|
77
|
+
return promise;
|
|
78
|
+
if (signal.aborted)
|
|
79
|
+
return Promise.reject(bluetoothAborted(operation));
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
let settled = false;
|
|
82
|
+
const onAbort = () => {
|
|
83
|
+
if (settled)
|
|
84
|
+
return;
|
|
85
|
+
settled = true;
|
|
86
|
+
reject(bluetoothAborted(operation));
|
|
87
|
+
};
|
|
88
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
89
|
+
void promise.then((value) => {
|
|
90
|
+
signal.removeEventListener('abort', onAbort);
|
|
91
|
+
if (settled) {
|
|
92
|
+
onLateResolution?.(value);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
settled = true;
|
|
96
|
+
resolve(value);
|
|
97
|
+
}, (error) => {
|
|
98
|
+
signal.removeEventListener('abort', onAbort);
|
|
99
|
+
if (settled)
|
|
100
|
+
return;
|
|
101
|
+
settled = true;
|
|
102
|
+
reject(error);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { MoldableBluetoothDevice, MoldableBluetoothRequestDeviceOptions } from './bluetooth-types';
|
|
2
|
+
import { type NativeHardwareBridgeOptions } from './bridge';
|
|
3
|
+
export type MoldableBluetoothTransport = {
|
|
4
|
+
kind: 'web-bluetooth';
|
|
5
|
+
device: MoldableBluetoothDevice;
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'desktop-bridge';
|
|
8
|
+
device: MoldableNativeBluetoothDevice;
|
|
9
|
+
};
|
|
10
|
+
export interface MoldableNativeBluetoothService {
|
|
11
|
+
uuid: string;
|
|
12
|
+
characteristicUuids: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface MoldableNativeBluetoothDevice {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly name: string | null;
|
|
17
|
+
readonly connected: boolean;
|
|
18
|
+
addEventListener(type: 'gattserverdisconnected', listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
19
|
+
removeEventListener(type: 'gattserverdisconnected', listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
20
|
+
getServices(options?: NativeHardwareBridgeOptions): Promise<MoldableNativeBluetoothService[]>;
|
|
21
|
+
read(serviceUuid: string, characteristicUuid: string, options?: NativeHardwareBridgeOptions): Promise<DataView>;
|
|
22
|
+
write(serviceUuid: string, characteristicUuid: string, data: BufferSource, options?: NativeHardwareBridgeOptions & {
|
|
23
|
+
withResponse?: boolean;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
observe(serviceUuid: string, characteristicUuid: string, listener: (value: DataView) => void, options?: NativeHardwareBridgeOptions): Promise<() => Promise<void>>;
|
|
26
|
+
disconnect(options?: NativeHardwareBridgeOptions): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Uses Web Bluetooth first, with a restrictive desktop BLE chooser fallback.
|
|
30
|
+
* The native branch mirrors physical disconnect and adapter-loss events.
|
|
31
|
+
*/
|
|
32
|
+
export declare function requestMoldableBluetoothWithFallback(options: MoldableBluetoothRequestDeviceOptions, bridgeOptions?: NativeHardwareBridgeOptions): Promise<MoldableBluetoothTransport>;
|
|
33
|
+
//# sourceMappingURL=bluetooth-native-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bluetooth-native-fallback.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/bluetooth-native-fallback.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,uBAAuB,EACvB,qCAAqC,EACtC,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,KAAK,2BAA2B,EAEjC,MAAM,UAAU,CAAA;AAMjB,MAAM,MAAM,0BAA0B,GAClC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,6BAA6B,CAAA;CAAE,CAAA;AAErE,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,MAAM,CAAA;IACZ,mBAAmB,EAAE,MAAM,EAAE,CAAA;CAC9B;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,gBAAgB,CACd,IAAI,EAAE,wBAAwB,EAC9B,QAAQ,EAAE,kCAAkC,EAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAA;IACP,mBAAmB,CACjB,IAAI,EAAE,wBAAwB,EAC9B,QAAQ,EAAE,kCAAkC,EAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GACvC,IAAI,CAAA;IACP,WAAW,CACT,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,8BAA8B,EAAE,CAAC,CAAA;IAC5C,IAAI,CACF,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,EAC1B,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACpB,KAAK,CACH,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,EAC1B,IAAI,EAAE,YAAY,EAClB,OAAO,CAAC,EAAE,2BAA2B,GAAG;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,GACjE,OAAO,CAAC,IAAI,CAAC,CAAA;IAChB,OAAO,CACL,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,EAC1B,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,EACnC,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IAC/B,UAAU,CAAC,OAAO,CAAC,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACjE;AA2PD;;;GAGG;AACH,wBAAsB,oCAAoC,CACxD,OAAO,EAAE,qCAAqC,EAC9C,aAAa,GAAE,2BAAgC,GAC9C,OAAO,CAAC,0BAA0B,CAAC,CAsCrC"}
|