@signality/core 0.0.1-alpha.3 → 0.1.0

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/README.md CHANGED
@@ -1,58 +1,62 @@
1
1
  # @signality/core
2
2
 
3
- Signal-first utility collection for Angular. Comprehensive library of reactive utilities for browser APIs, DOM elements, and common patterns.
3
+ A collection of atomic utilities for building reactive compositions in [Angular](https://angular.dev).
4
+
5
+ ## Key Benefits
6
+
7
+ - **Signal-first design** — built on top of Angular [Signals](https://angular.dev/guide/signals), abstracting away from RxJS
8
+ - **Automatic cleanup** — utilities manage resource lifecycles automatically
9
+ - **SSR-compatible** — browser APIs are guarded with safe defaults on the server
10
+ - **Reactive inputs** — seamlessly handles static and reactive values
11
+ - **Tree-Shakable** — only the code you use ends up in your bundle
12
+
13
+ ## Quick Example
14
+
15
+ ```ts
16
+ import { Component, effect } from '@angular/core';
17
+ import { storage, speechSynthesis, favicon } from '@signality/core';
18
+
19
+ @Component({
20
+ template: `
21
+ <input [(ngModel)]="value" />
22
+ <button (click)="synthesis.speak(value())">Speak</button>
23
+ `,
24
+ })
25
+ export class Demo {
26
+ readonly value = storage('key', ''); // Web Storage API
27
+ readonly synthesis = speechSynthesis(); // Web Speech API
28
+ readonly fav = favicon(); // Dynamic Favicon
29
+
30
+ constructor() {
31
+ effect(() => {
32
+ if (this.synthesis.isSpeaking()) {
33
+ this.fav.setEmoji('🔊');
34
+ } else {
35
+ this.fav.reset();
36
+ }
37
+ });
38
+ }
39
+ }
40
+ ```
41
+
42
+ ## Framework Compatibility
43
+
44
+ | Tool | Minimum Version |
45
+ |-------------|-----------------|
46
+ | **Angular** | `v20.0.0` |
4
47
 
5
48
  ## Installation
6
49
 
7
50
  ```bash
8
- npm install @signality/core
51
+ pnpm add @signality/core
9
52
  ```
10
53
 
11
- ## Features
12
-
13
- ### Browser Utilities
14
- - **`battery()`** — Reactive battery status and charging state
15
- - **`clipboard()`** — Reactive clipboard read/write operations
16
- - **`geolocation()`** — Reactive geolocation tracking
17
- - **`pageVisibility()`** — Reactive page visibility state
18
- - **`storage()`** — Reactive localStorage/sessionStorage with sync
19
- - **`network()`** — Reactive network connection status
20
- - **`fullscreen()`** — Reactive fullscreen API
21
- - And [many more...](https://signality.dev/browser/battery)
22
-
23
- ### Element Utilities
24
- - **`elementSize()`** — Reactive element dimensions
25
- - **`elementVisibility()`** — Reactive Intersection Observer
26
- - **`dropzone()`** — Reactive drag & drop zone
27
- - **`windowSize()`** — Reactive window dimensions
28
- - **`scrollPosition()`** — Reactive scroll position
29
- - And [many more...](https://signality.dev/elements/active-element)
30
-
31
- ### Utils
32
- - **`debounced()`** — Debounced writable signal
33
- - **`throttled()`** — Throttled writable signal
34
-
35
- ## Usage
36
-
37
- ```typescript
38
- import { battery, clipboard, elementSize, debounced } from '@signality/core';
39
-
40
- // Battery status
41
- const batteryStatus = battery();
42
- // batteryStatus.level(), batteryStatus.charging()
43
-
44
- // Clipboard operations
45
- const clipboard = clipboard();
46
- await clipboard.copy('Hello');
47
- const text = await clipboard.paste();
48
-
49
- // Element size tracking
50
- const element = viewChild<ElementRef<HTMLDivElement>>('myElement');
51
- const size = elementSize(element);
52
- // size.width(), size.height()
53
-
54
- // Debounced signal
55
- const searchQuery = debounced('', 300);
54
+ Or with npm/yarn:
55
+
56
+ ```bash
57
+ npm install @signality/core
58
+ # or
59
+ yarn add @signality/core
56
60
  ```
57
61
 
58
62
  ## Documentation
@@ -28,7 +28,6 @@ export * from '@signality/core/browser/storage';
28
28
  export * from '@signality/core/browser/text-direction';
29
29
  export * from '@signality/core/browser/text-selection';
30
30
  export * from '@signality/core/browser/vibration';
31
- export * from '@signality/core/browser/wake-lock';
32
31
  export * from '@signality/core/browser/web-notification';
33
32
  export * from '@signality/core/browser/web-share';
34
33
  export * from '@signality/core/browser/web-worker';
@@ -39,7 +39,7 @@ import { permissionState } from '@signality/core/browser/permission-state';
39
39
  */
40
40
  function speechRecognition(options) {
41
41
  const { runInContext } = setupContext(options?.injector, speechRecognition);
42
- return runInContext(({ isBrowser, injector, onCleanup }) => {
42
+ return runInContext(({ isBrowser, onCleanup }) => {
43
43
  const isSupported = constSignal(isBrowser && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window));
44
44
  if (!isSupported()) {
45
45
  return {
@@ -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, injector, 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":";;;;;AAgGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCG;AACG,SAAU,iBAAiB,CAAC,OAAkC,EAAA;AAClE,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC;IAE3E,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;AACzD,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS,KAAK,mBAAmB,IAAI,MAAM,IAAI,yBAAyB,IAAI,MAAM,CAAC,CACpF;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,WAAW,EAAE,WAAW,CAAC,KAAK,CAAC;AAC/B,gBAAA,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;AACrB,gBAAA,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC;AAC5B,gBAAA,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC;AACxB,gBAAA,KAAK,EAAE,OAAO;AACd,gBAAA,IAAI,EAAE,OAAO;AACb,gBAAA,KAAK,EAAE,OAAO;aACf;QACH;QAEA,MAAM,sBAAsB,GAAG,MAAM,CAAC,iBAAiB,IAAI,MAAM,CAAC,uBAAuB;AAEzF,QAAA,MAAM,WAAW,GAAG,IAAI,sBAAsB,EAAE;QAEhD,MAAM,EACJ,IAAI,GAAG,OAAO,EACd,cAAc,GAAG,KAAK,EACtB,UAAU,GAAG,KAAK,EAClB,eAAe,GAAG,CAAC,GACpB,GAAG,OAAO,IAAI,EAAE;AAEjB,QAAA,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;AAChC,QAAA,WAAW,CAAC,UAAU,GAAG,UAAU;AACnC,QAAA,WAAW,CAAC,cAAc,GAAG,cAAc;AAC3C,QAAA,WAAW,CAAC,eAAe,GAAG,eAAe;AAE7C,QAAA,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,uDAAC;AACjC,QAAA,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,gDAAC;AACvB,QAAA,MAAM,WAAW,GAAG,MAAM,CAAC,EAAE,uDAAC;AAC9B,QAAA,MAAM,KAAK,GAAG,MAAM,CAA6C,IAAI,iDAAC;AAEtE,QAAA,MAAM,YAAY,GAAG,CAAC,KAA6B,KAAI;YACrD,IAAI,eAAe,GAAG,EAAE;YACxB,IAAI,iBAAiB,GAAG,EAAE;AAE1B,YAAA,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBAC7D,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AAC/B,gBAAA,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,IAAI,EAAE;AAE5E,gBAAA,IAAI,MAAM,CAAC,OAAO,EAAE;oBAClB,eAAe,IAAI,UAAU;gBAC/B;qBAAO;oBACL,iBAAiB,IAAI,UAAU;gBACjC;YACF;YAEA,IAAI,eAAe,EAAE;gBACnB,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,EAAE,IAAI,eAAe,CAAC;AACtD,gBAAA,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB;iBAAO,IAAI,iBAAiB,EAAE;AAC5B,gBAAA,WAAW,CAAC,GAAG,CAAC,iBAAiB,CAAC;YACpC;AACF,QAAA,CAAC;AAED,QAAA,MAAM,WAAW,GAAG,CAAC,KAAkC,KAAI;AACzD,YAAA,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;AAChB,YAAA,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;AACxB,QAAA,CAAC;QAED,MAAM,WAAW,GAAG,MAAK;AACvB,YAAA,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;AACrB,YAAA,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YAEf,IAAI,CAAC,UAAU,EAAE;AACf,gBAAA,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACZ,gBAAA,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB;AACF,QAAA,CAAC;QAED,MAAM,SAAS,GAAG,MAAK;AACrB,YAAA,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;AACtB,YAAA,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;AAClC,QAAA,CAAC;AAED,QAAA,WAAW,CAAC,OAAO,GAAG,WAAW;AACjC,QAAA,WAAW,CAAC,KAAK,GAAG,SAAS;AAC7B,QAAA,WAAW,CAAC,OAAO,GAAG,WAAW;AACjC,QAAA,WAAW,CAAC,QAAQ,GAAG,YAAY;QAEnC,MAAM,KAAK,GAAG,MAAK;AACjB,YAAA,IAAI;AACF,gBAAA,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE;oBAC3B,WAAW,CAAC,KAAK,EAAE;gBACrB;YACF;YAAE,OAAO,GAAG,EAAE;AACZ,gBAAA,KAAK,CAAC,GAAG,CAAC,GAAY,CAAC;YACzB;AACF,QAAA,CAAC;QAED,MAAM,IAAI,GAAG,MAAK;AAChB,YAAA,IAAI,SAAS,CAAC,WAAW,CAAC,EAAE;gBAC1B,WAAW,CAAC,IAAI,EAAE;YACpB;AACF,QAAA,CAAC;QAED,MAAM,KAAK,GAAG,MAAK;AACjB,YAAA,IAAI,SAAS,CAAC,WAAW,CAAC,EAAE;gBAC1B,WAAW,CAAC,KAAK,EAAE;YACrB;AACF,QAAA,CAAC;QAED,SAAS,CAAC,KAAK,CAAC;AAEhB,QAAA,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE;AAClB,YAAA,OAAO,CAAC,IAAI,EAAE,OAAO,IAAG;AACtB,gBAAA,IAAI,CAAC,WAAW,EAAE,EAAE;AAClB,oBAAA,WAAW,CAAC,IAAI,GAAG,OAAO;gBAC5B;AACF,YAAA,CAAC,CAAC;QACJ;QAEA,OAAO,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,KAAK,IAAG;AAC7C,YAAA,IAAI,KAAK,KAAK,QAAQ,EAAE;AACtB,gBAAA,KAAK,EAAE;YACT;AACF,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;AACX,YAAA,WAAW,EAAE,WAAW,CAAC,UAAU,EAAE;AACrC,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;AACvB,YAAA,WAAW,EAAE,WAAW,CAAC,UAAU,EAAE;AACrC,YAAA,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE;YACzB,KAAK;YACL,IAAI;YACJ,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;AC7QA;;AAEG;;;;"}
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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"}
@@ -28,7 +28,6 @@ export * from '@signality/core/browser/storage';
28
28
  export * from '@signality/core/browser/text-direction';
29
29
  export * from '@signality/core/browser/text-selection';
30
30
  export * from '@signality/core/browser/vibration';
31
- export * from '@signality/core/browser/wake-lock';
32
31
  export * from '@signality/core/browser/web-notification';
33
32
  export * from '@signality/core/browser/web-share';
34
33
  export * from '@signality/core/browser/web-worker';
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser.mjs","sources":["../../../projects/core/browser/signality-core-browser.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;AAEG"}
1
+ {"version":3,"file":"signality-core-browser.mjs","sources":["../../../projects/core/browser/signality-core-browser.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;AAEG"}
@@ -36,17 +36,19 @@ function interval(callback, intervalMs, options) {
36
36
  return NOOP_EFFECT_REF;
37
37
  }
38
38
  let intervalId;
39
+ let watcherRef = null;
39
40
  const start = () => {
40
41
  clearInterval(intervalId);
41
42
  const ms = toValue.untracked(intervalMs);
42
43
  intervalId = ms > 0 ? setInterval(callback, ms) : undefined;
43
44
  };
44
45
  if (isSignal(intervalMs)) {
45
- watcher(intervalMs, start);
46
+ watcherRef = watcher(intervalMs, start);
46
47
  }
47
48
  const destroy = () => {
48
49
  clearInterval(intervalId);
49
50
  intervalId = undefined;
51
+ watcherRef?.destroy();
50
52
  };
51
53
  onCleanup(destroy);
52
54
  if (options?.immediate) {
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-scheduling-interval.mjs","sources":["../../../projects/core/scheduling/interval/index.ts","../../../projects/core/scheduling/interval/signality-core-scheduling-interval.ts"],"sourcesContent":["import { isSignal } from '@angular/core';\nimport { NOOP_EFFECT_REF, setupContext, type Timer, toValue } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\nimport { watcher } from '@signality/core/reactivity/watcher';\n\nexport interface IntervalOptions extends WithInjector {\n /**\n * Call the callback immediately, without waiting for the first tick.\n *\n * @default false\n */\n readonly immediate?: boolean;\n}\n\nexport interface IntervalRef {\n /**\n * Stop the interval permanently.\n */\n readonly destroy: () => void;\n}\n\n/**\n * Signal-based wrapper around [`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval).\n * Creates a reactive interval that executes a callback at specified intervals.\n * The interval starts immediately upon creation and can be stopped with `destroy()`.\n *\n * @param callback - Function to execute on each interval tick\n * @param intervalMs - Interval duration in milliseconds (can be a reactive signal)\n * @param options - Optional configuration\n * @returns An IntervalRef with a `destroy` method to stop the interval\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Ticks: {{ ticks() }}</p>\n * <button (click)=\"polling.destroy()\">Stop</button>\n * `,\n * })\n * export class PeriodicTask {\n * readonly ticks = signal(0);\n *\n * readonly polling = interval(() => {\n * this.ticks.update(n => n + 1);\n * }, 5000);\n * }\n * ```\n */\nexport function interval(\n callback: () => void,\n intervalMs: MaybeSignal<number>,\n options?: IntervalOptions\n): IntervalRef {\n const { runInContext } = setupContext(options?.injector, interval);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n let intervalId: Timer;\n\n const start = () => {\n clearInterval(intervalId);\n\n const ms = toValue.untracked(intervalMs);\n intervalId = ms > 0 ? setInterval(callback, ms) : undefined;\n };\n\n if (isSignal(intervalMs)) {\n watcher(intervalMs, start);\n }\n\n const destroy = () => {\n clearInterval(intervalId);\n intervalId = undefined;\n };\n\n onCleanup(destroy);\n\n if (options?.immediate) {\n callback();\n }\n\n start();\n\n return { destroy };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;SACa,QAAQ,CACtB,QAAoB,EACpB,UAA+B,EAC/B,OAAyB,EAAA;AAEzB,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;IAElE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;QAC9C,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,eAAe;QACxB;AAEA,QAAA,IAAI,UAAiB;QAErB,MAAM,KAAK,GAAG,MAAK;YACjB,aAAa,CAAC,UAAU,CAAC;YAEzB,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC;AACxC,YAAA,UAAU,GAAG,EAAE,GAAG,CAAC,GAAG,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,SAAS;AAC7D,QAAA,CAAC;AAED,QAAA,IAAI,QAAQ,CAAC,UAAU,CAAC,EAAE;AACxB,YAAA,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;QAC5B;QAEA,MAAM,OAAO,GAAG,MAAK;YACnB,aAAa,CAAC,UAAU,CAAC;YACzB,UAAU,GAAG,SAAS;AACxB,QAAA,CAAC;QAED,SAAS,CAAC,OAAO,CAAC;AAElB,QAAA,IAAI,OAAO,EAAE,SAAS,EAAE;AACtB,YAAA,QAAQ,EAAE;QACZ;AAEA,QAAA,KAAK,EAAE;QAEP,OAAO,EAAE,OAAO,EAAE;AACpB,IAAA,CAAC,CAAC;AACJ;;ACxFA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-scheduling-interval.mjs","sources":["../../../projects/core/scheduling/interval/index.ts","../../../projects/core/scheduling/interval/signality-core-scheduling-interval.ts"],"sourcesContent":["import { isSignal } from '@angular/core';\nimport { NOOP_EFFECT_REF, setupContext, type Timer, toValue } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\nimport { watcher, type WatcherRef } from '@signality/core/reactivity/watcher';\n\nexport interface IntervalOptions extends WithInjector {\n /**\n * Call the callback immediately, without waiting for the first tick.\n *\n * @default false\n */\n readonly immediate?: boolean;\n}\n\nexport interface IntervalRef {\n /**\n * Stop the interval permanently.\n */\n readonly destroy: () => void;\n}\n\n/**\n * Signal-based wrapper around [`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval).\n * Creates a reactive interval that executes a callback at specified intervals.\n * The interval starts immediately upon creation and can be stopped with `destroy()`.\n *\n * @param callback - Function to execute on each interval tick\n * @param intervalMs - Interval duration in milliseconds (can be a reactive signal)\n * @param options - Optional configuration\n * @returns An IntervalRef with a `destroy` method to stop the interval\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Ticks: {{ ticks() }}</p>\n * <button (click)=\"polling.destroy()\">Stop</button>\n * `,\n * })\n * export class PeriodicTask {\n * readonly ticks = signal(0);\n *\n * readonly polling = interval(() => {\n * this.ticks.update(n => n + 1);\n * }, 5000);\n * }\n * ```\n */\nexport function interval(\n callback: () => void,\n intervalMs: MaybeSignal<number>,\n options?: IntervalOptions\n): IntervalRef {\n const { runInContext } = setupContext(options?.injector, interval);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n let intervalId: Timer;\n let watcherRef: WatcherRef | null = null;\n\n const start = () => {\n clearInterval(intervalId);\n\n const ms = toValue.untracked(intervalMs);\n intervalId = ms > 0 ? setInterval(callback, ms) : undefined;\n };\n\n if (isSignal(intervalMs)) {\n watcherRef = watcher(intervalMs, start);\n }\n\n const destroy = () => {\n clearInterval(intervalId);\n intervalId = undefined;\n watcherRef?.destroy();\n };\n\n onCleanup(destroy);\n\n if (options?.immediate) {\n callback();\n }\n\n start();\n\n return { destroy };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;SACa,QAAQ,CACtB,QAAoB,EACpB,UAA+B,EAC/B,OAAyB,EAAA;AAEzB,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;IAElE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;QAC9C,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,eAAe;QACxB;AAEA,QAAA,IAAI,UAAiB;QACrB,IAAI,UAAU,GAAsB,IAAI;QAExC,MAAM,KAAK,GAAG,MAAK;YACjB,aAAa,CAAC,UAAU,CAAC;YAEzB,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC;AACxC,YAAA,UAAU,GAAG,EAAE,GAAG,CAAC,GAAG,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,SAAS;AAC7D,QAAA,CAAC;AAED,QAAA,IAAI,QAAQ,CAAC,UAAU,CAAC,EAAE;AACxB,YAAA,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;QACzC;QAEA,MAAM,OAAO,GAAG,MAAK;YACnB,aAAa,CAAC,UAAU,CAAC;YACzB,UAAU,GAAG,SAAS;YACtB,UAAU,EAAE,OAAO,EAAE;AACvB,QAAA,CAAC;QAED,SAAS,CAAC,OAAO,CAAC;AAElB,QAAA,IAAI,OAAO,EAAE,SAAS,EAAE;AACtB,YAAA,QAAQ,EAAE;QACZ;AAEA,QAAA,KAAK,EAAE;QAEP,OAAO,EAAE,OAAO,EAAE;AACpB,IAAA,CAAC,CAAC;AACJ;;AC1FA;;AAEG;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signality/core",
3
- "version": "0.0.1-alpha.3",
3
+ "version": "0.1.0",
4
4
  "license": "MIT",
5
5
  "author": "Vyacheslav Borodin <https://github.com/vs-borodin>",
6
6
  "description": "A foundational toolkit for Angular Signals",
@@ -23,11 +23,11 @@
23
23
  "homepage": "https://signality.dev",
24
24
  "sideEffects": false,
25
25
  "peerDependencies": {
26
- "@angular/common": ">=19.2.0",
27
- "@angular/core": ">=19.2.0",
28
- "@angular/forms": ">=19.2.0",
29
- "@angular/router": ">=19.2.0",
30
- "@angular/platform-browser": ">=19.2.0",
26
+ "@angular/common": ">=20.0.0",
27
+ "@angular/core": ">=20.0.0",
28
+ "@angular/forms": ">=20.0.0",
29
+ "@angular/router": ">=20.0.0",
30
+ "@angular/platform-browser": ">=20.0.0",
31
31
  "rxjs": ">=7.8.1"
32
32
  },
33
33
  "module": "fesm2022/signality-core.mjs",
@@ -196,10 +196,6 @@
196
196
  "types": "./browser/vibration/index.d.ts",
197
197
  "default": "./fesm2022/signality-core-browser-vibration.mjs"
198
198
  },
199
- "./browser/wake-lock": {
200
- "types": "./browser/wake-lock/index.d.ts",
201
- "default": "./fesm2022/signality-core-browser-wake-lock.mjs"
202
- },
203
199
  "./browser/web-notification": {
204
200
  "types": "./browser/web-notification/index.d.ts",
205
201
  "default": "./fesm2022/signality-core-browser-web-notification.mjs"
@@ -1,76 +0,0 @@
1
- import { type Signal } from '@angular/core';
2
- import type { WithInjector } from '@signality/core/types';
3
- export interface WakeLockOptions extends WithInjector {
4
- /**
5
- * Whether to automatically reacquire the wake lock when the document becomes visible again
6
- * after being hidden (e.g. tab switch or screen lock).
7
- *
8
- * Defaults to `true`.
9
- *
10
- * @see [Page Visibility API on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API)
11
- */
12
- readonly autoReacquire?: boolean;
13
- }
14
- export interface WakeLockRef {
15
- /**
16
- * Whether the Screen Wake Lock API is supported in the current browser.
17
- *
18
- * @see [Screen Wake Lock API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API#browser_compatibility)
19
- */
20
- readonly isSupported: Signal<boolean>;
21
- /**
22
- * Whether the wake lock is currently active.
23
- * `false` when the sentinel is released or the document is not visible.
24
- */
25
- readonly isActive: Signal<boolean>;
26
- /**
27
- * The active `WakeLockSentinel` instance, or `null` when no wake lock is held.
28
- *
29
- * @see [WakeLockSentinel on MDN](https://developer.mozilla.org/en-US/docs/Web/API/WakeLockSentinel)
30
- */
31
- readonly sentinel: Signal<WakeLockSentinel | null>;
32
- /**
33
- * Request a wake lock. No-op if a lock is already active.
34
- * If the document is not visible, the request will be deferred until it becomes visible
35
- * (only when `autoReacquire` is `true`).
36
- *
37
- * @see [WakeLock.request() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/WakeLock/request)
38
- */
39
- readonly request: () => Promise<void>;
40
- /**
41
- * Request a wake lock unconditionally, releasing the existing sentinel first if present.
42
- *
43
- * @see [WakeLockSentinel.release() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/WakeLockSentinel/release)
44
- */
45
- readonly forceRequest: () => Promise<void>;
46
- /**
47
- * Release the active wake lock and clear the sentinel.
48
- *
49
- * @see [WakeLockSentinel.release() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/WakeLockSentinel/release)
50
- */
51
- readonly release: () => Promise<void>;
52
- }
53
- /**
54
- * Signal-based wrapper around the [Screen Wake Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API).
55
- * Prevents the screen from turning off while the wake lock is active.
56
- *
57
- * @param options - Optional configuration
58
- * @returns A WakeLockRef with isSupported, isActive, sentinel signals and control methods
59
- *
60
- * @example
61
- * ```typescript
62
- * @Component({
63
- * template: `
64
- * @if (wl.isSupported()) {
65
- * <button (click)="wl.request()">Keep screen on</button>
66
- * <button (click)="wl.release()">Release</button>
67
- * <p>Active: {{ wl.isActive() }}</p>
68
- * }
69
- * `
70
- * })
71
- * export class WakeLockDemo {
72
- * readonly wl = wakeLock();
73
- * }
74
- * ```
75
- */
76
- export declare function wakeLock(options?: WakeLockOptions): WakeLockRef;
@@ -1,166 +0,0 @@
1
- import { signal, inject, computed, untracked, effect } from '@angular/core';
2
- import { setupContext, constSignal, NOOP_ASYNC_FN } from '@signality/core/internal';
3
- import { setupSync, listener } from '@signality/core/browser/listener';
4
- import { PAGE_VISIBILITY } from '@signality/core/browser/page-visibility';
5
-
6
- /**
7
- * Signal-based wrapper around the [Screen Wake Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API).
8
- * Prevents the screen from turning off while the wake lock is active.
9
- *
10
- * @param options - Optional configuration
11
- * @returns A WakeLockRef with isSupported, isActive, sentinel signals and control methods
12
- *
13
- * @example
14
- * ```typescript
15
- * @Component({
16
- * template: `
17
- * @if (wl.isSupported()) {
18
- * <button (click)="wl.request()">Keep screen on</button>
19
- * <button (click)="wl.release()">Release</button>
20
- * <p>Active: {{ wl.isActive() }}</p>
21
- * }
22
- * `
23
- * })
24
- * export class WakeLockDemo {
25
- * readonly wl = wakeLock();
26
- * }
27
- * ```
28
- */
29
- function wakeLock(options) {
30
- const { runInContext } = setupContext(options?.injector, wakeLock);
31
- return runInContext(({ isBrowser, onCleanup, injector }) => {
32
- const sentinel = signal(null, ...(ngDevMode ? [{ debugName: "sentinel" }] : []));
33
- const pendingRequest = signal(false, ...(ngDevMode ? [{ debugName: "pendingRequest" }] : []));
34
- const isSupported = constSignal(isBrowser &&
35
- 'wakeLock' in navigator &&
36
- typeof navigator.wakeLock?.request === 'function');
37
- if (!isSupported()) {
38
- return {
39
- isSupported,
40
- isActive: constSignal(false),
41
- sentinel: sentinel.asReadonly(),
42
- request: NOOP_ASYNC_FN,
43
- forceRequest: NOOP_ASYNC_FN,
44
- release: NOOP_ASYNC_FN,
45
- };
46
- }
47
- const visibility = inject(PAGE_VISIBILITY);
48
- const { autoReacquire = true } = options ?? {};
49
- const isActive = computed(() => {
50
- const s = sentinel();
51
- return !!s && !s.released && visibility() === 'visible';
52
- }, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
53
- let listenerRef = null;
54
- let isRequesting = false;
55
- const forceRequest = async () => {
56
- if (isRequesting)
57
- return;
58
- isRequesting = true;
59
- try {
60
- const currentSentinel = untracked(sentinel);
61
- if (currentSentinel) {
62
- if (listenerRef) {
63
- listenerRef.destroy();
64
- listenerRef = null;
65
- }
66
- if (!currentSentinel.released) {
67
- await currentSentinel.release();
68
- }
69
- }
70
- if (untracked(visibility) === 'visible') {
71
- const newSentinel = await navigator.wakeLock.request('screen');
72
- sentinel.set(newSentinel);
73
- listenerRef = setupSync(() => listener(newSentinel, 'release', () => {
74
- pendingRequest.set(false);
75
- sentinel.set(null);
76
- listenerRef = null;
77
- }, { injector }));
78
- }
79
- }
80
- catch (err) {
81
- sentinel.set(null);
82
- pendingRequest.set(false);
83
- if (listenerRef) {
84
- listenerRef.destroy();
85
- listenerRef = null;
86
- }
87
- if (ngDevMode) {
88
- console.warn('[wakeLock] Failed to acquire wake lock.', err);
89
- }
90
- }
91
- finally {
92
- isRequesting = false;
93
- }
94
- };
95
- const request = async () => {
96
- if (untracked(visibility) === 'visible') {
97
- await forceRequest();
98
- }
99
- else {
100
- pendingRequest.set(true);
101
- }
102
- };
103
- const release = async () => {
104
- pendingRequest.set(false);
105
- const currentSentinel = untracked(sentinel);
106
- sentinel.set(null);
107
- if (currentSentinel) {
108
- if (listenerRef) {
109
- listenerRef.destroy();
110
- listenerRef = null;
111
- }
112
- if (!currentSentinel.released) {
113
- try {
114
- await currentSentinel.release();
115
- }
116
- catch (err) {
117
- if (ngDevMode) {
118
- console.warn('[wakeLock] Failed to release wake lock.', err);
119
- }
120
- }
121
- }
122
- }
123
- };
124
- if (autoReacquire) {
125
- effect(() => {
126
- const isVisible = visibility() === 'visible';
127
- const isPending = pendingRequest();
128
- if (isVisible && isPending) {
129
- pendingRequest.set(false);
130
- forceRequest().catch(() => {
131
- /* handled inside forceRequest */
132
- });
133
- }
134
- });
135
- }
136
- onCleanup(() => {
137
- const currentSentinel = untracked(sentinel);
138
- if (currentSentinel) {
139
- if (listenerRef) {
140
- listenerRef.destroy();
141
- listenerRef = null;
142
- }
143
- if (!currentSentinel.released) {
144
- currentSentinel.release().catch(() => {
145
- /* ignore errors */
146
- });
147
- }
148
- }
149
- });
150
- return {
151
- isSupported,
152
- isActive,
153
- sentinel: sentinel.asReadonly(),
154
- request,
155
- forceRequest,
156
- release,
157
- };
158
- });
159
- }
160
-
161
- /**
162
- * Generated bundle index. Do not edit.
163
- */
164
-
165
- export { wakeLock };
166
- //# sourceMappingURL=signality-core-browser-wake-lock.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"signality-core-browser-wake-lock.mjs","sources":["../../../projects/core/browser/wake-lock/index.ts","../../../projects/core/browser/wake-lock/signality-core-browser-wake-lock.ts"],"sourcesContent":["import { computed, effect, inject, type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_ASYNC_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, type ListenerRef, setupSync } from '@signality/core/browser/listener';\nimport { PAGE_VISIBILITY } from '@signality/core/browser/page-visibility';\n\nexport interface WakeLockOptions extends WithInjector {\n /**\n * Whether to automatically reacquire the wake lock when the document becomes visible again\n * after being hidden (e.g. tab switch or screen lock).\n *\n * Defaults to `true`.\n *\n * @see [Page Visibility API on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API)\n */\n readonly autoReacquire?: boolean;\n}\n\nexport interface WakeLockRef {\n /**\n * Whether the Screen Wake Lock API is supported in the current browser.\n *\n * @see [Screen Wake Lock API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether the wake lock is currently active.\n * `false` when the sentinel is released or the document is not visible.\n */\n readonly isActive: Signal<boolean>;\n\n /**\n * The active `WakeLockSentinel` instance, or `null` when no wake lock is held.\n *\n * @see [WakeLockSentinel on MDN](https://developer.mozilla.org/en-US/docs/Web/API/WakeLockSentinel)\n */\n readonly sentinel: Signal<WakeLockSentinel | null>;\n\n /**\n * Request a wake lock. No-op if a lock is already active.\n * If the document is not visible, the request will be deferred until it becomes visible\n * (only when `autoReacquire` is `true`).\n *\n * @see [WakeLock.request() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/WakeLock/request)\n */\n readonly request: () => Promise<void>;\n\n /**\n * Request a wake lock unconditionally, releasing the existing sentinel first if present.\n *\n * @see [WakeLockSentinel.release() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/WakeLockSentinel/release)\n */\n readonly forceRequest: () => Promise<void>;\n\n /**\n * Release the active wake lock and clear the sentinel.\n *\n * @see [WakeLockSentinel.release() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/WakeLockSentinel/release)\n */\n readonly release: () => Promise<void>;\n}\n\n/**\n * Signal-based wrapper around the [Screen Wake Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API).\n * Prevents the screen from turning off while the wake lock is active.\n *\n * @param options - Optional configuration\n * @returns A WakeLockRef with isSupported, isActive, sentinel signals and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (wl.isSupported()) {\n * <button (click)=\"wl.request()\">Keep screen on</button>\n * <button (click)=\"wl.release()\">Release</button>\n * <p>Active: {{ wl.isActive() }}</p>\n * }\n * `\n * })\n * export class WakeLockDemo {\n * readonly wl = wakeLock();\n * }\n * ```\n */\nexport function wakeLock(options?: WakeLockOptions): WakeLockRef {\n const { runInContext } = setupContext(options?.injector, wakeLock);\n\n return runInContext(({ isBrowser, onCleanup, injector }) => {\n const sentinel = signal<WakeLockSentinel | null>(null);\n const pendingRequest = signal(false);\n\n const isSupported = constSignal(\n isBrowser &&\n 'wakeLock' in navigator &&\n typeof (navigator as NavigatorWithWakeLock).wakeLock?.request === 'function'\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n isActive: constSignal(false),\n sentinel: sentinel.asReadonly(),\n request: NOOP_ASYNC_FN,\n forceRequest: NOOP_ASYNC_FN,\n release: NOOP_ASYNC_FN,\n };\n }\n\n const visibility = inject(PAGE_VISIBILITY);\n\n const { autoReacquire = true } = options ?? {};\n\n const isActive = computed(() => {\n const s = sentinel();\n return !!s && !s.released && visibility() === 'visible';\n });\n\n let listenerRef: ListenerRef | null = null;\n let isRequesting = false;\n\n const forceRequest = async (): Promise<void> => {\n if (isRequesting) return;\n isRequesting = true;\n\n try {\n const currentSentinel = untracked(sentinel);\n\n if (currentSentinel) {\n if (listenerRef) {\n listenerRef.destroy();\n listenerRef = null;\n }\n\n if (!currentSentinel.released) {\n await currentSentinel.release();\n }\n }\n\n if (untracked(visibility) === 'visible') {\n const newSentinel = await (navigator as NavigatorWithWakeLock).wakeLock.request('screen');\n\n sentinel.set(newSentinel);\n\n listenerRef = setupSync(() =>\n listener(\n newSentinel,\n 'release',\n () => {\n pendingRequest.set(false);\n sentinel.set(null);\n listenerRef = null;\n },\n { injector }\n )\n );\n }\n } catch (err) {\n sentinel.set(null);\n pendingRequest.set(false);\n\n if (listenerRef) {\n listenerRef.destroy();\n listenerRef = null;\n }\n\n if (ngDevMode) {\n console.warn('[wakeLock] Failed to acquire wake lock.', err);\n }\n } finally {\n isRequesting = false;\n }\n };\n\n const request = async (): Promise<void> => {\n if (untracked(visibility) === 'visible') {\n await forceRequest();\n } else {\n pendingRequest.set(true);\n }\n };\n\n const release = async (): Promise<void> => {\n pendingRequest.set(false);\n\n const currentSentinel = untracked(sentinel);\n\n sentinel.set(null);\n\n if (currentSentinel) {\n if (listenerRef) {\n listenerRef.destroy();\n listenerRef = null;\n }\n if (!currentSentinel.released) {\n try {\n await currentSentinel.release();\n } catch (err) {\n if (ngDevMode) {\n console.warn('[wakeLock] Failed to release wake lock.', err);\n }\n }\n }\n }\n };\n\n if (autoReacquire) {\n effect(() => {\n const isVisible = visibility() === 'visible';\n const isPending = pendingRequest();\n\n if (isVisible && isPending) {\n pendingRequest.set(false);\n forceRequest().catch(() => {\n /* handled inside forceRequest */\n });\n }\n });\n }\n\n onCleanup(() => {\n const currentSentinel = untracked(sentinel);\n if (currentSentinel) {\n if (listenerRef) {\n listenerRef.destroy();\n listenerRef = null;\n }\n if (!currentSentinel.released) {\n currentSentinel.release().catch(() => {\n /* ignore errors */\n });\n }\n }\n });\n\n return {\n isSupported,\n isActive,\n sentinel: sentinel.asReadonly(),\n request,\n forceRequest,\n release,\n };\n });\n}\n\ninterface NavigatorWithWakeLock extends Navigator {\n readonly wakeLock: {\n readonly request: (type: 'screen') => Promise<WakeLockSentinel>;\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}