@userflow-tech/react-native 0.4.5 → 0.5.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 (417) hide show
  1. package/README.md +7 -2
  2. package/docs/anchoring.md +6 -1
  3. package/docs/troubleshooting.md +26 -0
  4. package/lib/commonjs/anchors/AutoAnchorRegistry.js +144 -34
  5. package/lib/commonjs/anchors/AutoAnchorRegistry.js.map +1 -1
  6. package/lib/commonjs/anchors/animationFrame.js +29 -1
  7. package/lib/commonjs/anchors/animationFrame.js.map +1 -1
  8. package/lib/commonjs/anchors/appActive.js +61 -0
  9. package/lib/commonjs/anchors/appActive.js.map +1 -0
  10. package/lib/commonjs/anchors/elementConditionEvaluator.js +25 -8
  11. package/lib/commonjs/anchors/elementConditionEvaluator.js.map +1 -1
  12. package/lib/commonjs/anchors/elementTapMonitor.js +67 -12
  13. package/lib/commonjs/anchors/elementTapMonitor.js.map +1 -1
  14. package/lib/commonjs/anchors/elementTargetMatch.js +33 -7
  15. package/lib/commonjs/anchors/elementTargetMatch.js.map +1 -1
  16. package/lib/commonjs/anchors/fiber/fiberInternals.js +66 -0
  17. package/lib/commonjs/anchors/fiber/fiberInternals.js.map +1 -1
  18. package/lib/commonjs/anchors/fiber/indexHostFibers.js +4 -3
  19. package/lib/commonjs/anchors/fiber/indexHostFibers.js.map +1 -1
  20. package/lib/commonjs/anchors/fiber/resolveFiberPath.js +30 -15
  21. package/lib/commonjs/anchors/fiber/resolveFiberPath.js.map +1 -1
  22. package/lib/commonjs/anchors/fiberAnchorCache.js +20 -0
  23. package/lib/commonjs/anchors/fiberAnchorCache.js.map +1 -1
  24. package/lib/commonjs/anchors/gestureHandlerHandoff.js +304 -0
  25. package/lib/commonjs/anchors/gestureHandlerHandoff.js.map +1 -0
  26. package/lib/commonjs/anchors/resolveAnchorHost.js +6 -0
  27. package/lib/commonjs/anchors/resolveAnchorHost.js.map +1 -1
  28. package/lib/commonjs/anchors/resolveAutoAnchorHost.js +11 -6
  29. package/lib/commonjs/anchors/resolveAutoAnchorHost.js.map +1 -1
  30. package/lib/commonjs/anchors/resolveElementTargets.js +63 -8
  31. package/lib/commonjs/anchors/resolveElementTargets.js.map +1 -1
  32. package/lib/commonjs/anchors/semanticMatcher.js +43 -21
  33. package/lib/commonjs/anchors/semanticMatcher.js.map +1 -1
  34. package/lib/commonjs/anchors/tapSlop.js +16 -0
  35. package/lib/commonjs/anchors/tapSlop.js.map +1 -0
  36. package/lib/commonjs/anchors/targetPressMonitor.js +66 -12
  37. package/lib/commonjs/anchors/targetPressMonitor.js.map +1 -1
  38. package/lib/commonjs/authoring/fingerprintFromInstance.js +5 -9
  39. package/lib/commonjs/authoring/fingerprintFromInstance.js.map +1 -1
  40. package/lib/commonjs/content/RichContentRenderer.js +3 -3
  41. package/lib/commonjs/content/RichContentRenderer.js.map +1 -1
  42. package/lib/commonjs/content/openSafeUrl.js +3 -1
  43. package/lib/commonjs/content/openSafeUrl.js.map +1 -1
  44. package/lib/commonjs/expo-router/useUserflowExpoRouter.js +6 -0
  45. package/lib/commonjs/expo-router/useUserflowExpoRouter.js.map +1 -1
  46. package/lib/commonjs/index.js +19 -5
  47. package/lib/commonjs/index.js.map +1 -1
  48. package/lib/commonjs/navigation/navigateUrlUtils.js +105 -0
  49. package/lib/commonjs/navigation/navigateUrlUtils.js.map +1 -0
  50. package/lib/commonjs/navigation/openExternalNavigateUrl.js +14 -0
  51. package/lib/commonjs/navigation/openExternalNavigateUrl.js.map +1 -0
  52. package/lib/commonjs/navigation/openFlowNavigateUrl.js +47 -41
  53. package/lib/commonjs/navigation/openFlowNavigateUrl.js.map +1 -1
  54. package/lib/commonjs/pairing/PairingController.js +3 -1
  55. package/lib/commonjs/pairing/PairingController.js.map +1 -1
  56. package/lib/commonjs/runtime/BannerHost.js +38 -29
  57. package/lib/commonjs/runtime/BannerHost.js.map +1 -1
  58. package/lib/commonjs/runtime/TargetPressBridge.js +55 -7
  59. package/lib/commonjs/runtime/TargetPressBridge.js.map +1 -1
  60. package/lib/commonjs/runtime/UserflowOverlay.js +5 -11
  61. package/lib/commonjs/runtime/UserflowOverlay.js.map +1 -1
  62. package/lib/commonjs/runtime/UserflowProvider.js +66 -40
  63. package/lib/commonjs/runtime/UserflowProvider.js.map +1 -1
  64. package/lib/commonjs/runtime/bannerEngine.js +8 -2
  65. package/lib/commonjs/runtime/bannerEngine.js.map +1 -1
  66. package/lib/commonjs/runtime/bannerFlowSpacer.js +71 -0
  67. package/lib/commonjs/runtime/bannerFlowSpacer.js.map +1 -0
  68. package/lib/commonjs/runtime/flowEngine.js +11 -0
  69. package/lib/commonjs/runtime/flowEngine.js.map +1 -1
  70. package/lib/commonjs/runtime/previewStart.js +39 -0
  71. package/lib/commonjs/runtime/previewStart.js.map +1 -0
  72. package/lib/commonjs/runtime/pushBannerLayoutContext.js +98 -0
  73. package/lib/commonjs/runtime/pushBannerLayoutContext.js.map +1 -0
  74. package/lib/commonjs/screenTracker.js +12 -0
  75. package/lib/commonjs/screenTracker.js.map +1 -1
  76. package/lib/commonjs/surfaces/BannerBar.js +79 -61
  77. package/lib/commonjs/surfaces/BannerBar.js.map +1 -1
  78. package/lib/commonjs/surfaces/BannerSurface.js +26 -0
  79. package/lib/commonjs/surfaces/BannerSurface.js.map +1 -1
  80. package/lib/commonjs/surfaces/TooltipSurface.js +43 -41
  81. package/lib/commonjs/surfaces/TooltipSurface.js.map +1 -1
  82. package/lib/commonjs/surfaces/bannerBarLayout.js +77 -10
  83. package/lib/commonjs/surfaces/bannerBarLayout.js.map +1 -1
  84. package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js +112 -63
  85. package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
  86. package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js +59 -11
  87. package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
  88. package/lib/commonjs/surfaces/hostAccessibilityTrap.js +28 -0
  89. package/lib/commonjs/surfaces/hostAccessibilityTrap.js.map +1 -0
  90. package/lib/commonjs/surfaces/resolveBannerTheme.js +6 -3
  91. package/lib/commonjs/surfaces/resolveBannerTheme.js.map +1 -1
  92. package/lib/commonjs/surfaces/tooltip/TooltipBubble.js +3 -2
  93. package/lib/commonjs/surfaces/tooltip/TooltipBubble.js.map +1 -1
  94. package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js +134 -0
  95. package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
  96. package/lib/commonjs/version.js +1 -1
  97. package/lib/module/anchors/AutoAnchorRegistry.js +145 -35
  98. package/lib/module/anchors/AutoAnchorRegistry.js.map +1 -1
  99. package/lib/module/anchors/animationFrame.js +29 -2
  100. package/lib/module/anchors/animationFrame.js.map +1 -1
  101. package/lib/module/anchors/appActive.js +57 -0
  102. package/lib/module/anchors/appActive.js.map +1 -0
  103. package/lib/module/anchors/elementConditionEvaluator.js +26 -9
  104. package/lib/module/anchors/elementConditionEvaluator.js.map +1 -1
  105. package/lib/module/anchors/elementTapMonitor.js +67 -12
  106. package/lib/module/anchors/elementTapMonitor.js.map +1 -1
  107. package/lib/module/anchors/elementTargetMatch.js +32 -7
  108. package/lib/module/anchors/elementTargetMatch.js.map +1 -1
  109. package/lib/module/anchors/fiber/fiberInternals.js +65 -0
  110. package/lib/module/anchors/fiber/fiberInternals.js.map +1 -1
  111. package/lib/module/anchors/fiber/indexHostFibers.js +4 -3
  112. package/lib/module/anchors/fiber/indexHostFibers.js.map +1 -1
  113. package/lib/module/anchors/fiber/resolveFiberPath.js +30 -15
  114. package/lib/module/anchors/fiber/resolveFiberPath.js.map +1 -1
  115. package/lib/module/anchors/fiberAnchorCache.js +19 -0
  116. package/lib/module/anchors/fiberAnchorCache.js.map +1 -1
  117. package/lib/module/anchors/gestureHandlerHandoff.js +298 -0
  118. package/lib/module/anchors/gestureHandlerHandoff.js.map +1 -0
  119. package/lib/module/anchors/resolveAnchorHost.js +1 -0
  120. package/lib/module/anchors/resolveAnchorHost.js.map +1 -1
  121. package/lib/module/anchors/resolveAutoAnchorHost.js +11 -6
  122. package/lib/module/anchors/resolveAutoAnchorHost.js.map +1 -1
  123. package/lib/module/anchors/resolveElementTargets.js +63 -8
  124. package/lib/module/anchors/resolveElementTargets.js.map +1 -1
  125. package/lib/module/anchors/semanticMatcher.js +43 -21
  126. package/lib/module/anchors/semanticMatcher.js.map +1 -1
  127. package/lib/module/anchors/tapSlop.js +11 -0
  128. package/lib/module/anchors/tapSlop.js.map +1 -0
  129. package/lib/module/anchors/targetPressMonitor.js +53 -8
  130. package/lib/module/anchors/targetPressMonitor.js.map +1 -1
  131. package/lib/module/authoring/fingerprintFromInstance.js +6 -10
  132. package/lib/module/authoring/fingerprintFromInstance.js.map +1 -1
  133. package/lib/module/content/RichContentRenderer.js +3 -3
  134. package/lib/module/content/RichContentRenderer.js.map +1 -1
  135. package/lib/module/content/openSafeUrl.js +3 -1
  136. package/lib/module/content/openSafeUrl.js.map +1 -1
  137. package/lib/module/expo-router/useUserflowExpoRouter.js +7 -1
  138. package/lib/module/expo-router/useUserflowExpoRouter.js.map +1 -1
  139. package/lib/module/index.js +22 -7
  140. package/lib/module/index.js.map +1 -1
  141. package/lib/module/navigation/navigateUrlUtils.js +94 -0
  142. package/lib/module/navigation/navigateUrlUtils.js.map +1 -0
  143. package/lib/module/navigation/openExternalNavigateUrl.js +11 -0
  144. package/lib/module/navigation/openExternalNavigateUrl.js.map +1 -0
  145. package/lib/module/navigation/openFlowNavigateUrl.js +22 -36
  146. package/lib/module/navigation/openFlowNavigateUrl.js.map +1 -1
  147. package/lib/module/pairing/PairingController.js +3 -1
  148. package/lib/module/pairing/PairingController.js.map +1 -1
  149. package/lib/module/runtime/BannerHost.js +38 -29
  150. package/lib/module/runtime/BannerHost.js.map +1 -1
  151. package/lib/module/runtime/TargetPressBridge.js +56 -8
  152. package/lib/module/runtime/TargetPressBridge.js.map +1 -1
  153. package/lib/module/runtime/UserflowOverlay.js +5 -11
  154. package/lib/module/runtime/UserflowOverlay.js.map +1 -1
  155. package/lib/module/runtime/UserflowProvider.js +66 -40
  156. package/lib/module/runtime/UserflowProvider.js.map +1 -1
  157. package/lib/module/runtime/bannerEngine.js +7 -2
  158. package/lib/module/runtime/bannerEngine.js.map +1 -1
  159. package/lib/module/runtime/bannerFlowSpacer.js +65 -0
  160. package/lib/module/runtime/bannerFlowSpacer.js.map +1 -0
  161. package/lib/module/runtime/flowEngine.js +11 -0
  162. package/lib/module/runtime/flowEngine.js.map +1 -1
  163. package/lib/module/runtime/previewStart.js +35 -0
  164. package/lib/module/runtime/previewStart.js.map +1 -0
  165. package/lib/module/runtime/pushBannerLayoutContext.js +91 -0
  166. package/lib/module/runtime/pushBannerLayoutContext.js.map +1 -0
  167. package/lib/module/screenTracker.js +11 -0
  168. package/lib/module/screenTracker.js.map +1 -1
  169. package/lib/module/surfaces/BannerBar.js +80 -62
  170. package/lib/module/surfaces/BannerBar.js.map +1 -1
  171. package/lib/module/surfaces/BannerSurface.js +26 -0
  172. package/lib/module/surfaces/BannerSurface.js.map +1 -1
  173. package/lib/module/surfaces/TooltipSurface.js +44 -42
  174. package/lib/module/surfaces/TooltipSurface.js.map +1 -1
  175. package/lib/module/surfaces/bannerBarLayout.js +72 -11
  176. package/lib/module/surfaces/bannerBarLayout.js.map +1 -1
  177. package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js +113 -64
  178. package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
  179. package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js +59 -11
  180. package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
  181. package/lib/module/surfaces/hostAccessibilityTrap.js +24 -0
  182. package/lib/module/surfaces/hostAccessibilityTrap.js.map +1 -0
  183. package/lib/module/surfaces/resolveBannerTheme.js +6 -3
  184. package/lib/module/surfaces/resolveBannerTheme.js.map +1 -1
  185. package/lib/module/surfaces/tooltip/TooltipBubble.js +3 -2
  186. package/lib/module/surfaces/tooltip/TooltipBubble.js.map +1 -1
  187. package/lib/module/surfaces/tooltip/targetSearchSchedule.js +128 -0
  188. package/lib/module/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
  189. package/lib/module/version.js +1 -1
  190. package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts +36 -2
  191. package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
  192. package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts +0 -4
  193. package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts.map +1 -1
  194. package/lib/typescript/commonjs/src/anchors/appActive.d.ts +8 -0
  195. package/lib/typescript/commonjs/src/anchors/appActive.d.ts.map +1 -0
  196. package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts +17 -0
  197. package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts.map +1 -1
  198. package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts +7 -0
  199. package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts.map +1 -1
  200. package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts +7 -0
  201. package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
  202. package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts +3 -2
  203. package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
  204. package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
  205. package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
  206. package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts +5 -0
  207. package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts.map +1 -1
  208. package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts +70 -0
  209. package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
  210. package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts +1 -0
  211. package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts.map +1 -1
  212. package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
  213. package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
  214. package/lib/typescript/commonjs/src/anchors/resolveElementTargets.d.ts.map +1 -1
  215. package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts +2 -0
  216. package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts.map +1 -1
  217. package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts +9 -0
  218. package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts.map +1 -0
  219. package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts +13 -8
  220. package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts.map +1 -1
  221. package/lib/typescript/commonjs/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
  222. package/lib/typescript/commonjs/src/content/RichContentRenderer.d.ts.map +1 -1
  223. package/lib/typescript/commonjs/src/content/openSafeUrl.d.ts.map +1 -1
  224. package/lib/typescript/commonjs/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
  225. package/lib/typescript/commonjs/src/index.d.ts +6 -5
  226. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  227. package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts +15 -0
  228. package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts.map +1 -0
  229. package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts +3 -0
  230. package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
  231. package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts +2 -5
  232. package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
  233. package/lib/typescript/commonjs/src/pairing/PairingController.d.ts +6 -0
  234. package/lib/typescript/commonjs/src/pairing/PairingController.d.ts.map +1 -1
  235. package/lib/typescript/commonjs/src/runtime/BannerHost.d.ts.map +1 -1
  236. package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts +3 -0
  237. package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts.map +1 -1
  238. package/lib/typescript/commonjs/src/runtime/UserflowOverlay.d.ts.map +1 -1
  239. package/lib/typescript/commonjs/src/runtime/UserflowProvider.d.ts.map +1 -1
  240. package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts +5 -1
  241. package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts.map +1 -1
  242. package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts +15 -0
  243. package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
  244. package/lib/typescript/commonjs/src/runtime/flowEngine.d.ts.map +1 -1
  245. package/lib/typescript/commonjs/src/runtime/previewStart.d.ts +13 -0
  246. package/lib/typescript/commonjs/src/runtime/previewStart.d.ts.map +1 -0
  247. package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts +17 -0
  248. package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
  249. package/lib/typescript/commonjs/src/screenTracker.d.ts +6 -0
  250. package/lib/typescript/commonjs/src/screenTracker.d.ts.map +1 -1
  251. package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts +3 -1
  252. package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts.map +1 -1
  253. package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts +5 -1
  254. package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts.map +1 -1
  255. package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts +1 -1
  256. package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts.map +1 -1
  257. package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts +17 -0
  258. package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts.map +1 -1
  259. package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
  260. package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
  261. package/lib/typescript/commonjs/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
  262. package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
  263. package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
  264. package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts +4 -1
  265. package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
  266. package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
  267. package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
  268. package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
  269. package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
  270. package/lib/typescript/commonjs/src/version.d.ts +1 -1
  271. package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts +36 -2
  272. package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
  273. package/lib/typescript/module/src/anchors/animationFrame.d.ts +0 -4
  274. package/lib/typescript/module/src/anchors/animationFrame.d.ts.map +1 -1
  275. package/lib/typescript/module/src/anchors/appActive.d.ts +8 -0
  276. package/lib/typescript/module/src/anchors/appActive.d.ts.map +1 -0
  277. package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts +17 -0
  278. package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts.map +1 -1
  279. package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts +7 -0
  280. package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts.map +1 -1
  281. package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts +7 -0
  282. package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
  283. package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts +3 -2
  284. package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
  285. package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
  286. package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
  287. package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts +5 -0
  288. package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts.map +1 -1
  289. package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts +70 -0
  290. package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
  291. package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts +1 -0
  292. package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts.map +1 -1
  293. package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
  294. package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
  295. package/lib/typescript/module/src/anchors/resolveElementTargets.d.ts.map +1 -1
  296. package/lib/typescript/module/src/anchors/semanticMatcher.d.ts +2 -0
  297. package/lib/typescript/module/src/anchors/semanticMatcher.d.ts.map +1 -1
  298. package/lib/typescript/module/src/anchors/tapSlop.d.ts +9 -0
  299. package/lib/typescript/module/src/anchors/tapSlop.d.ts.map +1 -0
  300. package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts +13 -8
  301. package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts.map +1 -1
  302. package/lib/typescript/module/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
  303. package/lib/typescript/module/src/content/RichContentRenderer.d.ts.map +1 -1
  304. package/lib/typescript/module/src/content/openSafeUrl.d.ts.map +1 -1
  305. package/lib/typescript/module/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
  306. package/lib/typescript/module/src/index.d.ts +6 -5
  307. package/lib/typescript/module/src/index.d.ts.map +1 -1
  308. package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts +15 -0
  309. package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts.map +1 -0
  310. package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts +3 -0
  311. package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
  312. package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts +2 -5
  313. package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
  314. package/lib/typescript/module/src/pairing/PairingController.d.ts +6 -0
  315. package/lib/typescript/module/src/pairing/PairingController.d.ts.map +1 -1
  316. package/lib/typescript/module/src/runtime/BannerHost.d.ts.map +1 -1
  317. package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts +3 -0
  318. package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts.map +1 -1
  319. package/lib/typescript/module/src/runtime/UserflowOverlay.d.ts.map +1 -1
  320. package/lib/typescript/module/src/runtime/UserflowProvider.d.ts.map +1 -1
  321. package/lib/typescript/module/src/runtime/bannerEngine.d.ts +5 -1
  322. package/lib/typescript/module/src/runtime/bannerEngine.d.ts.map +1 -1
  323. package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts +15 -0
  324. package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
  325. package/lib/typescript/module/src/runtime/flowEngine.d.ts.map +1 -1
  326. package/lib/typescript/module/src/runtime/previewStart.d.ts +13 -0
  327. package/lib/typescript/module/src/runtime/previewStart.d.ts.map +1 -0
  328. package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts +17 -0
  329. package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
  330. package/lib/typescript/module/src/screenTracker.d.ts +6 -0
  331. package/lib/typescript/module/src/screenTracker.d.ts.map +1 -1
  332. package/lib/typescript/module/src/surfaces/BannerBar.d.ts +3 -1
  333. package/lib/typescript/module/src/surfaces/BannerBar.d.ts.map +1 -1
  334. package/lib/typescript/module/src/surfaces/BannerSurface.d.ts +5 -1
  335. package/lib/typescript/module/src/surfaces/BannerSurface.d.ts.map +1 -1
  336. package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts +1 -1
  337. package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts.map +1 -1
  338. package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts +17 -0
  339. package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts.map +1 -1
  340. package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
  341. package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
  342. package/lib/typescript/module/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
  343. package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
  344. package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
  345. package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts +4 -1
  346. package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
  347. package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
  348. package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
  349. package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
  350. package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
  351. package/lib/typescript/module/src/version.d.ts +1 -1
  352. package/package.json +1 -1
  353. package/src/anchors/AutoAnchorRegistry.ts +166 -35
  354. package/src/anchors/animationFrame.ts +31 -1
  355. package/src/anchors/appActive.ts +63 -0
  356. package/src/anchors/elementConditionEvaluator.ts +29 -12
  357. package/src/anchors/elementTapMonitor.ts +68 -10
  358. package/src/anchors/elementTargetMatch.ts +34 -7
  359. package/src/anchors/fiber/fiberInternals.ts +68 -0
  360. package/src/anchors/fiber/indexHostFibers.ts +8 -3
  361. package/src/anchors/fiber/resolveFiberPath.ts +25 -16
  362. package/src/anchors/fiberAnchorCache.ts +19 -0
  363. package/src/anchors/gestureHandlerHandoff.ts +356 -0
  364. package/src/anchors/resolveAnchorHost.ts +2 -0
  365. package/src/anchors/resolveAutoAnchorHost.ts +18 -8
  366. package/src/anchors/resolveElementTargets.ts +64 -7
  367. package/src/anchors/semanticMatcher.ts +48 -27
  368. package/src/anchors/tapSlop.ts +18 -0
  369. package/src/anchors/targetPressMonitor.ts +56 -19
  370. package/src/authoring/fingerprintFromInstance.ts +4 -5
  371. package/src/content/RichContentRenderer.tsx +4 -3
  372. package/src/content/openSafeUrl.ts +3 -1
  373. package/src/expo-router/useUserflowExpoRouter.ts +10 -1
  374. package/src/index.ts +20 -9
  375. package/src/navigation/navigateUrlUtils.ts +100 -0
  376. package/src/navigation/openExternalNavigateUrl.ts +9 -0
  377. package/src/navigation/openFlowNavigateUrl.ts +36 -36
  378. package/src/pairing/PairingController.ts +9 -1
  379. package/src/runtime/BannerHost.tsx +41 -23
  380. package/src/runtime/TargetPressBridge.tsx +66 -9
  381. package/src/runtime/UserflowOverlay.tsx +7 -9
  382. package/src/runtime/UserflowProvider.tsx +56 -32
  383. package/src/runtime/bannerEngine.ts +10 -2
  384. package/src/runtime/bannerFlowSpacer.tsx +70 -0
  385. package/src/runtime/flowEngine.ts +11 -0
  386. package/src/runtime/previewStart.ts +49 -0
  387. package/src/runtime/pushBannerLayoutContext.tsx +121 -0
  388. package/src/screenTracker.ts +11 -0
  389. package/src/surfaces/BannerBar.tsx +93 -55
  390. package/src/surfaces/BannerSurface.tsx +59 -3
  391. package/src/surfaces/TooltipSurface.tsx +63 -51
  392. package/src/surfaces/bannerBarLayout.ts +80 -7
  393. package/src/surfaces/bannerWebView/BannerWebViewBar.tsx +149 -57
  394. package/src/surfaces/bannerWebView/buildCustomBannerHtml.ts +63 -11
  395. package/src/surfaces/hostAccessibilityTrap.ts +25 -0
  396. package/src/surfaces/resolveBannerTheme.ts +14 -3
  397. package/src/surfaces/tooltip/TooltipBubble.tsx +8 -1
  398. package/src/surfaces/tooltip/targetSearchSchedule.ts +140 -0
  399. package/src/version.ts +1 -1
  400. package/vendor/core/dist/engine/BannerSessionEngine.d.ts +29 -2
  401. package/vendor/core/dist/engine/BannerSessionEngine.d.ts.map +1 -1
  402. package/vendor/core/dist/engine/BannerSessionEngine.js +87 -6
  403. package/vendor/core/dist/engine/BannerSessionEngine.js.map +1 -1
  404. package/vendor/core/dist/engine/ClientConditionEngine.d.ts.map +1 -1
  405. package/vendor/core/dist/engine/ClientConditionEngine.js +30 -7
  406. package/vendor/core/dist/engine/ClientConditionEngine.js.map +1 -1
  407. package/vendor/core/dist/engine/PairingEngine.d.ts +6 -1
  408. package/vendor/core/dist/engine/PairingEngine.d.ts.map +1 -1
  409. package/vendor/core/dist/engine/PairingEngine.js +23 -3
  410. package/vendor/core/dist/engine/PairingEngine.js.map +1 -1
  411. package/vendor/core/dist/engine/handleActions.d.ts.map +1 -1
  412. package/vendor/core/dist/engine/handleActions.js +67 -2
  413. package/vendor/core/dist/engine/handleActions.js.map +1 -1
  414. package/vendor/core/dist/version.d.ts +1 -1
  415. package/vendor/core/dist/version.js +1 -1
  416. package/vendor/core/package.json +1 -1
  417. package/vendor/protocol/package.json +1 -1
