@mentra/miniapp 0.3.0-beta.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 (277) hide show
  1. package/README.md +259 -0
  2. package/dist/background/index.d.ts +60 -0
  3. package/dist/background/index.d.ts.map +1 -0
  4. package/dist/background/index.js +25 -0
  5. package/dist/background/index.js.map +1 -0
  6. package/dist/background/register.d.ts +38 -0
  7. package/dist/background/register.d.ts.map +1 -0
  8. package/dist/background/register.js +75 -0
  9. package/dist/background/register.js.map +1 -0
  10. package/dist/dev-reload.d.ts +21 -0
  11. package/dist/dev-reload.d.ts.map +1 -0
  12. package/dist/dev-reload.js +62 -0
  13. package/dist/dev-reload.js.map +1 -0
  14. package/dist/envelope.d.ts +23 -0
  15. package/dist/envelope.d.ts.map +1 -0
  16. package/dist/envelope.js +47 -0
  17. package/dist/envelope.js.map +1 -0
  18. package/dist/globals.d.ts +54 -0
  19. package/dist/globals.d.ts.map +1 -0
  20. package/dist/globals.js +12 -0
  21. package/dist/globals.js.map +1 -0
  22. package/dist/index.d.ts +59 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +31 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/modules/actions.d.ts +54 -0
  27. package/dist/modules/actions.d.ts.map +1 -0
  28. package/dist/modules/actions.js +122 -0
  29. package/dist/modules/actions.js.map +1 -0
  30. package/dist/modules/auth.d.ts +25 -0
  31. package/dist/modules/auth.d.ts.map +1 -0
  32. package/dist/modules/auth.js +52 -0
  33. package/dist/modules/auth.js.map +1 -0
  34. package/dist/modules/base64.d.ts +16 -0
  35. package/dist/modules/base64.d.ts.map +1 -0
  36. package/dist/modules/base64.js +92 -0
  37. package/dist/modules/base64.js.map +1 -0
  38. package/dist/modules/blob.d.ts +161 -0
  39. package/dist/modules/blob.d.ts.map +1 -0
  40. package/dist/modules/blob.js +277 -0
  41. package/dist/modules/blob.js.map +1 -0
  42. package/dist/modules/camera.d.ts +170 -0
  43. package/dist/modules/camera.d.ts.map +1 -0
  44. package/dist/modules/camera.js +116 -0
  45. package/dist/modules/camera.js.map +1 -0
  46. package/dist/modules/cloud.d.ts +34 -0
  47. package/dist/modules/cloud.d.ts.map +1 -0
  48. package/dist/modules/cloud.js +70 -0
  49. package/dist/modules/cloud.js.map +1 -0
  50. package/dist/modules/dashboard.d.ts +16 -0
  51. package/dist/modules/dashboard.d.ts.map +1 -0
  52. package/dist/modules/dashboard.js +27 -0
  53. package/dist/modules/dashboard.js.map +1 -0
  54. package/dist/modules/display.d.ts +106 -0
  55. package/dist/modules/display.d.ts.map +1 -0
  56. package/dist/modules/display.js +57 -0
  57. package/dist/modules/display.js.map +1 -0
  58. package/dist/modules/events.d.ts +160 -0
  59. package/dist/modules/events.d.ts.map +1 -0
  60. package/dist/modules/events.js +168 -0
  61. package/dist/modules/events.js.map +1 -0
  62. package/dist/modules/glasses.d.ts +28 -0
  63. package/dist/modules/glasses.d.ts.map +1 -0
  64. package/dist/modules/glasses.js +39 -0
  65. package/dist/modules/glasses.js.map +1 -0
  66. package/dist/modules/heading.d.ts +16 -0
  67. package/dist/modules/heading.d.ts.map +1 -0
  68. package/dist/modules/heading.js +20 -0
  69. package/dist/modules/heading.js.map +1 -0
  70. package/dist/modules/imu.d.ts +31 -0
  71. package/dist/modules/imu.d.ts.map +1 -0
  72. package/dist/modules/imu.js +40 -0
  73. package/dist/modules/imu.js.map +1 -0
  74. package/dist/modules/input.d.ts +34 -0
  75. package/dist/modules/input.d.ts.map +1 -0
  76. package/dist/modules/input.js +50 -0
  77. package/dist/modules/input.js.map +1 -0
  78. package/dist/modules/led.d.ts +35 -0
  79. package/dist/modules/led.d.ts.map +1 -0
  80. package/dist/modules/led.js +39 -0
  81. package/dist/modules/led.js.map +1 -0
  82. package/dist/modules/location.d.ts +27 -0
  83. package/dist/modules/location.d.ts.map +1 -0
  84. package/dist/modules/location.js +35 -0
  85. package/dist/modules/location.js.map +1 -0
  86. package/dist/modules/mic.d.ts +44 -0
  87. package/dist/modules/mic.d.ts.map +1 -0
  88. package/dist/modules/mic.js +67 -0
  89. package/dist/modules/mic.js.map +1 -0
  90. package/dist/modules/miniapps.d.ts +72 -0
  91. package/dist/modules/miniapps.d.ts.map +1 -0
  92. package/dist/modules/miniapps.js +48 -0
  93. package/dist/modules/miniapps.js.map +1 -0
  94. package/dist/modules/navigation.d.ts +326 -0
  95. package/dist/modules/navigation.d.ts.map +1 -0
  96. package/dist/modules/navigation.js +386 -0
  97. package/dist/modules/navigation.js.map +1 -0
  98. package/dist/modules/permissions.d.ts +58 -0
  99. package/dist/modules/permissions.d.ts.map +1 -0
  100. package/dist/modules/permissions.js +66 -0
  101. package/dist/modules/permissions.js.map +1 -0
  102. package/dist/modules/phone.d.ts +95 -0
  103. package/dist/modules/phone.d.ts.map +1 -0
  104. package/dist/modules/phone.js +110 -0
  105. package/dist/modules/phone.js.map +1 -0
  106. package/dist/modules/pivots/engine.d.ts +169 -0
  107. package/dist/modules/pivots/engine.d.ts.map +1 -0
  108. package/dist/modules/pivots/engine.js +779 -0
  109. package/dist/modules/pivots/engine.js.map +1 -0
  110. package/dist/modules/pivots/geometry.d.ts +84 -0
  111. package/dist/modules/pivots/geometry.d.ts.map +1 -0
  112. package/dist/modules/pivots/geometry.js +371 -0
  113. package/dist/modules/pivots/geometry.js.map +1 -0
  114. package/dist/modules/pivots/instructions.d.ts +139 -0
  115. package/dist/modules/pivots/instructions.d.ts.map +1 -0
  116. package/dist/modules/pivots/instructions.js +280 -0
  117. package/dist/modules/pivots/instructions.js.map +1 -0
  118. package/dist/modules/speaker.d.ts +169 -0
  119. package/dist/modules/speaker.d.ts.map +1 -0
  120. package/dist/modules/speaker.js +251 -0
  121. package/dist/modules/speaker.js.map +1 -0
  122. package/dist/modules/storage.d.ts +50 -0
  123. package/dist/modules/storage.d.ts.map +1 -0
  124. package/dist/modules/storage.js +99 -0
  125. package/dist/modules/storage.js.map +1 -0
  126. package/dist/modules/stream.d.ts +129 -0
  127. package/dist/modules/stream.d.ts.map +1 -0
  128. package/dist/modules/stream.js +52 -0
  129. package/dist/modules/stream.js.map +1 -0
  130. package/dist/modules/system.d.ts +45 -0
  131. package/dist/modules/system.d.ts.map +1 -0
  132. package/dist/modules/system.js +42 -0
  133. package/dist/modules/system.js.map +1 -0
  134. package/dist/modules/transcription.d.ts +75 -0
  135. package/dist/modules/transcription.d.ts.map +1 -0
  136. package/dist/modules/transcription.js +106 -0
  137. package/dist/modules/transcription.js.map +1 -0
  138. package/dist/modules/translation.d.ts +59 -0
  139. package/dist/modules/translation.d.ts.map +1 -0
  140. package/dist/modules/translation.js +119 -0
  141. package/dist/modules/translation.js.map +1 -0
  142. package/dist/modules/ui.d.ts +186 -0
  143. package/dist/modules/ui.d.ts.map +1 -0
  144. package/dist/modules/ui.js +293 -0
  145. package/dist/modules/ui.js.map +1 -0
  146. package/dist/protocol.d.ts +299 -0
  147. package/dist/protocol.d.ts.map +1 -0
  148. package/dist/protocol.js +325 -0
  149. package/dist/protocol.js.map +1 -0
  150. package/dist/react/MentraProvider.d.ts +33 -0
  151. package/dist/react/MentraProvider.d.ts.map +1 -0
  152. package/dist/react/MentraProvider.js +15 -0
  153. package/dist/react/MentraProvider.js.map +1 -0
  154. package/dist/react/MiniappHeader.d.ts +44 -0
  155. package/dist/react/MiniappHeader.d.ts.map +1 -0
  156. package/dist/react/MiniappHeader.js +32 -0
  157. package/dist/react/MiniappHeader.js.map +1 -0
  158. package/dist/react/index.d.ts +13 -0
  159. package/dist/react/index.d.ts.map +1 -0
  160. package/dist/react/index.js +9 -0
  161. package/dist/react/index.js.map +1 -0
  162. package/dist/react/useCapabilities.d.ts +26 -0
  163. package/dist/react/useCapabilities.d.ts.map +1 -0
  164. package/dist/react/useCapabilities.js +27 -0
  165. package/dist/react/useCapabilities.js.map +1 -0
  166. package/dist/react/useCapsuleHeaderStyle.d.ts +27 -0
  167. package/dist/react/useCapsuleHeaderStyle.d.ts.map +1 -0
  168. package/dist/react/useCapsuleHeaderStyle.js +36 -0
  169. package/dist/react/useCapsuleHeaderStyle.js.map +1 -0
  170. package/dist/react/useColorScheme.d.ts +18 -0
  171. package/dist/react/useColorScheme.d.ts.map +1 -0
  172. package/dist/react/useColorScheme.js +24 -0
  173. package/dist/react/useColorScheme.js.map +1 -0
  174. package/dist/react/useConnected.d.ts +17 -0
  175. package/dist/react/useConnected.d.ts.map +1 -0
  176. package/dist/react/useConnected.js +39 -0
  177. package/dist/react/useConnected.js.map +1 -0
  178. package/dist/react/useRpc.d.ts +29 -0
  179. package/dist/react/useRpc.d.ts.map +1 -0
  180. package/dist/react/useRpc.js +82 -0
  181. package/dist/react/useRpc.js.map +1 -0
  182. package/dist/react/useSafeArea.d.ts +21 -0
  183. package/dist/react/useSafeArea.d.ts.map +1 -0
  184. package/dist/react/useSafeArea.js +22 -0
  185. package/dist/react/useSafeArea.js.map +1 -0
  186. package/dist/session.d.ts +312 -0
  187. package/dist/session.d.ts.map +1 -0
  188. package/dist/session.js +653 -0
  189. package/dist/session.js.map +1 -0
  190. package/dist/sub-path-types.test-d.d.ts +28 -0
  191. package/dist/sub-path-types.test-d.d.ts.map +1 -0
  192. package/dist/sub-path-types.test-d.js +37 -0
  193. package/dist/sub-path-types.test-d.js.map +1 -0
  194. package/dist/transport/auto.d.ts +35 -0
  195. package/dist/transport/auto.d.ts.map +1 -0
  196. package/dist/transport/auto.js +125 -0
  197. package/dist/transport/auto.js.map +1 -0
  198. package/dist/transport/dispatch.d.ts +43 -0
  199. package/dist/transport/dispatch.d.ts.map +1 -0
  200. package/dist/transport/dispatch.js +91 -0
  201. package/dist/transport/dispatch.js.map +1 -0
  202. package/dist/transport/local-socket.d.ts +28 -0
  203. package/dist/transport/local-socket.d.ts.map +1 -0
  204. package/dist/transport/local-socket.js +80 -0
  205. package/dist/transport/local-socket.js.map +1 -0
  206. package/dist/transport/mock.d.ts +54 -0
  207. package/dist/transport/mock.d.ts.map +1 -0
  208. package/dist/transport/mock.js +207 -0
  209. package/dist/transport/mock.js.map +1 -0
  210. package/dist/transport/postmessage.d.ts +36 -0
  211. package/dist/transport/postmessage.d.ts.map +1 -0
  212. package/dist/transport/postmessage.js +78 -0
  213. package/dist/transport/postmessage.js.map +1 -0
  214. package/dist/transport/types.d.ts +25 -0
  215. package/dist/transport/types.d.ts.map +1 -0
  216. package/dist/transport/types.js +11 -0
  217. package/dist/transport/types.js.map +1 -0
  218. package/dist/ui/index.d.ts +98 -0
  219. package/dist/ui/index.d.ts.map +1 -0
  220. package/dist/ui/index.js +32 -0
  221. package/dist/ui/index.js.map +1 -0
  222. package/package.json +84 -0
  223. package/src/background/index.ts +152 -0
  224. package/src/background/register.ts +85 -0
  225. package/src/dev-reload.ts +60 -0
  226. package/src/envelope.ts +54 -0
  227. package/src/globals.ts +62 -0
  228. package/src/index.ts +171 -0
  229. package/src/modules/actions.ts +156 -0
  230. package/src/modules/auth.ts +68 -0
  231. package/src/modules/base64.ts +87 -0
  232. package/src/modules/blob.ts +341 -0
  233. package/src/modules/camera.ts +242 -0
  234. package/src/modules/cloud.ts +94 -0
  235. package/src/modules/dashboard.ts +31 -0
  236. package/src/modules/display.ts +122 -0
  237. package/src/modules/events.ts +291 -0
  238. package/src/modules/glasses.ts +44 -0
  239. package/src/modules/heading.ts +23 -0
  240. package/src/modules/imu.ts +44 -0
  241. package/src/modules/input.ts +71 -0
  242. package/src/modules/led.ts +61 -0
  243. package/src/modules/location.ts +39 -0
  244. package/src/modules/mic.ts +74 -0
  245. package/src/modules/miniapps.ts +90 -0
  246. package/src/modules/navigation.ts +670 -0
  247. package/src/modules/permissions.ts +84 -0
  248. package/src/modules/phone.ts +155 -0
  249. package/src/modules/pivots/engine.ts +863 -0
  250. package/src/modules/pivots/geometry.ts +409 -0
  251. package/src/modules/pivots/instructions.ts +343 -0
  252. package/src/modules/speaker.ts +335 -0
  253. package/src/modules/storage.ts +109 -0
  254. package/src/modules/stream.ts +163 -0
  255. package/src/modules/system.ts +76 -0
  256. package/src/modules/transcription.ts +145 -0
  257. package/src/modules/translation.ts +149 -0
  258. package/src/modules/ui.ts +498 -0
  259. package/src/protocol.ts +378 -0
  260. package/src/react/MentraProvider.tsx +50 -0
  261. package/src/react/MiniappHeader.tsx +122 -0
  262. package/src/react/index.ts +12 -0
  263. package/src/react/useCapabilities.ts +29 -0
  264. package/src/react/useCapsuleHeaderStyle.ts +54 -0
  265. package/src/react/useColorScheme.ts +26 -0
  266. package/src/react/useConnected.ts +47 -0
  267. package/src/react/useRpc.ts +106 -0
  268. package/src/react/useSafeArea.ts +40 -0
  269. package/src/session.ts +886 -0
  270. package/src/sub-path-types.test-d.ts +49 -0
  271. package/src/transport/auto.ts +143 -0
  272. package/src/transport/dispatch.ts +106 -0
  273. package/src/transport/local-socket.ts +93 -0
  274. package/src/transport/mock.ts +251 -0
  275. package/src/transport/postmessage.ts +95 -0
  276. package/src/transport/types.ts +26 -0
  277. package/src/ui/index.ts +122 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @fileoverview Routes-API instruction parsing helpers for the pivot
