@signality/core 0.0.1-alpha.2

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.
Files changed (261) hide show
  1. package/README.md +60 -0
  2. package/browser/battery/index.d.ts +34 -0
  3. package/browser/bluetooth/index.d.ts +56 -0
  4. package/browser/breakpoints/index.d.ts +32 -0
  5. package/browser/broadcast-channel/index.d.ts +42 -0
  6. package/browser/browser-language/index.d.ts +34 -0
  7. package/browser/clipboard/index.d.ts +48 -0
  8. package/browser/device-posture/index.d.ts +18 -0
  9. package/browser/display-media/index.d.ts +80 -0
  10. package/browser/eye-dropper/index.d.ts +47 -0
  11. package/browser/favicon/index.d.ts +39 -0
  12. package/browser/fps/index.d.ts +46 -0
  13. package/browser/gamepad/index.d.ts +28 -0
  14. package/browser/geolocation/index.d.ts +64 -0
  15. package/browser/index.d.ts +29 -0
  16. package/browser/input-modality/index.d.ts +26 -0
  17. package/browser/listener/index.d.ts +61 -0
  18. package/browser/media-query/index.d.ts +36 -0
  19. package/browser/network/index.d.ts +44 -0
  20. package/browser/online/index.d.ts +27 -0
  21. package/browser/page-visibility/index.d.ts +27 -0
  22. package/browser/picture-in-picture/index.d.ts +42 -0
  23. package/browser/pointer-lock-element/index.d.ts +22 -0
  24. package/browser/screen-orientation/index.d.ts +29 -0
  25. package/browser/speech-recognition/index.d.ts +77 -0
  26. package/browser/speech-synthesis/index.d.ts +76 -0
  27. package/browser/storage/index.d.ts +142 -0
  28. package/browser/text-direction/index.d.ts +43 -0
  29. package/browser/vibration/index.d.ts +37 -0
  30. package/browser/wake-lock/index.d.ts +37 -0
  31. package/browser/web-notification/index.d.ts +58 -0
  32. package/browser/web-share/index.d.ts +42 -0
  33. package/browser/web-worker/index.d.ts +52 -0
  34. package/elements/active-element/index.d.ts +27 -0
  35. package/elements/dropzone/index.d.ts +61 -0
  36. package/elements/element-focus/index.d.ts +38 -0
  37. package/elements/element-focus-within/index.d.ts +29 -0
  38. package/elements/element-hover/index.d.ts +27 -0
  39. package/elements/element-size/index.d.ts +40 -0
  40. package/elements/element-visibility/index.d.ts +53 -0
  41. package/elements/index.d.ts +16 -0
  42. package/elements/mouse-position/index.d.ts +64 -0
  43. package/elements/on-click-outside/index.d.ts +42 -0
  44. package/elements/on-disconnect/index.d.ts +45 -0
  45. package/elements/on-long-press/index.d.ts +44 -0
  46. package/elements/pointer-swipe/index.d.ts +58 -0
  47. package/elements/scroll-position/index.d.ts +96 -0
  48. package/elements/swipe/index.d.ts +49 -0
  49. package/elements/text-selection/index.d.ts +39 -0
  50. package/elements/window-size/index.d.ts +46 -0
  51. package/fesm2022/signality-core-browser-battery.mjs +80 -0
  52. package/fesm2022/signality-core-browser-battery.mjs.map +1 -0
  53. package/fesm2022/signality-core-browser-bluetooth.mjs +112 -0
  54. package/fesm2022/signality-core-browser-bluetooth.mjs.map +1 -0
  55. package/fesm2022/signality-core-browser-breakpoints.mjs +51 -0
  56. package/fesm2022/signality-core-browser-breakpoints.mjs.map +1 -0
  57. package/fesm2022/signality-core-browser-broadcast-channel.mjs +74 -0
  58. package/fesm2022/signality-core-browser-broadcast-channel.mjs.map +1 -0
  59. package/fesm2022/signality-core-browser-browser-language.mjs +48 -0
  60. package/fesm2022/signality-core-browser-browser-language.mjs.map +1 -0
  61. package/fesm2022/signality-core-browser-clipboard.mjs +102 -0
  62. package/fesm2022/signality-core-browser-clipboard.mjs.map +1 -0
  63. package/fesm2022/signality-core-browser-device-posture.mjs +40 -0
  64. package/fesm2022/signality-core-browser-device-posture.mjs.map +1 -0
  65. package/fesm2022/signality-core-browser-display-media.mjs +121 -0
  66. package/fesm2022/signality-core-browser-display-media.mjs.map +1 -0
  67. package/fesm2022/signality-core-browser-eye-dropper.mjs +82 -0
  68. package/fesm2022/signality-core-browser-eye-dropper.mjs.map +1 -0
  69. package/fesm2022/signality-core-browser-favicon.mjs +100 -0
  70. package/fesm2022/signality-core-browser-favicon.mjs.map +1 -0
  71. package/fesm2022/signality-core-browser-fps.mjs +103 -0
  72. package/fesm2022/signality-core-browser-fps.mjs.map +1 -0
  73. package/fesm2022/signality-core-browser-gamepad.mjs +93 -0
  74. package/fesm2022/signality-core-browser-gamepad.mjs.map +1 -0
  75. package/fesm2022/signality-core-browser-geolocation.mjs +120 -0
  76. package/fesm2022/signality-core-browser-geolocation.mjs.map +1 -0
  77. package/fesm2022/signality-core-browser-input-modality.mjs +64 -0
  78. package/fesm2022/signality-core-browser-input-modality.mjs.map +1 -0
  79. package/fesm2022/signality-core-browser-listener.mjs +132 -0
  80. package/fesm2022/signality-core-browser-listener.mjs.map +1 -0
  81. package/fesm2022/signality-core-browser-media-query.mjs +55 -0
  82. package/fesm2022/signality-core-browser-media-query.mjs.map +1 -0
  83. package/fesm2022/signality-core-browser-network.mjs +76 -0
  84. package/fesm2022/signality-core-browser-network.mjs.map +1 -0
  85. package/fesm2022/signality-core-browser-online.mjs +49 -0
  86. package/fesm2022/signality-core-browser-online.mjs.map +1 -0
  87. package/fesm2022/signality-core-browser-page-visibility.mjs +47 -0
  88. package/fesm2022/signality-core-browser-page-visibility.mjs.map +1 -0
  89. package/fesm2022/signality-core-browser-picture-in-picture.mjs +93 -0
  90. package/fesm2022/signality-core-browser-picture-in-picture.mjs.map +1 -0
  91. package/fesm2022/signality-core-browser-pointer-lock-element.mjs +43 -0
  92. package/fesm2022/signality-core-browser-pointer-lock-element.mjs.map +1 -0
  93. package/fesm2022/signality-core-browser-screen-orientation.mjs +43 -0
  94. package/fesm2022/signality-core-browser-screen-orientation.mjs.map +1 -0
  95. package/fesm2022/signality-core-browser-speech-recognition.mjs +171 -0
  96. package/fesm2022/signality-core-browser-speech-recognition.mjs.map +1 -0
  97. package/fesm2022/signality-core-browser-speech-synthesis.mjs +146 -0
  98. package/fesm2022/signality-core-browser-speech-synthesis.mjs.map +1 -0
  99. package/fesm2022/signality-core-browser-storage.mjs +261 -0
  100. package/fesm2022/signality-core-browser-storage.mjs.map +1 -0
  101. package/fesm2022/signality-core-browser-text-direction.mjs +62 -0
  102. package/fesm2022/signality-core-browser-text-direction.mjs.map +1 -0
  103. package/fesm2022/signality-core-browser-vibration.mjs +94 -0
  104. package/fesm2022/signality-core-browser-vibration.mjs.map +1 -0
  105. package/fesm2022/signality-core-browser-wake-lock.mjs +149 -0
  106. package/fesm2022/signality-core-browser-wake-lock.mjs.map +1 -0
  107. package/fesm2022/signality-core-browser-web-notification.mjs +137 -0
  108. package/fesm2022/signality-core-browser-web-notification.mjs.map +1 -0
  109. package/fesm2022/signality-core-browser-web-share.mjs +92 -0
  110. package/fesm2022/signality-core-browser-web-share.mjs.map +1 -0
  111. package/fesm2022/signality-core-browser-web-worker.mjs +105 -0
  112. package/fesm2022/signality-core-browser-web-worker.mjs.map +1 -0
  113. package/fesm2022/signality-core-browser.mjs +34 -0
  114. package/fesm2022/signality-core-browser.mjs.map +1 -0
  115. package/fesm2022/signality-core-elements-active-element.mjs +88 -0
  116. package/fesm2022/signality-core-elements-active-element.mjs.map +1 -0
  117. package/fesm2022/signality-core-elements-dropzone.mjs +158 -0
  118. package/fesm2022/signality-core-elements-dropzone.mjs.map +1 -0
  119. package/fesm2022/signality-core-elements-element-focus-within.mjs +56 -0
  120. package/fesm2022/signality-core-elements-element-focus-within.mjs.map +1 -0
  121. package/fesm2022/signality-core-elements-element-focus.mjs +54 -0
  122. package/fesm2022/signality-core-elements-element-focus.mjs.map +1 -0
  123. package/fesm2022/signality-core-elements-element-hover.mjs +48 -0
  124. package/fesm2022/signality-core-elements-element-hover.mjs.map +1 -0
  125. package/fesm2022/signality-core-elements-element-size.mjs +73 -0
  126. package/fesm2022/signality-core-elements-element-size.mjs.map +1 -0
  127. package/fesm2022/signality-core-elements-element-visibility.mjs +76 -0
  128. package/fesm2022/signality-core-elements-element-visibility.mjs.map +1 -0
  129. package/fesm2022/signality-core-elements-mouse-position.mjs +109 -0
  130. package/fesm2022/signality-core-elements-mouse-position.mjs.map +1 -0
  131. package/fesm2022/signality-core-elements-on-click-outside.mjs +97 -0
  132. package/fesm2022/signality-core-elements-on-click-outside.mjs.map +1 -0
  133. package/fesm2022/signality-core-elements-on-disconnect.mjs +99 -0
  134. package/fesm2022/signality-core-elements-on-disconnect.mjs.map +1 -0
  135. package/fesm2022/signality-core-elements-on-long-press.mjs +84 -0
  136. package/fesm2022/signality-core-elements-on-long-press.mjs.map +1 -0
  137. package/fesm2022/signality-core-elements-pointer-swipe.mjs +116 -0
  138. package/fesm2022/signality-core-elements-pointer-swipe.mjs.map +1 -0
  139. package/fesm2022/signality-core-elements-scroll-position.mjs +175 -0
  140. package/fesm2022/signality-core-elements-scroll-position.mjs.map +1 -0
  141. package/fesm2022/signality-core-elements-swipe.mjs +107 -0
  142. package/fesm2022/signality-core-elements-swipe.mjs.map +1 -0
  143. package/fesm2022/signality-core-elements-text-selection.mjs +70 -0
  144. package/fesm2022/signality-core-elements-text-selection.mjs.map +1 -0
  145. package/fesm2022/signality-core-elements-window-size.mjs +81 -0
  146. package/fesm2022/signality-core-elements-window-size.mjs.map +1 -0
  147. package/fesm2022/signality-core-elements.mjs +21 -0
  148. package/fesm2022/signality-core-elements.mjs.map +1 -0
  149. package/fesm2022/signality-core-forms-cva.mjs +140 -0
  150. package/fesm2022/signality-core-forms-cva.mjs.map +1 -0
  151. package/fesm2022/signality-core-forms.mjs +6 -0
  152. package/fesm2022/signality-core-forms.mjs.map +1 -0
  153. package/fesm2022/signality-core-internal.mjs +268 -0
  154. package/fesm2022/signality-core-internal.mjs.map +1 -0
  155. package/fesm2022/signality-core-observers-intersection-observer.mjs +70 -0
  156. package/fesm2022/signality-core-observers-intersection-observer.mjs.map +1 -0
  157. package/fesm2022/signality-core-observers-mutation-observer.mjs +77 -0
  158. package/fesm2022/signality-core-observers-mutation-observer.mjs.map +1 -0
  159. package/fesm2022/signality-core-observers-performance-observer.mjs +84 -0
  160. package/fesm2022/signality-core-observers-performance-observer.mjs.map +1 -0
  161. package/fesm2022/signality-core-observers-resize-observer.mjs +69 -0
  162. package/fesm2022/signality-core-observers-resize-observer.mjs.map +1 -0
  163. package/fesm2022/signality-core-observers.mjs +9 -0
  164. package/fesm2022/signality-core-observers.mjs.map +1 -0
  165. package/fesm2022/signality-core-reactivity-debounced.mjs +27 -0
  166. package/fesm2022/signality-core-reactivity-debounced.mjs.map +1 -0
  167. package/fesm2022/signality-core-reactivity-throttled.mjs +27 -0
  168. package/fesm2022/signality-core-reactivity-throttled.mjs.map +1 -0
  169. package/fesm2022/signality-core-reactivity-watcher.mjs +36 -0
  170. package/fesm2022/signality-core-reactivity-watcher.mjs.map +1 -0
  171. package/fesm2022/signality-core-reactivity.mjs +8 -0
  172. package/fesm2022/signality-core-reactivity.mjs.map +1 -0
  173. package/fesm2022/signality-core-router-fragment.mjs +41 -0
  174. package/fesm2022/signality-core-router-fragment.mjs.map +1 -0
  175. package/fesm2022/signality-core-router-params.mjs +45 -0
  176. package/fesm2022/signality-core-router-params.mjs.map +1 -0
  177. package/fesm2022/signality-core-router-query-params.mjs +67 -0
  178. package/fesm2022/signality-core-router-query-params.mjs.map +1 -0
  179. package/fesm2022/signality-core-router-route-data.mjs +46 -0
  180. package/fesm2022/signality-core-router-route-data.mjs.map +1 -0
  181. package/fesm2022/signality-core-router-router-listener.mjs +50 -0
  182. package/fesm2022/signality-core-router-router-listener.mjs.map +1 -0
  183. package/fesm2022/signality-core-router-title.mjs +54 -0
  184. package/fesm2022/signality-core-router-title.mjs.map +1 -0
  185. package/fesm2022/signality-core-router-url.mjs +53 -0
  186. package/fesm2022/signality-core-router-url.mjs.map +1 -0
  187. package/fesm2022/signality-core-router.mjs +12 -0
  188. package/fesm2022/signality-core-router.mjs.map +1 -0
  189. package/fesm2022/signality-core-scheduling-debounce-callback.mjs +59 -0
  190. package/fesm2022/signality-core-scheduling-debounce-callback.mjs.map +1 -0
  191. package/fesm2022/signality-core-scheduling-interval.mjs +110 -0
  192. package/fesm2022/signality-core-scheduling-interval.mjs.map +1 -0
  193. package/fesm2022/signality-core-scheduling-throttle-callback.mjs +66 -0
  194. package/fesm2022/signality-core-scheduling-throttle-callback.mjs.map +1 -0
  195. package/fesm2022/signality-core-scheduling.mjs +8 -0
  196. package/fesm2022/signality-core-scheduling.mjs.map +1 -0
  197. package/fesm2022/signality-core-types.mjs +4 -0
  198. package/fesm2022/signality-core-types.mjs.map +1 -0
  199. package/fesm2022/signality-core.mjs +13 -0
  200. package/fesm2022/signality-core.mjs.map +1 -0
  201. package/forms/cva/index.d.ts +60 -0
  202. package/forms/index.d.ts +1 -0
  203. package/index.d.ts +8 -0
  204. package/internal/constants/index.d.ts +2 -0
  205. package/internal/constants/mobile-regex.d.ts +1 -0
  206. package/internal/constants/stubs.d.ts +32 -0
  207. package/internal/index.d.ts +4 -0
  208. package/internal/providers/index.d.ts +3 -0
  209. package/internal/providers/is-browser.d.ts +2 -0
  210. package/internal/providers/is-mobile.d.ts +2 -0
  211. package/internal/providers/is-server.d.ts +2 -0
  212. package/internal/types/index.d.ts +2 -0
  213. package/internal/types/timer.d.ts +1 -0
  214. package/internal/types/union.d.ts +1 -0
  215. package/internal/utils/bom/index.d.ts +1 -0
  216. package/internal/utils/bom/is-window.d.ts +1 -0
  217. package/internal/utils/const-signal.d.ts +10 -0
  218. package/internal/utils/context.d.ts +18 -0
  219. package/internal/utils/create-token.d.ts +8 -0
  220. package/internal/utils/dom/get-active-element.d.ts +1 -0
  221. package/internal/utils/dom/get-event-target.d.ts +1 -0
  222. package/internal/utils/dom/get-pip-element.d.ts +1 -0
  223. package/internal/utils/dom/get-shadow-root.d.ts +1 -0
  224. package/internal/utils/dom/index.d.ts +6 -0
  225. package/internal/utils/dom/is-element.d.ts +1 -0
  226. package/internal/utils/dom/is-node-within.d.ts +1 -0
  227. package/internal/utils/index.d.ts +10 -0
  228. package/internal/utils/is-plain-object.d.ts +1 -0
  229. package/internal/utils/is-query-signal.d.ts +10 -0
  230. package/internal/utils/proxy-signal.d.ts +18 -0
  231. package/internal/utils/to-element.d.ts +12 -0
  232. package/internal/utils/to-value.d.ts +6 -0
  233. package/observers/index.d.ts +4 -0
  234. package/observers/intersection-observer/index.d.ts +42 -0
  235. package/observers/mutation-observer/index.d.ts +45 -0
  236. package/observers/performance-observer/index.d.ts +58 -0
  237. package/observers/resize-observer/index.d.ts +40 -0
  238. package/package.json +343 -0
  239. package/reactivity/debounced/index.d.ts +50 -0
  240. package/reactivity/index.d.ts +3 -0
  241. package/reactivity/throttled/index.d.ts +53 -0
  242. package/reactivity/watcher/index.d.ts +68 -0
  243. package/router/fragment/index.d.ts +26 -0
  244. package/router/index.d.ts +7 -0
  245. package/router/params/index.d.ts +28 -0
  246. package/router/query-params/index.d.ts +80 -0
  247. package/router/route-data/index.d.ts +28 -0
  248. package/router/router-listener/index.d.ts +83 -0
  249. package/router/title/index.d.ts +29 -0
  250. package/router/url/index.d.ts +32 -0
  251. package/scheduling/debounce-callback/index.d.ts +28 -0
  252. package/scheduling/index.d.ts +3 -0
  253. package/scheduling/interval/index.d.ts +51 -0
  254. package/scheduling/throttle-callback/index.d.ts +30 -0
  255. package/types/index.d.ts +6 -0
  256. package/types/maybe-element-signal.d.ts +2 -0
  257. package/types/maybe-signal.d.ts +2 -0
  258. package/types/signal-value.d.ts +2 -0
  259. package/types/signal-values.d.ts +5 -0
  260. package/types/unref-element.d.ts +2 -0
  261. package/types/with-injector.d.ts +8 -0
