@symbiote-native/sensors 0.2.0 → 0.3.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.
Files changed (100) hide show
  1. package/README.md +5 -5
  2. package/build/angular/services/accelerometer.service/index.js +7 -12
  3. package/build/angular/services/barometer.service/index.js +7 -12
  4. package/build/angular/services/device-motion.service/index.js +7 -12
  5. package/build/angular/services/gyroscope.service/index.js +7 -12
  6. package/build/angular/services/light-sensor.service/index.js +7 -12
  7. package/build/angular/services/magnetometer-uncalibrated.service/index.js +8 -12
  8. package/build/angular/services/magnetometer.service/index.js +7 -12
  9. package/build/angular/services/pedometer.service/index.js +6 -10
  10. package/build/core/device-motion.js +3 -4
  11. package/build/svelte/index.d.ts +16 -0
  12. package/build/svelte/index.js +14 -0
  13. package/build/svelte/runes/use-accelerometer.svelte.d.ts +4 -0
  14. package/build/svelte/runes/use-accelerometer.svelte.js +31 -0
  15. package/build/svelte/runes/use-barometer.svelte.d.ts +4 -0
  16. package/build/svelte/runes/use-barometer.svelte.js +22 -0
  17. package/build/svelte/runes/use-device-motion.svelte.d.ts +4 -0
  18. package/build/svelte/runes/use-device-motion.svelte.js +22 -0
  19. package/build/svelte/runes/use-gyroscope.svelte.d.ts +4 -0
  20. package/build/svelte/runes/use-gyroscope.svelte.js +22 -0
  21. package/build/svelte/runes/use-light-sensor.svelte.d.ts +4 -0
  22. package/build/svelte/runes/use-light-sensor.svelte.js +22 -0
  23. package/build/svelte/runes/use-magnetometer-uncalibrated.svelte.d.ts +4 -0
  24. package/build/svelte/runes/use-magnetometer-uncalibrated.svelte.js +22 -0
  25. package/build/svelte/runes/use-magnetometer.svelte.d.ts +4 -0
  26. package/build/svelte/runes/use-magnetometer.svelte.js +22 -0
  27. package/build/svelte/runes/use-pedometer.svelte.d.ts +4 -0
  28. package/build/svelte/runes/use-pedometer.svelte.js +20 -0
  29. package/build-ngc/angular/services/accelerometer.service/index.js +10 -15
  30. package/build-ngc/angular/services/accelerometer.service/index.js.map +1 -1
  31. package/build-ngc/angular/services/barometer.service/index.js +10 -15
  32. package/build-ngc/angular/services/barometer.service/index.js.map +1 -1
  33. package/build-ngc/angular/services/device-motion.service/index.js +10 -15
  34. package/build-ngc/angular/services/device-motion.service/index.js.map +1 -1
  35. package/build-ngc/angular/services/gyroscope.service/index.js +10 -15
  36. package/build-ngc/angular/services/gyroscope.service/index.js.map +1 -1
  37. package/build-ngc/angular/services/light-sensor.service/index.js +10 -15
  38. package/build-ngc/angular/services/light-sensor.service/index.js.map +1 -1
  39. package/build-ngc/angular/services/magnetometer-uncalibrated.service/index.js +11 -15
  40. package/build-ngc/angular/services/magnetometer-uncalibrated.service/index.js.map +1 -1
  41. package/build-ngc/angular/services/magnetometer.service/index.js +10 -15
  42. package/build-ngc/angular/services/magnetometer.service/index.js.map +1 -1
  43. package/build-ngc/angular/services/pedometer.service/index.js +9 -13
  44. package/build-ngc/angular/services/pedometer.service/index.js.map +1 -1
  45. package/build-ngc/core/device-motion.js +3 -4
  46. package/build-ngc/core/device-motion.js.map +1 -1
  47. package/native-link.json +52 -0
  48. package/package.json +31 -13
  49. package/src/angular/services/accelerometer.service/index.ts +7 -12
  50. package/src/angular/services/barometer.service/index.ts +7 -12
  51. package/src/angular/services/device-motion.service/index.ts +7 -12
  52. package/src/angular/services/gyroscope.service/index.ts +7 -12
  53. package/src/angular/services/light-sensor.service/index.ts +7 -12
  54. package/src/angular/services/magnetometer-uncalibrated.service/index.ts +8 -12
  55. package/src/angular/services/magnetometer.service/index.ts +7 -12
  56. package/src/angular/services/pedometer.service/index.ts +6 -10
  57. package/src/core/device-motion.ts +3 -4
  58. package/src/svelte/index.ts +29 -0
  59. package/src/svelte/runes/use-accelerometer.svelte.ts +36 -0
  60. package/src/svelte/runes/use-barometer.svelte.ts +27 -0
  61. package/src/svelte/runes/use-device-motion.svelte.ts +27 -0
  62. package/src/svelte/runes/use-gyroscope.svelte.ts +27 -0
  63. package/src/svelte/runes/use-light-sensor.svelte.ts +27 -0
  64. package/src/svelte/runes/use-magnetometer-uncalibrated.svelte.ts +31 -0
  65. package/src/svelte/runes/use-magnetometer.svelte.ts +27 -0
  66. package/src/svelte/runes/use-pedometer.svelte.ts +23 -0
  67. package/src/svelte/svelte-ambient.d.ts +6 -0
  68. package/src/angular/services/accelerometer.service/accelerometer.service.test.ts +0 -118
  69. package/src/angular/services/barometer.service/barometer.service.test.ts +0 -122
  70. package/src/angular/services/device-motion.service/device-motion.service.test.ts +0 -125
  71. package/src/angular/services/gyroscope.service/gyroscope.service.test.ts +0 -118
  72. package/src/angular/services/light-sensor.service/light-sensor.service.test.ts +0 -118
  73. package/src/angular/services/magnetometer-uncalibrated.service/magnetometer-uncalibrated.service.test.ts +0 -123
  74. package/src/angular/services/magnetometer.service/magnetometer.service.test.ts +0 -118
  75. package/src/angular/services/pedometer.service/pedometer.service.test.ts +0 -86
  76. package/src/core/accelerometer.test.ts +0 -49
  77. package/src/core/barometer.test.ts +0 -46
  78. package/src/core/device-motion.test.ts +0 -60
  79. package/src/core/device-sensor.test.ts +0 -76
  80. package/src/core/gyroscope.test.ts +0 -46
  81. package/src/core/light-sensor.test.ts +0 -49
  82. package/src/core/magnetometer-uncalibrated.test.ts +0 -49
  83. package/src/core/magnetometer.test.ts +0 -49
  84. package/src/core/pedometer.test.ts +0 -118
  85. package/src/react/hooks/use-accelerometer/use-accelerometer.test.tsx +0 -81
  86. package/src/react/hooks/use-barometer/use-barometer.test.tsx +0 -83
  87. package/src/react/hooks/use-device-motion/use-device-motion.test.tsx +0 -88
  88. package/src/react/hooks/use-gyroscope/use-gyroscope.test.tsx +0 -79
  89. package/src/react/hooks/use-light-sensor/use-light-sensor.test.tsx +0 -79
  90. package/src/react/hooks/use-magnetometer/use-magnetometer.test.tsx +0 -81
  91. package/src/react/hooks/use-magnetometer-uncalibrated/use-magnetometer-uncalibrated.test.tsx +0 -88
  92. package/src/react/hooks/use-pedometer/use-pedometer.test.tsx +0 -69
  93. package/src/vue/composables/use-accelerometer/use-accelerometer.test.ts +0 -96
  94. package/src/vue/composables/use-barometer/use-barometer.test.ts +0 -100
  95. package/src/vue/composables/use-device-motion/use-device-motion.test.ts +0 -104
  96. package/src/vue/composables/use-gyroscope/use-gyroscope.test.ts +0 -96
  97. package/src/vue/composables/use-light-sensor/use-light-sensor.test.ts +0 -96
  98. package/src/vue/composables/use-magnetometer/use-magnetometer.test.ts +0 -96
  99. package/src/vue/composables/use-magnetometer-uncalibrated/use-magnetometer-uncalibrated.test.ts +0 -103
  100. package/src/vue/composables/use-pedometer/use-pedometer.test.ts +0 -77
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/sensors",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "expo-sensors wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Accelerometer, Barometer, DeviceMotion, Gyroscope, LightSensor, Magnetometer, MagnetometerUncalibrated, Pedometer.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,6 +30,10 @@
30
30
  "types": "./build/react/index.d.ts",
