@signality/core 0.0.1-alpha.4 → 0.1.1
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/browser/bluetooth/index.d.ts +63 -0
- package/browser/picture-in-picture/index.d.ts +4 -0
- package/browser/speech-recognition/index.d.ts +14 -0
- package/fesm2022/signality-core-browser-bluetooth.mjs +2 -1
- package/fesm2022/signality-core-browser-bluetooth.mjs.map +1 -1
- package/fesm2022/signality-core-browser-picture-in-picture.mjs +4 -10
- package/fesm2022/signality-core-browser-picture-in-picture.mjs.map +1 -1
- package/fesm2022/signality-core-browser-speech-recognition.mjs.map +1 -1
- package/package.json +5 -5
|
@@ -81,3 +81,66 @@ export interface BluetoothRef {
|
|
|
81
81
|
* ```
|
|
82
82
|
*/
|
|
83
83
|
export declare function bluetooth(options?: BluetoothOptions): BluetoothRef;
|
|
84
|
+
/**
|
|
85
|
+
* Local type definitions for Web Bluetooth API.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* External `@types/web-bluetooth` package may conflict with user's other libraries
|
|
89
|
+
* or become outdated. For better DX, we define minimal required interfaces locally
|
|
90
|
+
* without polluting the global namespace with experimental APIs.
|
|
91
|
+
*/
|
|
92
|
+
type BluetoothServiceUUID = number | string;
|
|
93
|
+
interface BluetoothDataFilter {
|
|
94
|
+
readonly dataPrefix?: BufferSource;
|
|
95
|
+
readonly mask?: BufferSource;
|
|
96
|
+
}
|
|
97
|
+
interface BluetoothManufacturerDataFilter extends BluetoothDataFilter {
|
|
98
|
+
readonly companyIdentifier: number;
|
|
99
|
+
}
|
|
100
|
+
interface BluetoothServiceDataFilter extends BluetoothDataFilter {
|
|
101
|
+
readonly service: BluetoothServiceUUID;
|
|
102
|
+
}
|
|
103
|
+
interface BluetoothLEScanFilter {
|
|
104
|
+
readonly name?: string;
|
|
105
|
+
readonly namePrefix?: string;
|
|
106
|
+
readonly services?: BluetoothServiceUUID[];
|
|
107
|
+
readonly manufacturerData?: BluetoothManufacturerDataFilter[];
|
|
108
|
+
readonly serviceData?: BluetoothServiceDataFilter[];
|
|
109
|
+
}
|
|
110
|
+
interface BluetoothRemoteGATTService extends EventTarget {
|
|
111
|
+
readonly device: BluetoothDevice;
|
|
112
|
+
readonly uuid: string;
|
|
113
|
+
readonly isPrimary: boolean;
|
|
114
|
+
getCharacteristic(characteristic: BluetoothCharacteristicUUID): Promise<BluetoothRemoteGATTCharacteristic>;
|
|
115
|
+
getCharacteristics(characteristic?: BluetoothCharacteristicUUID): Promise<BluetoothRemoteGATTCharacteristic[]>;
|
|
116
|
+
}
|
|
117
|
+
type BluetoothCharacteristicUUID = number | string;
|
|
118
|
+
interface BluetoothRemoteGATTCharacteristic extends EventTarget {
|
|
119
|
+
readonly service: BluetoothRemoteGATTService;
|
|
120
|
+
readonly uuid: string;
|
|
121
|
+
readonly value?: DataView;
|
|
122
|
+
readValue(): Promise<DataView>;
|
|
123
|
+
writeValue(value: BufferSource): Promise<void>;
|
|
124
|
+
startNotifications(): Promise<BluetoothRemoteGATTCharacteristic>;
|
|
125
|
+
stopNotifications(): Promise<BluetoothRemoteGATTCharacteristic>;
|
|
126
|
+
}
|
|
127
|
+
interface BluetoothRemoteGATTServer {
|
|
128
|
+
readonly device: BluetoothDevice;
|
|
129
|
+
readonly connected: boolean;
|
|
130
|
+
connect(): Promise<BluetoothRemoteGATTServer>;
|
|
131
|
+
disconnect(): void;
|
|
132
|
+
getPrimaryService(service: BluetoothServiceUUID): Promise<BluetoothRemoteGATTService>;
|
|
133
|
+
getPrimaryServices(service?: BluetoothServiceUUID): Promise<BluetoothRemoteGATTService[]>;
|
|
134
|
+
}
|
|
135
|
+
interface BluetoothDevice extends EventTarget {
|
|
136
|
+
readonly id: string;
|
|
137
|
+
readonly name?: string;
|
|
138
|
+
readonly gatt?: BluetoothRemoteGATTServer;
|
|
139
|
+
forget(): Promise<void>;
|
|
140
|
+
watchAdvertisements(options?: {
|
|
141
|
+
signal?: AbortSignal;
|
|
142
|
+
}): Promise<void>;
|
|
143
|
+
readonly watchingAdvertisements: boolean;
|
|
144
|
+
addEventListener(type: 'gattserverdisconnected', listener: (ev: Event) => void): void;
|
|
145
|
+
}
|
|
146
|
+
export {};
|
|
@@ -17,6 +17,10 @@ export interface PictureInPictureRef {
|
|
|
17
17
|
/**
|
|
18
18
|
* Enter Picture-in-Picture mode for the target video element.
|
|
19
19
|
*
|
|
20
|
+
* @throws {DOMException} `'NotAllowedError'` — the document is not allowed to use PiP
|
|
21
|
+
* @throws {DOMException} `'InvalidStateError'` — the video element has `disablePictureInPicture` attribute
|
|
22
|
+
* @throws {DOMException} `'NotSupportedError'` — Picture-in-Picture is not supported
|
|
23
|
+
*
|
|
20
24
|
* @see [HTMLVideoElement: requestPictureInPicture() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/requestPictureInPicture)
|
|
21
25
|
*/
|
|
22
26
|
readonly enter: () => Promise<void>;
|
|
@@ -113,3 +113,17 @@ export interface SpeechRecognitionRef {
|
|
|
113
113
|
* ```
|
|
114
114
|
*/
|
|
115
115
|
export declare function speechRecognition(options?: SpeechRecognitionOptions): SpeechRecognitionRef;
|
|
116
|
+
/**
|
|
117
|
+
* Local type definitions for Web Speech API (Speech Recognition).
|
|
118
|
+
*
|
|
119
|
+
* @remarks
|
|
120
|
+
* External `@types/dom-speech-recognition` package may conflict with user's other libraries
|
|
121
|
+
* or become outdated. For better DX, we define minimal required interfaces locally
|
|
122
|
+
* without polluting the global namespace with experimental APIs.
|
|
123
|
+
*/
|
|
124
|
+
type SpeechRecognitionErrorCode = 'aborted' | 'audio-capture' | 'bad-grammar' | 'language-not-supported' | 'network' | 'no-speech' | 'not-allowed' | 'service-not-allowed';
|
|
125
|
+
interface SpeechRecognitionErrorEvent extends Event {
|
|
126
|
+
readonly error: SpeechRecognitionErrorCode;
|
|
127
|
+
readonly message: string;
|
|
128
|
+
}
|
|
129
|
+
export {};
|
|
@@ -74,7 +74,8 @@ function bluetooth(options) {
|
|
|
74
74
|
isConnecting.set(true);
|
|
75
75
|
error.set(null);
|
|
76
76
|
try {
|
|
77
|
-
const
|
|
77
|
+
const bt = navigator.bluetooth;
|
|
78
|
+
const btDevice = await bt.requestDevice(requestOptions);
|
|
78
79
|
device.set(btDevice);
|
|
79
80
|
disconnectListener = setupSync(() => listener(btDevice, 'gattserverdisconnected', disconnect, { injector }));
|
|
80
81
|
const gattServer = await btDevice.gatt?.connect();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signality-core-browser-bluetooth.mjs","sources":["../../../projects/core/browser/bluetooth/index.ts","../../../projects/core/browser/bluetooth/signality-core-browser-bluetooth.ts"],"sourcesContent":["import { type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_ASYNC_FN, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, ListenerRef, setupSync } from '@signality/core/browser/listener';\n\nexport interface BluetoothOptions extends WithInjector {\n /**\n * Accept any Bluetooth device without filtering.\n *\n * @default true\n * @see [requestDevice: acceptAllDevices on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth/requestDevice#acceptalldevices)\n */\n readonly acceptAllDevices?: boolean;\n\n /**\n * Filters for device selection. Mutually exclusive with `acceptAllDevices`.\n *\n * @default undefined\n * @see [requestDevice: filters on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth/requestDevice#filters)\n */\n readonly filters?: BluetoothLEScanFilter[];\n\n /**\n * Optional GATT services to access on the connected device.\n *\n * @default []\n * @see [requestDevice: optionalServices on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth/requestDevice#optionalservices)\n */\n readonly optionalServices?: BluetoothServiceUUID[];\n}\n\nexport interface BluetoothRef {\n /**\n * Whether Web Bluetooth API is supported in the current browser.\n *\n * @see [Web Bluetooth API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether a device is currently connected.\n */\n readonly isConnected: Signal<boolean>;\n\n /**\n * Whether a connection is in progress.\n */\n readonly isConnecting: Signal<boolean>;\n\n /**\n * Connected Bluetooth device.\n */\n readonly device: Signal<BluetoothDevice | null>;\n\n /**\n * GATT server of a connected device.\n */\n readonly server: Signal<BluetoothRemoteGATTServer | null>;\n\n /**\n * The last error that occurred.\n */\n readonly error: Signal<Error | null>;\n\n /**\n * Request device connection.\n */\n readonly request: () => Promise<void>;\n\n /**\n * Disconnect from a device.\n */\n readonly disconnect: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Web Bluetooth API](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth).\n *\n * @param options - Optional configuration\n * @returns A BluetoothRef with connection state and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <button (click)=\"bt.request()\">Connect</button>\n * @if (bt.isConnected()) {\n * <p>{{ bt.device()?.name }}</p>\n * }\n * `\n * })\n * export class BluetoothDemo {\n * readonly bt = bluetooth();\n * }\n * ```\n */\nexport function bluetooth(options?: BluetoothOptions): BluetoothRef {\n const { runInContext } = setupContext(options?.injector, bluetooth);\n\n return runInContext(({ isBrowser, injector, onCleanup }) => {\n const isSupported = constSignal(isBrowser && 'bluetooth' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n isConnected: constSignal(false),\n isConnecting: constSignal(false),\n device: constSignal(null),\n server: constSignal(null),\n error: constSignal(null),\n request: NOOP_ASYNC_FN,\n disconnect: NOOP_FN,\n };\n }\n\n const requestOptions = {\n ...(options?.filters?.length\n ? { filters: options.filters }\n : { acceptAllDevices: options?.acceptAllDevices ?? true }),\n optionalServices: options?.optionalServices ?? [],\n };\n\n const isConnected = signal(false);\n const isConnecting = signal(false);\n const device = signal<BluetoothDevice | null>(null);\n const server = signal<BluetoothRemoteGATTServer | null>(null);\n const error = signal<Error | null>(null);\n\n let disconnectListener: ListenerRef | null = null;\n\n const disconnect = () => {\n if (disconnectListener) {\n disconnectListener?.destroy();\n disconnectListener = null;\n }\n\n const activeDevice = untracked(device);\n\n if (activeDevice?.gatt?.connected) {\n activeDevice.gatt.disconnect();\n }\n\n device.set(null);\n server.set(null);\n isConnected.set(false);\n };\n\n const request = async (): Promise<void> => {\n if (untracked(isConnecting)) {\n return;\n }\n\n if (untracked(isConnected)) {\n disconnect();\n }\n\n isConnecting.set(true);\n error.set(null);\n\n try {\n const btDevice = await navigator.bluetooth.requestDevice(requestOptions);\n\n device.set(btDevice);\n\n disconnectListener = setupSync(() =>\n listener(btDevice, 'gattserverdisconnected', disconnect, { injector })\n );\n\n const gattServer = await btDevice.gatt?.connect();\n\n if (gattServer) {\n server.set(gattServer);\n isConnected.set(true);\n }\n } catch (e) {\n error.set(e as Error);\n disconnect();\n } finally {\n isConnecting.set(false);\n }\n };\n\n onCleanup(disconnect);\n\n return {\n isSupported,\n isConnected: isConnected.asReadonly(),\n isConnecting: isConnecting.asReadonly(),\n device: device.asReadonly(),\n server: server.asReadonly(),\n error: error.asReadonly(),\n request,\n disconnect,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AA2EA;;;;;;;;;;;;;;;;;;;;AAoBG;AACG,SAAU,SAAS,CAAC,OAA0B,EAAA;AAClD,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC;IAEnE,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;QACzD,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,IAAI,WAAW,IAAI,SAAS,CAAC;AAEtE,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,WAAW,EAAE,WAAW,CAAC,KAAK,CAAC;AAC/B,gBAAA,YAAY,EAAE,WAAW,CAAC,KAAK,CAAC;AAChC,gBAAA,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC;AACzB,gBAAA,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC;AACzB,gBAAA,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC;AACxB,gBAAA,OAAO,EAAE,aAAa;AACtB,gBAAA,UAAU,EAAE,OAAO;aACpB;QACH;AAEA,QAAA,MAAM,cAAc,GAAG;AACrB,YAAA,IAAI,OAAO,EAAE,OAAO,EAAE;AACpB,kBAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO;kBAC1B,EAAE,gBAAgB,EAAE,OAAO,EAAE,gBAAgB,IAAI,IAAI,EAAE,CAAC;AAC5D,YAAA,gBAAgB,EAAE,OAAO,EAAE,gBAAgB,IAAI,EAAE;SAClD;AAED,QAAA,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,uDAAC;AACjC,QAAA,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,wDAAC;AAClC,QAAA,MAAM,MAAM,GAAG,MAAM,CAAyB,IAAI,kDAAC;AACnD,QAAA,MAAM,MAAM,GAAG,MAAM,CAAmC,IAAI,kDAAC;AAC7D,QAAA,MAAM,KAAK,GAAG,MAAM,CAAe,IAAI,iDAAC;QAExC,IAAI,kBAAkB,GAAuB,IAAI;QAEjD,MAAM,UAAU,GAAG,MAAK;YACtB,IAAI,kBAAkB,EAAE;gBACtB,kBAAkB,EAAE,OAAO,EAAE;gBAC7B,kBAAkB,GAAG,IAAI;YAC3B;AAEA,YAAA,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,CAAC;AAEtC,YAAA,IAAI,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE;AACjC,gBAAA,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE;YAChC;AAEA,YAAA,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;AAChB,YAAA,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;AAChB,YAAA,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;AACxB,QAAA,CAAC;AAED,QAAA,MAAM,OAAO,GAAG,YAA0B;AACxC,YAAA,IAAI,SAAS,CAAC,YAAY,CAAC,EAAE;gBAC3B;YACF;AAEA,YAAA,IAAI,SAAS,CAAC,WAAW,CAAC,EAAE;AAC1B,gBAAA,UAAU,EAAE;YACd;AAEA,YAAA,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,YAAA,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AAEf,YAAA,IAAI;gBACF,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,aAAa,CAAC,cAAc,CAAC;AAExE,gBAAA,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;AAEpB,gBAAA,kBAAkB,GAAG,SAAS,CAAC,MAC7B,QAAQ,CAAC,QAAQ,EAAE,wBAAwB,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,CACvE;gBAED,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE;gBAEjD,IAAI,UAAU,EAAE;AACd,oBAAA,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC;AACtB,oBAAA,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;gBACvB;YACF;YAAE,OAAO,CAAC,EAAE;AACV,gBAAA,KAAK,CAAC,GAAG,CAAC,CAAU,CAAC;AACrB,gBAAA,UAAU,EAAE;YACd;oBAAU;AACR,gBAAA,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC;YACzB;AACF,QAAA,CAAC;QAED,SAAS,CAAC,UAAU,CAAC;QAErB,OAAO;YACL,WAAW;AACX,YAAA,WAAW,EAAE,WAAW,CAAC,UAAU,EAAE;AACrC,YAAA,YAAY,EAAE,YAAY,CAAC,UAAU,EAAE;AACvC,YAAA,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE;AAC3B,YAAA,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE;AAC3B,YAAA,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE;YACzB,OAAO;YACP,UAAU;SACX;AACH,IAAA,CAAC,CAAC;AACJ;;ACnMA;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"signality-core-browser-bluetooth.mjs","sources":["../../../projects/core/browser/bluetooth/index.ts","../../../projects/core/browser/bluetooth/signality-core-browser-bluetooth.ts"],"sourcesContent":["import { type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_ASYNC_FN, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, ListenerRef, setupSync } from '@signality/core/browser/listener';\n\nexport interface BluetoothOptions extends WithInjector {\n /**\n * Accept any Bluetooth device without filtering.\n *\n * @default true\n * @see [requestDevice: acceptAllDevices on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth/requestDevice#acceptalldevices)\n */\n readonly acceptAllDevices?: boolean;\n\n /**\n * Filters for device selection. Mutually exclusive with `acceptAllDevices`.\n *\n * @default undefined\n * @see [requestDevice: filters on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth/requestDevice#filters)\n */\n readonly filters?: BluetoothLEScanFilter[];\n\n /**\n * Optional GATT services to access on the connected device.\n *\n * @default []\n * @see [requestDevice: optionalServices on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth/requestDevice#optionalservices)\n */\n readonly optionalServices?: BluetoothServiceUUID[];\n}\n\nexport interface BluetoothRef {\n /**\n * Whether Web Bluetooth API is supported in the current browser.\n *\n * @see [Web Bluetooth API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether a device is currently connected.\n */\n readonly isConnected: Signal<boolean>;\n\n /**\n * Whether a connection is in progress.\n */\n readonly isConnecting: Signal<boolean>;\n\n /**\n * Connected Bluetooth device.\n */\n readonly device: Signal<BluetoothDevice | null>;\n\n /**\n * GATT server of a connected device.\n */\n readonly server: Signal<BluetoothRemoteGATTServer | null>;\n\n /**\n * The last error that occurred.\n */\n readonly error: Signal<Error | null>;\n\n /**\n * Request device connection.\n */\n readonly request: () => Promise<void>;\n\n /**\n * Disconnect from a device.\n */\n readonly disconnect: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Web Bluetooth API](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth).\n *\n * @param options - Optional configuration\n * @returns A BluetoothRef with connection state and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <button (click)=\"bt.request()\">Connect</button>\n * @if (bt.isConnected()) {\n * <p>{{ bt.device()?.name }}</p>\n * }\n * `\n * })\n * export class BluetoothDemo {\n * readonly bt = bluetooth();\n * }\n * ```\n */\nexport function bluetooth(options?: BluetoothOptions): BluetoothRef {\n const { runInContext } = setupContext(options?.injector, bluetooth);\n\n return runInContext(({ isBrowser, injector, onCleanup }) => {\n const isSupported = constSignal(isBrowser && 'bluetooth' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n isConnected: constSignal(false),\n isConnecting: constSignal(false),\n device: constSignal(null),\n server: constSignal(null),\n error: constSignal(null),\n request: NOOP_ASYNC_FN,\n disconnect: NOOP_FN,\n };\n }\n\n const requestOptions = {\n ...(options?.filters?.length\n ? { filters: options.filters }\n : { acceptAllDevices: options?.acceptAllDevices ?? true }),\n optionalServices: options?.optionalServices ?? [],\n };\n\n const isConnected = signal(false);\n const isConnecting = signal(false);\n const device = signal<BluetoothDevice | null>(null);\n const server = signal<BluetoothRemoteGATTServer | null>(null);\n const error = signal<Error | null>(null);\n\n let disconnectListener: ListenerRef | null = null;\n\n const disconnect = () => {\n if (disconnectListener) {\n disconnectListener?.destroy();\n disconnectListener = null;\n }\n\n const activeDevice = untracked(device);\n\n if (activeDevice?.gatt?.connected) {\n activeDevice.gatt.disconnect();\n }\n\n device.set(null);\n server.set(null);\n isConnected.set(false);\n };\n\n const request = async (): Promise<void> => {\n if (untracked(isConnecting)) {\n return;\n }\n\n if (untracked(isConnected)) {\n disconnect();\n }\n\n isConnecting.set(true);\n error.set(null);\n\n try {\n const bt: Bluetooth = (navigator as any).bluetooth;\n const btDevice = await bt.requestDevice(requestOptions);\n\n device.set(btDevice);\n\n disconnectListener = setupSync(() =>\n listener(btDevice, 'gattserverdisconnected', disconnect, { injector })\n );\n\n const gattServer = await btDevice.gatt?.connect();\n\n if (gattServer) {\n server.set(gattServer);\n isConnected.set(true);\n }\n } catch (e) {\n error.set(e as Error);\n disconnect();\n } finally {\n isConnecting.set(false);\n }\n };\n\n onCleanup(disconnect);\n\n return {\n isSupported,\n isConnected: isConnected.asReadonly(),\n isConnecting: isConnecting.asReadonly(),\n device: device.asReadonly(),\n server: server.asReadonly(),\n error: error.asReadonly(),\n request,\n disconnect,\n };\n });\n}\n\n/**\n * Local type definitions for Web Bluetooth API.\n *\n * @remarks\n * External `@types/web-bluetooth` package may conflict with user's other libraries\n * or become outdated. For better DX, we define minimal required interfaces locally\n * without polluting the global namespace with experimental APIs.\n */\ntype BluetoothServiceUUID = number | string;\n\ninterface BluetoothDataFilter {\n readonly dataPrefix?: BufferSource;\n readonly mask?: BufferSource;\n}\n\ninterface BluetoothManufacturerDataFilter extends BluetoothDataFilter {\n readonly companyIdentifier: number;\n}\n\ninterface BluetoothServiceDataFilter extends BluetoothDataFilter {\n readonly service: BluetoothServiceUUID;\n}\n\ninterface BluetoothLEScanFilter {\n readonly name?: string;\n readonly namePrefix?: string;\n readonly services?: BluetoothServiceUUID[];\n readonly manufacturerData?: BluetoothManufacturerDataFilter[];\n readonly serviceData?: BluetoothServiceDataFilter[];\n}\n\ninterface BluetoothRemoteGATTService extends EventTarget {\n readonly device: BluetoothDevice;\n readonly uuid: string;\n readonly isPrimary: boolean;\n getCharacteristic(\n characteristic: BluetoothCharacteristicUUID\n ): Promise<BluetoothRemoteGATTCharacteristic>;\n getCharacteristics(\n characteristic?: BluetoothCharacteristicUUID\n ): Promise<BluetoothRemoteGATTCharacteristic[]>;\n}\n\ntype BluetoothCharacteristicUUID = number | string;\n\ninterface BluetoothRemoteGATTCharacteristic extends EventTarget {\n readonly service: BluetoothRemoteGATTService;\n readonly uuid: string;\n readonly value?: DataView;\n readValue(): Promise<DataView>;\n writeValue(value: BufferSource): Promise<void>;\n startNotifications(): Promise<BluetoothRemoteGATTCharacteristic>;\n stopNotifications(): Promise<BluetoothRemoteGATTCharacteristic>;\n}\n\ninterface BluetoothRemoteGATTServer {\n readonly device: BluetoothDevice;\n readonly connected: boolean;\n connect(): Promise<BluetoothRemoteGATTServer>;\n disconnect(): void;\n getPrimaryService(service: BluetoothServiceUUID): Promise<BluetoothRemoteGATTService>;\n getPrimaryServices(service?: BluetoothServiceUUID): Promise<BluetoothRemoteGATTService[]>;\n}\n\ninterface BluetoothDevice extends EventTarget {\n readonly id: string;\n readonly name?: string;\n readonly gatt?: BluetoothRemoteGATTServer;\n forget(): Promise<void>;\n watchAdvertisements(options?: { signal?: AbortSignal }): Promise<void>;\n readonly watchingAdvertisements: boolean;\n addEventListener(type: 'gattserverdisconnected', listener: (ev: Event) => void): void;\n}\n\ninterface Bluetooth {\n requestDevice(options?: {\n filters?: BluetoothLEScanFilter[];\n acceptAllDevices?: boolean;\n optionalServices?: BluetoothServiceUUID[];\n }): Promise<BluetoothDevice>;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { signal, untracked } from '@angular/core';
|
|
2
|
-
import { setupContext, constSignal, NOOP_ASYNC_FN, toElement, getPipElement } from '@signality/core/internal';
|
|
2
|
+
import { setupContext, constSignal, NOOP_ASYNC_FN, toElement, assertElement, getPipElement } from '@signality/core/internal';
|
|
3
3
|
import { listener } from '@signality/core/browser/listener';
|
|
4
4
|
import { onDisconnect } from '@signality/core/elements/on-disconnect';
|
|
5
5
|
|
|
@@ -45,19 +45,13 @@ function pictureInPicture(target, options) {
|
|
|
45
45
|
const isActive = signal(false, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
|
|
46
46
|
const enter = async () => {
|
|
47
47
|
const targetEl = toElement.untracked(target);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
if (ngDevMode) {
|
|
53
|
-
console.warn(`[pictureInPicture] Failed to enter Picture-in-Picture mode.`, error);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
48
|
+
assertElement(targetEl, 'pictureInPicture');
|
|
49
|
+
await targetEl.requestPictureInPicture();
|
|
56
50
|
};
|
|
57
51
|
const exit = async () => {
|
|
58
52
|
const targetEl = toElement.untracked(target);
|
|
59
53
|
const pipEl = getPipElement(document);
|
|
60
|
-
if (targetEl
|
|
54
|
+
if (targetEl === pipEl) {
|
|
61
55
|
try {
|
|
62
56
|
await document.exitPictureInPicture();
|
|
63
57
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signality-core-browser-picture-in-picture.mjs","sources":["../../../projects/core/browser/picture-in-picture/index.ts","../../../projects/core/browser/picture-in-picture/signality-core-browser-picture-in-picture.ts"],"sourcesContent":["import { signal, type Signal, untracked } from '@angular/core';\nimport {\n constSignal,\n getPipElement,\n NOOP_ASYNC_FN,\n setupContext,\n toElement,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport type PictureInPictureOptions = WithInjector;\n\nexport interface PictureInPictureRef {\n /**\n * Whether the Picture-in-Picture API is supported in the current browser.\n *\n * @see [Picture-in-Picture API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Picture-in-Picture_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether the target video element is currently displayed in Picture-in-Picture mode.\n *\n * @see [Document: pictureInPictureElement on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/pictureInPictureElement)\n */\n readonly isActive: Signal<boolean>;\n\n /**\n * Enter Picture-in-Picture mode for the target video element.\n *\n * @see [HTMLVideoElement: requestPictureInPicture() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/requestPictureInPicture)\n */\n readonly enter: () => Promise<void>;\n\n /**\n * Exit Picture-in-Picture mode.\n *\n * @see [Document: exitPictureInPicture() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/exitPictureInPicture)\n */\n readonly exit: () => Promise<void>;\n\n /**\n * Toggle Picture-in-Picture mode — enters if inactive, exits if active.\n */\n readonly toggle: () => Promise<void>;\n}\n\n/**\n * Signal-based wrapper around the [Picture-in-Picture API](https://developer.mozilla.org/en-US/docs/Web/API/Picture-in-Picture_API).\n *\n * Automatically exits Picture-in-Picture when the target element is disconnected from the DOM.\n *\n * @param target - Video element\n * @param options - Optional configuration\n * @returns A {@link PictureInPictureRef} with `isSupported`, `isActive` signals and `enter`/`exit`/`toggle` methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (pip.isSupported()) {\n * <video #video src=\"video.mp4\"></video>\n * <button (click)=\"pip.toggle()\">Toggle PiP</button>\n * <p>Active: {{ pip.isActive() }}</p>\n * }\n * `\n * })\n * export class PiPDemo {\n * readonly video = viewChild<HTMLVideoElement>('video');\n * readonly pip = pictureInPicture(this.video);\n * }\n * ```\n */\nexport function pictureInPicture(\n target: MaybeElementSignal<HTMLVideoElement>,\n options?: PictureInPictureOptions\n): PictureInPictureRef {\n const { runInContext } = setupContext(options?.injector, pictureInPicture);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(\n isBrowser && 'pictureInPictureEnabled' in document && document.pictureInPictureEnabled\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n isActive: constSignal(false),\n enter: NOOP_ASYNC_FN,\n exit: NOOP_ASYNC_FN,\n toggle: NOOP_ASYNC_FN,\n };\n }\n\n const isActive = signal(false);\n\n const enter = async (): Promise<void> => {\n const targetEl = toElement.untracked(target);\n
|
|
1
|
+
{"version":3,"file":"signality-core-browser-picture-in-picture.mjs","sources":["../../../projects/core/browser/picture-in-picture/index.ts","../../../projects/core/browser/picture-in-picture/signality-core-browser-picture-in-picture.ts"],"sourcesContent":["import { signal, type Signal, untracked } from '@angular/core';\nimport {\n assertElement,\n constSignal,\n getPipElement,\n NOOP_ASYNC_FN,\n setupContext,\n toElement,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport type PictureInPictureOptions = WithInjector;\n\nexport interface PictureInPictureRef {\n /**\n * Whether the Picture-in-Picture API is supported in the current browser.\n *\n * @see [Picture-in-Picture API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Picture-in-Picture_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether the target video element is currently displayed in Picture-in-Picture mode.\n *\n * @see [Document: pictureInPictureElement on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/pictureInPictureElement)\n */\n readonly isActive: Signal<boolean>;\n\n /**\n * Enter Picture-in-Picture mode for the target video element.\n *\n * @throws {DOMException} `'NotAllowedError'` — the document is not allowed to use PiP\n * @throws {DOMException} `'InvalidStateError'` — the video element has `disablePictureInPicture` attribute\n * @throws {DOMException} `'NotSupportedError'` — Picture-in-Picture is not supported\n *\n * @see [HTMLVideoElement: requestPictureInPicture() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/requestPictureInPicture)\n */\n readonly enter: () => Promise<void>;\n\n /**\n * Exit Picture-in-Picture mode.\n *\n * @see [Document: exitPictureInPicture() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/exitPictureInPicture)\n */\n readonly exit: () => Promise<void>;\n\n /**\n * Toggle Picture-in-Picture mode — enters if inactive, exits if active.\n */\n readonly toggle: () => Promise<void>;\n}\n\n/**\n * Signal-based wrapper around the [Picture-in-Picture API](https://developer.mozilla.org/en-US/docs/Web/API/Picture-in-Picture_API).\n *\n * Automatically exits Picture-in-Picture when the target element is disconnected from the DOM.\n *\n * @param target - Video element\n * @param options - Optional configuration\n * @returns A {@link PictureInPictureRef} with `isSupported`, `isActive` signals and `enter`/`exit`/`toggle` methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (pip.isSupported()) {\n * <video #video src=\"video.mp4\"></video>\n * <button (click)=\"pip.toggle()\">Toggle PiP</button>\n * <p>Active: {{ pip.isActive() }}</p>\n * }\n * `\n * })\n * export class PiPDemo {\n * readonly video = viewChild<HTMLVideoElement>('video');\n * readonly pip = pictureInPicture(this.video);\n * }\n * ```\n */\nexport function pictureInPicture(\n target: MaybeElementSignal<HTMLVideoElement>,\n options?: PictureInPictureOptions\n): PictureInPictureRef {\n const { runInContext } = setupContext(options?.injector, pictureInPicture);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(\n isBrowser && 'pictureInPictureEnabled' in document && document.pictureInPictureEnabled\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n isActive: constSignal(false),\n enter: NOOP_ASYNC_FN,\n exit: NOOP_ASYNC_FN,\n toggle: NOOP_ASYNC_FN,\n };\n }\n\n const isActive = signal(false);\n\n const enter = async (): Promise<void> => {\n const targetEl = toElement.untracked(target);\n assertElement(targetEl, 'pictureInPicture');\n await targetEl.requestPictureInPicture();\n };\n\n const exit = async (): Promise<void> => {\n const targetEl = toElement.untracked(target);\n const pipEl = getPipElement(document);\n\n if (targetEl === pipEl) {\n try {\n await document.exitPictureInPicture();\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[pictureInPicture] Failed to exit Picture-in-Picture mode.`, error);\n }\n }\n }\n };\n\n const toggle = async (): Promise<void> => {\n if (untracked(isActive)) {\n await exit();\n } else {\n await enter();\n }\n };\n\n listener(target, 'enterpictureinpicture', () => isActive.set(true));\n listener(target, 'leavepictureinpicture', () => isActive.set(false));\n\n onDisconnect(target, async targetEl => {\n const pipEl = getPipElement(document);\n\n if (pipEl && targetEl === pipEl) {\n try {\n await document.exitPictureInPicture();\n } catch (error) {\n if (ngDevMode) {\n console.warn(\n `[pictureInPicture] Failed to exit Picture-in-Picture mode on disconnect.`,\n error\n );\n }\n }\n isActive.set(false);\n }\n });\n\n return {\n isSupported,\n isActive,\n enter,\n exit,\n toggle,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAsDA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACG,SAAU,gBAAgB,CAC9B,MAA4C,EAC5C,OAAiC,EAAA;AAEjC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC;AAE1E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAI;AACpC,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS,IAAI,yBAAyB,IAAI,QAAQ,IAAI,QAAQ,CAAC,uBAAuB,CACvF;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,KAAK,EAAE,aAAa;AACpB,gBAAA,IAAI,EAAE,aAAa;AACnB,gBAAA,MAAM,EAAE,aAAa;aACtB;QACH;AAEA,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AAE9B,QAAA,MAAM,KAAK,GAAG,YAA0B;YACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC;AAC5C,YAAA,aAAa,CAAC,QAAQ,EAAE,kBAAkB,CAAC;AAC3C,YAAA,MAAM,QAAQ,CAAC,uBAAuB,EAAE;AAC1C,QAAA,CAAC;AAED,QAAA,MAAM,IAAI,GAAG,YAA0B;YACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC;AAC5C,YAAA,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC;AAErC,YAAA,IAAI,QAAQ,KAAK,KAAK,EAAE;AACtB,gBAAA,IAAI;AACF,oBAAA,MAAM,QAAQ,CAAC,oBAAoB,EAAE;gBACvC;gBAAE,OAAO,KAAK,EAAE;oBACd,IAAI,SAAS,EAAE;AACb,wBAAA,OAAO,CAAC,IAAI,CAAC,4DAA4D,EAAE,KAAK,CAAC;oBACnF;gBACF;YACF;AACF,QAAA,CAAC;AAED,QAAA,MAAM,MAAM,GAAG,YAA0B;AACvC,YAAA,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE;gBACvB,MAAM,IAAI,EAAE;YACd;iBAAO;gBACL,MAAM,KAAK,EAAE;YACf;AACF,QAAA,CAAC;AAED,QAAA,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACnE,QAAA,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAEpE,QAAA,YAAY,CAAC,MAAM,EAAE,OAAM,QAAQ,KAAG;AACpC,YAAA,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC;AAErC,YAAA,IAAI,KAAK,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC/B,gBAAA,IAAI;AACF,oBAAA,MAAM,QAAQ,CAAC,oBAAoB,EAAE;gBACvC;gBAAE,OAAO,KAAK,EAAE;oBACd,IAAI,SAAS,EAAE;AACb,wBAAA,OAAO,CAAC,IAAI,CACV,0EAA0E,EAC1E,KAAK,CACN;oBACH;gBACF;AACA,gBAAA,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;YACrB;AACF,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;YACX,QAAQ;YACR,KAAK;YACL,IAAI;YACJ,MAAM;SACP;AACH,IAAA,CAAC,CAAC;AACJ;;ACjKA;;AAEG;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signality-core-browser-speech-recognition.mjs","sources":["../../../projects/core/browser/speech-recognition/index.ts","../../../projects/core/browser/speech-recognition/signality-core-browser-speech-recognition.ts"],"sourcesContent":["import { isSignal, type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext, toValue } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\nimport { watcher } from '@signality/core/reactivity/watcher';\nimport { permissionState } from '@signality/core/browser/permission-state';\n\nexport interface SpeechRecognitionOptions extends WithInjector {\n /**\n * BCP 47 language tag for recognition (e.g. `'en-US'`).\n *\n * @default 'en-US'\n * @see [SpeechRecognition: lang on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/lang)\n */\n readonly lang?: MaybeSignal<string>;\n\n /**\n * Whether to return interim (in-progress) results alongside final ones.\n *\n * @default false\n * @see [SpeechRecognition: interimResults on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/interimResults)\n */\n readonly interimResults?: boolean;\n\n /**\n * Whether recognition continues after the user stops speaking.\n *\n * @default false\n * @see [SpeechRecognition: continuous on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/continuous)\n */\n readonly continuous?: boolean;\n\n /**\n * Maximum number of alternative recognition results per utterance.\n *\n * @default 1\n * @see [SpeechRecognition: maxAlternatives on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/maxAlternatives)\n */\n readonly maxAlternatives?: number;\n}\n\nexport interface SpeechRecognitionRef {\n /**\n * Whether the Speech Recognition API is supported in the current browser.\n *\n * @see [SpeechRecognition browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether speech recognition is currently active and listening.\n */\n readonly isListening: Signal<boolean>;\n\n /**\n * Accumulated final transcript text.\n *\n * @see [SpeechRecognitionResult on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionResult)\n */\n readonly text: Signal<string>;\n\n /**\n * In-progress interim transcript. Only populated when `interimResults` is `true`.\n *\n * @see [SpeechRecognition: interimResults on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/interimResults)\n */\n readonly interimText: Signal<string>;\n\n /**\n * The last recognition error, or `null` if no error occurred.\n *\n * @see [SpeechRecognitionErrorEvent on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionErrorEvent)\n */\n readonly error: Signal<SpeechRecognitionErrorEvent | Error | null>;\n\n /**\n * Start listening for speech input.\n *\n * @see [SpeechRecognition: start() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/start)\n */\n readonly start: () => void;\n\n /**\n * Stop listening and return any remaining results.\n *\n * @see [SpeechRecognition: stop() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/stop)\n */\n readonly stop: () => void;\n\n /**\n * Abort recognition immediately without returning results.\n *\n * @see [SpeechRecognition: abort() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/abort)\n */\n readonly abort: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Speech Recognition API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition).\n *\n * @param options - Optional configuration\n * @returns A SpeechRecognitionRef with isSupported, isListening, text, interimText, error signals and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (recognition.isSupported()) {\n * <button (click)=\"toggleRecognition()\">\n * {{ recognition.isListening() ? 'Stop' : 'Start' }} Recording\n * </button>\n * <p>{{ recognition.text() }}</p>\n * @if (recognition.interimText()) {\n * <p><em>{{ recognition.interimText() }}</em></p>\n * }\n * }\n * `\n * })\n * export class SpeechComponent {\n * readonly recognition = speechRecognition();\n *\n * toggleRecognition() {\n * if (this.recognition.isListening()) {\n * this.recognition.stop();\n * } else {\n * this.recognition.start();\n * }\n * }\n * }\n * ```\n */\nexport function speechRecognition(options?: SpeechRecognitionOptions): SpeechRecognitionRef {\n const { runInContext } = setupContext(options?.injector, speechRecognition);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(\n isBrowser && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n isListening: constSignal(false),\n text: constSignal(''),\n interimText: constSignal(''),\n error: constSignal(null),\n start: NOOP_FN,\n stop: NOOP_FN,\n abort: NOOP_FN,\n };\n }\n\n const SpeechRecognitionClass = window.SpeechRecognition || window.webkitSpeechRecognition;\n\n const recognition = new SpeechRecognitionClass();\n\n const {\n lang = 'en-US',\n interimResults = false,\n continuous = false,\n maxAlternatives = 1,\n } = options ?? {};\n\n recognition.lang = toValue(lang);\n recognition.continuous = continuous;\n recognition.interimResults = interimResults;\n recognition.maxAlternatives = maxAlternatives;\n\n const isListening = signal(false);\n const text = signal('');\n const interimText = signal('');\n const error = signal<SpeechRecognitionErrorEvent | Error | null>(null);\n\n const handleResult = (event: SpeechRecognitionEvent) => {\n let finalTranscript = '';\n let interimTranscript = '';\n\n for (let i = event.resultIndex; i < event.results.length; i++) {\n const result = event.results[i];\n const transcript = result[0]?.transcript || result.item(0)?.transcript || '';\n\n if (result.isFinal) {\n finalTranscript += transcript;\n } else {\n interimTranscript += transcript;\n }\n }\n\n if (finalTranscript) {\n text.update(t => (t ? t + ' ' : '') + finalTranscript);\n interimText.set('');\n } else if (interimTranscript) {\n interimText.set(interimTranscript);\n }\n };\n\n const handleError = (event: SpeechRecognitionErrorEvent) => {\n error.set(event);\n isListening.set(false);\n };\n\n const handleStart = () => {\n isListening.set(true);\n error.set(null);\n\n if (!continuous) {\n text.set('');\n interimText.set('');\n }\n };\n\n const handleEnd = () => {\n isListening.set(false);\n recognition.lang = toValue(lang);\n };\n\n recognition.onstart = handleStart;\n recognition.onend = handleEnd;\n recognition.onerror = handleError;\n recognition.onresult = handleResult;\n\n const start = () => {\n try {\n if (!untracked(isListening)) {\n recognition.start();\n }\n } catch (err) {\n error.set(err as Error);\n }\n };\n\n const stop = () => {\n if (untracked(isListening)) {\n recognition.stop();\n }\n };\n\n const abort = () => {\n if (untracked(isListening)) {\n recognition.abort();\n }\n };\n\n onCleanup(abort);\n\n if (isSignal(lang)) {\n watcher(lang, newLang => {\n if (!isListening()) {\n recognition.lang = newLang;\n }\n });\n }\n\n watcher(permissionState('microphone'), state => {\n if (state === 'denied') {\n abort();\n }\n });\n\n return {\n isSupported,\n isListening: isListening.asReadonly(),\n text: text.asReadonly(),\n interimText: interimText.asReadonly(),\n error: error.asReadonly(),\n start,\n stop,\n abort,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"signality-core-browser-speech-recognition.mjs","sources":["../../../projects/core/browser/speech-recognition/index.ts","../../../projects/core/browser/speech-recognition/signality-core-browser-speech-recognition.ts"],"sourcesContent":["import { isSignal, type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext, toValue } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\nimport { watcher } from '@signality/core/reactivity/watcher';\nimport { permissionState } from '@signality/core/browser/permission-state';\n\nexport interface SpeechRecognitionOptions extends WithInjector {\n /**\n * BCP 47 language tag for recognition (e.g. `'en-US'`).\n *\n * @default 'en-US'\n * @see [SpeechRecognition: lang on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/lang)\n */\n readonly lang?: MaybeSignal<string>;\n\n /**\n * Whether to return interim (in-progress) results alongside final ones.\n *\n * @default false\n * @see [SpeechRecognition: interimResults on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/interimResults)\n */\n readonly interimResults?: boolean;\n\n /**\n * Whether recognition continues after the user stops speaking.\n *\n * @default false\n * @see [SpeechRecognition: continuous on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/continuous)\n */\n readonly continuous?: boolean;\n\n /**\n * Maximum number of alternative recognition results per utterance.\n *\n * @default 1\n * @see [SpeechRecognition: maxAlternatives on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/maxAlternatives)\n */\n readonly maxAlternatives?: number;\n}\n\nexport interface SpeechRecognitionRef {\n /**\n * Whether the Speech Recognition API is supported in the current browser.\n *\n * @see [SpeechRecognition browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether speech recognition is currently active and listening.\n */\n readonly isListening: Signal<boolean>;\n\n /**\n * Accumulated final transcript text.\n *\n * @see [SpeechRecognitionResult on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionResult)\n */\n readonly text: Signal<string>;\n\n /**\n * In-progress interim transcript. Only populated when `interimResults` is `true`.\n *\n * @see [SpeechRecognition: interimResults on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/interimResults)\n */\n readonly interimText: Signal<string>;\n\n /**\n * The last recognition error, or `null` if no error occurred.\n *\n * @see [SpeechRecognitionErrorEvent on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionErrorEvent)\n */\n readonly error: Signal<SpeechRecognitionErrorEvent | Error | null>;\n\n /**\n * Start listening for speech input.\n *\n * @see [SpeechRecognition: start() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/start)\n */\n readonly start: () => void;\n\n /**\n * Stop listening and return any remaining results.\n *\n * @see [SpeechRecognition: stop() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/stop)\n */\n readonly stop: () => void;\n\n /**\n * Abort recognition immediately without returning results.\n *\n * @see [SpeechRecognition: abort() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/abort)\n */\n readonly abort: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Speech Recognition API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition).\n *\n * @param options - Optional configuration\n * @returns A SpeechRecognitionRef with isSupported, isListening, text, interimText, error signals and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (recognition.isSupported()) {\n * <button (click)=\"toggleRecognition()\">\n * {{ recognition.isListening() ? 'Stop' : 'Start' }} Recording\n * </button>\n * <p>{{ recognition.text() }}</p>\n * @if (recognition.interimText()) {\n * <p><em>{{ recognition.interimText() }}</em></p>\n * }\n * }\n * `\n * })\n * export class SpeechComponent {\n * readonly recognition = speechRecognition();\n *\n * toggleRecognition() {\n * if (this.recognition.isListening()) {\n * this.recognition.stop();\n * } else {\n * this.recognition.start();\n * }\n * }\n * }\n * ```\n */\nexport function speechRecognition(options?: SpeechRecognitionOptions): SpeechRecognitionRef {\n const { runInContext } = setupContext(options?.injector, speechRecognition);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(\n isBrowser && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n isListening: constSignal(false),\n text: constSignal(''),\n interimText: constSignal(''),\n error: constSignal(null),\n start: NOOP_FN,\n stop: NOOP_FN,\n abort: NOOP_FN,\n };\n }\n\n const SpeechRecognitionClass =\n (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition;\n\n const recognition: SpeechRecognition = new SpeechRecognitionClass();\n\n const {\n lang = 'en-US',\n interimResults = false,\n continuous = false,\n maxAlternatives = 1,\n } = options ?? {};\n\n recognition.lang = toValue(lang);\n recognition.continuous = continuous;\n recognition.interimResults = interimResults;\n recognition.maxAlternatives = maxAlternatives;\n\n const isListening = signal(false);\n const text = signal('');\n const interimText = signal('');\n const error = signal<SpeechRecognitionErrorEvent | Error | null>(null);\n\n const handleResult = (event: SpeechRecognitionEvent) => {\n let finalTranscript = '';\n let interimTranscript = '';\n\n for (let i = event.resultIndex; i < event.results.length; i++) {\n const result = event.results[i];\n const transcript = result[0]?.transcript || result.item(0)?.transcript || '';\n\n if (result.isFinal) {\n finalTranscript += transcript;\n } else {\n interimTranscript += transcript;\n }\n }\n\n if (finalTranscript) {\n text.update(t => (t ? t + ' ' : '') + finalTranscript);\n interimText.set('');\n } else if (interimTranscript) {\n interimText.set(interimTranscript);\n }\n };\n\n const handleError = (event: SpeechRecognitionErrorEvent) => {\n error.set(event);\n isListening.set(false);\n };\n\n const handleStart = () => {\n isListening.set(true);\n error.set(null);\n\n if (!continuous) {\n text.set('');\n interimText.set('');\n }\n };\n\n const handleEnd = () => {\n isListening.set(false);\n recognition.lang = toValue(lang);\n };\n\n recognition.onstart = handleStart;\n recognition.onend = handleEnd;\n recognition.onerror = handleError;\n recognition.onresult = handleResult;\n\n const start = () => {\n try {\n if (!untracked(isListening)) {\n recognition.start();\n }\n } catch (err) {\n error.set(err as Error);\n }\n };\n\n const stop = () => {\n if (untracked(isListening)) {\n recognition.stop();\n }\n };\n\n const abort = () => {\n if (untracked(isListening)) {\n recognition.abort();\n }\n };\n\n onCleanup(abort);\n\n if (isSignal(lang)) {\n watcher(lang, newLang => {\n if (!isListening()) {\n recognition.lang = newLang;\n }\n });\n }\n\n watcher(permissionState('microphone'), state => {\n if (state === 'denied') {\n abort();\n }\n });\n\n return {\n isSupported,\n isListening: isListening.asReadonly(),\n text: text.asReadonly(),\n interimText: interimText.asReadonly(),\n error: error.asReadonly(),\n start,\n stop,\n abort,\n };\n });\n}\n\n/**\n * Local type definitions for Web Speech API (Speech Recognition).\n *\n * @remarks\n * External `@types/dom-speech-recognition` package may conflict with user's other libraries\n * or become outdated. For better DX, we define minimal required interfaces locally\n * without polluting the global namespace with experimental APIs.\n */\ntype SpeechRecognitionErrorCode =\n | 'aborted'\n | 'audio-capture'\n | 'bad-grammar'\n | 'language-not-supported'\n | 'network'\n | 'no-speech'\n | 'not-allowed'\n | 'service-not-allowed';\n\ninterface SpeechRecognitionAlternative {\n readonly transcript: string;\n readonly confidence: number;\n}\n\ninterface SpeechRecognitionResult {\n readonly isFinal: boolean;\n readonly length: number;\n item(index: number): SpeechRecognitionAlternative;\n [index: number]: SpeechRecognitionAlternative;\n}\n\ninterface SpeechRecognitionResultList {\n readonly length: number;\n item(index: number): SpeechRecognitionResult;\n [index: number]: SpeechRecognitionResult;\n}\n\ninterface SpeechGrammar {\n src: string;\n weight: number;\n}\n\ninterface SpeechGrammarList {\n readonly length: number;\n addFromString(string: string, weight?: number): void;\n addFromURI(src: string, weight?: number): void;\n item(index: number): SpeechGrammar;\n [index: number]: SpeechGrammar;\n}\n\ninterface SpeechRecognitionEvent extends Event {\n readonly resultIndex: number;\n readonly results: SpeechRecognitionResultList;\n}\n\ninterface SpeechRecognitionErrorEvent extends Event {\n readonly error: SpeechRecognitionErrorCode;\n readonly message: string;\n}\n\ninterface SpeechRecognition extends EventTarget {\n continuous: boolean;\n grammars: SpeechGrammarList;\n interimResults: boolean;\n lang: string;\n maxAlternatives: number;\n onaudioend: ((this: SpeechRecognition, ev: Event) => void) | null;\n onaudiostart: ((this: SpeechRecognition, ev: Event) => void) | null;\n onend: ((this: SpeechRecognition, ev: Event) => void) | null;\n onerror: ((this: SpeechRecognition, ev: SpeechRecognitionErrorEvent) => void) | null;\n onnomatch: ((this: SpeechRecognition, ev: SpeechRecognitionEvent) => void) | null;\n onresult: ((this: SpeechRecognition, ev: SpeechRecognitionEvent) => void) | null;\n onsoundend: ((this: SpeechRecognition, ev: Event) => void) | null;\n onsoundstart: ((this: SpeechRecognition, ev: Event) => void) | null;\n onspeechend: ((this: SpeechRecognition, ev: Event) => void) | null;\n onspeechstart: ((this: SpeechRecognition, ev: Event) => void) | null;\n onstart: ((this: SpeechRecognition, ev: Event) => void) | null;\n abort(): void;\n start(audioTrack?: MediaStreamTrack): void;\n stop(): void;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signality/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Vyacheslav Borodin <https://github.com/vs-borodin>",
|
|
6
6
|
"description": "A foundational toolkit for Angular Signals",
|
|
@@ -300,14 +300,14 @@
|
|
|
300
300
|
"types": "./reactivity/watcher/index.d.ts",
|
|
301
301
|
"default": "./fesm2022/signality-core-reactivity-watcher.mjs"
|
|
302
302
|
},
|
|
303
|
-
"./router/fragment": {
|
|
304
|
-
"types": "./router/fragment/index.d.ts",
|
|
305
|
-
"default": "./fesm2022/signality-core-router-fragment.mjs"
|
|
306
|
-
},
|
|
307
303
|
"./router/params": {
|
|
308
304
|
"types": "./router/params/index.d.ts",
|
|
309
305
|
"default": "./fesm2022/signality-core-router-params.mjs"
|
|
310
306
|
},
|
|
307
|
+
"./router/fragment": {
|
|
308
|
+
"types": "./router/fragment/index.d.ts",
|
|
309
|
+
"default": "./fesm2022/signality-core-router-fragment.mjs"
|
|
310
|
+
},
|
|
311
311
|
"./router/query-params": {
|
|
312
312
|
"types": "./router/query-params/index.d.ts",
|
|
313
313
|
"default": "./fesm2022/signality-core-router-query-params.mjs"
|