@@ -26,19 +26,46 @@ export function anchorIdsAboveTap(tap: ElementTap): ReadonlySet<string> {
26
26
  const ids = new Set<string>();
27
27
  let current: AnyFiber = tap.targetFiber;
28
28
  for (let depth = 0; current && depth < MAX_ANCESTOR_WALK; depth += 1) {
29
- const props = getMemoizedProps(current);
30
- if (props) {
31
- for (const tier of ANCHOR_PROP_TIERS) {
32
- const value = props[tier];
33
- if (typeof value === 'string') ids.add(value);
34
- }
35
- }
29
+ collectAnchorIds(current, ids);
36
30
  current = getReturn(current);
37
31
  }
38
32
  anchorIdsByTap.set(tap, ids);
39
33
  return ids;
40
34
  }
41
35
 
36
+ /**
37
+ * The fiber on `start` (or an ancestor) that carries `anchorId`.
38
+ *
39
+ * The touch-start fiber is kept after the press navigates away, so a click can
40
+ * still match when a live scan of the new screen no longer finds the id.
41
+ */
42
+ export function fiberBearingAnchorId(start: AnyFiber, anchorId: string): AnyFiber | null {
43
+ let current: AnyFiber = start;
44
+ for (let depth = 0; current && depth < MAX_ANCESTOR_WALK; depth += 1) {
45
+ if (fiberHasAnchorId(current, anchorId)) return current;
46
+ current = getReturn(current);
47
+ }
48
+ return null;
49
+ }
50
+
51
+ function fiberHasAnchorId(fiber: AnyFiber, anchorId: string): boolean {
52
+ const props = getMemoizedProps(fiber);
53
+ if (!props) return false;
54
+ for (const tier of ANCHOR_PROP_TIERS) {
55
+ if (props[tier] === anchorId) return true;
56
+ }
57
+ return false;
58
+ }
59
+
60
+ function collectAnchorIds(fiber: AnyFiber, ids: Set<string>): void {
61
+ const props = getMemoizedProps(fiber);
62
+ if (!props) return;
63
+ for (const tier of ANCHOR_PROP_TIERS) {
64
+ const value = props[tier];
65
+ if (typeof value === 'string') ids.add(value);
66
+ }
67
+ }
68
+
42
69
  export function sameFiber(a: AnyFiber, b: AnyFiber): boolean {
43
70
  if (a == null || b == null) return false;
44
71
  if (a === b) return true;
@@ -319,6 +319,74 @@ export function resolveCurrentFiber(fiber: AnyFiber): AnyFiber {
319
319
  return fiber;
320
320
  }
321
321
 
322
+ /** Where a fiber sits: its tree's current fiber, and whether it is under it. */
323
+ interface TreePlace {
324
+ current: AnyFiber;
325
+ inTree: boolean;
326
+ }
327
+
328
+ /**
329
+ * {@link resolveCurrentFiber} for many fibers in one synchronous pass. Where
330
+ * each ancestor sits is remembered, so the pass walks every ancestor once
331
+ * instead of once per fiber below it. Discard it when the pass ends: a render
332
+ * moves `return` pointers.
333
+ */
334
+ export function createCurrentFiberResolver(): (fiber: AnyFiber) => AnyFiber {
335
+ const placeOf = new Map<object, TreePlace | null>();
336
+
337
+ const locate = (fiber: AnyFiber): TreePlace | null => {
338
+ const path: AnyFiber[] = [];
339
+ let above: TreePlace | null = null;
340
+ let cur: AnyFiber = fiber;
341
+ for (let depth = 0; cur && depth < MAX_ANCESTOR_WALK; depth += 1) {
342
+ const known = placeOf.get(cur);
343
+ if (known !== undefined) {
344
+ above = known;
345
+ break;
346
+ }
347
+ path.push(cur);
348
+ // Only a HostRoot (or a detached fiber) has no parent, so the tag is
349
+ // read once, at the top, not at every step.
350
+ const parent = getReturn(cur);
351
+ if (!parent) {
352
+ const rootState =
353
+ getFiberTag(cur) === HOST_ROOT_TAG
354
+ ? (getStateNode(cur) as { current?: AnyFiber } | null)
355
+ : null;
356
+ above = rootState?.current ? { current: rootState.current, inTree: false } : null;
357
+ break;
358
+ }
359
+ cur = parent;
360
+ }
361
+ // Top down: a fiber is in the current tree when the current fiber is it
362
+ // or one of its ancestors.
363
+ let place = above;
364
+ for (let i = path.length - 1; i >= 0; i -= 1) {
365
+ if (place && !place.inTree && path[i] === place.current) {
366
+ place = { current: place.current, inTree: true };
367
+ }
368
+ placeOf.set(path[i], place);
369
+ }
370
+ return place;
371
+ };
372
+
373
+ return (fiber) => {
374
+ if (!fiber) return fiber;
375
+ try {
376
+ const alternate = getAlternate(fiber);
377
+ if (!alternate) return fiber;
378
+ const own = locate(fiber);
379
+ if (!own || own.inTree) return fiber;
380
+ const other = locate(alternate);
381
+ // Two trees for one pair only happens on a corrupt graph.
382
+ if (!other || other.current !== own.current) return resolveCurrentFiber(fiber);
383
+ return other.inTree ? alternate : fiber;
384
+ } catch {
385
+ return fiber;
386
+ }
387
+ };
388
+ }
389
+
322
390
  function isInTree(fiber: AnyFiber, root: AnyFiber): boolean {
323
391
  let cur: AnyFiber = fiber;
324
392
  for (let depth = 0; cur && depth < MAX_ANCESTOR_WALK; depth += 1) {
@@ -111,13 +111,18 @@ export function createLazyHostFiberIndex(
111
111
 
112
112
  /**
113
113
  * Fiber for a public instance: direct pointer first, then the scan-root index
114
- * by identity / native tag.
114
+ * by identity / native tag. Pass a `createCurrentFiberResolver()` resolver when
115
+ * looking up many instances in one pass.
115
116
  */
116
- export function lookupHostFiber(index: HostFiberIndexSource, instance: unknown): AnyFiber | null {
117
+ export function lookupHostFiber(
118
+ index: HostFiberIndexSource,
119
+ instance: unknown,
120
+ resolveCurrent: (fiber: AnyFiber) => AnyFiber = resolveCurrentFiber,
121
+ ): AnyFiber | null {
117
122
  const direct = fiberFromPublicInstance(instance);
118
123
  if (direct) {
119
124
  try {
120
- return resolveCurrentFiber(direct);
125
+ return resolveCurrent(direct);
121
126
  } catch {
122
127
  return direct;
123
128
  }
@@ -31,10 +31,13 @@ import { walkVisibleFibers } from './walkVisibleFibers';
31
31
  const MIN_SCORE = 40;
32
32
  const UNIQUENESS_MARGIN = 12;
33
33
 
34
+ /** Why a fiber path did not resolve. Element conditions map these differently. */
35
+ export type FiberPathMissReason = 'empty' | 'no-root' | 'no-match';
36
+
34
37
  export type FiberPathResolution =
35
38
  | { status: 'found'; fiber: AnyFiber; cacheId: string; textDisagreed?: boolean }
36
39
  | { status: 'ambiguous' }
37
- | { status: 'unknown' };
40
+ | { status: 'unknown'; reason: FiberPathMissReason };
38
41
 
39
42
  function stringProp(value: unknown): string | undefined {
40
43
  return typeof value === 'string' && value.trim() ? value.trim() : undefined;
@@ -50,18 +53,13 @@ function sameFiber(a: AnyFiber, b: AnyFiber): boolean {
50
53
  return alt != null && alt === b;
51
54
  }
52
55
 
53
- function liveSegment(fiber: AnyFiber): MobileFiberSegment {
56
+ /** The live segment without its text, which needs a subtree walk. */
57
+ function liveIdentity(fiber: AnyFiber): MobileFiberSegment {
54
58
  const props = getLiveHostProps(fiber);
55
59
  const role = stringProp(props?.accessibilityRole) ?? stringProp(props?.role);
56
60
  const testID = stringProp(props?.testID);
57
61
  const accessibilityLabel =
58
62
  stringProp(props?.accessibilityLabel) ?? stringProp(props?.['aria-label']);
59
- let text: string | undefined;
60
- try {
61
- text = readHostText(fiber, 1);
62
- } catch {
63
- text = undefined;
64
- }
65
63
  const hostKind = getHostTypeName(fiber);
66
64
  return {
67
65
  displayName: typeof fiber?.type === 'string' ? fiber.type : '',
@@ -69,10 +67,19 @@ function liveSegment(fiber: AnyFiber): MobileFiberSegment {
69
67
  ...(role ? { role } : {}),
70
68
  ...(testID ? { testID } : {}),
71
69
  ...(accessibilityLabel ? { accessibilityLabel } : {}),
72
- ...(text ? { text } : {}),
73
70
  };
74
71
  }
75
72
 
73
+ function liveSegment(fiber: AnyFiber): MobileFiberSegment {
74
+ let text: string | undefined;
75
+ try {
76
+ text = readHostText(fiber, 1);
77
+ } catch {
78
+ text = undefined;
79
+ }
80
+ return { ...liveIdentity(fiber), ...(text ? { text } : {}) };
81
+ }
82
+
76
83
  function ancestorsOf(fiber: AnyFiber): AnyFiber[] {
77
84
  const chain: AnyFiber[] = [];
78
85
  let current: AnyFiber | null = fiber;
@@ -90,7 +97,8 @@ function scoreAlignment(expected: MobileFiberSegment[], actual: AnyFiber[]): num
90
97
  let kindAligned = 0;
91
98
  for (let i = 0; i < n; i += 1) {
92
99
  const exp = expected[expected.length - 1 - i]!;
93
- const live = liveSegment(actual[actual.length - 1 - i]!);
100
+ // Alignment never compares text, so skip the subtree walk for every ancestor.
101
+ const live = liveIdentity(actual[actual.length - 1 - i]!);
94
102
  const expKind = exp.hostKind ?? exp.displayName;
95
103
  const liveKind = live.hostKind ?? live.displayName;
96
104
  if (expKind && liveKind && expKind !== liveKind) break;
@@ -107,13 +115,14 @@ function scoreHost(
107
115
  path: MobileFiberPath,
108
116
  fiber: AnyFiber,
109
117
  ): { score: number; textDisagreed: boolean } {
110
- const live = liveSegment(fiber);
111
118
  const leaf = path.segments[path.segments.length - 1];
112
119
  const leafKind = leaf?.hostKind ?? path.leafKind;
113
- const liveKind = live.hostKind ?? '';
120
+ // Kind first: most hosts fail here, before any props or text are read.
121
+ const liveKind = getHostTypeName(fiber) ?? '';
114
122
  if (leafKind && liveKind && leafKind !== liveKind) {
115
123
  return { score: -Infinity, textDisagreed: false };
116
124
  }
125
+ const live = liveSegment(fiber);
117
126
 
118
127
  let score = 20;
119
128
  if (leaf?.role && live.role === leaf.role) score += 15;
@@ -151,15 +160,15 @@ function scoreHost(
151
160
 
152
161
  function resolveScored(path: MobileFiberPath, root: AnyFiber): FiberPathResolution {
153
162
  const scored: Array<{ fiber: AnyFiber; score: number; textDisagreed: boolean }> = [];
163
+ // The walk already skips covered screens, so no per-host visibility walk.
154
164
  walkVisibleFibers(root, (fiber) => {
155
165
  if (!isHostFiber(fiber)) return;
156
- if (!isFiberOnVisibleScreen(fiber)) return;
157
166
  const { score, textDisagreed } = scoreHost(path, fiber);
158
167
  if (score > -Infinity) scored.push({ fiber, score, textDisagreed });
159
168
  });
160
169
  scored.sort((a, b) => b.score - a.score);
161
170
  const best = scored[0];
162
- if (!best || best.score < MIN_SCORE) return { status: 'unknown' };
171
+ if (!best || best.score < MIN_SCORE) return { status: 'unknown', reason: 'no-match' };
163
172
  const second = scored[1];
164
173
  if (second && best.score - second.score < UNIQUENESS_MARGIN) return { status: 'ambiguous' };
165
174
  return {
@@ -175,7 +184,7 @@ function resolveScored(path: MobileFiberPath, root: AnyFiber): FiberPathResoluti
175
184
  * and reuses the manual fiber scan.
176
185
  */
177
186
  export function resolveFiberPath(path: MobileFiberPath | null | undefined): FiberPathResolution {
178
- if (!path || path.segments.length === 0) return { status: 'unknown' };
187
+ if (!path || path.segments.length === 0) return { status: 'unknown', reason: 'empty' };
179
188
 
180
189
  const leaf = path.segments[path.segments.length - 1];
181
190
  const exactId = leaf?.testID;
@@ -200,7 +209,7 @@ export function resolveFiberPath(path: MobileFiberPath | null | undefined): Fibe
200
209
  }
201
210
 
202
211
  const root = fiberScanRoot.getFiber();
203
- if (!root) return { status: 'unknown' };
212
+ if (!root) return { status: 'unknown', reason: 'no-root' };
204
213
 
205
214
  const resolved = resolveScored(path, root);
206
215
  if (resolved.status === 'found') {
@@ -7,6 +7,7 @@ interface FiberCacheEntry {
7
7
 
8
8
  const fiberCache = new Map<string, FiberCacheEntry>();
9
9
  let currentScreenKey: string | null = null;
10
+ const screenChangeListeners = new Set<() => void>();
10
11
 
11
12
  export function getFiberAnchorScreenKey(): string | null {
12
13
  return currentScreenKey;
@@ -17,6 +18,24 @@ export function notifyAnchorScreenChange(screenKey: string | null): void {
17
18
  if (screenKey === currentScreenKey) return;
18
19
  currentScreenKey = screenKey;
19
20
  fiberCache.clear();
21
+ for (const listener of Array.from(screenChangeListeners)) {
22
+ try {
23
+ listener();
24
+ } catch {
25
+ // A listener must not break screen tracking.
26
+ }
27
+ }
28
+ }
29
+
30
+ /**
31
+ * `listener` runs after the active screen changes, once the cache above is
32
+ * cleared. Searches that back off while a target is missing restart from here.
33
+ */
34
+ export function subscribeAnchorScreenChange(listener: () => void): () => void {
35
+ screenChangeListeners.add(listener);
36
+ return () => {
37
+ screenChangeListeners.delete(listener);
38
+ };
20
39
  }
21
40
 
22
41
  /** Drop the fiber cache (preview restart / tests). */
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Taps that react-native-gesture-handler takes away from React Native.
3
+ *
4
+ * When a gesture-handler control activates, it cancels RN's touch stream, so
5
+ * the bridge sees `touchcancel` instead of `touchend`. The handler tag is read
6
+ * off the touched fiber (no import of gesture-handler), and the touch counts
7
+ * as a tap only if that handler later ends from ACTIVE on
8
+ * `onGestureHandlerStateChange`. Apps that never hit this path never subscribe.
9
+ *
10
+ * State numbers are gesture-handler's `State` enum. A future major that
11
+ * renumbers them degrades to "no tap", which is today's behavior.
12
+ */
13
+ import type { AnyFiber } from './fiber/fiberInternals';
14
+ import {
15
+ getMemoizedProps,
16
+ getReturn,
17
+ getStateNode,
18
+ MAX_ANCESTOR_WALK,
19
+ } from './fiber/fiberInternals';
20
+ import { type TouchPoint, withinTapSlop } from './tapSlop';
21
+
22
+ /** gesture-handler `State` values this hand-off acts on. */
23
+ export const GESTURE_HANDLER_STATE = {
24
+ FAILED: 1,
25
+ CANCELLED: 3,
26
+ ACTIVE: 4,
27
+ END: 5,
28
+ } as const;
29
+
30
+ /** Drop a handed-off touch that never reaches a terminal state. */
31
+ export const GESTURE_HANDLER_HANDOFF_TIMEOUT_MS = 10_000;
32
+
33
+ const STATE_CHANGE_EVENT = 'onGestureHandlerStateChange';
34
+
35
+ export interface HandedOffTouch {
36
+ identifier: number | string;
37
+ pageX: number;
38
+ pageY: number;
39
+ handlerTags: readonly number[];
40
+ /**
41
+ * The gesture completed as a tap. `end` is set when the payload carried
42
+ * window coordinates; otherwise only the start point is known.
43
+ */
44
+ onResolve: (end: TouchPoint | null) => void;
45
+ }
46
+
47
+ interface DeviceEventSubscription {
48
+ remove: () => void;
49
+ }
50
+
51
+ interface DeviceEventEmitterLike {
52
+ addListener(event: string, listener: (payload: unknown) => void): DeviceEventSubscription;
53
+ }
54
+
55
+ interface GestureStatePayload {
56
+ handlerTag: number;
57
+ state: number;
58
+ oldState?: number;
59
+ pointerInside?: boolean;
60
+ translationX?: number;
61
+ translationY?: number;
62
+ absoluteX?: number;
63
+ absoluteY?: number;
64
+ /** Present only so a long-press payload can be told apart from a tap. */
65
+ hasDuration: boolean;
66
+ }
67
+
68
+ interface PendingTouch extends HandedOffTouch {
69
+ id: number;
70
+ settled: boolean;
71
+ /** Tags that already reached a terminal state without counting as a tap. */
72
+ finishedTags: Set<number>;
73
+ timer: ReturnType<typeof setTimeout>;
74
+ }
75
+
76
+ /**
77
+ * Handler tags for the control under `fiber`.
78
+ *
79
+ * Old-API buttons keep `handlerTag` on the class instance. `GestureDetector`
80
+ * keeps a `gesture` prop whose `toGestureArray()` entries each have one.
81
+ * Negative tags (gesture-handler's own root helper) are ignored.
82
+ *
83
+ * The walk stops at the nearest fiber that has a tag. A native handler with
84
+ * `disallowInterruption` (gesture-handler `ScrollView`, `FlatList`,
85
+ * `RefreshControl`) is a container, not a tap, so a scroll that starts inside
86
+ * it is dropped instead of latching the row. Never throws.
87
+ */
88
+ export function collectGestureHandlerTags(fiber: AnyFiber | null): number[] {
89
+ if (fiber == null) return [];
90
+ try {
91
+ let current: AnyFiber | null = fiber;
92
+ let steps = 0;
93
+ while (current && steps < MAX_ANCESTOR_WALK) {
94
+ steps += 1;
95
+ const found = tagsOnFiber(current);
96
+ if (found.pressable.length > 0) return found.pressable;
97
+ // The nearest handler is a scroll container. An outer control did not
98
+ // receive this touch.
99
+ if (found.container) return [];
100
+ current = getReturn(current);
101
+ }
102
+ } catch {
103
+ return [];
104
+ }
105
+ return [];
106
+ }
107
+
108
+ export class GestureHandlerHandoff {
109
+ private readonly pending = new Map<number, PendingTouch>();
110
+ private nextId = 1;
111
+ private subscription: DeviceEventSubscription | null = null;
112
+
113
+ /**
114
+ * Keep `touch` until its handler finishes or it expires.
115
+ * Returns a stop function; calling it drops the touch without a tap.
116
+ */
117
+ track(touch: HandedOffTouch): () => void {
118
+ if (touch.handlerTags.length === 0) return () => {};
119
+ const id = this.nextId;
120
+ this.nextId += 1;
121
+ const pending: PendingTouch = {
122
+ ...touch,
123
+ id,
124
+ settled: false,
125
+ finishedTags: new Set(),
126
+ timer: setTimeout(() => {
127
+ this.drop(id);
128
+ }, GESTURE_HANDLER_HANDOFF_TIMEOUT_MS),
129
+ };
130
+ this.pending.set(id, pending);
131
+ this.ensureListening();
132
+ return () => this.drop(id);
133
+ }
134
+
135
+ /** Test helper. Drops every pending touch and unsubscribes. */
136
+ resetForTests(): void {
137
+ for (const touch of this.pending.values()) {
138
+ touch.settled = true;
139
+ clearTimeout(touch.timer);
140
+ }
141
+ this.pending.clear();
142
+ this.stopListeningIfIdle();
143
+ }
144
+
145
+ /** @internal Test hook: how many touches are still waiting. */
146
+ get pendingCount(): number {
147
+ return this.pending.size;
148
+ }
149
+
150
+ private ensureListening(): void {
151
+ if (this.subscription || this.pending.size === 0) return;
152
+ const emitter = deviceEventEmitter();
153
+ if (!emitter) return;
154
+ this.subscription = emitter.addListener(STATE_CHANGE_EVENT, (payload: unknown) => {
155
+ try {
156
+ this.handleStateChange(payload);
157
+ } catch {
158
+ // A bad payload must not break the next gesture.
159
+ }
160
+ });
161
+ }
162
+
163
+ private stopListeningIfIdle(): void {
164
+ if (this.pending.size > 0 || !this.subscription) return;
165
+ this.subscription.remove();
166
+ this.subscription = null;
167
+ }
168
+
169
+ private handleStateChange(event: unknown): void {
170
+ const payload = asPayload(event);
171
+ if (!payload) return;
172
+ const matching = [...this.pending.values()].filter(
173
+ (touch) => !touch.settled && touch.handlerTags.includes(payload.handlerTag),
174
+ );
175
+ for (const touch of matching) {
176
+ this.apply(touch, payload);
177
+ }
178
+ }
179
+
180
+ private apply(touch: PendingTouch, payload: GestureStatePayload): void {
181
+ if (touch.settled || !isTerminal(payload.state)) return;
182
+ if (isCompletedTap(touch, payload)) {
183
+ this.resolve(touch, endPoint(payload));
184
+ return;
185
+ }
186
+ touch.finishedTags.add(payload.handlerTag);
187
+ const allDone = touch.handlerTags.every((tag) => touch.finishedTags.has(tag));
188
+ if (allDone) this.drop(touch.id);
189
+ }
190
+
191
+ private resolve(touch: PendingTouch, end: TouchPoint | null): void {
192
+ const notify = touch.onResolve;
193
+ this.drop(touch.id);
194
+ try {
195
+ notify(end);
196
+ } catch {
197
+ // One monitor's failure must not skip the other touch on this event.
198
+ }
199
+ }
200
+
201
+ private drop(id: number): void {
202
+ const touch = this.pending.get(id);
203
+ if (!touch || touch.settled) return;
204
+ touch.settled = true;
205
+ clearTimeout(touch.timer);
206
+ this.pending.delete(id);
207
+ this.stopListeningIfIdle();
208
+ }
209
+ }
210
+
211
+ /** Shared singleton used by the tap monitors. */
212
+ export const gestureHandlerHandoff = new GestureHandlerHandoff();
213
+
214
+ interface FiberTags {
215
+ /** Tags that can count as a tap. */
216
+ pressable: number[];
217
+ /** The fiber has a container handler and no pressable tag. */
218
+ container: boolean;
219
+ }
220
+
221
+ function tagsOnFiber(fiber: AnyFiber): FiberTags {
222
+ try {
223
+ const pressable: number[] = [];
224
+ const seen = new Set<number>();
225
+ let container = false;
226
+ const add = (tag: number | undefined, isContainer: boolean): void => {
227
+ if (tag === undefined) return;
228
+ if (isContainer) {
229
+ container = true;
230
+ return;
231
+ }
232
+ if (seen.has(tag)) return;
233
+ seen.add(tag);
234
+ pressable.push(tag);
235
+ };
236
+
237
+ const props = getMemoizedProps(fiber);
238
+ add(handlerTagOf(getStateNode(fiber)), props?.disallowInterruption === true);
239
+
240
+ const gesture = props?.gesture;
241
+ if (gesture && typeof gesture === 'object' && 'toGestureArray' in gesture) {
242
+ const toArray = (gesture as { toGestureArray?: unknown }).toGestureArray;
243
+ if (typeof toArray === 'function') {
244
+ const list = (toArray as () => unknown).call(gesture);
245
+ if (Array.isArray(list)) {
246
+ for (const item of list) {
247
+ const tag = handlerTagOf(item) ?? handlerTagOf(readHandlers(item));
248
+ add(tag, disallowsInterruption(item));
249
+ }
250
+ }
251
+ }
252
+ }
253
+ return { pressable, container: container && pressable.length === 0 };
254
+ } catch {
255
+ return { pressable: [], container: false };
256
+ }
257
+ }
258
+
259
+ /** Gesture-handler scroll containers set this so a drag is not stolen. */
260
+ function disallowsInterruption(value: unknown): boolean {
261
+ if (!value || typeof value !== 'object' || !('config' in value)) return false;
262
+ const config = (value as { config?: unknown }).config;
263
+ if (!config || typeof config !== 'object' || !('disallowInterruption' in config)) return false;
264
+ return (config as { disallowInterruption?: unknown }).disallowInterruption === true;
265
+ }
266
+
267
+ function readHandlers(value: unknown): unknown {
268
+ if (!value || typeof value !== 'object' || !('handlers' in value)) return undefined;
269
+ return (value as { handlers?: unknown }).handlers;
270
+ }
271
+
272
+ /** A mounted gesture-handler tag. `-1` means "not attached yet". */
273
+ function handlerTagOf(value: unknown): number | undefined {
274
+ if (!value || typeof value !== 'object' || !('handlerTag' in value)) return undefined;
275
+ const tag = (value as { handlerTag?: unknown }).handlerTag;
276
+ if (typeof tag !== 'number' || !Number.isFinite(tag) || tag < 0) return undefined;
277
+ return tag;
278
+ }
279
+
280
+ function deviceEventEmitter(): DeviceEventEmitterLike | null {
281
+ try {
282
+ // Lazy so a tap that is never cancelled does not load React Native's emitter
283
+ // into unit tests that only exercise the monitors.
284
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
285
+ const reactNative = require('react-native') as {
286
+ DeviceEventEmitter?: DeviceEventEmitterLike;
287
+ };
288
+ const emitter = reactNative.DeviceEventEmitter;
289
+ if (!emitter || typeof emitter.addListener !== 'function') return null;
290
+ return emitter;
291
+ } catch {
292
+ return null;
293
+ }
294
+ }
295
+
296
+ function asPayload(event: unknown): GestureStatePayload | null {
297
+ if (!event || typeof event !== 'object') return null;
298
+ const raw = event as Record<string, unknown>;
299
+ const body =
300
+ raw.nativeEvent && typeof raw.nativeEvent === 'object'
301
+ ? (raw.nativeEvent as Record<string, unknown>)
302
+ : raw;
303
+ if (typeof body.handlerTag !== 'number' || typeof body.state !== 'number') return null;
304
+ return {
305
+ handlerTag: body.handlerTag,
306
+ state: body.state,
307
+ ...(typeof body.oldState === 'number' ? { oldState: body.oldState } : {}),
308
+ ...(typeof body.pointerInside === 'boolean' ? { pointerInside: body.pointerInside } : {}),
309
+ ...(typeof body.translationX === 'number' ? { translationX: body.translationX } : {}),
310
+ ...(typeof body.translationY === 'number' ? { translationY: body.translationY } : {}),
311
+ ...(typeof body.absoluteX === 'number' ? { absoluteX: body.absoluteX } : {}),
312
+ ...(typeof body.absoluteY === 'number' ? { absoluteY: body.absoluteY } : {}),
313
+ hasDuration: 'duration' in body && body.duration != null,
314
+ };
315
+ }
316
+
317
+ function isTerminal(state: number): boolean {
318
+ return (
319
+ state === GESTURE_HANDLER_STATE.FAILED ||
320
+ state === GESTURE_HANDLER_STATE.CANCELLED ||
321
+ state === GESTURE_HANDLER_STATE.END
322
+ );
323
+ }
324
+
325
+ function isCompletedTap(touch: PendingTouch, payload: GestureStatePayload): boolean {
326
+ if (payload.state !== GESTURE_HANDLER_STATE.END) return false;
327
+ if (payload.oldState !== GESTURE_HANDLER_STATE.ACTIVE) return false;
328
+ if (payload.pointerInside === false) return false;
329
+ if (payload.hasDuration) return false;
330
+ if (
331
+ payload.translationX !== undefined &&
332
+ payload.translationY !== undefined &&
333
+ !withinTapSlop(
334
+ { pageX: 0, pageY: 0 },
335
+ { pageX: payload.translationX, pageY: payload.translationY },
336
+ )
337
+ ) {
338
+ return false;
339
+ }
340
+ if (payload.absoluteX !== undefined && payload.absoluteY !== undefined) {
341
+ if (
342
+ !withinTapSlop(
343
+ { pageX: touch.pageX, pageY: touch.pageY },
344
+ { pageX: payload.absoluteX, pageY: payload.absoluteY },
345
+ )
346
+ ) {
347
+ return false;
348
+ }
349
+ }
350
+ return true;
351
+ }
352
+
353
+ function endPoint(payload: GestureStatePayload): TouchPoint | null {
354
+ if (payload.absoluteX === undefined || payload.absoluteY === undefined) return null;
355
+ return { pageX: payload.absoluteX, pageY: payload.absoluteY };
356
+ }
@@ -15,6 +15,8 @@ import {
15
15
  setCachedFiberAnchor,
16
16
  } from './fiberAnchorCache';
17
17
 
18
+ export { subscribeAnchorScreenChange } from './fiberAnchorCache';
19
+
18
20
  export type AnchorResolution =
19
21
  | { status: 'found'; host: AnchorHost; fiber?: AnyFiber }
20
22
  | { status: 'ambiguous' }