31
31
  "default": "./build/react/index.js"
32
32
  },
33
+ "./svelte": {
34
+ "types": "./build/svelte/index.d.ts",
35
+ "default": "./build/svelte/index.js"
36
+ },
33
37
  "./angular": {
34
38
  "types": "./build-ngc/angular/index.d.ts",
35
39
  "react-native": "./build-ngc/angular/index.js",
@@ -39,7 +43,11 @@
39
43
  "files": [
40
44
  "src",
41
45
  "build",
42
- "build-ngc"
46
+ "build-ngc",
47
+ "native-link.json",
48
+ "!src/**/*.test.*",
49
+ "!src/**/*.spec.*",
50
+ "!src/**/*.detox.*"
43
51
  ],
44
52
  "publishConfig": {
45
53
  "access": "public"
@@ -49,15 +57,17 @@
49
57
  "expo-modules-core": "57.0.5"
50
58
  },
51
59
  "peerDependencies": {
60
+ "@symbiote-native/angular": ">=0.6.2",
52
61
  "@symbiote-native/engine": ">=0.1.7",
62
+ "@symbiote-native/react": ">=0.2.8",
63
+ "@symbiote-native/svelte": ">=0.1.0",
64
+ "@symbiote-native/vue": ">=0.3.8",
53
65
  "@angular/core": ">=20",
54
66
  "@vue/runtime-core": "^3.5.13",
55
67
  "react": ">=19.0.0",
56
68
  "react-native": ">=0.86",
57
- "vue": ">=3.5.0",
58
- "@symbiote-native/angular": "0.6.1",
59
- "@symbiote-native/react": "0.2.8",
60
- "@symbiote-native/vue": "0.3.8"
69
+ "svelte": ">=5.56.0",
70
+ "vue": ">=3.5.0"
61
71
  },
62
72
  "peerDependenciesMeta": {
63
73
  "@symbiote-native/angular": {
@@ -66,6 +76,9 @@
66
76
  "@symbiote-native/react": {
67
77
  "optional": true
68
78
  },
79
+ "@symbiote-native/svelte": {
80
+ "optional": true
81
+ },
69
82
  "@symbiote-native/vue": {
70
83
  "optional": true
71
84
  },
@@ -78,24 +91,29 @@
78
91
  "react": {
79
92
  "optional": true
80
93
  },
94
+ "svelte": {
95
+ "optional": true
96
+ },
81
97
  "vue": {
82
98
  "optional": true
83
99
  }
84
100
  },
85
101
  "devDependencies": {
86
- "@angular/compiler": "^22",
87
- "@angular/compiler-cli": "^22",
88
- "@angular/core": "^22",
102
+ "@angular/compiler": "~22.0.8",
103
+ "@angular/compiler-cli": "~22.0.8",
104
+ "@angular/core": "~22.0.8",
89
105
  "@types/node": "^26.0.0",
90
106
  "@types/react": "^19.2.0",
91
107
  "@vue/runtime-core": "^3.5.13",
92
108
  "react": "19.2.3",
109
+ "svelte": "^5.56.0",
93
110
  "typescript": "~6.0.0",
94
- "@symbiote-native/angular": "0.6.1",
95
- "@symbiote-native/engine": "0.1.7",
96
- "@symbiote-native/react": "0.2.8",
111
+ "@symbiote-native/angular": "0.7.0",
112
+ "@symbiote-native/engine": "0.2.0",
113
+ "@symbiote-native/react": "0.3.0",
114
+ "@symbiote-native/svelte": "0.2.0",
97
115
  "@symbiote-native/test-utils": "0.1.6",
98
- "@symbiote-native/vue": "0.3.8"
116
+ "@symbiote-native/vue": "0.4.0"
99
117
  },
100
118
  "scripts": {
101
119
  "typecheck": "tsc --build",
@@ -1,23 +1,16 @@
1
1
  import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
2
  import { Accelerometer, type IAccelerometerMeasurement } from '../../../core';
3
3
 
4
- // Angular twin of React's `useAccelerometer` hook and Vue's `useAccelerometer` composable.
5
- // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
6
- // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
7
- // inside an injection context).
4
+ // Angular twin of React's `useAccelerometer` hook / Vue's `useAccelerometer` composable.
5
+ // Angular has no per-instance hook, so `connect()` stands in: call it ONCE, inside an injection
6
+ // context (typically a component's field initializer).
8
7
  //
9
8
  // readonly measurement = inject(AccelerometerService).connect();
10
9
  // // template: {{ measurement()?.x }}
11
- //
12
- // Unlike HideAnimationService.connect(), there is no per-render config to re-sync — the
13
- // subscription doesn't depend on anything the caller's own signals could change between
14
- // renders — so a single effect() that subscribes once and cleans up once is enough; no
15
- // `updateConfig`-style effect that re-runs on every read is needed here.
16
10
  @Injectable({ providedIn: 'root' })
17
11
  export class AccelerometerService {
18
- // Captured in the constructor (itself always run inside an injection context by Angular's
19
- // own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
20
- // code that is not, on its own, an active injection context — mirrors HideAnimationService.
12
+ // Captured in the constructor (always an injection context) so `connect()` can build an
13
+ // `effect()` even when called from a field initializer that isn't one on its own.
21
14
  private readonly injector = inject(Injector);
22
15
 
23
16
  connect(updateIntervalMs?: number): Signal<IAccelerometerMeasurement | null> {
@@ -25,6 +18,8 @@ export class AccelerometerService {
25
18
 
26
19
  effect(
27
20
  onCleanup => {
21
+ // updateIntervalMs applies once, at connect time — the effect has no reactive read to
22
+ // re-run on, so a later change is not picked up.
28
23
  if (updateIntervalMs !== undefined) {
29
24
  Accelerometer.setUpdateInterval(updateIntervalMs);
30
25
  }
@@ -1,23 +1,16 @@
1
1
  import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
2
  import { Barometer, type IBarometerMeasurement } from '../../../core';
3
3
 
4
- // Angular twin of React's `useBarometer` hook and Vue's `useBarometer` composable.
5
- // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
6
- // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
7
- // inside an injection context).
4
+ // Angular twin of React's `useBarometer` hook / Vue's `useBarometer` composable. Angular has no
5
+ // per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
6
+ // (typically a component's field initializer).
8
7
  //
9
8
  // readonly measurement = inject(BarometerService).connect();
10
9
  // // template: {{ measurement()?.pressure }}
11
- //
12
- // Unlike HideAnimationService.connect(), there is no per-render config to re-sync — the
13
- // subscription doesn't depend on anything the caller's own signals could change between
14
- // renders — so a single effect() that subscribes once and cleans up once is enough; no
15
- // `updateConfig`-style effect that re-runs on every read is needed here.
16
10
  @Injectable({ providedIn: 'root' })
17
11
  export class BarometerService {
18
- // Captured in the constructor (itself always run inside an injection context by Angular's
19
- // own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
20
- // code that is not, on its own, an active injection context — mirrors HideAnimationService.
12
+ // Captured in the constructor (always an injection context) so `connect()` can build an
13
+ // `effect()` even when called from a field initializer that isn't one on its own.
21
14
  private readonly injector = inject(Injector);
22
15
 
23
16
  connect(updateIntervalMs?: number): Signal<IBarometerMeasurement | null> {
@@ -25,6 +18,8 @@ export class BarometerService {
25
18
 
26
19
  effect(
27
20
  onCleanup => {
21
+ // updateIntervalMs applies once, at connect time — the effect has no reactive read to
22
+ // re-run on, so a later change is not picked up.
28
23
  if (updateIntervalMs !== undefined) {
29
24
  Barometer.setUpdateInterval(updateIntervalMs);
30
25
  }
@@ -1,23 +1,16 @@
1
1
  import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
2
  import { DeviceMotion, type IDeviceMotionMeasurement } from '../../../core';
3
3
 
4
- // Angular twin of React's `useDeviceMotion` hook and Vue's `useDeviceMotion` composable.
5
- // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
6
- // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
7
- // inside an injection context).
4
+ // Angular twin of React's `useDeviceMotion` hook / Vue's `useDeviceMotion` composable. Angular
5
+ // has no per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
6
+ // (typically a component's field initializer).
8
7
  //
9
8
  // readonly measurement = inject(DeviceMotionService).connect();
10
9
  // // template: {{ measurement()?.rotation?.alpha }}
11
- //
12
- // Unlike HideAnimationService.connect(), there is no per-render config to re-sync — the
13
- // subscription doesn't depend on anything the caller's own signals could change between
14
- // renders — so a single effect() that subscribes once and cleans up once is enough; no
15
- // `updateConfig`-style effect that re-runs on every read is needed here.
16
10
  @Injectable({ providedIn: 'root' })
17
11
  export class DeviceMotionService {
18
- // Captured in the constructor (itself always run inside an injection context by Angular's
19
- // own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
20
- // code that is not, on its own, an active injection context — mirrors HideAnimationService.
12
+ // Captured in the constructor (always an injection context) so `connect()` can build an
13
+ // `effect()` even when called from a field initializer that isn't one on its own.
21
14
  private readonly injector = inject(Injector);
22
15
 
23
16
  connect(updateIntervalMs?: number): Signal<IDeviceMotionMeasurement | null> {
@@ -25,6 +18,8 @@ export class DeviceMotionService {
25
18
 
26
19
  effect(
27
20
  onCleanup => {
21
+ // updateIntervalMs applies once, at connect time — the effect has no reactive read to
22
+ // re-run on, so a later change is not picked up.
28
23
  if (updateIntervalMs !== undefined) {
29
24
  DeviceMotion.setUpdateInterval(updateIntervalMs);
30
25
  }
@@ -1,23 +1,16 @@
1
1
  import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
2
  import { Gyroscope, type IGyroscopeMeasurement } from '../../../core';
3
3
 
4
- // Angular twin of React's `useGyroscope` hook and Vue's `useGyroscope` composable.
5
- // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
6
- // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
7
- // inside an injection context).
4
+ // Angular twin of React's `useGyroscope` hook / Vue's `useGyroscope` composable. Angular has no
5
+ // per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
6
+ // (typically a component's field initializer).
8
7
  //
9
8
  // readonly measurement = inject(GyroscopeService).connect();
10
9
  // // template: {{ measurement()?.x }}
11
- //
12
- // Unlike HideAnimationService.connect(), there is no per-render config to re-sync — the
13
- // subscription doesn't depend on anything the caller's own signals could change between
14
- // renders — so a single effect() that subscribes once and cleans up once is enough; no
15
- // `updateConfig`-style effect that re-runs on every read is needed here.
16
10
  @Injectable({ providedIn: 'root' })
17
11
  export class GyroscopeService {
18
- // Captured in the constructor (itself always run inside an injection context by Angular's
19
- // own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
20
- // code that is not, on its own, an active injection context — mirrors HideAnimationService.
12
+ // Captured in the constructor (always an injection context) so `connect()` can build an
13
+ // `effect()` even when called from a field initializer that isn't one on its own.
21
14
  private readonly injector = inject(Injector);
22
15
 
23
16
  connect(updateIntervalMs?: number): Signal<IGyroscopeMeasurement | null> {
@@ -25,6 +18,8 @@ export class GyroscopeService {
25
18
 
26
19
  effect(
27
20
  onCleanup => {
21
+ // updateIntervalMs applies once, at connect time — the effect has no reactive read to
22
+ // re-run on, so a later change is not picked up.
28
23
  if (updateIntervalMs !== undefined) {
29
24
  Gyroscope.setUpdateInterval(updateIntervalMs);
30
25
  }
@@ -1,23 +1,16 @@
1
1
  import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
2
  import { LightSensor, type ILightSensorMeasurement } from '../../../core';
3
3
 
4
- // Angular twin of React's `useLightSensor` hook and Vue's `useLightSensor` composable.
5
- // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
6
- // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
7
- // inside an injection context).
4
+ // Angular twin of React's `useLightSensor` hook / Vue's `useLightSensor` composable. Angular
5
+ // has no per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
6
+ // (typically a component's field initializer).
8
7
  //
9
8
  // readonly measurement = inject(LightSensorService).connect();
10
9
  // // template: {{ measurement()?.illuminance }}
11
- //
12
- // Unlike HideAnimationService.connect(), there is no per-render config to re-sync — the
13
- // subscription doesn't depend on anything the caller's own signals could change between
14
- // renders — so a single effect() that subscribes once and cleans up once is enough; no
15
- // `updateConfig`-style effect that re-runs on every read is needed here.
16
10
  @Injectable({ providedIn: 'root' })
17
11
  export class LightSensorService {
18
- // Captured in the constructor (itself always run inside an injection context by Angular's
19
- // own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
20
- // code that is not, on its own, an active injection context — mirrors HideAnimationService.
12
+ // Captured in the constructor (always an injection context) so `connect()` can build an
13
+ // `effect()` even when called from a field initializer that isn't one on its own.
21
14
  private readonly injector = inject(Injector);
22
15
 
23
16
  connect(updateIntervalMs?: number): Signal<ILightSensorMeasurement | null> {
@@ -25,6 +18,8 @@ export class LightSensorService {
25
18
 
26
19
  effect(
27
20
  onCleanup => {
21
+ // updateIntervalMs applies once, at connect time — the effect has no reactive read to
22
+ // re-run on, so a later change is not picked up.
28
23
  if (updateIntervalMs !== undefined) {
29
24
  LightSensor.setUpdateInterval(updateIntervalMs);
30
25
  }
@@ -1,23 +1,17 @@
1
1
  import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
2
  import { MagnetometerUncalibrated, type IMagnetometerUncalibratedMeasurement } from '../../../core';
3
3
 
4
- // Angular twin of React's `useMagnetometerUncalibrated` hook and Vue's
5
- // `useMagnetometerUncalibrated` composable. Angular has no per-instance hook — state and
6
- // lifecycle live in DI instead, so `connect()` stands in for the hook's role: call it ONCE
7
- // (typically from a component's field initializer, inside an injection context).
4
+ // Angular twin of React's `useMagnetometerUncalibrated` hook / Vue's
5
+ // `useMagnetometerUncalibrated` composable. Angular has no per-instance hook, so `connect()`
6
+ // stands in: call it ONCE, inside an injection context (typically a component's field
7
+ // initializer).
8
8
  //
9
9
  // readonly measurement = inject(MagnetometerUncalibratedService).connect();
10
10
  // // template: {{ measurement()?.x }}
11
- //
12
- // Unlike HideAnimationService.connect(), there is no per-render config to re-sync — the
13
- // subscription doesn't depend on anything the caller's own signals could change between
14
- // renders — so a single effect() that subscribes once and cleans up once is enough; no
15
- // `updateConfig`-style effect that re-runs on every read is needed here.
16
11
  @Injectable({ providedIn: 'root' })
17
12
  export class MagnetometerUncalibratedService {
18
- // Captured in the constructor (itself always run inside an injection context by Angular's
19
- // own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
20
- // code that is not, on its own, an active injection context — mirrors HideAnimationService.
13
+ // Captured in the constructor (always an injection context) so `connect()` can build an
14
+ // `effect()` even when called from a field initializer that isn't one on its own.
21
15
  private readonly injector = inject(Injector);
22
16
 
23
17
  connect(updateIntervalMs?: number): Signal<IMagnetometerUncalibratedMeasurement | null> {
@@ -25,6 +19,8 @@ export class MagnetometerUncalibratedService {
25
19
 
26
20
  effect(
27
21
  onCleanup => {
22
+ // updateIntervalMs applies once, at connect time — the effect has no reactive read to
23
+ // re-run on, so a later change is not picked up.
28
24
  if (updateIntervalMs !== undefined) {
29
25
  MagnetometerUncalibrated.setUpdateInterval(updateIntervalMs);
30
26
  }
@@ -1,23 +1,16 @@
1
1
  import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
2
  import { Magnetometer, type IMagnetometerMeasurement } from '../../../core';
3
3
 
4
- // Angular twin of React's `useMagnetometer` hook and Vue's `useMagnetometer` composable.
5
- // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
6
- // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
7
- // inside an injection context).
4
+ // Angular twin of React's `useMagnetometer` hook / Vue's `useMagnetometer` composable. Angular
5
+ // has no per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
6
+ // (typically a component's field initializer).
8
7
  //
9
8
  // readonly measurement = inject(MagnetometerService).connect();
10
9
  // // template: {{ measurement()?.x }}
11
- //
12
- // Unlike HideAnimationService.connect(), there is no per-render config to re-sync — the
13
- // subscription doesn't depend on anything the caller's own signals could change between
14
- // renders — so a single effect() that subscribes once and cleans up once is enough; no
15
- // `updateConfig`-style effect that re-runs on every read is needed here.
16
10
  @Injectable({ providedIn: 'root' })
17
11
  export class MagnetometerService {
18
- // Captured in the constructor (itself always run inside an injection context by Angular's
19
- // own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
20
- // code that is not, on its own, an active injection context — mirrors HideAnimationService.
12
+ // Captured in the constructor (always an injection context) so `connect()` can build an
13
+ // `effect()` even when called from a field initializer that isn't one on its own.
21
14
  private readonly injector = inject(Injector);
22
15
 
23
16
  connect(updateIntervalMs?: number): Signal<IMagnetometerMeasurement | null> {
@@ -25,6 +18,8 @@ export class MagnetometerService {
25
18
 
26
19
  effect(
27
20
  onCleanup => {
21
+ // updateIntervalMs applies once, at connect time — the effect has no reactive read to
22
+ // re-run on, so a later change is not picked up.
28
23
  if (updateIntervalMs !== undefined) {
29
24
  Magnetometer.setUpdateInterval(updateIntervalMs);
30
25
  }
@@ -1,21 +1,17 @@
1
1
  import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
2
  import { watchStepCount, type IPedometerResult } from '../../../core';
3
3
 
4
- // Angular twin of React's `usePedometer` hook and Vue's `usePedometer` composable. Angular has
5
- // no per-instance hook — state and lifecycle live in DI instead, so `connect()` stands in for
6
- // the hook's role: call it ONCE (typically from a component's field initializer, inside an
7
- // injection context).
4
+ // Angular twin of React's `usePedometer` hook / Vue's `usePedometer` composable. Angular has no
5
+ // per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
6
+ // (typically a component's field initializer). Pedometer has no setUpdateInterval, unlike the
7
+ // other sensor services, so there is no interval param here.
8
8
  //
9
9
  // readonly result = inject(PedometerService).connect();
10
10
  // // template: {{ result()?.steps }}
11
- //
12
- // Unlike AccelerometerService.connect(), there is no updateIntervalMs to apply — Pedometer has
13
- // no setUpdateInterval — so a single effect() that subscribes once and cleans up once is enough.
14
11
  @Injectable({ providedIn: 'root' })
15
12
  export class PedometerService {
16
- // Captured in the constructor (itself always run inside an injection context by Angular's own
17
- // DI) so `connect()` can create an `effect()` even when called from plain field-initializer
18
- // code that is not, on its own, an active injection context — mirrors AccelerometerService.
13
+ // Captured in the constructor (always an injection context) so `connect()` can build an
14
+ // `effect()` even when called from a field initializer that isn't one on its own.
19
15
  private readonly injector = inject(Injector);
20
16
 
21
17
  connect(): Signal<IPedometerResult | null> {
@@ -24,8 +24,8 @@ export enum DeviceMotionOrientation {
24
24
 
25
25
  export class DeviceMotionSensor extends DeviceSensor<IDeviceMotionMeasurement> {
26
26
  // Standard gravitational acceleration for Earth (m/s^2), read off the native module rather
27
- // than hardcoded — mirrors upstream's dual exposure as both an instance property and the
28
- // standalone `gravity` export below.
27
+ // than hardcoded — mirrors upstream's dual exposure as an instance property and this
28
+ // standalone export.
29
29
  readonly gravity: number = exponentDeviceMotion.Gravity;
30
30
  }
31
31
 
@@ -36,6 +36,5 @@ export const DeviceMotion = new DeviceMotionSensor(
36
36
  DEVICE_MOTION_DID_UPDATE_EVENT_NAME,
37
37
  );
38
38
 
39
- // Standalone export mirroring the instance property above, for callers that only need the
40
- // constant and not the full sensor singleton.
39
+ // Same value as `DeviceMotion.gravity`, for callers that only need the constant.
41
40
  export const gravity: number = exponentDeviceMotion.Gravity;
@@ -0,0 +1,29 @@
1
+ // @symbiote-native/sensors/svelte: the Svelte entry over the framework-agnostic core.
2
+ // useAccelerometer wires the Accelerometer singleton's addListener lifecycle onto Svelte's own
3
+ // `$effect` (runes/use-accelerometer.svelte.ts) — mirrors the lifecycle-bucket naming convention
4
+ // of adapters/svelte/src/runes (never `hooks/`/`composables/`, those are React's and Vue's terms).
5
+
6
+ export { useAccelerometer } from './runes/use-accelerometer.svelte';
7
+ export type { IAccelerometerMeasurement } from '../core';
8
+ export { useBarometer } from './runes/use-barometer.svelte';
9
+ export type { IBarometerMeasurement } from '../core';
10
+ export { useDeviceMotion } from './runes/use-device-motion.svelte';
11
+ export { DeviceMotionOrientation, gravity, type IDeviceMotionMeasurement } from '../core';
12
+ export { useGyroscope } from './runes/use-gyroscope.svelte';
13
+ export type { IGyroscopeMeasurement } from '../core';
14
+ export { useLightSensor } from './runes/use-light-sensor.svelte';
15
+ export type { ILightSensorMeasurement } from '../core';
16
+ export { useMagnetometer } from './runes/use-magnetometer.svelte';
17
+ export type { IMagnetometerMeasurement } from '../core';
18
+ export { useMagnetometerUncalibrated } from './runes/use-magnetometer-uncalibrated.svelte';
19
+ export type { IMagnetometerUncalibratedMeasurement } from '../core';
20
+ export { usePedometer } from './runes/use-pedometer.svelte';
21
+ export {
22
+ watchStepCount,
23
+ getStepCountAsync,
24
+ isAvailableAsync,
25
+ getPermissionsAsync,
26
+ requestPermissionsAsync,
27
+ type IPedometerResult,
28
+ type IPedometerUpdateCallback,
29
+ } from '../core';
@@ -0,0 +1,36 @@
1
+ // Svelte lifecycle wiring over the framework-agnostic Accelerometer singleton (core/).
2
+ // `.svelte.ts` (not `.ts`): runes ($state/$effect) only work in files with this extension
3
+ // outside an actual `.svelte` component; `runes/` is the framework's own lifecycle-bucket term
4
+ // (CLAUDE.md's <adapter_src_follows_framework_idioms>).
5
+ //
6
+ // Returns a boxed getter, not a bare `$state`: Svelte 5 reactivity is lexically scoped to the
7
+ // declaring module and doesn't survive being returned as a raw value, so callers read `.current`
8
+ // like unwrapping Vue's `Ref.value`.
9
+ //
10
+ // `updateIntervalMs` is a plain number, not a getter: it applies once at subscribe time and
11
+ // never reacts to a later change, matching the Vue/React twins.
12
+ import { Accelerometer, type EventSubscription, type IAccelerometerMeasurement } from '../../core';
13
+
14
+ export function useAccelerometer(updateIntervalMs?: number): {
15
+ readonly current: IAccelerometerMeasurement | null;
16
+ } {
17
+ let measurement = $state<IAccelerometerMeasurement | null>(null);
18
+
19
+ $effect(() => {
20
+ if (updateIntervalMs !== undefined) {
21
+ Accelerometer.setUpdateInterval(updateIntervalMs);
22
+ }
23
+ // `measurement` is only ever written here, never read, so the effect has an empty dependency
24
+ // set and runs exactly once on mount, cleaning up exactly once on unmount.
25
+ const subscription: EventSubscription = Accelerometer.addListener(next => {
26
+ measurement = next;
27
+ });
28
+ return () => subscription.remove();
29
+ });
30
+
31
+ return {
32
+ get current(): IAccelerometerMeasurement | null {
33
+ return measurement;
34
+ },
35
+ };
36
+ }
@@ -0,0 +1,27 @@
1
+ // Svelte lifecycle wiring over the framework-agnostic Barometer singleton (core/) — mirrors
2
+ // use-accelerometer.svelte.ts's shape exactly. See its header for why this lives in `runes/` with
3
+ // a `.svelte.ts` extension, returns a boxed getter object, and takes a plain `updateIntervalMs`
4
+ // rather than a getter.
5
+ import { Barometer, type EventSubscription, type IBarometerMeasurement } from '../../core';
6
+
7
+ export function useBarometer(updateIntervalMs?: number): {
8
+ readonly current: IBarometerMeasurement | null;
9
+ } {
10
+ let measurement = $state<IBarometerMeasurement | null>(null);
11
+
12
+ $effect(() => {
13
+ if (updateIntervalMs !== undefined) {
14
+ Barometer.setUpdateInterval(updateIntervalMs);
15
+ }
16
+ const subscription: EventSubscription = Barometer.addListener(next => {
17
+ measurement = next;
18
+ });
19
+ return () => subscription.remove();
20
+ });
21
+
22
+ return {
23
+ get current(): IBarometerMeasurement | null {
24
+ return measurement;
25
+ },
26
+ };
27
+ }
@@ -0,0 +1,27 @@
1
+ // Svelte lifecycle wiring over the framework-agnostic DeviceMotion singleton (core/) — mirrors
2
+ // use-accelerometer.svelte.ts's shape exactly. See its header for why this lives in `runes/` with
3
+ // a `.svelte.ts` extension, returns a boxed getter object, and takes a plain `updateIntervalMs`
4
+ // rather than a getter.
5
+ import { DeviceMotion, type EventSubscription, type IDeviceMotionMeasurement } from '../../core';
6
+
7
+ export function useDeviceMotion(updateIntervalMs?: number): {
8
+ readonly current: IDeviceMotionMeasurement | null;
9
+ } {
10
+ let measurement = $state<IDeviceMotionMeasurement | null>(null);
11
+
12
+ $effect(() => {
13
+ if (updateIntervalMs !== undefined) {
14
+ DeviceMotion.setUpdateInterval(updateIntervalMs);
15
+ }
16
+ const subscription: EventSubscription = DeviceMotion.addListener(next => {
17
+ measurement = next;
18
+ });
19
+ return () => subscription.remove();
20
+ });
21
+
22
+ return {
23
+ get current(): IDeviceMotionMeasurement | null {
24
+ return measurement;
25
+ },
26
+ };
27
+ }
@@ -0,0 +1,27 @@
1
+ // Svelte lifecycle wiring over the framework-agnostic Gyroscope singleton (core/) — mirrors
2
+ // use-accelerometer.svelte.ts's shape exactly. See its header for why this lives in `runes/` with
3
+ // a `.svelte.ts` extension, returns a boxed getter object, and takes a plain `updateIntervalMs`
4
+ // rather than a getter.
5
+ import { Gyroscope, type EventSubscription, type IGyroscopeMeasurement } from '../../core';
6
+
7
+ export function useGyroscope(updateIntervalMs?: number): {
8
+ readonly current: IGyroscopeMeasurement | null;
9
+ } {
10
+ let measurement = $state<IGyroscopeMeasurement | null>(null);
11
+
12
+ $effect(() => {
13
+ if (updateIntervalMs !== undefined) {
14
+ Gyroscope.setUpdateInterval(updateIntervalMs);
15
+ }
16
+ const subscription: EventSubscription = Gyroscope.addListener(next => {
17
+ measurement = next;
18
+ });
19
+ return () => subscription.remove();
20
+ });
21
+
22
+ return {
23
+ get current(): IGyroscopeMeasurement | null {
24
+ return measurement;
25
+ },
26
+ };
27
+ }
@@ -0,0 +1,27 @@
1
+ // Svelte lifecycle wiring over the framework-agnostic LightSensor singleton (core/) — mirrors
2
+ // use-accelerometer.svelte.ts's shape exactly. See its header for why this lives in `runes/` with
3
+ // a `.svelte.ts` extension, returns a boxed getter object, and takes a plain `updateIntervalMs`
4
+ // rather than a getter.
5
+ import { LightSensor, type EventSubscription, type ILightSensorMeasurement } from '../../core';
6
+
7
+ export function useLightSensor(updateIntervalMs?: number): {
8
+ readonly current: ILightSensorMeasurement | null;
9
+ } {
10
+ let measurement = $state<ILightSensorMeasurement | null>(null);
11
+
12
+ $effect(() => {
13
+ if (updateIntervalMs !== undefined) {
14
+ LightSensor.setUpdateInterval(updateIntervalMs);
15
+ }
16
+ const subscription: EventSubscription = LightSensor.addListener(next => {
17
+ measurement = next;
18
+ });
19
+ return () => subscription.remove();
20
+ });
21
+
22
+ return {
23
+ get current(): ILightSensorMeasurement | null {
24
+ return measurement;
25
+ },
26
+ };
27
+ }
@@ -0,0 +1,31 @@
1
+ // Svelte lifecycle wiring over the framework-agnostic MagnetometerUncalibrated singleton (core/) —
2
+ // mirrors use-accelerometer.svelte.ts's shape exactly. See its header for why this lives in
3
+ // `runes/` with a `.svelte.ts` extension, returns a boxed getter object, and takes a plain
4
+ // `updateIntervalMs` rather than a getter.
5
+ import {
6
+ MagnetometerUncalibrated,
7
+ type EventSubscription,
8
+ type IMagnetometerUncalibratedMeasurement,
9
+ } from '../../core';
10
+
11
+ export function useMagnetometerUncalibrated(updateIntervalMs?: number): {
12
+ readonly current: IMagnetometerUncalibratedMeasurement | null;
13
+ } {
14
+ let measurement = $state<IMagnetometerUncalibratedMeasurement | null>(null);
15
+
16
+ $effect(() => {
17
+ if (updateIntervalMs !== undefined) {
18
+ MagnetometerUncalibrated.setUpdateInterval(updateIntervalMs);
19
+ }
20
+ const subscription: EventSubscription = MagnetometerUncalibrated.addListener(next => {
21
+ measurement = next;
22
+ });
23
+ return () => subscription.remove();
24
+ });
25
+
26
+ return {
27
+ get current(): IMagnetometerUncalibratedMeasurement | null {
28
+ return measurement;
29
+ },
30
+ };
31
+ }