@@ -0,0 +1,74 @@
1
+ import { signal } from '@angular/core';
2
+ import { setupContext, NOOP_FN, constSignal } from '@signality/core/internal';
3
+ import { setupSync, listener } from '@signality/core/browser/listener';
4
+
5
+ /**
6
+ * Signal-based wrapper around the [Broadcast Channel API](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API).
7
+ *
8
+ * @param name - Channel name (must match across tabs)
9
+ * @param options - Optional configuration
10
+ * @returns A BroadcastChannelRef with data signal and control methods
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * @Component({
15
+ * template: `
16
+ * @if (channel.data(); as data) {
17
+ * <p>Received: {{ data }}</p>
18
+ * }
19
+ * <button (click)="sendMessage()">Send Message</button>
20
+ * `
21
+ * })
22
+ * class ChatComponent {
23
+ * readonly channel = broadcastChannel<string>('my-channel');
24
+ *
25
+ * sendMessage() {
26
+ * this.channel.post('Hello from this tab!');
27
+ * }
28
+ * }
29
+ * ```
30
+ */
31
+ function broadcastChannel(name, options) {
32
+ const { runInContext } = setupContext(options?.injector, broadcastChannel);
33
+ return runInContext(({ isServer, onCleanup }) => {
34
+ if (isServer) {
35
+ return {
36
+ data: constSignal(null),
37
+ error: constSignal(null),
38
+ isClosed: constSignal(false),
39
+ post: NOOP_FN,
40
+ close: NOOP_FN,
41
+ };
42
+ }
43
+ const data = signal(null, ...(ngDevMode ? [{ debugName: "data" }] : []));
44
+ const error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
45
+ const isClosed = signal(false, ...(ngDevMode ? [{ debugName: "isClosed" }] : []));
46
+ const channel = new BroadcastChannel(name);
47
+ const post = (message) => {
48
+ channel.postMessage(message);
49
+ };
50
+ const close = () => {
51
+ channel.close();
52
+ isClosed.set(true);
53
+ };
54
+ setupSync(() => {
55
+ listener(channel, 'message', (e) => data.set(e.data));
56
+ listener(channel, 'messageerror', error.set);
57
+ });
58
+ onCleanup(close);
59
+ return {
60
+ data: data.asReadonly(),
61
+ error: error.asReadonly(),
62
+ isClosed: isClosed.asReadonly(),
63
+ post,
64
+ close,
65
+ };
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Generated bundle index. Do not edit.
71
+ */
72
+
73
+ export { broadcastChannel };
74
+ //# sourceMappingURL=signality-core-browser-broadcast-channel.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-browser-broadcast-channel.mjs","sources":["../../../projects/core/browser/broadcast-channel/index.ts","../../../projects/core/browser/broadcast-channel/signality-core-browser-broadcast-channel.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type BroadcastChannelOptions = WithInjector;\n\nexport interface BroadcastChannelRef<T> {\n /** Last received data */\n readonly data: Signal<T | null>;\n\n /** The last error that occurred */\n readonly error: Signal<MessageEvent | null>;\n\n /** Whether the channel is closed */\n readonly isClosed: Signal<boolean>;\n\n /** Send a message to all tabs */\n readonly post: (data: T) => void;\n\n /** Close the channel */\n readonly close: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Broadcast Channel API](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API).\n *\n * @param name - Channel name (must match across tabs)\n * @param options - Optional configuration\n * @returns A BroadcastChannelRef with data signal and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (channel.data(); as data) {\n * <p>Received: {{ data }}</p>\n * }\n * <button (click)=\"sendMessage()\">Send Message</button>\n * `\n * })\n * class ChatComponent {\n * readonly channel = broadcastChannel<string>('my-channel');\n *\n * sendMessage() {\n * this.channel.post('Hello from this tab!');\n * }\n * }\n * ```\n */\nexport function broadcastChannel<T>(\n name: string,\n options?: BroadcastChannelOptions\n): BroadcastChannelRef<T> {\n const { runInContext } = setupContext(options?.injector, broadcastChannel);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return {\n data: constSignal(null),\n error: constSignal(null),\n isClosed: constSignal(false),\n post: NOOP_FN,\n close: NOOP_FN,\n };\n }\n\n const data = signal<T | null>(null);\n const error = signal<MessageEvent | null>(null);\n const isClosed = signal(false);\n\n const channel = new BroadcastChannel(name);\n\n const post = (message: T) => {\n channel.postMessage(message);\n };\n\n const close = () => {\n channel.close();\n isClosed.set(true);\n };\n\n setupSync(() => {\n listener(channel, 'message', (e: MessageEvent<T>) => data.set(e.data));\n listener(channel, 'messageerror', error.set);\n });\n\n onCleanup(close);\n\n return {\n data: data.asReadonly(),\n error: error.asReadonly(),\n isClosed: isClosed.asReadonly(),\n post,\n close,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAwBA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACG,SAAU,gBAAgB,CAC9B,IAAY,EACZ,OAAiC,EAAA;AAEjC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC;IAE1E,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;QAC9C,IAAI,QAAQ,EAAE;YACZ,OAAO;AACL,gBAAA,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;AACvB,gBAAA,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC;AACxB,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,IAAI,EAAE,OAAO;AACb,gBAAA,KAAK,EAAE,OAAO;aACf;QACH;AAEA,QAAA,MAAM,IAAI,GAAG,MAAM,CAAW,IAAI,gDAAC;AACnC,QAAA,MAAM,KAAK,GAAG,MAAM,CAAsB,IAAI,iDAAC;AAC/C,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AAE9B,QAAA,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC;AAE1C,QAAA,MAAM,IAAI,GAAG,CAAC,OAAU,KAAI;AAC1B,YAAA,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC;AAC9B,QAAA,CAAC;QAED,MAAM,KAAK,GAAG,MAAK;YACjB,OAAO,CAAC,KAAK,EAAE;AACf,YAAA,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;AACpB,QAAA,CAAC;QAED,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,CAAkB,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACtE,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC;AAC9C,QAAA,CAAC,CAAC;QAEF,SAAS,CAAC,KAAK,CAAC;QAEhB,OAAO;AACL,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;AACvB,YAAA,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE;AACzB,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;YAC/B,IAAI;YACJ,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;ACjGA;;AAEG;;;;"}
@@ -0,0 +1,48 @@
1
+ import { signal } from '@angular/core';
2
+ import { setupContext, constSignal, createToken } from '@signality/core/internal';
3
+ import { setupSync, listener } from '@signality/core/browser/listener';
4
+
5
+ /**
6
+ * Reactive wrapper around the [Navigator.language](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language) property.
7
+ *
8
+ * @param options - Optional configuration including signal options and injector
9
+ * @returns A signal containing the current browser language in BCP 47 format (e.g., 'en-US', 'fr-FR')
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * @Component({
14
+ * template: `
15
+ * <p>Current language: {{ language() }}</p>
16
+ * @if (language() === 'en-US') {
17
+ * <p>Welcome!</p>
18
+ * } @else if (language() === 'fr-FR') {
19
+ * <p>Bienvenue!</p>
20
+ * }
21
+ * `
22
+ * })
23
+ * class LanguageComponent {
24
+ * readonly language = browserLanguage();
25
+ * }
26
+ * ```
27
+ */
28
+ function browserLanguage(options) {
29
+ const { runInContext } = setupContext(options?.injector, browserLanguage);
30
+ return runInContext(({ isServer }) => {
31
+ if (isServer) {
32
+ return constSignal(options?.initialValue ?? '');
33
+ }
34
+ const language = signal(navigator.language, options);
35
+ setupSync(() => {
36
+ listener(window, 'languagechange', () => language.set(navigator.language));
37
+ });
38
+ return language.asReadonly();
39
+ });
40
+ }
41
+ const BROWSER_LANGUAGE = /* @__PURE__ */ createToken(browserLanguage);
42
+
43
+ /**
44
+ * Generated bundle index. Do not edit.
45
+ */
46
+
47
+ export { BROWSER_LANGUAGE, browserLanguage };
48
+ //# sourceMappingURL=signality-core-browser-browser-language.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-browser-browser-language.mjs","sources":["../../../projects/core/browser/browser-language/index.ts","../../../projects/core/browser/browser-language/signality-core-browser-browser-language.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport interface BrowserLanguageOptions extends CreateSignalOptions<string>, WithInjector {\n /**\n * Initial value for SSR.\n * @default ''\n */\n readonly initialValue?: string;\n}\n\n/**\n * Reactive wrapper around the [Navigator.language](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language) property.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current browser language in BCP 47 format (e.g., 'en-US', 'fr-FR')\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Current language: {{ language() }}</p>\n * @if (language() === 'en-US') {\n * <p>Welcome!</p>\n * } @else if (language() === 'fr-FR') {\n * <p>Bienvenue!</p>\n * }\n * `\n * })\n * class LanguageComponent {\n * readonly language = browserLanguage();\n * }\n * ```\n */\nexport function browserLanguage(options?: BrowserLanguageOptions): Signal<string> {\n const { runInContext } = setupContext(options?.injector, browserLanguage);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(options?.initialValue ?? '');\n }\n\n const language = signal(navigator.language, options);\n\n setupSync(() => {\n listener(window, 'languagechange', () => language.set(navigator.language));\n });\n\n return language.asReadonly();\n });\n}\n\nexport const BROWSER_LANGUAGE = /* @__PURE__ */ createToken(browserLanguage);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAaA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,eAAe,CAAC,OAAgC,EAAA;AAC9D,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC;AAEzE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;YACZ,OAAO,WAAW,CAAC,OAAO,EAAE,YAAY,IAAI,EAAE,CAAC;QACjD;QAEA,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC;QAEpD,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC5E,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,QAAQ,CAAC,UAAU,EAAE;AAC9B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,gBAAgB,mBAAmB,WAAW,CAAC,eAAe;;ACtD3E;;AAEG;;;;"}
@@ -0,0 +1,102 @@
1
+ import { signal } from '@angular/core';
2
+ import { setupContext, constSignal, NOOP_ASYNC_FN, toValue } from '@signality/core/internal';
3
+
4
+ /**
5
+ * Signal-based wrapper around the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API).
6
+ *
7
+ * @param options - Optional configuration
8
+ * @returns A ClipboardRef with text, copied, isSupported signals and copy/paste methods
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * @Component({
13
+ * template: `
14
+ * <input #input value="Hello World!" />
15
+ * <button (click)="copyText(input.value)">Copy</button>
16
+ * @if (cb.copied()) {
17
+ * <span>Copied!</span>
18
+ * }
19
+ * `
20
+ * })
21
+ * class ClipboardComponent {
22
+ * readonly cb = clipboard();
23
+ *
24
+ * async copyText(text: string) {
25
+ * await this.cb.copy(text);
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ function clipboard(options) {
31
+ const { runInContext } = setupContext(options?.injector, clipboard);
32
+ return runInContext(({ isBrowser, onCleanup }) => {
33
+ const isSupported = constSignal(isBrowser && 'clipboard' in navigator && typeof navigator.clipboard?.writeText === 'function');
34
+ if (!isSupported()) {
35
+ return {
36
+ isSupported,
37
+ text: constSignal(''),
38
+ copied: constSignal(false),
39
+ copy: NOOP_ASYNC_FN,
40
+ paste: () => Promise.resolve(''),
41
+ };
42
+ }
43
+ const copiedDuration = options?.copiedDuration ?? 1500;
44
+ const text = signal('', ...(ngDevMode ? [{ debugName: "text" }] : []));
45
+ const copied = signal(false, ...(ngDevMode ? [{ debugName: "copied" }] : []));
46
+ let copiedTimeout;
47
+ const copy = async (value) => {
48
+ try {
49
+ await navigator.clipboard.writeText(value);
50
+ text.set(value);
51
+ copied.set(true);
52
+ if (copiedTimeout) {
53
+ clearTimeout(copiedTimeout);
54
+ }
55
+ copiedTimeout = setTimeout(() => {
56
+ copied.set(false);
57
+ copiedTimeout = undefined;
58
+ }, toValue.untracked(copiedDuration));
59
+ }
60
+ catch (error) {
61
+ copied.set(false);
62
+ if (ngDevMode) {
63
+ console.warn(`[clipboard] Failed to copy text to clipboard. ` +
64
+ `This may be due to permission denied or clipboard access failed.`, error);
65
+ }
66
+ }
67
+ };
68
+ const paste = async () => {
69
+ try {
70
+ const value = await navigator.clipboard.readText();
71
+ text.set(value);
72
+ return value;
73
+ }
74
+ catch (error) {
75
+ if (ngDevMode) {
76
+ console.warn(`[clipboard] Failed to read text from clipboard. ` +
77
+ `This may be due to permission denied or clipboard access failed.`, error);
78
+ }
79
+ return '';
80
+ }
81
+ };
82
+ onCleanup(() => {
83
+ if (copiedTimeout) {
84
+ clearTimeout(copiedTimeout);
85
+ }
86
+ });
87
+ return {
88
+ isSupported,
89
+ text: text.asReadonly(),
90
+ copied: copied.asReadonly(),
91
+ copy,
92
+ paste,
93
+ };
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Generated bundle index. Do not edit.
99
+ */
100
+
101
+ export { clipboard };
102
+ //# sourceMappingURL=signality-core-browser-clipboard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-browser-clipboard.mjs","sources":["../../../projects/core/browser/clipboard/index.ts","../../../projects/core/browser/clipboard/signality-core-browser-clipboard.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport {\n constSignal,\n NOOP_ASYNC_FN,\n setupContext,\n type Timer,\n toValue,\n} from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\n\nexport interface ClipboardOptions extends WithInjector {\n /**\n * How long `copied` stays `true` after copy (ms).\n * @default 1500\n */\n readonly copiedDuration?: MaybeSignal<number>;\n}\n\nexport interface ClipboardRef {\n /** Whether Clipboard API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Current clipboard text content */\n readonly text: Signal<string>;\n\n /** Whether content was recently copied (resets after timeout) */\n readonly copied: Signal<boolean>;\n\n /** Copy text to clipboard */\n readonly copy: (text: string) => Promise<void>;\n\n /** Read text from clipboard */\n readonly paste: () => Promise<string>;\n}\n\n/**\n * Signal-based wrapper around the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API).\n *\n * @param options - Optional configuration\n * @returns A ClipboardRef with text, copied, isSupported signals and copy/paste methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <input #input value=\"Hello World!\" />\n * <button (click)=\"copyText(input.value)\">Copy</button>\n * @if (cb.copied()) {\n * <span>Copied!</span>\n * }\n * `\n * })\n * class ClipboardComponent {\n * readonly cb = clipboard();\n *\n * async copyText(text: string) {\n * await this.cb.copy(text);\n * }\n * }\n * ```\n */\nexport function clipboard(options?: ClipboardOptions): ClipboardRef {\n const { runInContext } = setupContext(options?.injector, clipboard);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(\n isBrowser && 'clipboard' in navigator && typeof navigator.clipboard?.writeText === 'function'\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n text: constSignal(''),\n copied: constSignal(false),\n copy: NOOP_ASYNC_FN,\n paste: () => Promise.resolve(''),\n };\n }\n\n const copiedDuration = options?.copiedDuration ?? 1500;\n\n const text = signal('');\n const copied = signal(false);\n\n let copiedTimeout: Timer;\n\n const copy = async (value: string): Promise<void> => {\n try {\n await navigator.clipboard.writeText(value);\n\n text.set(value);\n copied.set(true);\n\n if (copiedTimeout) {\n clearTimeout(copiedTimeout);\n }\n\n copiedTimeout = setTimeout(() => {\n copied.set(false);\n copiedTimeout = undefined;\n }, toValue.untracked(copiedDuration));\n } catch (error) {\n copied.set(false);\n if (ngDevMode) {\n console.warn(\n `[clipboard] Failed to copy text to clipboard. ` +\n `This may be due to permission denied or clipboard access failed.`,\n error\n );\n }\n }\n };\n\n const paste = async (): Promise<string> => {\n try {\n const value = await navigator.clipboard.readText();\n text.set(value);\n return value;\n } catch (error) {\n if (ngDevMode) {\n console.warn(\n `[clipboard] Failed to read text from clipboard. ` +\n `This may be due to permission denied or clipboard access failed.`,\n error\n );\n }\n return '';\n }\n };\n\n onCleanup(() => {\n if (copiedTimeout) {\n clearTimeout(copiedTimeout);\n }\n });\n\n return {\n isSupported,\n text: text.asReadonly(),\n copied: copied.asReadonly(),\n copy,\n paste,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAmCA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;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,SAAS,EAAE,KAAI;AAC/C,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS,IAAI,WAAW,IAAI,SAAS,IAAI,OAAO,SAAS,CAAC,SAAS,EAAE,SAAS,KAAK,UAAU,CAC9F;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;AACrB,gBAAA,MAAM,EAAE,WAAW,CAAC,KAAK,CAAC;AAC1B,gBAAA,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;aACjC;QACH;AAEA,QAAA,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,IAAI;AAEtD,QAAA,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,gDAAC;AACvB,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,kDAAC;AAE5B,QAAA,IAAI,aAAoB;AAExB,QAAA,MAAM,IAAI,GAAG,OAAO,KAAa,KAAmB;AAClD,YAAA,IAAI;gBACF,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;AAE1C,gBAAA,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AACf,gBAAA,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;gBAEhB,IAAI,aAAa,EAAE;oBACjB,YAAY,CAAC,aAAa,CAAC;gBAC7B;AAEA,gBAAA,aAAa,GAAG,UAAU,CAAC,MAAK;AAC9B,oBAAA,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;oBACjB,aAAa,GAAG,SAAS;gBAC3B,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;YACvC;YAAE,OAAO,KAAK,EAAE;AACd,gBAAA,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;gBACjB,IAAI,SAAS,EAAE;oBACb,OAAO,CAAC,IAAI,CACV,CAAA,8CAAA,CAAgD;wBAC9C,CAAA,gEAAA,CAAkE,EACpE,KAAK,CACN;gBACH;YACF;AACF,QAAA,CAAC;AAED,QAAA,MAAM,KAAK,GAAG,YAA4B;AACxC,YAAA,IAAI;gBACF,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE;AAClD,gBAAA,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AACf,gBAAA,OAAO,KAAK;YACd;YAAE,OAAO,KAAK,EAAE;gBACd,IAAI,SAAS,EAAE;oBACb,OAAO,CAAC,IAAI,CACV,CAAA,gDAAA,CAAkD;wBAChD,CAAA,gEAAA,CAAkE,EACpE,KAAK,CACN;gBACH;AACA,gBAAA,OAAO,EAAE;YACX;AACF,QAAA,CAAC;QAED,SAAS,CAAC,MAAK;YACb,IAAI,aAAa,EAAE;gBACjB,YAAY,CAAC,aAAa,CAAC;YAC7B;AACF,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;AACX,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;AACvB,YAAA,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE;YAC3B,IAAI;YACJ,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;AChJA;;AAEG;;;;"}
@@ -0,0 +1,40 @@
1
+ import { signal } from '@angular/core';
2
+ import { setupContext, constSignal } from '@signality/core/internal';
3
+ import { setupSync, listener } from '@signality/core/browser/listener';
4
+
5
+ /**
6
+ * Signal-based wrapper around the [Device Posture API](https://developer.mozilla.org/en-US/docs/Web/API/Device_Posture_API).
7
+ * Track device posture state for foldable devices.
8
+ *
9
+ * @param options - Optional configuration including injector
10
+ * @returns A DevicePostureRef with type signal
11
+ *
12
+ */
13
+ function devicePosture(options) {
14
+ const { runInContext } = setupContext(options?.injector, devicePosture);
15
+ return runInContext(({ isBrowser }) => {
16
+ const isSupported = constSignal(isBrowser && 'devicePosture' in navigator && !!navigator.devicePosture);
17
+ if (!isSupported()) {
18
+ return {
19
+ isSupported,
20
+ type: constSignal('continuous'),
21
+ };
22
+ }
23
+ const { devicePosture } = navigator;
24
+ const type = signal(devicePosture.type, ...(ngDevMode ? [{ debugName: "type" }] : []));
25
+ setupSync(() => {
26
+ listener(devicePosture, 'change', () => type.set(devicePosture.type));
27
+ });
28
+ return {
29
+ isSupported,
30
+ type: type.asReadonly(),
31
+ };
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Generated bundle index. Do not edit.
37
+ */
38
+
39
+ export { devicePosture };
40
+ //# sourceMappingURL=signality-core-browser-device-posture.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-browser-device-posture.mjs","sources":["../../../projects/core/browser/device-posture/index.ts","../../../projects/core/browser/device-posture/signality-core-browser-device-posture.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type DevicePostureType = 'continuous' | 'folded';\n\nexport interface DevicePostureRef {\n /** Whether Device Posture API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Current device posture */\n readonly type: Signal<DevicePostureType>;\n}\n\n/**\n * Signal-based wrapper around the [Device Posture API](https://developer.mozilla.org/en-US/docs/Web/API/Device_Posture_API).\n * Track device posture state for foldable devices.\n *\n * @param options - Optional configuration including injector\n * @returns A DevicePostureRef with type signal\n *\n */\nexport function devicePosture(options?: WithInjector): DevicePostureRef {\n const { runInContext } = setupContext(options?.injector, devicePosture);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(\n isBrowser && 'devicePosture' in navigator && !!navigator.devicePosture\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n type: constSignal('continuous'),\n };\n }\n\n const { devicePosture } = navigator as NavigatorWithDevicePosture;\n\n const type = signal<DevicePostureType>(devicePosture.type);\n\n setupSync(() => {\n listener(devicePosture, 'change', () => type.set(devicePosture.type));\n });\n\n return {\n isSupported,\n type: type.asReadonly(),\n };\n });\n}\n\ninterface DevicePosture extends EventTarget {\n readonly type: DevicePostureType;\n}\n\ninterface NavigatorWithDevicePosture extends Navigator {\n readonly devicePosture: DevicePosture;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAeA;;;;;;;AAOG;AACG,SAAU,aAAa,CAAC,OAAsB,EAAA;AAClD,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC;AAEvE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAI;AACpC,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS,IAAI,eAAe,IAAI,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC,aAAa,CACvE;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,IAAI,EAAE,WAAW,CAAC,YAAY,CAAC;aAChC;QACH;AAEA,QAAA,MAAM,EAAE,aAAa,EAAE,GAAG,SAAuC;QAEjE,MAAM,IAAI,GAAG,MAAM,CAAoB,aAAa,CAAC,IAAI,gDAAC;QAE1D,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;AACvE,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;AACX,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;SACxB;AACH,IAAA,CAAC,CAAC;AACJ;;ACnDA;;AAEG;;;;"}
@@ -0,0 +1,121 @@
1
+ import { signal, computed, untracked } from '@angular/core';
2
+ import { setupContext, constSignal, NOOP_FN } from '@signality/core/internal';
3
+ import { listener } from '@signality/core/browser/listener';
4
+
5
+ /**
6
+ * Signal-based wrapper around the [Screen Capture API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Capture_API).
7
+ * Capture screen content with Angular signals.
8
+ *
9
+ * @param options - Optional default configuration
10
+ * @returns A DisplayMediaRef with isSupported, stream, error signals and control methods
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * @Component({
15
+ * template: `
16
+ * @if (screen.isSupported()) {
17
+ * <button (click)="toggleCapture()">
18
+ * {{ screen.isActive() ? 'Stop' : 'Start' }} Screen Capture
19
+ * </button>
20
+ * @if (screen.stream(); as stream) {
21
+ * <video [srcObject]="stream" autoplay></video>
22
+ * }
23
+ * @if (screen.error(); as error) {
24
+ * <p class="error">{{ error.message }}</p>
25
+ * }
26
+ * } @else {
27
+ * <p>Screen Capture API not supported</p>
28
+ * }
29
+ * `
30
+ * })
31
+ * class ScreenCaptureComponent {
32
+ * readonly screen = displayMedia();
33
+ *
34
+ * async toggleCapture() {
35
+ * if (this.screen.isActive()) {
36
+ * this.screen.stop();
37
+ * } else {
38
+ * await this.screen.start();
39
+ * }
40
+ * }
41
+ * }
42
+ * ```
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * // With custom constraints
47
+ * const screen = displayMedia({
48
+ * video: {
49
+ * width: { ideal: 1920 },
50
+ * height: { ideal: 1080 },
51
+ * },
52
+ * audio: true,
53
+ * });
54
+ * ```
55
+ */
56
+ function displayMedia(options) {
57
+ const { runInContext } = setupContext(options?.injector, displayMedia);
58
+ return runInContext(({ isBrowser, injector, onCleanup }) => {
59
+ const isSupported = constSignal(isBrowser &&
60
+ 'mediaDevices' in navigator &&
61
+ typeof navigator.mediaDevices?.getDisplayMedia === 'function');
62
+ if (!isSupported()) {
63
+ return {
64
+ isSupported,
65
+ isActive: constSignal(false),
66
+ stream: constSignal(null),
67
+ error: constSignal(null),
68
+ start: () => Promise.resolve(null),
69
+ stop: NOOP_FN,
70
+ };
71
+ }
72
+ const defaults = {
73
+ video: options?.video ?? true,
74
+ audio: options?.audio ?? false,
75
+ };
76
+ const stream = signal(null, ...(ngDevMode ? [{ debugName: "stream" }] : []));
77
+ const error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
78
+ const isActive = computed(() => stream() !== null, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
79
+ const start = async (overrides) => {
80
+ try {
81
+ stop();
82
+ const mergedOptions = { ...defaults, ...overrides };
83
+ const mediaStream = await navigator.mediaDevices.getDisplayMedia(mergedOptions);
84
+ stream.set(mediaStream);
85
+ error.set(null);
86
+ mediaStream
87
+ .getTracks()
88
+ .forEach(track => listener(track, 'ended', () => stop(), { injector }));
89
+ return mediaStream;
90
+ }
91
+ catch (err) {
92
+ error.set(err);
93
+ return null;
94
+ }
95
+ };
96
+ const stop = () => {
97
+ const currStream = untracked(stream);
98
+ if (currStream) {
99
+ currStream.getTracks().forEach(track => track.stop());
100
+ stream.set(null);
101
+ }
102
+ error.set(null);
103
+ };
104
+ onCleanup(stop);
105
+ return {
106
+ isSupported,
107
+ isActive: isActive,
108
+ stream: stream.asReadonly(),
109
+ error: error.asReadonly(),
110
+ start,
111
+ stop,
112
+ };
113
+ });
114
+ }
115
+
116
+ /**
117
+ * Generated bundle index. Do not edit.
118
+ */
119
+
120
+ export { displayMedia };
121
+ //# sourceMappingURL=signality-core-browser-display-media.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-browser-display-media.mjs","sources":["../../../projects/core/browser/display-media/index.ts","../../../projects/core/browser/display-media/signality-core-browser-display-media.ts"],"sourcesContent":["import { computed, type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\n\nexport interface DisplayMediaOptions extends WithInjector {\n /**\n * Video constraints.\n * @default true\n */\n readonly video?: boolean | MediaTrackConstraints;\n\n /**\n * Audio constraints.\n * @default false\n */\n readonly audio?: boolean | MediaTrackConstraints;\n}\n\nexport interface DisplayMediaRef {\n /** Whether Screen Capture API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Whether currently capturing */\n readonly isActive: Signal<boolean>;\n\n /** Current media stream */\n readonly stream: Signal<MediaStream | null>;\n\n /** Last error */\n readonly error: Signal<Error | null>;\n\n /** Start screen capture */\n readonly start: (options?: DisplayMediaOptions) => Promise<MediaStream | null>;\n\n /** Stop screen capture */\n readonly stop: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Screen Capture API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Capture_API).\n * Capture screen content with Angular signals.\n *\n * @param options - Optional default configuration\n * @returns A DisplayMediaRef with isSupported, stream, error signals and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (screen.isSupported()) {\n * <button (click)=\"toggleCapture()\">\n * {{ screen.isActive() ? 'Stop' : 'Start' }} Screen Capture\n * </button>\n * @if (screen.stream(); as stream) {\n * <video [srcObject]=\"stream\" autoplay></video>\n * }\n * @if (screen.error(); as error) {\n * <p class=\"error\">{{ error.message }}</p>\n * }\n * } @else {\n * <p>Screen Capture API not supported</p>\n * }\n * `\n * })\n * class ScreenCaptureComponent {\n * readonly screen = displayMedia();\n *\n * async toggleCapture() {\n * if (this.screen.isActive()) {\n * this.screen.stop();\n * } else {\n * await this.screen.start();\n * }\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // With custom constraints\n * const screen = displayMedia({\n * video: {\n * width: { ideal: 1920 },\n * height: { ideal: 1080 },\n * },\n * audio: true,\n * });\n * ```\n */\nexport function displayMedia(options?: DisplayMediaOptions): DisplayMediaRef {\n const { runInContext } = setupContext(options?.injector, displayMedia);\n\n return runInContext(({ isBrowser, injector, onCleanup }) => {\n const isSupported = constSignal(\n isBrowser &&\n 'mediaDevices' in navigator &&\n typeof navigator.mediaDevices?.getDisplayMedia === 'function'\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n isActive: constSignal(false),\n stream: constSignal(null),\n error: constSignal(null),\n start: () => Promise.resolve(null),\n stop: NOOP_FN,\n };\n }\n\n const defaults: DisplayMediaOptions = {\n video: options?.video ?? true,\n audio: options?.audio ?? false,\n };\n\n const stream = signal<MediaStream | null>(null);\n const error = signal<Error | null>(null);\n const isActive = computed(() => stream() !== null);\n\n const start = async (overrides?: DisplayMediaOptions): Promise<MediaStream | null> => {\n try {\n stop();\n\n const mergedOptions = { ...defaults, ...overrides };\n const mediaStream = await navigator.mediaDevices.getDisplayMedia(mergedOptions);\n\n stream.set(mediaStream);\n error.set(null);\n\n mediaStream\n .getTracks()\n .forEach(track => listener(track, 'ended', () => stop(), { injector }));\n\n return mediaStream;\n } catch (err) {\n error.set(err as DOMException | TypeError);\n return null;\n }\n };\n\n const stop = () => {\n const currStream = untracked(stream);\n if (currStream) {\n currStream.getTracks().forEach(track => track.stop());\n stream.set(null);\n }\n error.set(null);\n };\n\n onCleanup(stop);\n\n return {\n isSupported,\n isActive: isActive,\n stream: stream.asReadonly(),\n error: error.asReadonly(),\n start,\n stop,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import { signal } from '@angular/core';
2
+ import { setupContext, constSignal, NOOP_FN, NOOP_ASYNC_FN } from '@signality/core/internal';
3
+
4
+ /**
5
+ * Signal-based wrapper around the [EyeDropper API](https://developer.mozilla.org/en-US/docs/Web/API/EyeDropper_API).
6
+ *
7
+ * @param options - Optional configuration
8
+ * @returns An {@link EyeDropperRef} with `isSupported`, `sRGBHex` signals and `open`/`close` methods
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * @Component({
13
+ * template: `
14
+ * @if (eyeDropper.isSupported()) {
15
+ * <button (click)="pickColor()">Pick Color</button>
16
+ * <div [style.background-color]="eyeDropper.sRGBHex()">
17
+ * Selected: {{ eyeDropper.sRGBHex() }}
18
+ * </div>
19
+ * }
20
+ * `
21
+ * })
22
+ * class ColorPickerComponent {
23
+ * readonly eyeDropper = eyeDropper();
24
+ *
25
+ * async pickColor() {
26
+ * await this.eyeDropper.open();
27
+ * }
28
+ * }
29
+ * ```
30
+ */
31
+ function eyeDropper(options) {
32
+ const { runInContext } = setupContext(options?.injector, eyeDropper);
33
+ return runInContext(({ isBrowser, onCleanup }) => {
34
+ const isSupported = constSignal(isBrowser && 'EyeDropper' in window);
35
+ if (!isSupported()) {
36
+ return {
37
+ isSupported,
38
+ sRGBHex: constSignal(''),
39
+ open: NOOP_ASYNC_FN,
40
+ close: NOOP_FN,
41
+ };
42
+ }
43
+ const sRGBHex = signal(options?.initialValue ?? '', ...(ngDevMode ? [{ debugName: "sRGBHex" }] : []));
44
+ let abortController = null;
45
+ const open = async () => {
46
+ close();
47
+ abortController = new AbortController();
48
+ const eyeDropper = new window.EyeDropper();
49
+ try {
50
+ const result = await eyeDropper.open({ signal: abortController.signal });
51
+ sRGBHex.set(result.sRGBHex);
52
+ }
53
+ catch (error) {
54
+ if (ngDevMode) {
55
+ console.warn(`[eyeDropper] Failed to open eyedropper. ` +
56
+ `This may be due to user cancellation or an error occurred.`, error);
57
+ }
58
+ }
59
+ finally {
60
+ abortController = null;
61
+ }
62
+ };
63
+ const close = () => {
64
+ abortController?.abort();
65
+ abortController = null;
66
+ };
67
+ onCleanup(close);
68
+ return {
69
+ isSupported,
70
+ sRGBHex: sRGBHex.asReadonly(),
71
+ open,
72
+ close,
73
+ };
74
+ });
75
+ }
76
+
77
+ /**
78
+ * Generated bundle index. Do not edit.
79
+ */
80
+
81
+ export { eyeDropper };
82
+ //# sourceMappingURL=signality-core-browser-eye-dropper.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-browser-eye-dropper.mjs","sources":["../../../projects/core/browser/eye-dropper/index.ts","../../../projects/core/browser/eye-dropper/signality-core-browser-eye-dropper.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, NOOP_ASYNC_FN, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\n\nexport interface EyeDropperOptions extends WithInjector {\n /**\n * Initial color value in sRGB hex format.\n * @default ''\n */\n readonly initialValue?: string;\n}\n\nexport interface EyeDropperRef {\n /** Current selected color in sRGB hex format */\n readonly sRGBHex: Signal<string>;\n\n /** Whether EyeDropper API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Open the eyedropper tool to select a color */\n readonly open: () => Promise<void>;\n\n /** Cancel the active eyedropper operation */\n readonly close: () => void;\n}\n\n/**\n * Signal-based wrapper around the [EyeDropper API](https://developer.mozilla.org/en-US/docs/Web/API/EyeDropper_API).\n *\n * @param options - Optional configuration\n * @returns An {@link EyeDropperRef} with `isSupported`, `sRGBHex` signals and `open`/`close` methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (eyeDropper.isSupported()) {\n * <button (click)=\"pickColor()\">Pick Color</button>\n * <div [style.background-color]=\"eyeDropper.sRGBHex()\">\n * Selected: {{ eyeDropper.sRGBHex() }}\n * </div>\n * }\n * `\n * })\n * class ColorPickerComponent {\n * readonly eyeDropper = eyeDropper();\n *\n * async pickColor() {\n * await this.eyeDropper.open();\n * }\n * }\n * ```\n */\nexport function eyeDropper(options?: EyeDropperOptions): EyeDropperRef {\n const { runInContext } = setupContext(options?.injector, eyeDropper);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(isBrowser && 'EyeDropper' in window);\n\n if (!isSupported()) {\n return {\n isSupported,\n sRGBHex: constSignal(''),\n open: NOOP_ASYNC_FN,\n close: NOOP_FN,\n };\n }\n\n const sRGBHex = signal(options?.initialValue ?? '');\n\n let abortController: AbortController | null = null;\n\n const open = async (): Promise<void> => {\n close();\n\n abortController = new AbortController();\n const eyeDropper: EyeDropper = new (window as any).EyeDropper();\n\n try {\n const result = await eyeDropper.open({ signal: abortController.signal });\n sRGBHex.set(result.sRGBHex);\n } catch (error) {\n if (ngDevMode) {\n console.warn(\n `[eyeDropper] Failed to open eyedropper. ` +\n `This may be due to user cancellation or an error occurred.`,\n error\n );\n }\n } finally {\n abortController = null;\n }\n };\n\n const close = () => {\n abortController?.abort();\n abortController = null;\n };\n\n onCleanup(close);\n\n return {\n isSupported,\n sRGBHex: sRGBHex.asReadonly(),\n open,\n close,\n };\n });\n}\n\ninterface EyeDropper {\n // eslint-disable-next-line @typescript-eslint/no-misused-new\n new (): EyeDropper;\n readonly open: (options?: EyeDropperOpenOptions) => Promise<{ sRGBHex: string }>;\n [Symbol.toStringTag]: 'EyeDropper';\n}\n\ninterface EyeDropperOpenOptions {\n /**\n * AbortSignal to cancel the eyedropper operation.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal\n */\n readonly signal?: AbortSignal;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AA0BA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;AACG,SAAU,UAAU,CAAC,OAA2B,EAAA;AACpD,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC;IAEpE,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAI;QAC/C,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,IAAI,YAAY,IAAI,MAAM,CAAC;AAEpE,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC;AACxB,gBAAA,IAAI,EAAE,aAAa;AACnB,gBAAA,KAAK,EAAE,OAAO;aACf;QACH;QAEA,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,YAAY,IAAI,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;QAEnD,IAAI,eAAe,GAA2B,IAAI;AAElD,QAAA,MAAM,IAAI,GAAG,YAA0B;AACrC,YAAA,KAAK,EAAE;AAEP,YAAA,eAAe,GAAG,IAAI,eAAe,EAAE;AACvC,YAAA,MAAM,UAAU,GAAe,IAAK,MAAc,CAAC,UAAU,EAAE;AAE/D,YAAA,IAAI;AACF,gBAAA,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,CAAC;AACxE,gBAAA,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;YAC7B;YAAE,OAAO,KAAK,EAAE;gBACd,IAAI,SAAS,EAAE;oBACb,OAAO,CAAC,IAAI,CACV,CAAA,wCAAA,CAA0C;wBACxC,CAAA,0DAAA,CAA4D,EAC9D,KAAK,CACN;gBACH;YACF;oBAAU;gBACR,eAAe,GAAG,IAAI;YACxB;AACF,QAAA,CAAC;QAED,MAAM,KAAK,GAAG,MAAK;YACjB,eAAe,EAAE,KAAK,EAAE;YACxB,eAAe,GAAG,IAAI;AACxB,QAAA,CAAC;QAED,SAAS,CAAC,KAAK,CAAC;QAEhB,OAAO;YACL,WAAW;AACX,YAAA,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE;YAC7B,IAAI;YACJ,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;AC5GA;;AAEG;;;;"}
@@ -0,0 +1,100 @@
1
+ import { inject, signal, untracked } from '@angular/core';
2
+ import { APP_BASE_HREF } from '@angular/common';
3
+ import { setupContext, NOOP_FN, constSignal, createToken } from '@signality/core/internal';
4
+
5
+ /**
6
+ * Reactive favicon manipulation.
7
+ * Dynamically change the page favicon based on application state.
8
+ *
9
+ * @param options - Optional configuration
10
+ * @returns A FaviconRef with favicon control methods
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * @Component({
15
+ * template: `
16
+ * <button (click)="setNotification()">Set Notification</button>
17
+ * <button (click)="fav.reset()">Reset Favicon</button>
18
+ * <p>Current: {{ fav.current() }}</p>
19
+ * `
20
+ * })
21
+ * class FaviconComponent {
22
+ * readonly fav = favicon();
23
+ *
24
+ * setNotification() {
25
+ * this.fav.setEmoji('🔴');
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ function favicon(options) {
31
+ const { runInContext } = setupContext(options?.injector, favicon);
32
+ return runInContext(({ isServer }) => {
33
+ if (isServer) {
34
+ return {
35
+ current: constSignal(''),
36
+ original: constSignal(''),
37
+ set: NOOP_FN,
38
+ setEmoji: NOOP_FN,
39
+ reset: NOOP_FN,
40
+ };
41
+ }
42
+ const appBaseHref = inject(APP_BASE_HREF, { optional: true });
43
+ const baseUrl = options?.baseUrl ?? appBaseHref ?? '';
44
+ const getLinkElement = () => {
45
+ let link = document.querySelector('link[rel*="icon"]');
46
+ if (!link) {
47
+ link = document.createElement('link');
48
+ link.rel = 'icon';
49
+ document.head.appendChild(link);
50
+ }
51
+ return link;
52
+ };
53
+ const { href = '' } = getLinkElement();
54
+ const current = signal(href, ...(ngDevMode ? [{ debugName: "current" }] : []));
55
+ const original = signal(href, ...(ngDevMode ? [{ debugName: "original" }] : []));
56
+ const set = (url) => {
57
+ const fullUrl = baseUrl + url;
58
+ const linkEl = getLinkElement();
59
+ linkEl.href = fullUrl;
60
+ current.set(fullUrl);
61
+ };
62
+ const setEmoji = (emoji) => {
63
+ const canvas = document.createElement('canvas');
64
+ canvas.width = 32;
65
+ canvas.height = 32;
66
+ const ctx = canvas.getContext('2d');
67
+ if (!ctx)
68
+ return;
69
+ ctx.font = '28px serif';
70
+ ctx.textAlign = 'center';
71
+ ctx.textBaseline = 'middle';
72
+ ctx.fillText(emoji, 16, 18);
73
+ const dataUrl = canvas.toDataURL('image/png');
74
+ const linkEl = getLinkElement();
75
+ linkEl.href = dataUrl;
76
+ current.set(dataUrl);
77
+ };
78
+ const reset = () => {
79
+ const linkEl = getLinkElement();
80
+ const originalHref = untracked(original);
81
+ linkEl.href = originalHref;
82
+ current.set(originalHref);
83
+ };
84
+ return {
85
+ current: current.asReadonly(),
86
+ original: original.asReadonly(),
87
+ set,
88
+ setEmoji,
89
+ reset,
90
+ };
91
+ });
92
+ }
93
+ const FAVICON = /* @__PURE__ */ createToken(favicon);
94
+
95
+ /**
96
+ * Generated bundle index. Do not edit.
97
+ */
98
+
99
+ export { FAVICON, favicon };
100
+ //# sourceMappingURL=signality-core-browser-favicon.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-browser-favicon.mjs","sources":["../../../projects/core/browser/favicon/index.ts","../../../projects/core/browser/favicon/signality-core-browser-favicon.ts"],"sourcesContent":["import { inject, type Signal, signal, untracked } from '@angular/core';\nimport { APP_BASE_HREF } from '@angular/common';\nimport { constSignal, createToken, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\n\nexport interface FaviconOptions extends WithInjector {\n readonly baseUrl?: string;\n}\n\nexport interface FaviconRef {\n readonly current: Signal<string>;\n readonly original: Signal<string>;\n readonly set: (url: string) => void;\n readonly setEmoji: (emoji: string) => void;\n readonly reset: () => void;\n}\n\n/**\n * Reactive favicon manipulation.\n * Dynamically change the page favicon based on application state.\n *\n * @param options - Optional configuration\n * @returns A FaviconRef with favicon control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <button (click)=\"setNotification()\">Set Notification</button>\n * <button (click)=\"fav.reset()\">Reset Favicon</button>\n * <p>Current: {{ fav.current() }}</p>\n * `\n * })\n * class FaviconComponent {\n * readonly fav = favicon();\n *\n * setNotification() {\n * this.fav.setEmoji('🔴');\n * }\n * }\n * ```\n */\nexport function favicon(options?: FaviconOptions): FaviconRef {\n const { runInContext } = setupContext(options?.injector, favicon);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return {\n current: constSignal(''),\n original: constSignal(''),\n set: NOOP_FN,\n setEmoji: NOOP_FN,\n reset: NOOP_FN,\n };\n }\n\n const appBaseHref = inject(APP_BASE_HREF, { optional: true });\n const baseUrl = options?.baseUrl ?? appBaseHref ?? '';\n\n const getLinkElement = (): HTMLLinkElement => {\n let link = document.querySelector<HTMLLinkElement>('link[rel*=\"icon\"]');\n\n if (!link) {\n link = document.createElement('link');\n link.rel = 'icon';\n document.head.appendChild(link);\n }\n\n return link;\n };\n\n const { href = '' } = getLinkElement();\n const current = signal(href);\n const original = signal(href);\n\n const set = (url: string) => {\n const fullUrl = baseUrl + url;\n const linkEl = getLinkElement();\n linkEl.href = fullUrl;\n current.set(fullUrl);\n };\n\n const setEmoji = (emoji: string) => {\n const canvas = document.createElement('canvas');\n canvas.width = 32;\n canvas.height = 32;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n ctx.font = '28px serif';\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText(emoji, 16, 18);\n\n const dataUrl = canvas.toDataURL('image/png');\n const linkEl = getLinkElement();\n linkEl.href = dataUrl;\n current.set(dataUrl);\n };\n\n const reset = () => {\n const linkEl = getLinkElement();\n const originalHref = untracked(original);\n linkEl.href = originalHref;\n current.set(originalHref);\n };\n\n return {\n current: current.asReadonly(),\n original: original.asReadonly(),\n set,\n setEmoji,\n reset,\n };\n });\n}\n\nexport const FAVICON = /* @__PURE__ */ createToken(favicon);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}