@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
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NativeHardwareCapabilityId } from './types';
|
|
2
|
+
type MediaCapability = Extract<NativeHardwareCapabilityId, 'camera' | 'microphone' | 'screen-capture'>;
|
|
3
|
+
/**
|
|
4
|
+
* Enumerates browser-visible audio and video input/output devices.
|
|
5
|
+
* Pass the capability that initiated the enumeration for accurate error details.
|
|
6
|
+
*/
|
|
7
|
+
export declare function enumerateMoldableMediaDevices(capability?: Extract<MediaCapability, 'camera' | 'microphone'>): Promise<MediaDeviceInfo[]>;
|
|
8
|
+
/** Requests a video-only camera stream using the standard MediaDevices API. */
|
|
9
|
+
export declare function getMoldableCameraStream(constraints?: boolean | MediaTrackConstraints): Promise<MediaStream>;
|
|
10
|
+
/** Requests an audio-only microphone stream using the standard MediaDevices API. */
|
|
11
|
+
export declare function getMoldableMicrophoneStream(constraints?: boolean | MediaTrackConstraints): Promise<MediaStream>;
|
|
12
|
+
/** Requests a screen/window stream using the standard display-media picker. */
|
|
13
|
+
export declare function getMoldableDisplayStream(options?: DisplayMediaStreamOptions): Promise<MediaStream>;
|
|
14
|
+
/** Stops every audio and video track owned by a stream. Safe to call repeatedly. */
|
|
15
|
+
export declare function stopMoldableMediaStream(stream: MediaStream): void;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/media.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,0BAA0B,EAE3B,MAAM,SAAS,CAAA;AAEhB,KAAK,eAAe,GAAG,OAAO,CAC5B,0BAA0B,EAC1B,QAAQ,GAAG,YAAY,GAAG,gBAAgB,CAC3C,CAAA;AAmED;;;GAGG;AACH,wBAAsB,6BAA6B,CACjD,UAAU,GAAE,OAAO,CAAC,eAAe,EAAE,QAAQ,GAAG,YAAY,CAAY,GACvE,OAAO,CAAC,eAAe,EAAE,CAAC,CAS5B;AAED,+EAA+E;AAC/E,wBAAsB,uBAAuB,CAC3C,WAAW,GAAE,OAAO,GAAG,qBAA4B,GAClD,OAAO,CAAC,WAAW,CAAC,CAWtB;AAED,oFAAoF;AACpF,wBAAsB,2BAA2B,CAC/C,WAAW,GAAE,OAAO,GAAG,qBAA4B,GAClD,OAAO,CAAC,WAAW,CAAC,CAWtB;AAED,+EAA+E;AAC/E,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,yBAAyD,GACjE,OAAO,CAAC,WAAW,CAAC,CAUtB;AAED,oFAAoF;AACpF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAEjE"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { NativeHardwareBridgeError, toNativeHardwareBridgeError, } from './bridge';
|
|
2
|
+
function mediaDevicesFor(capability, method) {
|
|
3
|
+
if (typeof navigator === 'undefined' ||
|
|
4
|
+
!navigator.mediaDevices ||
|
|
5
|
+
typeof navigator.mediaDevices[method] !== 'function') {
|
|
6
|
+
throw mediaError(capability, new DOMException('Media API unavailable', 'NotSupportedError'));
|
|
7
|
+
}
|
|
8
|
+
return navigator.mediaDevices;
|
|
9
|
+
}
|
|
10
|
+
function mediaError(capability, error) {
|
|
11
|
+
if (error instanceof NativeHardwareBridgeError)
|
|
12
|
+
return error;
|
|
13
|
+
const name = error instanceof DOMException ? error.name : undefined;
|
|
14
|
+
let code = 'host_error';
|
|
15
|
+
let retryable = false;
|
|
16
|
+
if (name === 'NotSupportedError')
|
|
17
|
+
code = 'unsupported';
|
|
18
|
+
else if (name === 'NotAllowedError' || name === 'SecurityError') {
|
|
19
|
+
code = 'permission_denied';
|
|
20
|
+
}
|
|
21
|
+
else if (name === 'AbortError') {
|
|
22
|
+
code = 'aborted';
|
|
23
|
+
retryable = true;
|
|
24
|
+
}
|
|
25
|
+
else if (name === 'NotFoundError' ||
|
|
26
|
+
name === 'DevicesNotFoundError' ||
|
|
27
|
+
name === 'NotReadableError' ||
|
|
28
|
+
name === 'TrackStartError') {
|
|
29
|
+
code = 'unavailable';
|
|
30
|
+
retryable = true;
|
|
31
|
+
}
|
|
32
|
+
else if (name === 'OverconstrainedError' ||
|
|
33
|
+
name === 'ConstraintNotSatisfiedError' ||
|
|
34
|
+
error instanceof TypeError) {
|
|
35
|
+
code = 'invalid_request';
|
|
36
|
+
}
|
|
37
|
+
const fallbackMessage = `Unable to access ${capability}`;
|
|
38
|
+
return toNativeHardwareBridgeError({
|
|
39
|
+
code,
|
|
40
|
+
message: error instanceof Error
|
|
41
|
+
? error.message || fallbackMessage
|
|
42
|
+
: fallbackMessage,
|
|
43
|
+
details: { capability, ...(name ? { cause: name } : {}) },
|
|
44
|
+
retryable,
|
|
45
|
+
}, fallbackMessage);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Enumerates browser-visible audio and video input/output devices.
|
|
49
|
+
* Pass the capability that initiated the enumeration for accurate error details.
|
|
50
|
+
*/
|
|
51
|
+
export async function enumerateMoldableMediaDevices(capability = 'camera') {
|
|
52
|
+
try {
|
|
53
|
+
return await mediaDevicesFor(capability, 'enumerateDevices').enumerateDevices();
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw mediaError(capability, error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Requests a video-only camera stream using the standard MediaDevices API. */
|
|
60
|
+
export async function getMoldableCameraStream(constraints = true) {
|
|
61
|
+
const capability = 'camera';
|
|
62
|
+
try {
|
|
63
|
+
return await mediaDevicesFor(capability, 'getUserMedia').getUserMedia({
|
|
64
|
+
audio: false,
|
|
65
|
+
video: constraints,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw mediaError(capability, error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Requests an audio-only microphone stream using the standard MediaDevices API. */
|
|
73
|
+
export async function getMoldableMicrophoneStream(constraints = true) {
|
|
74
|
+
const capability = 'microphone';
|
|
75
|
+
try {
|
|
76
|
+
return await mediaDevicesFor(capability, 'getUserMedia').getUserMedia({
|
|
77
|
+
audio: constraints,
|
|
78
|
+
video: false,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
throw mediaError(capability, error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Requests a screen/window stream using the standard display-media picker. */
|
|
86
|
+
export async function getMoldableDisplayStream(options = { video: true, audio: false }) {
|
|
87
|
+
const capability = 'screen-capture';
|
|
88
|
+
try {
|
|
89
|
+
return await mediaDevicesFor(capability, 'getDisplayMedia').getDisplayMedia(options);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw mediaError(capability, error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Stops every audio and video track owned by a stream. Safe to call repeatedly. */
|
|
96
|
+
export function stopMoldableMediaStream(stream) {
|
|
97
|
+
for (const track of stream.getTracks())
|
|
98
|
+
track.stop();
|
|
99
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MoldableMIDIAccessOptions, MoldableMIDIConnectionOptions } from './midi-types';
|
|
2
|
+
/** Checks only whether this runtime exposes Web MIDI; it does not request permission. */
|
|
3
|
+
export declare function isMoldableMIDISupported(): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Requests Web MIDI access. Invoke this directly from a trusted user action.
|
|
6
|
+
* SysEx access is disabled unless `sysex: true` is explicitly supplied.
|
|
7
|
+
*/
|
|
8
|
+
export declare function requestMoldableMIDIAccess(options?: MoldableMIDIAccessOptions): Promise<MIDIAccess>;
|
|
9
|
+
export declare function getMoldableMIDIInputs(access: MIDIAccess): MIDIInput[];
|
|
10
|
+
export declare function getMoldableMIDIOutputs(access: MIDIAccess): MIDIOutput[];
|
|
11
|
+
/** Subscribes to device connection changes and returns an idempotent cleanup. */
|
|
12
|
+
export declare function observeMoldableMIDIState(access: MIDIAccess, listener: (event: MIDIConnectionEvent) => void, options?: MoldableMIDIConnectionOptions): () => void;
|
|
13
|
+
//# sourceMappingURL=midi-access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"midi-access.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/midi-access.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,yBAAyB,EACzB,6BAA6B,EAC9B,MAAM,cAAc,CAAA;AAiBrB,yFAAyF;AACzF,wBAAgB,uBAAuB,IAAI,OAAO,CAKjD;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,UAAU,CAAC,CAsBrB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,EAAE,CAIrE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,EAAE,CAIvE;AAED,iFAAiF;AACjF,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,EAC9C,OAAO,GAAE,6BAAkC,GAC1C,MAAM,IAAI,CAaZ"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { toNativeHardwareBridgeError } from './bridge';
|
|
2
|
+
import { abortableMIDI, midiError } from './midi-internal';
|
|
3
|
+
function assertUserGesture() {
|
|
4
|
+
if (typeof navigator === 'undefined')
|
|
5
|
+
return;
|
|
6
|
+
const activation = navigator.userActivation;
|
|
7
|
+
if (activation && !activation.isActive) {
|
|
8
|
+
throw toNativeHardwareBridgeError({
|
|
9
|
+
code: 'invalid_request',
|
|
10
|
+
message: 'MIDI access must be requested from a user-initiated action.',
|
|
11
|
+
details: { capability: 'midi', cause: 'user_gesture_required' },
|
|
12
|
+
retryable: true,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Checks only whether this runtime exposes Web MIDI; it does not request permission. */
|
|
17
|
+
export function isMoldableMIDISupported() {
|
|
18
|
+
return (typeof navigator !== 'undefined' &&
|
|
19
|
+
typeof navigator.requestMIDIAccess === 'function');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Requests Web MIDI access. Invoke this directly from a trusted user action.
|
|
23
|
+
* SysEx access is disabled unless `sysex: true` is explicitly supplied.
|
|
24
|
+
*/
|
|
25
|
+
export async function requestMoldableMIDIAccess(options = {}) {
|
|
26
|
+
const operation = 'request MIDI access';
|
|
27
|
+
if (!isMoldableMIDISupported()) {
|
|
28
|
+
throw midiError(new DOMException('Web MIDI is unavailable', 'NotSupportedError'), operation);
|
|
29
|
+
}
|
|
30
|
+
assertUserGesture();
|
|
31
|
+
try {
|
|
32
|
+
return await abortableMIDI(navigator.requestMIDIAccess({
|
|
33
|
+
software: options.software,
|
|
34
|
+
sysex: options.sysex === true,
|
|
35
|
+
}), options.signal, operation);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw midiError(error, operation);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function getMoldableMIDIInputs(access) {
|
|
42
|
+
const inputs = [];
|
|
43
|
+
access.inputs.forEach((input) => inputs.push(input));
|
|
44
|
+
return inputs;
|
|
45
|
+
}
|
|
46
|
+
export function getMoldableMIDIOutputs(access) {
|
|
47
|
+
const outputs = [];
|
|
48
|
+
access.outputs.forEach((output) => outputs.push(output));
|
|
49
|
+
return outputs;
|
|
50
|
+
}
|
|
51
|
+
/** Subscribes to device connection changes and returns an idempotent cleanup. */
|
|
52
|
+
export function observeMoldableMIDIState(access, listener, options = {}) {
|
|
53
|
+
if (options.signal?.aborted)
|
|
54
|
+
return () => undefined;
|
|
55
|
+
let active = true;
|
|
56
|
+
const cleanup = () => {
|
|
57
|
+
if (!active)
|
|
58
|
+
return;
|
|
59
|
+
active = false;
|
|
60
|
+
access.removeEventListener('statechange', listener);
|
|
61
|
+
options.signal?.removeEventListener('abort', cleanup);
|
|
62
|
+
};
|
|
63
|
+
access.addEventListener('statechange', listener);
|
|
64
|
+
options.signal?.addEventListener('abort', cleanup, { once: true });
|
|
65
|
+
return cleanup;
|
|
66
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { NativeHardwareBridgeError } from './bridge';
|
|
2
|
+
export type ClearableMIDIOutput = MIDIOutput & {
|
|
3
|
+
clear?: () => void;
|
|
4
|
+
};
|
|
5
|
+
export declare function midiError(error: unknown, operation: string): NativeHardwareBridgeError;
|
|
6
|
+
export declare function midiAborted(operation: string): NativeHardwareBridgeError;
|
|
7
|
+
export declare function abortableMIDI<T>(promise: Promise<T>, signal: AbortSignal | undefined, operation: string, onLateResolution?: (value: T) => void): Promise<T>;
|
|
8
|
+
export declare function findMIDIPort<TPort extends MIDIPort>(ports: {
|
|
9
|
+
forEach(callback: (port: TPort) => void): void;
|
|
10
|
+
}, id: string): TPort | undefined;
|
|
11
|
+
export declare function missingMIDIPort(type: 'input' | 'output', id: string): never;
|
|
12
|
+
//# sourceMappingURL=midi-internal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"midi-internal.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/midi-internal.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EAE1B,MAAM,UAAU,CAAA;AAGjB,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,CAAA;AAErE,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,GAChB,yBAAyB,CAoC3B;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,CAKxE;AAED,wBAAgB,aAAa,CAAC,CAAC,EAC7B,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;AAED,wBAAgB,YAAY,CAAC,KAAK,SAAS,QAAQ,EACjD,KAAK,EAAE;IAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAA;CAAE,EACzD,EAAE,EAAE,MAAM,GACT,KAAK,GAAG,SAAS,CAMnB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,KAAK,CAO3E"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { NativeHardwareBridgeError, toNativeHardwareBridgeError, } from './bridge';
|
|
2
|
+
export function midiError(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 = 'unavailable';
|
|
17
|
+
retryable = true;
|
|
18
|
+
}
|
|
19
|
+
else if (name === 'InvalidAccessError' || name === 'InvalidStateError') {
|
|
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: 'midi',
|
|
33
|
+
operation,
|
|
34
|
+
...(name ? { cause: name } : {}),
|
|
35
|
+
},
|
|
36
|
+
retryable,
|
|
37
|
+
}, fallbackMessage);
|
|
38
|
+
}
|
|
39
|
+
export function midiAborted(operation) {
|
|
40
|
+
return midiError(new DOMException(`MIDI ${operation} was aborted`, 'AbortError'), operation);
|
|
41
|
+
}
|
|
42
|
+
export function abortableMIDI(promise, signal, operation, onLateResolution) {
|
|
43
|
+
if (!signal)
|
|
44
|
+
return promise;
|
|
45
|
+
if (signal.aborted)
|
|
46
|
+
return Promise.reject(midiAborted(operation));
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
let settled = false;
|
|
49
|
+
const onAbort = () => {
|
|
50
|
+
if (settled)
|
|
51
|
+
return;
|
|
52
|
+
settled = true;
|
|
53
|
+
reject(midiAborted(operation));
|
|
54
|
+
};
|
|
55
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
56
|
+
void promise.then((value) => {
|
|
57
|
+
signal.removeEventListener('abort', onAbort);
|
|
58
|
+
if (settled) {
|
|
59
|
+
onLateResolution?.(value);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
settled = true;
|
|
63
|
+
resolve(value);
|
|
64
|
+
}, (error) => {
|
|
65
|
+
signal.removeEventListener('abort', onAbort);
|
|
66
|
+
if (settled)
|
|
67
|
+
return;
|
|
68
|
+
settled = true;
|
|
69
|
+
reject(error);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export function findMIDIPort(ports, id) {
|
|
74
|
+
let result;
|
|
75
|
+
ports.forEach((port) => {
|
|
76
|
+
if (port.id === id)
|
|
77
|
+
result = port;
|
|
78
|
+
});
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
export function missingMIDIPort(type, id) {
|
|
82
|
+
throw toNativeHardwareBridgeError({
|
|
83
|
+
code: 'unavailable',
|
|
84
|
+
message: `MIDI ${type} ${id} is unavailable.`,
|
|
85
|
+
details: { capability: 'midi', portType: type, portId: id },
|
|
86
|
+
retryable: true,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MoldableMIDIAccessOptions } from './midi-types';
|
|
2
|
+
export type MoldableMIDITransport = {
|
|
3
|
+
kind: 'web-midi';
|
|
4
|
+
access: MIDIAccess;
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'desktop-bridge';
|
|
7
|
+
access: MoldableNativeMIDIAccess;
|
|
8
|
+
};
|
|
9
|
+
export interface MoldableNativeMIDIPort {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly kind: 'input' | 'output';
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly closed: boolean;
|
|
14
|
+
/** Native fallback sends immediately; Web MIDI retains scheduled sends. */
|
|
15
|
+
send(data: Iterable<number>): Promise<void>;
|
|
16
|
+
observeMessages(listener: (data: Uint8Array, timestamp: number) => void): () => void;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export interface MoldableNativeMIDIAccess {
|
|
20
|
+
requestPort(kind: 'input' | 'output'): Promise<MoldableNativeMIDIPort>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Uses Web MIDI when the webview implements it; otherwise returns the scoped
|
|
24
|
+
* desktop fallback. Native port selection always opens the host-owned chooser.
|
|
25
|
+
*/
|
|
26
|
+
export declare function requestMoldableMIDIWithFallback(options?: MoldableMIDIAccessOptions): Promise<MoldableMIDITransport>;
|
|
27
|
+
//# sourceMappingURL=midi-native-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"midi-native-fallback.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/midi-native-fallback.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AAO7D,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,wBAAwB,CAAA;CAAE,CAAA;AAEhE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,2EAA2E;IAC3E,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3C,eAAe,CACb,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,GACtD,MAAM,IAAI,CAAA;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAA;CACvE;AA+HD;;;GAGG;AACH,wBAAsB,+BAA+B,CACnD,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,qBAAqB,CAAC,CAwChC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { callNativeHardwareBridge, } from './bridge';
|
|
2
|
+
import { isMoldableMIDISupported, requestMoldableMIDIAccess, } from './midi-access';
|
|
3
|
+
const REQUEST_TYPE = 'moldable:native-midi';
|
|
4
|
+
const RESULT_TYPE = 'moldable:native-midi-result';
|
|
5
|
+
const EVENT_TYPE = 'moldable:native-midi-event';
|
|
6
|
+
const MAX_MESSAGE_BYTES = 16_384;
|
|
7
|
+
function isNativeProbeResult(value) {
|
|
8
|
+
return (typeof value === 'object' &&
|
|
9
|
+
value !== null &&
|
|
10
|
+
value.available === true &&
|
|
11
|
+
Object.keys(value).length === 1);
|
|
12
|
+
}
|
|
13
|
+
function isNativePortResult(value) {
|
|
14
|
+
if (typeof value !== 'object' || value === null)
|
|
15
|
+
return false;
|
|
16
|
+
const port = value;
|
|
17
|
+
return (typeof port.handleId === 'string' &&
|
|
18
|
+
port.handleId.length > 0 &&
|
|
19
|
+
(port.kind === 'input' || port.kind === 'output') &&
|
|
20
|
+
typeof port.name === 'string');
|
|
21
|
+
}
|
|
22
|
+
function bytes(value) {
|
|
23
|
+
const result = Array.from(value);
|
|
24
|
+
if (result.length === 0 ||
|
|
25
|
+
result.length > MAX_MESSAGE_BYTES ||
|
|
26
|
+
result.some((byte) => !Number.isInteger(byte) || byte < 0 || byte > 255)) {
|
|
27
|
+
throw new TypeError('MIDI data must contain 1 to 16384 bytes.');
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
function nativePort(port, options) {
|
|
32
|
+
let closed = false;
|
|
33
|
+
const listeners = new Set();
|
|
34
|
+
const onMessage = (event) => {
|
|
35
|
+
if (event.source !== window.parent ||
|
|
36
|
+
typeof event.data !== 'object' ||
|
|
37
|
+
!event.data)
|
|
38
|
+
return;
|
|
39
|
+
const message = event.data;
|
|
40
|
+
if (message.type !== EVENT_TYPE ||
|
|
41
|
+
typeof message.event !== 'object' ||
|
|
42
|
+
!message.event)
|
|
43
|
+
return;
|
|
44
|
+
const value = message.event;
|
|
45
|
+
if (value.handleId !== port.handleId || !Array.isArray(value.data))
|
|
46
|
+
return;
|
|
47
|
+
const data = value.data;
|
|
48
|
+
if (!data.every((byte) => Number.isInteger(byte) && Number(byte) >= 0 && Number(byte) <= 255))
|
|
49
|
+
return;
|
|
50
|
+
const timestamp = typeof value.timestamp === 'number' ? value.timestamp : 0;
|
|
51
|
+
for (const listener of listeners)
|
|
52
|
+
listener(Uint8Array.from(data), timestamp);
|
|
53
|
+
};
|
|
54
|
+
window.addEventListener('message', onMessage);
|
|
55
|
+
return {
|
|
56
|
+
id: port.handleId,
|
|
57
|
+
kind: port.kind,
|
|
58
|
+
name: port.name,
|
|
59
|
+
get closed() {
|
|
60
|
+
return closed;
|
|
61
|
+
},
|
|
62
|
+
async send(data) {
|
|
63
|
+
if (closed)
|
|
64
|
+
throw new DOMException('The MIDI port is closed.', 'InvalidStateError');
|
|
65
|
+
if (port.kind !== 'output')
|
|
66
|
+
throw new DOMException('Cannot send to a MIDI input.', 'InvalidStateError');
|
|
67
|
+
await callNativeHardwareBridge({
|
|
68
|
+
type: REQUEST_TYPE,
|
|
69
|
+
action: 'send',
|
|
70
|
+
handleId: port.handleId,
|
|
71
|
+
data: bytes(data),
|
|
72
|
+
}, RESULT_TYPE, options);
|
|
73
|
+
},
|
|
74
|
+
observeMessages(listener) {
|
|
75
|
+
if (closed)
|
|
76
|
+
throw new DOMException('The MIDI port is closed.', 'InvalidStateError');
|
|
77
|
+
if (port.kind !== 'input')
|
|
78
|
+
throw new DOMException('Only MIDI inputs receive messages.', 'InvalidStateError');
|
|
79
|
+
listeners.add(listener);
|
|
80
|
+
return () => listeners.delete(listener);
|
|
81
|
+
},
|
|
82
|
+
async close() {
|
|
83
|
+
if (closed)
|
|
84
|
+
return;
|
|
85
|
+
await callNativeHardwareBridge({ type: REQUEST_TYPE, action: 'close', handleId: port.handleId }, RESULT_TYPE, options);
|
|
86
|
+
closed = true;
|
|
87
|
+
listeners.clear();
|
|
88
|
+
window.removeEventListener('message', onMessage);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Uses Web MIDI when the webview implements it; otherwise returns the scoped
|
|
94
|
+
* desktop fallback. Native port selection always opens the host-owned chooser.
|
|
95
|
+
*/
|
|
96
|
+
export async function requestMoldableMIDIWithFallback(options = {}) {
|
|
97
|
+
if (isMoldableMIDISupported()) {
|
|
98
|
+
return {
|
|
99
|
+
kind: 'web-midi',
|
|
100
|
+
access: await requestMoldableMIDIAccess(options),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const bridgeOptions = { signal: options.signal };
|
|
104
|
+
const probe = await callNativeHardwareBridge({ type: REQUEST_TYPE, action: 'probe' }, RESULT_TYPE, { timeoutMs: 120_000, ...bridgeOptions });
|
|
105
|
+
if (!isNativeProbeResult(probe)) {
|
|
106
|
+
throw new TypeError('The desktop returned an invalid MIDI capability.');
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
kind: 'desktop-bridge',
|
|
110
|
+
access: {
|
|
111
|
+
async requestPort(kind) {
|
|
112
|
+
const result = await callNativeHardwareBridge({
|
|
113
|
+
type: REQUEST_TYPE,
|
|
114
|
+
action: 'requestPort',
|
|
115
|
+
kind,
|
|
116
|
+
sysex: options.sysex === true,
|
|
117
|
+
}, RESULT_TYPE, { timeoutMs: 120_000, ...bridgeOptions });
|
|
118
|
+
if (!isNativePortResult(result)) {
|
|
119
|
+
throw new TypeError('The desktop returned an invalid MIDI port.');
|
|
120
|
+
}
|
|
121
|
+
// The access request's AbortSignal must not poison later close/send
|
|
122
|
+
// operations and strand a host-owned port after access succeeds.
|
|
123
|
+
return nativePort(result, {});
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MoldableMIDIConnectionOptions, MoldableMIDIInputConnection, MoldableMIDIOutputConnection } from './midi-types';
|
|
2
|
+
/** Opens one input port and owns its message listener until `close()` or abort. */
|
|
3
|
+
export declare function openMoldableMIDIInput(access: MIDIAccess, inputId: string, listener: (event: MIDIMessageEvent) => void, options?: MoldableMIDIConnectionOptions): Promise<MoldableMIDIInputConnection>;
|
|
4
|
+
/** Opens one output port and validates outbound MIDI bytes before sending. */
|
|
5
|
+
export declare function openMoldableMIDIOutput(access: MIDIAccess, outputId: string, options?: MoldableMIDIConnectionOptions): Promise<MoldableMIDIOutputConnection>;
|
|
6
|
+
//# sourceMappingURL=midi-ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"midi-ports.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/midi-ports.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,6BAA6B,EAC7B,2BAA2B,EAC3B,4BAA4B,EAC7B,MAAM,cAAc,CAAA;AAErB,mFAAmF;AACnF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,EAC3C,OAAO,GAAE,6BAAkC,GAC1C,OAAO,CAAC,2BAA2B,CAAC,CAwCtC;AAED,8EAA8E;AAC9E,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,6BAAkC,GAC1C,OAAO,CAAC,4BAA4B,CAAC,CA+EvC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { abortableMIDI, findMIDIPort, midiAborted, midiError, missingMIDIPort, } from './midi-internal';
|
|
2
|
+
/** Opens one input port and owns its message listener until `close()` or abort. */
|
|
3
|
+
export async function openMoldableMIDIInput(access, inputId, listener, options = {}) {
|
|
4
|
+
const input = findMIDIPort(access.inputs, inputId);
|
|
5
|
+
if (!input)
|
|
6
|
+
return missingMIDIPort('input', inputId);
|
|
7
|
+
const operation = `open MIDI input ${inputId}`;
|
|
8
|
+
try {
|
|
9
|
+
await abortableMIDI(input.open(), options.signal, operation, (port) => {
|
|
10
|
+
void port.close();
|
|
11
|
+
});
|
|
12
|
+
if (options.signal?.aborted) {
|
|
13
|
+
await input.close();
|
|
14
|
+
throw midiAborted(operation);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
throw midiError(error, operation);
|
|
19
|
+
}
|
|
20
|
+
let closed = false;
|
|
21
|
+
const close = async () => {
|
|
22
|
+
if (closed)
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
await input.close();
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
throw midiError(error, `close MIDI input ${inputId}`);
|
|
29
|
+
}
|
|
30
|
+
closed = true;
|
|
31
|
+
input.removeEventListener('midimessage', listener);
|
|
32
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
33
|
+
};
|
|
34
|
+
const onAbort = () => void close().catch(() => undefined);
|
|
35
|
+
input.addEventListener('midimessage', listener);
|
|
36
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
37
|
+
return {
|
|
38
|
+
input,
|
|
39
|
+
get closed() {
|
|
40
|
+
return closed;
|
|
41
|
+
},
|
|
42
|
+
close,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Opens one output port and validates outbound MIDI bytes before sending. */
|
|
46
|
+
export async function openMoldableMIDIOutput(access, outputId, options = {}) {
|
|
47
|
+
const output = findMIDIPort(access.outputs, outputId);
|
|
48
|
+
if (!output)
|
|
49
|
+
return missingMIDIPort('output', outputId);
|
|
50
|
+
const operation = `open MIDI output ${outputId}`;
|
|
51
|
+
try {
|
|
52
|
+
await abortableMIDI(output.open(), options.signal, operation, (port) => {
|
|
53
|
+
void port.close();
|
|
54
|
+
});
|
|
55
|
+
if (options.signal?.aborted) {
|
|
56
|
+
await output.close();
|
|
57
|
+
throw midiAborted(operation);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw midiError(error, operation);
|
|
62
|
+
}
|
|
63
|
+
let closed = false;
|
|
64
|
+
const close = async () => {
|
|
65
|
+
if (closed)
|
|
66
|
+
return;
|
|
67
|
+
try {
|
|
68
|
+
;
|
|
69
|
+
output.clear?.call(output);
|
|
70
|
+
await output.close();
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw midiError(error, `close MIDI output ${outputId}`);
|
|
74
|
+
}
|
|
75
|
+
closed = true;
|
|
76
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
77
|
+
};
|
|
78
|
+
const onAbort = () => void close().catch(() => undefined);
|
|
79
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
80
|
+
return {
|
|
81
|
+
output,
|
|
82
|
+
get closed() {
|
|
83
|
+
return closed;
|
|
84
|
+
},
|
|
85
|
+
send(data, timestamp) {
|
|
86
|
+
if (closed) {
|
|
87
|
+
throw midiError(new DOMException('MIDI output is closed', 'InvalidStateError'), `send to MIDI output ${outputId}`);
|
|
88
|
+
}
|
|
89
|
+
const bytes = Array.from(data);
|
|
90
|
+
if (bytes.length === 0 ||
|
|
91
|
+
bytes.some((byte) => !Number.isInteger(byte) || byte < 0 || byte > 255)) {
|
|
92
|
+
throw midiError(new TypeError('MIDI data must contain bytes between 0 and 255'), `send to MIDI output ${outputId}`);
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
output.send(bytes, timestamp);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
throw midiError(error, `send to MIDI output ${outputId}`);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
clear() {
|
|
102
|
+
const clear = output.clear;
|
|
103
|
+
if (!clear) {
|
|
104
|
+
throw midiError(new DOMException('Clearing MIDI output is unavailable', 'NotSupportedError'), `clear MIDI output ${outputId}`);
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
clear.call(output);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
throw midiError(error, `clear MIDI output ${outputId}`);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
close,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface MoldableMIDIAccessOptions {
|
|
2
|
+
/**
|
|
3
|
+
* System Exclusive access is sensitive and is never enabled implicitly.
|
|
4
|
+
* Callers must set this to `true` from a user-initiated action.
|
|
5
|
+
*/
|
|
6
|
+
sysex?: boolean;
|
|
7
|
+
software?: boolean;
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
export interface MoldableMIDIConnectionOptions {
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
}
|
|
13
|
+
export interface MoldableMIDIInputConnection {
|
|
14
|
+
readonly input: MIDIInput;
|
|
15
|
+
readonly closed: boolean;
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export interface MoldableMIDIOutputConnection {
|
|
19
|
+
readonly output: MIDIOutput;
|
|
20
|
+
readonly closed: boolean;
|
|
21
|
+
send(data: Iterable<number>, timestamp?: DOMHighResTimeStamp): void;
|
|
22
|
+
clear(): void;
|
|
23
|
+
close(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=midi-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"midi-types.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/midi-types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,6BAA6B;IAC5C,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAA;IACnE,KAAK,IAAI,IAAI,CAAA;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { getMoldableMIDIInputs, getMoldableMIDIOutputs, isMoldableMIDISupported, observeMoldableMIDIState, requestMoldableMIDIAccess, } from './midi-access';
|
|
2
|
+
export { openMoldableMIDIInput, openMoldableMIDIOutput } from './midi-ports';
|
|
3
|
+
export { requestMoldableMIDIWithFallback } from './midi-native-fallback';
|
|
4
|
+
export type { MoldableMIDITransport, MoldableNativeMIDIAccess, MoldableNativeMIDIPort, } from './midi-native-fallback';
|
|
5
|
+
export type { MoldableMIDIAccessOptions, MoldableMIDIConnectionOptions, MoldableMIDIInputConnection, MoldableMIDIOutputConnection, } from './midi-types';
|
|
6
|
+
//# sourceMappingURL=midi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"midi.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/midi.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAC5E,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAA;AACxE,YAAY,EACV,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EACV,yBAAyB,EACzB,6BAA6B,EAC7B,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { getMoldableMIDIInputs, getMoldableMIDIOutputs, isMoldableMIDISupported, observeMoldableMIDIState, requestMoldableMIDIAccess, } from './midi-access';
|
|
2
|
+
export { openMoldableMIDIInput, openMoldableMIDIOutput } from './midi-ports';
|
|
3
|
+
export { requestMoldableMIDIWithFallback } from './midi-native-fallback';
|