3
+ * engine.
4
+ *
5
+ * The Routes REST API returns per-step `navigationInstruction.instructions`
6
+ * strings like "Turn left onto Octavia Blvd" that unambiguously name the
7
+ * road being entered. Parsing these is the most reliable way to label a
8
+ * turn — more reliable than `StepInfo.road` from the Android Nav SDK,
9
+ * which lags one step behind for the live currentStep + uses a destination
10
+ * placeholder for the arrival leg.
11
+ *
12
+ * Originally lived in the Navigation miniapp's NavigationPage.tsx; moved
13
+ * into the SDK so every miniapp using `navigation.onPivot()` gets the
14
+ * same accurate road names without having to re-implement the parser.
15
+ *
16
+ * All helpers are pure — no side effects, no shared state.
17
+ */
18
+ import type { LatLng, ManeuverKind } from "../navigation";
19
+ /**
20
+ * Pull a short road name out of a Routes-API instruction string.
21
+ * Instructions look like:
22
+ * "Turn left onto Octavia Blvd"
23
+ * "Slight right onto Octavia St"
24
+ * "Turn right onto Haight St\nDestination will be on the right"
25
+ *
26
+ * Returns null when the parse fails (e.g. instruction is missing, the
27
+ * "onto" clause is absent, or the captured text contains another
28
+ * maneuver verb / direction word). Callers treat null as "no road
29
+ * name available" and hide the label.
30
+ *
31
+ * Only "onto" is matched here — `\bon\b` was previously used as a
32
+ * fallback for depart steps ("Head north on Market St") but it
33
+ * over-matched, capturing maneuver verbs from stacked instructions
34
+ * ("Turn left, then turn right on..."). The depart step is the
35
+ * very first step and is dropped by the slice(0, -1) loop before
36
+ * this parser is called anyway, so removing the fallback costs us
37
+ * nothing real.
38
+ */
39
+ export declare function roadNameFromInstruction(instruction?: string | null): string | null;
40
+ export declare function sameRoad(a: string, b: string): boolean;
41
+ /**
42
+ * Coarse left/right classifier for a maneuver string. Collapses all
43
+ * left variants (TURN_LEFT, SLIGHT_LEFT, SHARP_LEFT, UTURN_LEFT) to
44
+ * "left" and the rights to "right". Returns null when the maneuver
45
+ * isn't a directional turn (STRAIGHT, NAME_CHANGE, ARRIVE, etc.).
46
+ */
47
+ export declare function turnDirection(maneuver?: string | null): "left" | "right" | null;
48
+ export declare function bendAngleAt(points: LatLng[], junction: LatLng): number | null;
49
+ /**
50
+ * Signed polyline bend at a junction. Positive = right turn, negative
51
+ * = left turn, in [-180, 180]. Used to pick a pivot's left/right
52
+ * direction from geometry when the Routes API's first-step maneuver
53
+ * is misleading (e.g. a curb-alignment micro-jog labeled TURN_LEFT
54
+ * right before the real right turn off Hayes onto Gough).
55
+ */
56
+ export declare function signedBendAt(points: LatLng[], junction: LatLng): number | null;
57
+ /**
58
+ * Minimum bend (degrees) for a junction to count as a real turn worth
59
+ * a pivot. Below this the route is effectively straight through the
60
+ * point; the Routes API may name it as a turn for legal/lane reasons
61
+ * but the user perceives no direction change.
62
+ */
63
+ export declare const MIN_TURN_ANGLE_DEG = 30;
64
+ /**
65
+ * Shape of a Routes-API computed step (matches `ComputedRouteStep` in
66
+ * the navigation module). Re-declared locally to avoid a circular
67
+ * import; the field set this helper needs is small.
68
+ */
69
+ type ComputedStep = {
70
+ lat: number;
71
+ lng: number;
72
+ endLat: number;
73
+ endLng: number;
74
+ distanceMeters: number;
75
+ maneuver?: ManeuverKind;
76
+ instruction?: string;
77
+ /**
78
+ * Pre-resolved road name from the host's hybrid resolver (Phase 1).
79
+ * Preferred over `instruction` parsing when present — same parser
80
+ * runs host-side, just earlier in the pipeline. Older callers that
81
+ * only pass `instruction` still work via the parse fallback below.
82
+ */
83
+ road?: string | null;
84
+ };
85
+ /**
86
+ * A pivot derived from Routes-API computed steps. Carries the
87
+ * instruction-parsed road labels alongside the geometric corner,
88
+ * ready to be merged into the SDK's `Pivot` shape by the caller.
89
+ *
90
+ * `fromRoad` is required — we drop pivots whose entry road couldn't
91
+ * be parsed. `toRoad` is nullable because the LAST real turn before
92
+ * arrival has a known fromRoad but unknown toRoad (the arrival
93
+ * step's instruction is "Destination will be on the right", no road
94
+ * name in it). The pivot engine treats those nulls as candidates
95
+ * for the reverse-geocode fallback.
96
+ */
97
+ export type InstructionPivot = {
98
+ lat: number;
99
+ lng: number;
100
+ fromRoad: string;
101
+ toRoad: string | null;
102
+ direction: "left" | "right" | null;
103
+ maneuver: ManeuverKind;
104
+ };
105
+ /**
106
+ * Derive accurate turn pivots from a Routes-API step list. Each
107
+ * computed step's `instruction` describes the maneuver that BEGINS
108
+ * that step ("Turn left onto Guerrero St"). A pivot sits at
109
+ * `step[i].end` — the junction where you leave step[i]'s road and
110
+ * turn onto step[i+1]'s road. So for each pair (step[i], step[i+1]):
111
+ *
112
+ * fromRoad = roadNameFromInstruction(step[i].instruction)
113
+ * toRoad = roadNameFromInstruction(step[i+1].instruction)
114
+ * anchor = (step[i].endLat, step[i].endLng)
115
+ * maneuver = step[i+1].maneuver (the actual turn type at the corner)
116
+ *
117
+ * A pivot survives these filters:
118
+ * 1. `fromRoad` parsed successfully from step[i]'s instruction.
119
+ * Drop the pivot otherwise — we don't know the road the user
120
+ * came from, and labeling the dot wouldn't make sense.
121
+ * 2. If `toRoad` parsed too, it must differ from `fromRoad` (drop
122
+ * "stay on same road" jogs). If `toRoad` failed to parse, keep
123
+ * the pivot with `toRoad: null` so the engine can recover it
124
+ * via reverse-geocode. This is what keeps the LAST real turn
125
+ * before arrival in the list — that step's instruction is
126
+ * "Destination will be on the right" with no road name, but
127
+ * the turn itself is real and the user needs to see it.
128
+ * 3. The drawn polyline bends ≥ MIN_TURN_ANGLE_DEG at the junction
129
+ * (drops phantom turns where the geometry is visually straight).
130
+ *
131
+ * The LAST step (the arrival leg itself) is dropped by slice(0, -1) —
132
+ * the destination isn't a turn.
133
+ *
134
+ * Mirrors the Navigation miniapp's preview-turn extraction so live
135
+ * pivots match preview accuracy.
136
+ */
137
+ export declare function extractPivotsFromComputedSteps(steps: ComputedStep[] | undefined, polyline: LatLng[]): InstructionPivot[];
138
+ export {};
139
+ //# sourceMappingURL=instructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../../src/modules/pivots/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAE,YAAY,EAAC,MAAM,eAAe,CAAA;AAqBvD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CA0BlF;AAaD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAM/E;AAgBD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG7E;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmB9E;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC;;;;GAIG;AACH,KAAK,YAAY,GAAG;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB,CAAA;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;IAClC,QAAQ,EAAE,YAAY,CAAA;CACvB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,YAAY,EAAE,GAAG,SAAS,EACjC,QAAQ,EAAE,MAAM,EAAE,GACjB,gBAAgB,EAAE,CA6FpB"}
@@ -0,0 +1,280 @@
1
+ /**
2
+ * @fileoverview Routes-API instruction parsing helpers for the pivot
3
+ * engine.
4
+ *
5
+ * The Routes REST API returns per-step `navigationInstruction.instructions`
6
+ * strings like "Turn left onto Octavia Blvd" that unambiguously name the
7
+ * road being entered. Parsing these is the most reliable way to label a
8
+ * turn — more reliable than `StepInfo.road` from the Android Nav SDK,
9
+ * which lags one step behind for the live currentStep + uses a destination
10
+ * placeholder for the arrival leg.
11
+ *
12
+ * Originally lived in the Navigation miniapp's NavigationPage.tsx; moved
13
+ * into the SDK so every miniapp using `navigation.onPivot()` gets the
14
+ * same accurate road names without having to re-implement the parser.
15
+ *
16
+ * All helpers are pure — no side effects, no shared state.
17
+ */
18
+ import { bearingDeg, haversineMeters, signedAngleDiff } from "./geometry";
19
+ /**
20
+ * Bare direction words that can sneak through the regex when the
21
+ * instruction's "onto"/"on" clause is followed directly by a bare
22
+ * direction ("Head south on the right"). Reject these so the parser
23
+ * returns null and the caller can hide the label instead of showing
24
+ * "the right" as a road name.
25
+ */
26
+ const DIRECTION_WORDS = new Set(["right", "left", "the right", "the left", "north", "south", "east", "west"]);
27
+ /**
28
+ * Maneuver verbs that appear in stacked instructions like
29
+ * "Turn left, then turn right onto Linden St" or fragments like
30
+ * "Continue onto Octavia". If a captured "road" contains any of
31
+ * these, it's not a road name — it's leftover instruction syntax
32
+ * from a multi-clause sentence. The parser rejects the capture.
33
+ */
34
+ const MANEUVER_VERBS = /\b(turn|slight|sharp|continue|head|merge|exit|uturn|u-turn|then|keep)\b/i;
35
+ /**
36
+ * Pull a short road name out of a Routes-API instruction string.
37
+ * Instructions look like:
38
+ * "Turn left onto Octavia Blvd"
39
+ * "Slight right onto Octavia St"
40
+ * "Turn right onto Haight St\nDestination will be on the right"
41
+ *
42
+ * Returns null when the parse fails (e.g. instruction is missing, the
43
+ * "onto" clause is absent, or the captured text contains another
44
+ * maneuver verb / direction word). Callers treat null as "no road
45
+ * name available" and hide the label.
46
+ *
47
+ * Only "onto" is matched here — `\bon\b` was previously used as a
48
+ * fallback for depart steps ("Head north on Market St") but it
49
+ * over-matched, capturing maneuver verbs from stacked instructions
50
+ * ("Turn left, then turn right on..."). The depart step is the
51
+ * very first step and is dropped by the slice(0, -1) loop before
52
+ * this parser is called anyway, so removing the fallback costs us
53
+ * nothing real.
54
+ */
55
+ export function roadNameFromInstruction(instruction) {
56
+ if (!instruction)
57
+ return null;
58
+ // Only the first line — drop "Destination will be on the right" etc.
59
+ const firstLine = instruction.split("\n")[0] ?? "";
60
+ // Match "onto ROAD" only. The "on" fallback was removed because it
61
+ // matched inside maneuver fragments and captured verbs as fake road
62
+ // names. Real turn instructions always use "onto"; depart steps
63
+ // (the only legitimate "on" users) aren't passed through this path.
64
+ const m = firstLine.match(/\bonto\s+(.+)$/i);
65
+ let raw = (m ? m[1] : "").trim();
66
+ if (!raw)
67
+ return null;
68
+ // A captured road name never contains a comma. If it does, we
69
+ // grabbed the rest of a multi-clause sentence by mistake.
70
+ if (raw.includes(","))
71
+ return null;
72
+ // Cut trailing direction/continuation clauses the API appends.
73
+ raw = raw.split(/\s+(?:toward|towards|to|and|then|for)\b/i)[0]?.trim() ?? "";
74
+ // Strip a unit/suite suffix ("Hayes St #116" → "Hayes St").
75
+ raw = raw.replace(/\s+#.*$/, "").trim();
76
+ if (!raw)
77
+ return null;
78
+ // Reject a bare direction word that slipped through.
79
+ if (DIRECTION_WORDS.has(raw.toLowerCase()))
80
+ return null;
81
+ // Reject anything containing maneuver verbs — the regex caught the
82
+ // wrong clause of a stacked instruction. Real road names don't
83
+ // contain "turn", "slight", "continue", etc.
84
+ if (MANEUVER_VERBS.test(raw))
85
+ return null;
86
+ return raw;
87
+ }
88
+ /**
89
+ * Equality check for road names that's robust to suffix variation
90
+ * ("Gough St" vs "Gough Street"), case, and trailing punctuation.
91
+ * Used to drop "stay on the same road" turns where the routes API
92
+ * names a turn but the user perceives a single continuous street.
93
+ */
94
+ const ROAD_SUFFIXES = /\b(st|street|ave|avenue|blvd|boulevard|rd|road|dr|drive|ln|lane|way|ct|court|pl|place|ter|terrace|hwy|highway)\b\.?/g;
95
+ function normalizeRoad(road) {
96
+ return road.toLowerCase().replace(ROAD_SUFFIXES, "").replace(/[^a-z0-9 ]/g, "").replace(/\s+/g, " ").trim();
97
+ }
98
+ export function sameRoad(a, b) {
99
+ return normalizeRoad(a) === normalizeRoad(b);
100
+ }
101
+ /**
102
+ * Coarse left/right classifier for a maneuver string. Collapses all
103
+ * left variants (TURN_LEFT, SLIGHT_LEFT, SHARP_LEFT, UTURN_LEFT) to
104
+ * "left" and the rights to "right". Returns null when the maneuver
105
+ * isn't a directional turn (STRAIGHT, NAME_CHANGE, ARRIVE, etc.).
106
+ */
107
+ export function turnDirection(maneuver) {
108
+ if (!maneuver)
109
+ return null;
110
+ const m = maneuver.toUpperCase();
111
+ if (m.includes("LEFT"))
112
+ return "left";
113
+ if (m.includes("RIGHT"))
114
+ return "right";
115
+ return null;
116
+ }
117
+ /**
118
+ * How sharply the polyline actually bends at a given junction, in
119
+ * degrees [0, 180]. Probe walks outward from the polyline point
120
+ * nearest the junction until ~PROBE_METERS in each direction, then
121
+ * compares incoming vs outgoing bearings. The wide probe (22m) is
122
+ * deliberate: real street corners are rounded over 20–30m and a
123
+ * tighter probe under-reports the angle.
124
+ *
125
+ * Used to drop phantom turns the Routes API names at complex
126
+ * interchanges (Market → Gough → Market) where the drawn polyline is
127
+ * visually one straight line. Returns null when the polyline is too
128
+ * short to measure.
129
+ */
130
+ const PROBE_METERS = 22;
131
+ export function bendAngleAt(points, junction) {
132
+ const signed = signedBendAt(points, junction);
133
+ return signed == null ? null : Math.abs(signed);
134
+ }
135
+ /**
136
+ * Signed polyline bend at a junction. Positive = right turn, negative
137
+ * = left turn, in [-180, 180]. Used to pick a pivot's left/right
138
+ * direction from geometry when the Routes API's first-step maneuver
139
+ * is misleading (e.g. a curb-alignment micro-jog labeled TURN_LEFT
140
+ * right before the real right turn off Hayes onto Gough).
141
+ */
142
+ export function signedBendAt(points, junction) {
143
+ if (points.length < 3)
144
+ return null;
145
+ let mid = 0;
146
+ let bestDist = Infinity;
147
+ for (let i = 0; i < points.length; i++) {
148
+ const d = haversineMeters(points[i], junction);
149
+ if (d < bestDist) {
150
+ bestDist = d;
151
+ mid = i;
152
+ }
153
+ }
154
+ let before = mid;
155
+ while (before > 0 && haversineMeters(points[before], points[mid]) < PROBE_METERS)
156
+ before--;
157
+ let after = mid;
158
+ while (after < points.length - 1 && haversineMeters(points[after], points[mid]) < PROBE_METERS)
159
+ after++;
160
+ if (before === mid || after === mid)
161
+ return null;
162
+ const incoming = bearingDeg(points[before], points[mid]);
163
+ const outgoing = bearingDeg(points[mid], points[after]);
164
+ return signedAngleDiff(outgoing, incoming);
165
+ }
166
+ /**
167
+ * Minimum bend (degrees) for a junction to count as a real turn worth
168
+ * a pivot. Below this the route is effectively straight through the
169
+ * point; the Routes API may name it as a turn for legal/lane reasons
170
+ * but the user perceives no direction change.
171
+ */
172
+ export const MIN_TURN_ANGLE_DEG = 30;
173
+ /**
174
+ * Derive accurate turn pivots from a Routes-API step list. Each
175
+ * computed step's `instruction` describes the maneuver that BEGINS
176
+ * that step ("Turn left onto Guerrero St"). A pivot sits at
177
+ * `step[i].end` — the junction where you leave step[i]'s road and
178
+ * turn onto step[i+1]'s road. So for each pair (step[i], step[i+1]):
179
+ *
180
+ * fromRoad = roadNameFromInstruction(step[i].instruction)
181
+ * toRoad = roadNameFromInstruction(step[i+1].instruction)
182
+ * anchor = (step[i].endLat, step[i].endLng)
183
+ * maneuver = step[i+1].maneuver (the actual turn type at the corner)
184
+ *
185
+ * A pivot survives these filters:
186
+ * 1. `fromRoad` parsed successfully from step[i]'s instruction.
187
+ * Drop the pivot otherwise — we don't know the road the user
188
+ * came from, and labeling the dot wouldn't make sense.
189
+ * 2. If `toRoad` parsed too, it must differ from `fromRoad` (drop
190
+ * "stay on same road" jogs). If `toRoad` failed to parse, keep
191
+ * the pivot with `toRoad: null` so the engine can recover it
192
+ * via reverse-geocode. This is what keeps the LAST real turn
193
+ * before arrival in the list — that step's instruction is
194
+ * "Destination will be on the right" with no road name, but
195
+ * the turn itself is real and the user needs to see it.
196
+ * 3. The drawn polyline bends ≥ MIN_TURN_ANGLE_DEG at the junction
197
+ * (drops phantom turns where the geometry is visually straight).
198
+ *
199
+ * The LAST step (the arrival leg itself) is dropped by slice(0, -1) —
200
+ * the destination isn't a turn.
201
+ *
202
+ * Mirrors the Navigation miniapp's preview-turn extraction so live
203
+ * pivots match preview accuracy.
204
+ */
205
+ export function extractPivotsFromComputedSteps(steps, polyline) {
206
+ if (!steps || steps.length < 2)
207
+ return [];
208
+ const initial = steps.map((s, i) => ({
209
+ stepIdx: i,
210
+ name: s.road ?? roadNameFromInstruction(s.instruction),
211
+ }));
212
+ let annotated = initial;
213
+ for (let pass = 0; pass < 4; pass++) {
214
+ const collapsed = [];
215
+ for (let i = 0; i < annotated.length; i++) {
216
+ const prev = collapsed[collapsed.length - 1];
217
+ const here = annotated[i];
218
+ const next = annotated[i + 1];
219
+ if (prev?.name &&
220
+ next?.name &&
221
+ here.name &&
222
+ !sameRoad(prev.name, here.name) &&
223
+ sameRoad(prev.name, next.name)) {
224
+ // `here` is a sandwiched micro-jog — drop it. `next` will be
225
+ // collapsed against `prev` on the same-road check below.
226
+ continue;
227
+ }
228
+ collapsed.push(here);
229
+ }
230
+ if (collapsed.length === annotated.length)
231
+ break;
232
+ annotated = collapsed;
233
+ }
234
+ const out = [];
235
+ // Pair each annotated entry with the next one. Note we no longer
236
+ // walk `steps` directly — `annotated` is the collapsed view, so
237
+ // adjacent annotated entries already represent the real road
238
+ // transitions. `stepIdx` is preserved so we can still look up the
239
+ // junction coordinates and maneuver from the original `steps` array.
240
+ for (let i = 0; i < annotated.length - 1; i++) {
241
+ const here = annotated[i];
242
+ const nextAnn = annotated[i + 1];
243
+ const s = steps[here.stepIdx];
244
+ const next = steps[nextAnn.stepIdx];
245
+ const fromRoad = here.name;
246
+ const toRoad = nextAnn.name;
247
+ // fromRoad is required; toRoad is nullable (last-turn-before-arrival
248
+ // has no road name in the arrival step's instruction).
249
+ if (!fromRoad)
250
+ continue;
251
+ if (toRoad && sameRoad(fromRoad, toRoad))
252
+ continue;
253
+ if (!Number.isFinite(s.endLat) || !Number.isFinite(s.endLng))
254
+ continue;
255
+ const junction = { lat: s.endLat, lng: s.endLng };
256
+ const signedBend = signedBendAt(polyline, junction);
257
+ // No polyline to measure → keep (don't drop a labeled turn just
258
+ // because geometry was unavailable).
259
+ if (signedBend != null && Math.abs(signedBend) < MIN_TURN_ANGLE_DEG)
260
+ continue;
261
+ // Direction precedence: trust polyline geometry first. The Routes
262
+ // API decomposes some junctions into micro-step jogs whose first
263
+ // step's maneuver lies (e.g. a "TURN_LEFT" curb-alignment jog
264
+ // right before the actual right turn off Hayes onto Gough).
265
+ // Geometry doesn't lie. Falls back to the maneuver string when
266
+ // the polyline is too short to measure a bend.
267
+ const geomDir = signedBend == null ? null : signedBend > 0 ? "right" : "left";
268
+ const direction = geomDir ?? turnDirection(next.maneuver);
269
+ out.push({
270
+ lat: s.endLat,
271
+ lng: s.endLng,
272
+ fromRoad,
273
+ toRoad,
274
+ direction,
275
+ maneuver: next.maneuver ?? (direction === "left" ? "TURN_LEFT" : "TURN_RIGHT"),
276
+ });
277
+ }
278
+ return out;
279
+ }
280
+ //# sourceMappingURL=instructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../../src/modules/pivots/instructions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * @fileoverview SpeakerModule — phone-side audio output.
3
+ *
4
+ * Mirrors cloud SDK v3's SpeakerManager naming. Audio *input* (transcription,
5
+ * audio chunks, VAD) lives on session.mic — the split is by I/O direction.
6
+ *
7
+ * Imperative surface:
8
+ * speaker.play({audioUrl}) — play an arbitrary URL via the phone's
9
+ * AudioPlaybackService.
10
+ * speaker.speak(text) — send a SPEAK request. Phone streams cloud
11
+ * TTS when connected and falls back to local
12
+ * offline TTS when cloud is unavailable.
13
+ * Resolves when playback completes; rejects
14
+ * with a TTS_* error code on cloud failure.
15
+ * speaker.createStream(opts) — open a live PCM output stream (16-bit LE).
16
+ * Returns a SpeakerStreamWriter: write() PCM
17
+ * chunks as they arrive (e.g. from a WebSocket
18
+ * in the background), close() to drain, abort()
19
+ * to drop. write() resolves with {bufferedMs}
20
+ * so producers can throttle.
21
+ * speaker.stop() — stop any audio this miniapp is playing.
22
+ *
23
+ * State observability:
24
+ * speaker.state — current SpeakerState (sync getter).
25
+ * speaker.isPlaying — true iff state === "playing".
26
+ * speaker.onStateChange(h) — fires on every state transition.
27
+ *
28
+ * State machine (per miniapp):
29
+ * idle ─── speak()/play() ──► loading ──► playing ──► stopped
30
+ * │ │ │
31
+ * └── error ───┴── stop ───┘
32
+ *
33
+ * `error` is transient — fires once with errorCode set, then settles to
34
+ * `stopped` so isPlaying reads false correctly.
35
+ */
36
+ import { MiniappSession } from "../session";
37
+ import type { UnsubscribeFn } from "./events";
38
+ export interface PlayAudioOptions {
39
+ audioUrl: string;
40
+ volume?: number;
41
+ stopOtherAudio?: boolean;
42
+ }
43
+ export interface SpeakOptions {
44
+ voice_id?: string;
45
+ voice_settings?: Record<string, unknown>;
46
+ volume?: number;
47
+ stopOtherAudio?: boolean;
48
+ /**
49
+ * Force this call to use on-device offline TTS, skipping cloud TTS
50
+ * entirely — even when cloud is connected. Rejects with `TTS_LOCAL_UNAVAILABLE`
51
+ * if the offline model isn't ready instead of falling back to cloud.
52
+ */
53
+ forceLocal?: boolean;
54
+ }
55
+ export interface SpeakResult {
56
+ /** True if playback completed; false if playback was interrupted. */
57
+ completed: boolean;
58
+ }
59
+ export type SpeakerState = "idle" | "loading" | "playing" | "stopped" | "error";
60
+ /** Sample rates the native chunk player accepts. */
61
+ export type SpeakerStreamSampleRate = 16000 | 24000 | 48000;
62
+ export interface SpeakerStreamOptions {
63
+ /** PCM sample rate in Hz. Default 16000. */
64
+ sampleRate?: SpeakerStreamSampleRate;
65
+ /** Channel count. v1 is mono only. Default 1. */
66
+ channels?: 1;
67
+ /** Playback volume 0..1. Default 1. */
68
+ volume?: number;
69
+ /** Stop any other audio this miniapp is playing first. Default true. */
70
+ stopOtherAudio?: boolean;
71
+ }
72
+ export interface SpeakerStreamWriteResult {
73
+ /** Milliseconds of audio queued host-side but not yet played. */
74
+ bufferedMs: number;
75
+ }
76
+ /** Raw bytes per bridge write. PCM chunks are small; keep messages snappy. */
77
+ export declare const SPEAKER_WRITE_CHUNK_BYTES: number;
78
+ /**
79
+ * Soft backpressure ceiling. When the host reports more than this buffered,
80
+ * it holds the WRITE reply until the buffer drains below it, so a producer
81
+ * that awaits each write self-throttles to realtime.
82
+ */
83
+ export declare const SPEAKER_STREAM_MAX_BUFFERED_MS = 2000;
84
+ /**
85
+ * Live PCM writer returned by `speaker.createStream()`. 16-bit little-endian
86
+ * PCM only — decode compressed formats (MP3/Opus) server-side before sending.
87
+ *
88
+ * Call `write()` as chunks arrive (each is auto-split into bridge-safe
89
+ * messages), then `close()` to drain and finish, or `abort()` to drop
90
+ * immediately. Shaped like BlobWriter so the two stream APIs feel the same.
91
+ */
92
+ export declare class SpeakerStreamWriter {
93
+ private readonly session;
94
+ readonly streamId: string;
95
+ private settled;
96
+ private writeChain;
97
+ constructor(session: MiniappSession, streamId: string);
98
+ /**
99
+ * Append raw PCM bytes (16-bit LE). Auto-chunked. Resolves with the host's
100
+ * buffered-ms after the last chunk. The host holds the reply while its
101
+ * buffer is above the backpressure ceiling, so a producer that awaits each
102
+ * write self-throttles to realtime.
103
+ */
104
+ write(chunk: Uint8Array | ArrayBuffer): Promise<SpeakerStreamWriteResult>;
105
+ /** Append already-base64-encoded PCM bytes (e.g. relayed straight off a WS frame). */
106
+ writeBase64(b64: string): Promise<SpeakerStreamWriteResult>;
107
+ /** Drain the remaining buffer and finish. Resolves when playback has ended. */
108
+ close(): Promise<{
109
+ durationMs?: number;
110
+ }>;
111
+ /** Stop immediately and drop any buffered audio. Idempotent. */
112
+ abort(): Promise<void>;
113
+ private assertOpen;
114
+ private send;
115
+ /** Preserve PCM ordering even when a producer issues overlapping writes. */
116
+ private enqueueWrite;
117
+ }
118
+ export interface SpeakerStateEvent {
119
+ state: SpeakerState;
120
+ /** When state === "error", the underlying error code (TTS_*, INTERNAL). */
121
+ errorCode?: string;
122
+ errorMessage?: string;
123
+ /** When state === "stopped", how many ms the playback ran (best-effort). */
124
+ durationMs?: number;
125
+ }
126
+ export declare class SpeakerModule {
127
+ private readonly session;
128
+ private _state;
129
+ private _lastEvent;
130
+ constructor(session: MiniappSession);
131
+ /** Current speaker playback state. */
132
+ get state(): SpeakerState;
133
+ /** True iff state === "playing". */
134
+ get isPlaying(): boolean;
135
+ /** Play a URL. Resolves when playback completes on the phone. */
136
+ play(options: PlayAudioOptions): Promise<void>;
137
+ /**
138
+ * Speak text through the phone. The host streams cloud TTS when connected,
139
+ * then falls back to local offline TTS.
140
+ *
141
+ * Rejects with a MiniappRequestError containing a `code` field on cloud-side
142
+ * TTS failures: `TTS_TEXT_TOO_LONG`, `TTS_INVALID_VOICE`, `TTS_UPSTREAM_ERROR`.
143
+ */
144
+ speak(text: string, options?: SpeakOptions): Promise<SpeakResult>;
145
+ /**
146
+ * Open a live PCM output stream (16-bit LE) to the phone's audio playback
147
+ * service. Use from the background to play audio that arrives in chunks
148
+ * (e.g. live meeting audio over a WebSocket) with low latency — unlike
149
+ * `play()`, nothing has to be a file or URL first.
150
+ *
151
+ * One stream per miniapp: opening a second closes the first. Rejects with
152
+ * `NOT_IMPLEMENTED` on hosts that predate streaming. Miniapps that depend on
153
+ * this API must declare `minHostVersion: "2.13.0"` or newer.
154
+ */
155
+ createStream(options?: SpeakerStreamOptions): Promise<SpeakerStreamWriter>;
156
+ /** Stop any audio this miniapp is currently playing. */
157
+ stop(): void;
158
+ /**
159
+ * Subscribe to speaker state transitions. Fires for every change. Does NOT
160
+ * fire immediately with the current value — call `state` separately if you
161
+ * want the seed.
162
+ */
163
+ onStateChange(handler: (event: SpeakerStateEvent) => void): UnsubscribeFn;
164
+ /** @internal — applied by MiniappSession on inbound SPEAKER_STATE envelope. */
165
+ _applyState(event: SpeakerStateEvent): void;
166
+ /** @internal — for tests. */
167
+ _getLastEvent(): SpeakerStateEvent;
168
+ }
169
+ //# sourceMappingURL=speaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speaker.d.ts","sourceRoot":"","sources":["../../src/modules/speaker.ts"],"names":[],"mappings":"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"}