@userflow-tech/react-native 0.4.4 → 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 (431) hide show
  1. package/README.md +124 -34
  2. package/docs/anchoring.md +6 -1
  3. package/docs/deep-linking.md +26 -0
  4. package/docs/troubleshooting.md +50 -23
  5. package/expo-plugin/hostDependencyWarnings.d.ts +6 -4
  6. package/expo-plugin/hostDependencyWarnings.d.ts.map +1 -1
  7. package/expo-plugin/hostDependencyWarnings.js +8 -6
  8. package/expo-plugin/hostDependencyWarnings.js.map +1 -1
  9. package/lib/commonjs/anchors/AutoAnchorRegistry.js +144 -34
  10. package/lib/commonjs/anchors/AutoAnchorRegistry.js.map +1 -1
  11. package/lib/commonjs/anchors/animationFrame.js +29 -1
  12. package/lib/commonjs/anchors/animationFrame.js.map +1 -1
  13. package/lib/commonjs/anchors/appActive.js +61 -0
  14. package/lib/commonjs/anchors/appActive.js.map +1 -0
  15. package/lib/commonjs/anchors/elementConditionEvaluator.js +25 -8
  16. package/lib/commonjs/anchors/elementConditionEvaluator.js.map +1 -1
  17. package/lib/commonjs/anchors/elementTapMonitor.js +67 -12
  18. package/lib/commonjs/anchors/elementTapMonitor.js.map +1 -1
  19. package/lib/commonjs/anchors/elementTargetMatch.js +33 -7
  20. package/lib/commonjs/anchors/elementTargetMatch.js.map +1 -1
  21. package/lib/commonjs/anchors/fiber/fiberInternals.js +66 -0
  22. package/lib/commonjs/anchors/fiber/fiberInternals.js.map +1 -1
  23. package/lib/commonjs/anchors/fiber/indexHostFibers.js +4 -3
  24. package/lib/commonjs/anchors/fiber/indexHostFibers.js.map +1 -1
  25. package/lib/commonjs/anchors/fiber/resolveFiberPath.js +30 -15
  26. package/lib/commonjs/anchors/fiber/resolveFiberPath.js.map +1 -1
  27. package/lib/commonjs/anchors/fiberAnchorCache.js +20 -0
  28. package/lib/commonjs/anchors/fiberAnchorCache.js.map +1 -1
  29. package/lib/commonjs/anchors/gestureHandlerHandoff.js +304 -0
  30. package/lib/commonjs/anchors/gestureHandlerHandoff.js.map +1 -0
  31. package/lib/commonjs/anchors/resolveAnchorHost.js +6 -0
  32. package/lib/commonjs/anchors/resolveAnchorHost.js.map +1 -1
  33. package/lib/commonjs/anchors/resolveAutoAnchorHost.js +11 -6
  34. package/lib/commonjs/anchors/resolveAutoAnchorHost.js.map +1 -1
  35. package/lib/commonjs/anchors/resolveElementTargets.js +63 -8
  36. package/lib/commonjs/anchors/resolveElementTargets.js.map +1 -1
  37. package/lib/commonjs/anchors/semanticMatcher.js +43 -21
  38. package/lib/commonjs/anchors/semanticMatcher.js.map +1 -1
  39. package/lib/commonjs/anchors/tapSlop.js +16 -0
  40. package/lib/commonjs/anchors/tapSlop.js.map +1 -0
  41. package/lib/commonjs/anchors/targetPressMonitor.js +66 -12
  42. package/lib/commonjs/anchors/targetPressMonitor.js.map +1 -1
  43. package/lib/commonjs/authoring/fingerprintFromInstance.js +5 -9
  44. package/lib/commonjs/authoring/fingerprintFromInstance.js.map +1 -1
  45. package/lib/commonjs/content/RichContentRenderer.js +3 -3
  46. package/lib/commonjs/content/RichContentRenderer.js.map +1 -1
  47. package/lib/commonjs/content/openSafeUrl.js +3 -1
  48. package/lib/commonjs/content/openSafeUrl.js.map +1 -1
  49. package/lib/commonjs/expo-router/useUserflowExpoRouter.js +6 -0
  50. package/lib/commonjs/expo-router/useUserflowExpoRouter.js.map +1 -1
  51. package/lib/commonjs/index.js +19 -5
  52. package/lib/commonjs/index.js.map +1 -1
  53. package/lib/commonjs/installCheck.js +2 -2
  54. package/lib/commonjs/installCheck.js.map +1 -1
  55. package/lib/commonjs/navigation/navigateUrlUtils.js +105 -0
  56. package/lib/commonjs/navigation/navigateUrlUtils.js.map +1 -0
  57. package/lib/commonjs/navigation/openExternalNavigateUrl.js +14 -0
  58. package/lib/commonjs/navigation/openExternalNavigateUrl.js.map +1 -0
  59. package/lib/commonjs/navigation/openFlowNavigateUrl.js +47 -41
  60. package/lib/commonjs/navigation/openFlowNavigateUrl.js.map +1 -1
  61. package/lib/commonjs/pairing/PairingController.js +3 -1
  62. package/lib/commonjs/pairing/PairingController.js.map +1 -1
  63. package/lib/commonjs/runtime/BannerHost.js +38 -29
  64. package/lib/commonjs/runtime/BannerHost.js.map +1 -1
  65. package/lib/commonjs/runtime/TargetPressBridge.js +55 -7
  66. package/lib/commonjs/runtime/TargetPressBridge.js.map +1 -1
  67. package/lib/commonjs/runtime/UserflowOverlay.js +5 -11
  68. package/lib/commonjs/runtime/UserflowOverlay.js.map +1 -1
  69. package/lib/commonjs/runtime/UserflowProvider.js +66 -40
  70. package/lib/commonjs/runtime/UserflowProvider.js.map +1 -1
  71. package/lib/commonjs/runtime/bannerEngine.js +8 -2
  72. package/lib/commonjs/runtime/bannerEngine.js.map +1 -1
  73. package/lib/commonjs/runtime/bannerFlowSpacer.js +71 -0
  74. package/lib/commonjs/runtime/bannerFlowSpacer.js.map +1 -0
  75. package/lib/commonjs/runtime/flowEngine.js +11 -0
  76. package/lib/commonjs/runtime/flowEngine.js.map +1 -1
  77. package/lib/commonjs/runtime/previewStart.js +39 -0
  78. package/lib/commonjs/runtime/previewStart.js.map +1 -0
  79. package/lib/commonjs/runtime/pushBannerLayoutContext.js +98 -0
  80. package/lib/commonjs/runtime/pushBannerLayoutContext.js.map +1 -0
  81. package/lib/commonjs/screenTracker.js +12 -0
  82. package/lib/commonjs/screenTracker.js.map +1 -1
  83. package/lib/commonjs/surfaces/BannerBar.js +79 -61
  84. package/lib/commonjs/surfaces/BannerBar.js.map +1 -1
  85. package/lib/commonjs/surfaces/BannerSurface.js +26 -0
  86. package/lib/commonjs/surfaces/BannerSurface.js.map +1 -1
  87. package/lib/commonjs/surfaces/TooltipSurface.js +43 -41
  88. package/lib/commonjs/surfaces/TooltipSurface.js.map +1 -1
  89. package/lib/commonjs/surfaces/bannerBarLayout.js +77 -10
  90. package/lib/commonjs/surfaces/bannerBarLayout.js.map +1 -1
  91. package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js +112 -63
  92. package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
  93. package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js +59 -11
  94. package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
  95. package/lib/commonjs/surfaces/hostAccessibilityTrap.js +28 -0
  96. package/lib/commonjs/surfaces/hostAccessibilityTrap.js.map +1 -0
  97. package/lib/commonjs/surfaces/resolveBannerTheme.js +6 -3
  98. package/lib/commonjs/surfaces/resolveBannerTheme.js.map +1 -1
  99. package/lib/commonjs/surfaces/tooltip/TooltipBubble.js +3 -2
  100. package/lib/commonjs/surfaces/tooltip/TooltipBubble.js.map +1 -1
  101. package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js +134 -0
  102. package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
  103. package/lib/commonjs/version.js +1 -1
  104. package/lib/module/anchors/AutoAnchorRegistry.js +145 -35
  105. package/lib/module/anchors/AutoAnchorRegistry.js.map +1 -1
  106. package/lib/module/anchors/animationFrame.js +29 -2
  107. package/lib/module/anchors/animationFrame.js.map +1 -1
  108. package/lib/module/anchors/appActive.js +57 -0
  109. package/lib/module/anchors/appActive.js.map +1 -0
  110. package/lib/module/anchors/elementConditionEvaluator.js +26 -9
  111. package/lib/module/anchors/elementConditionEvaluator.js.map +1 -1
  112. package/lib/module/anchors/elementTapMonitor.js +67 -12
  113. package/lib/module/anchors/elementTapMonitor.js.map +1 -1
  114. package/lib/module/anchors/elementTargetMatch.js +32 -7
  115. package/lib/module/anchors/elementTargetMatch.js.map +1 -1
  116. package/lib/module/anchors/fiber/fiberInternals.js +65 -0
  117. package/lib/module/anchors/fiber/fiberInternals.js.map +1 -1
  118. package/lib/module/anchors/fiber/indexHostFibers.js +4 -3
  119. package/lib/module/anchors/fiber/indexHostFibers.js.map +1 -1
  120. package/lib/module/anchors/fiber/resolveFiberPath.js +30 -15
  121. package/lib/module/anchors/fiber/resolveFiberPath.js.map +1 -1
  122. package/lib/module/anchors/fiberAnchorCache.js +19 -0
  123. package/lib/module/anchors/fiberAnchorCache.js.map +1 -1
  124. package/lib/module/anchors/gestureHandlerHandoff.js +298 -0
  125. package/lib/module/anchors/gestureHandlerHandoff.js.map +1 -0
  126. package/lib/module/anchors/resolveAnchorHost.js +1 -0
  127. package/lib/module/anchors/resolveAnchorHost.js.map +1 -1
  128. package/lib/module/anchors/resolveAutoAnchorHost.js +11 -6
  129. package/lib/module/anchors/resolveAutoAnchorHost.js.map +1 -1
  130. package/lib/module/anchors/resolveElementTargets.js +63 -8
  131. package/lib/module/anchors/resolveElementTargets.js.map +1 -1
  132. package/lib/module/anchors/semanticMatcher.js +43 -21
  133. package/lib/module/anchors/semanticMatcher.js.map +1 -1
  134. package/lib/module/anchors/tapSlop.js +11 -0
  135. package/lib/module/anchors/tapSlop.js.map +1 -0
  136. package/lib/module/anchors/targetPressMonitor.js +53 -8
  137. package/lib/module/anchors/targetPressMonitor.js.map +1 -1
  138. package/lib/module/authoring/fingerprintFromInstance.js +6 -10
  139. package/lib/module/authoring/fingerprintFromInstance.js.map +1 -1
  140. package/lib/module/content/RichContentRenderer.js +3 -3
  141. package/lib/module/content/RichContentRenderer.js.map +1 -1
  142. package/lib/module/content/openSafeUrl.js +3 -1
  143. package/lib/module/content/openSafeUrl.js.map +1 -1
  144. package/lib/module/expo-router/useUserflowExpoRouter.js +7 -1
  145. package/lib/module/expo-router/useUserflowExpoRouter.js.map +1 -1
  146. package/lib/module/index.js +22 -7
  147. package/lib/module/index.js.map +1 -1
  148. package/lib/module/installCheck.js +2 -2
  149. package/lib/module/installCheck.js.map +1 -1
  150. package/lib/module/navigation/navigateUrlUtils.js +94 -0
  151. package/lib/module/navigation/navigateUrlUtils.js.map +1 -0
  152. package/lib/module/navigation/openExternalNavigateUrl.js +11 -0
  153. package/lib/module/navigation/openExternalNavigateUrl.js.map +1 -0
  154. package/lib/module/navigation/openFlowNavigateUrl.js +22 -36
  155. package/lib/module/navigation/openFlowNavigateUrl.js.map +1 -1
  156. package/lib/module/pairing/PairingController.js +3 -1
  157. package/lib/module/pairing/PairingController.js.map +1 -1
  158. package/lib/module/runtime/BannerHost.js +38 -29
  159. package/lib/module/runtime/BannerHost.js.map +1 -1
  160. package/lib/module/runtime/TargetPressBridge.js +56 -8
  161. package/lib/module/runtime/TargetPressBridge.js.map +1 -1
  162. package/lib/module/runtime/UserflowOverlay.js +5 -11
  163. package/lib/module/runtime/UserflowOverlay.js.map +1 -1
  164. package/lib/module/runtime/UserflowProvider.js +66 -40
  165. package/lib/module/runtime/UserflowProvider.js.map +1 -1
  166. package/lib/module/runtime/bannerEngine.js +7 -2
  167. package/lib/module/runtime/bannerEngine.js.map +1 -1
  168. package/lib/module/runtime/bannerFlowSpacer.js +65 -0
  169. package/lib/module/runtime/bannerFlowSpacer.js.map +1 -0
  170. package/lib/module/runtime/flowEngine.js +11 -0
  171. package/lib/module/runtime/flowEngine.js.map +1 -1
  172. package/lib/module/runtime/previewStart.js +35 -0
  173. package/lib/module/runtime/previewStart.js.map +1 -0
  174. package/lib/module/runtime/pushBannerLayoutContext.js +91 -0
  175. package/lib/module/runtime/pushBannerLayoutContext.js.map +1 -0
  176. package/lib/module/screenTracker.js +11 -0
  177. package/lib/module/screenTracker.js.map +1 -1
  178. package/lib/module/surfaces/BannerBar.js +80 -62
  179. package/lib/module/surfaces/BannerBar.js.map +1 -1
  180. package/lib/module/surfaces/BannerSurface.js +26 -0
  181. package/lib/module/surfaces/BannerSurface.js.map +1 -1
  182. package/lib/module/surfaces/TooltipSurface.js +44 -42
  183. package/lib/module/surfaces/TooltipSurface.js.map +1 -1
  184. package/lib/module/surfaces/bannerBarLayout.js +72 -11
  185. package/lib/module/surfaces/bannerBarLayout.js.map +1 -1
  186. package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js +113 -64
  187. package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
  188. package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js +59 -11
  189. package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
  190. package/lib/module/surfaces/hostAccessibilityTrap.js +24 -0
  191. package/lib/module/surfaces/hostAccessibilityTrap.js.map +1 -0
  192. package/lib/module/surfaces/resolveBannerTheme.js +6 -3
  193. package/lib/module/surfaces/resolveBannerTheme.js.map +1 -1
  194. package/lib/module/surfaces/tooltip/TooltipBubble.js +3 -2
  195. package/lib/module/surfaces/tooltip/TooltipBubble.js.map +1 -1
  196. package/lib/module/surfaces/tooltip/targetSearchSchedule.js +128 -0
  197. package/lib/module/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
  198. package/lib/module/version.js +1 -1
  199. package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts +36 -2
  200. package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
  201. package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts +0 -4
  202. package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts.map +1 -1
  203. package/lib/typescript/commonjs/src/anchors/appActive.d.ts +8 -0
  204. package/lib/typescript/commonjs/src/anchors/appActive.d.ts.map +1 -0
  205. package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts +17 -0
  206. package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts.map +1 -1
  207. package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts +7 -0
  208. package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts.map +1 -1
  209. package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts +7 -0
  210. package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
  211. package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts +3 -2
  212. package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
  213. package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
  214. package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
  215. package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts +5 -0
  216. package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts.map +1 -1
  217. package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts +70 -0
  218. package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
  219. package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts +1 -0
  220. package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts.map +1 -1
  221. package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
  222. package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
  223. package/lib/typescript/commonjs/src/anchors/resolveElementTargets.d.ts.map +1 -1
  224. package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts +2 -0
  225. package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts.map +1 -1
  226. package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts +9 -0
  227. package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts.map +1 -0
  228. package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts +13 -8
  229. package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts.map +1 -1
  230. package/lib/typescript/commonjs/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
  231. package/lib/typescript/commonjs/src/content/RichContentRenderer.d.ts.map +1 -1
  232. package/lib/typescript/commonjs/src/content/openSafeUrl.d.ts.map +1 -1
  233. package/lib/typescript/commonjs/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
  234. package/lib/typescript/commonjs/src/index.d.ts +6 -5
  235. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  236. package/lib/typescript/commonjs/src/installCheck.d.ts +1 -1
  237. package/lib/typescript/commonjs/src/installCheck.d.ts.map +1 -1
  238. package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts +15 -0
  239. package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts.map +1 -0
  240. package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts +3 -0
  241. package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
  242. package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts +2 -5
  243. package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
  244. package/lib/typescript/commonjs/src/pairing/PairingController.d.ts +6 -0
  245. package/lib/typescript/commonjs/src/pairing/PairingController.d.ts.map +1 -1
  246. package/lib/typescript/commonjs/src/runtime/BannerHost.d.ts.map +1 -1
  247. package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts +3 -0
  248. package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts.map +1 -1
  249. package/lib/typescript/commonjs/src/runtime/UserflowOverlay.d.ts.map +1 -1
  250. package/lib/typescript/commonjs/src/runtime/UserflowProvider.d.ts.map +1 -1
  251. package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts +5 -1
  252. package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts.map +1 -1
  253. package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts +15 -0
  254. package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
  255. package/lib/typescript/commonjs/src/runtime/flowEngine.d.ts.map +1 -1
  256. package/lib/typescript/commonjs/src/runtime/previewStart.d.ts +13 -0
  257. package/lib/typescript/commonjs/src/runtime/previewStart.d.ts.map +1 -0
  258. package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts +17 -0
  259. package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
  260. package/lib/typescript/commonjs/src/screenTracker.d.ts +6 -0
  261. package/lib/typescript/commonjs/src/screenTracker.d.ts.map +1 -1
  262. package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts +3 -1
  263. package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts.map +1 -1
  264. package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts +5 -1
  265. package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts.map +1 -1
  266. package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts +1 -1
  267. package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts.map +1 -1
  268. package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts +17 -0
  269. package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts.map +1 -1
  270. package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
  271. package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
  272. package/lib/typescript/commonjs/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
  273. package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
  274. package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
  275. package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts +4 -1
  276. package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
  277. package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
  278. package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
  279. package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
  280. package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
  281. package/lib/typescript/commonjs/src/version.d.ts +1 -1
  282. package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts +36 -2
  283. package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
  284. package/lib/typescript/module/src/anchors/animationFrame.d.ts +0 -4
  285. package/lib/typescript/module/src/anchors/animationFrame.d.ts.map +1 -1
  286. package/lib/typescript/module/src/anchors/appActive.d.ts +8 -0
  287. package/lib/typescript/module/src/anchors/appActive.d.ts.map +1 -0
  288. package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts +17 -0
  289. package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts.map +1 -1
  290. package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts +7 -0
  291. package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts.map +1 -1
  292. package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts +7 -0
  293. package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
  294. package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts +3 -2
  295. package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
  296. package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
  297. package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
  298. package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts +5 -0
  299. package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts.map +1 -1
  300. package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts +70 -0
  301. package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
  302. package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts +1 -0
  303. package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts.map +1 -1
  304. package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
  305. package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
  306. package/lib/typescript/module/src/anchors/resolveElementTargets.d.ts.map +1 -1
  307. package/lib/typescript/module/src/anchors/semanticMatcher.d.ts +2 -0
  308. package/lib/typescript/module/src/anchors/semanticMatcher.d.ts.map +1 -1
  309. package/lib/typescript/module/src/anchors/tapSlop.d.ts +9 -0
  310. package/lib/typescript/module/src/anchors/tapSlop.d.ts.map +1 -0
  311. package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts +13 -8
  312. package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts.map +1 -1
  313. package/lib/typescript/module/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
  314. package/lib/typescript/module/src/content/RichContentRenderer.d.ts.map +1 -1
  315. package/lib/typescript/module/src/content/openSafeUrl.d.ts.map +1 -1
  316. package/lib/typescript/module/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
  317. package/lib/typescript/module/src/index.d.ts +6 -5
  318. package/lib/typescript/module/src/index.d.ts.map +1 -1
  319. package/lib/typescript/module/src/installCheck.d.ts +1 -1
  320. package/lib/typescript/module/src/installCheck.d.ts.map +1 -1
  321. package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts +15 -0
  322. package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts.map +1 -0
  323. package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts +3 -0
  324. package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
  325. package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts +2 -5
  326. package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
  327. package/lib/typescript/module/src/pairing/PairingController.d.ts +6 -0
  328. package/lib/typescript/module/src/pairing/PairingController.d.ts.map +1 -1
  329. package/lib/typescript/module/src/runtime/BannerHost.d.ts.map +1 -1
  330. package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts +3 -0
  331. package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts.map +1 -1
  332. package/lib/typescript/module/src/runtime/UserflowOverlay.d.ts.map +1 -1
  333. package/lib/typescript/module/src/runtime/UserflowProvider.d.ts.map +1 -1
  334. package/lib/typescript/module/src/runtime/bannerEngine.d.ts +5 -1
  335. package/lib/typescript/module/src/runtime/bannerEngine.d.ts.map +1 -1
  336. package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts +15 -0
  337. package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
  338. package/lib/typescript/module/src/runtime/flowEngine.d.ts.map +1 -1
  339. package/lib/typescript/module/src/runtime/previewStart.d.ts +13 -0
  340. package/lib/typescript/module/src/runtime/previewStart.d.ts.map +1 -0
  341. package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts +17 -0
  342. package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
  343. package/lib/typescript/module/src/screenTracker.d.ts +6 -0
  344. package/lib/typescript/module/src/screenTracker.d.ts.map +1 -1
  345. package/lib/typescript/module/src/surfaces/BannerBar.d.ts +3 -1
  346. package/lib/typescript/module/src/surfaces/BannerBar.d.ts.map +1 -1
  347. package/lib/typescript/module/src/surfaces/BannerSurface.d.ts +5 -1
  348. package/lib/typescript/module/src/surfaces/BannerSurface.d.ts.map +1 -1
  349. package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts +1 -1
  350. package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts.map +1 -1
  351. package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts +17 -0
  352. package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts.map +1 -1
  353. package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
  354. package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
  355. package/lib/typescript/module/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
  356. package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
  357. package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
  358. package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts +4 -1
  359. package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
  360. package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
  361. package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
  362. package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
  363. package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
  364. package/lib/typescript/module/src/version.d.ts +1 -1
  365. package/package.json +1 -1
  366. package/src/anchors/AutoAnchorRegistry.ts +166 -35
  367. package/src/anchors/animationFrame.ts +31 -1
  368. package/src/anchors/appActive.ts +63 -0
  369. package/src/anchors/elementConditionEvaluator.ts +29 -12
  370. package/src/anchors/elementTapMonitor.ts +68 -10
  371. package/src/anchors/elementTargetMatch.ts +34 -7
  372. package/src/anchors/fiber/fiberInternals.ts +68 -0
  373. package/src/anchors/fiber/indexHostFibers.ts +8 -3
  374. package/src/anchors/fiber/resolveFiberPath.ts +25 -16
  375. package/src/anchors/fiberAnchorCache.ts +19 -0
  376. package/src/anchors/gestureHandlerHandoff.ts +356 -0
  377. package/src/anchors/resolveAnchorHost.ts +2 -0
  378. package/src/anchors/resolveAutoAnchorHost.ts +18 -8
  379. package/src/anchors/resolveElementTargets.ts +64 -7
  380. package/src/anchors/semanticMatcher.ts +48 -27
  381. package/src/anchors/tapSlop.ts +18 -0
  382. package/src/anchors/targetPressMonitor.ts +56 -19
  383. package/src/authoring/fingerprintFromInstance.ts +4 -5
  384. package/src/content/RichContentRenderer.tsx +4 -3
  385. package/src/content/openSafeUrl.ts +3 -1
  386. package/src/expo-router/useUserflowExpoRouter.ts +10 -1
  387. package/src/index.ts +20 -9
  388. package/src/installCheck.ts +4 -4
  389. package/src/navigation/navigateUrlUtils.ts +100 -0
  390. package/src/navigation/openExternalNavigateUrl.ts +9 -0
  391. package/src/navigation/openFlowNavigateUrl.ts +36 -36
  392. package/src/pairing/PairingController.ts +9 -1
  393. package/src/runtime/BannerHost.tsx +41 -23
  394. package/src/runtime/TargetPressBridge.tsx +66 -9
  395. package/src/runtime/UserflowOverlay.tsx +7 -9
  396. package/src/runtime/UserflowProvider.tsx +56 -32
  397. package/src/runtime/bannerEngine.ts +10 -2
  398. package/src/runtime/bannerFlowSpacer.tsx +70 -0
  399. package/src/runtime/flowEngine.ts +11 -0
  400. package/src/runtime/previewStart.ts +49 -0
  401. package/src/runtime/pushBannerLayoutContext.tsx +121 -0
  402. package/src/screenTracker.ts +11 -0
  403. package/src/surfaces/BannerBar.tsx +93 -55
  404. package/src/surfaces/BannerSurface.tsx +59 -3
  405. package/src/surfaces/TooltipSurface.tsx +63 -51
  406. package/src/surfaces/bannerBarLayout.ts +80 -7
  407. package/src/surfaces/bannerWebView/BannerWebViewBar.tsx +149 -57
  408. package/src/surfaces/bannerWebView/buildCustomBannerHtml.ts +63 -11
  409. package/src/surfaces/hostAccessibilityTrap.ts +25 -0
  410. package/src/surfaces/resolveBannerTheme.ts +14 -3
  411. package/src/surfaces/tooltip/TooltipBubble.tsx +8 -1
  412. package/src/surfaces/tooltip/targetSearchSchedule.ts +140 -0
  413. package/src/version.ts +1 -1
  414. package/vendor/core/dist/engine/BannerSessionEngine.d.ts +29 -2
  415. package/vendor/core/dist/engine/BannerSessionEngine.d.ts.map +1 -1
  416. package/vendor/core/dist/engine/BannerSessionEngine.js +87 -6
  417. package/vendor/core/dist/engine/BannerSessionEngine.js.map +1 -1
  418. package/vendor/core/dist/engine/ClientConditionEngine.d.ts.map +1 -1
  419. package/vendor/core/dist/engine/ClientConditionEngine.js +30 -7
  420. package/vendor/core/dist/engine/ClientConditionEngine.js.map +1 -1
  421. package/vendor/core/dist/engine/PairingEngine.d.ts +6 -1
  422. package/vendor/core/dist/engine/PairingEngine.d.ts.map +1 -1
  423. package/vendor/core/dist/engine/PairingEngine.js +23 -3
  424. package/vendor/core/dist/engine/PairingEngine.js.map +1 -1
  425. package/vendor/core/dist/engine/handleActions.d.ts.map +1 -1
  426. package/vendor/core/dist/engine/handleActions.js +67 -2
  427. package/vendor/core/dist/engine/handleActions.js.map +1 -1
  428. package/vendor/core/dist/version.d.ts +1 -1
  429. package/vendor/core/dist/version.js +1 -1
  430. package/vendor/core/package.json +1 -1
  431. package/vendor/protocol/package.json +1 -1
@@ -24,6 +24,17 @@ function locationKey(location: ClientLocationUpdate): string {
24
24
  return 'path' in location ? `path:${location.path}` : `screen:${location.screen}`;
25
25
  }
26
26
 
27
+ /**
28
+ * The screen id stored on an AUTO envelope (`path` for Expo Router, `screen`
29
+ * otherwise). Missing or blank locations are `'unknown'`, the same sentinel
30
+ * the element picker writes when it has not seen a screen yet.
31
+ */
32
+ export function screenIdFromLocation(location: ClientLocationUpdate | null | undefined): string {
33
+ if (!location) return 'unknown';
34
+ const id = 'path' in location ? location.path : location.screen;
35
+ return id || 'unknown';
36
+ }
37
+
27
38
  /** Event attributes for `screen_viewed`, or `null` when the location has no identity. */
28
39
  export function screenViewedAttributes(
29
40
  location: ClientLocationUpdate,
@@ -11,14 +11,18 @@ import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
11
11
  import { BannerStepButton } from '../content/BannerStepButton';
12
12
  import { RichContentRenderer } from '../content/RichContentRenderer';
13
13
  import { mixThemeColors } from '../content/themeColor';
14
+ import { usePushBannerHostInsetRegistration } from '../runtime/pushBannerLayoutContext';
14
15
 
15
16
  import {
16
17
  BANNER_CLOSE_SLOT_SIZE,
17
18
  BANNER_ROOT_PADDING,
19
+ bannerBarRootStyle,
18
20
  bannerFrameStyle,
19
21
  bannerInnerStyle,
22
+ bannerPushLayoutChrome,
20
23
  resolveBannerBarBorderRadius,
21
24
  } from './bannerBarLayout';
25
+ import { useUserflowSafeAreaInsets } from './safeArea';
22
26
  import {
23
27
  BANNER_BUTTON_GAP,
24
28
  BANNER_CLOSE_BORDER_RADIUS,
@@ -28,6 +32,8 @@ import { resolveBannerBackgroundColor, resolveBannerForegroundColor } from './re
28
32
 
29
33
  export interface BannerBarProps {
30
34
  session: BannerSessionFragment;
35
+ pushLayoutEdge?: 'top' | 'bottom' | null;
36
+ overlaySafeEdge?: 'top' | 'bottom' | null;
31
37
  onActions: (actions: FlowActionFragment[]) => void;
32
38
  onDismiss: (endReason: EndFlowReasonInput) => void;
33
39
  }
@@ -38,6 +44,8 @@ export interface BannerBarProps {
38
44
  */
39
45
  export function BannerBar({
40
46
  session,
47
+ pushLayoutEdge = null,
48
+ overlaySafeEdge = null,
41
49
  onActions,
42
50
  onDismiss,
43
51
  }: BannerBarProps): React.ReactElement | null {
@@ -50,10 +58,20 @@ export function BannerBar({
50
58
  [theme, backgroundColor, foregroundColor],
51
59
  );
52
60
  const lookupAttribute = React.useMemo(() => genSessionDataLookup(session.data), [session.data]);
53
- const opacity = React.useRef(new Animated.Value(banner?.animate ? 0 : 1)).current;
61
+ const insets = useUserflowSafeAreaInsets();
62
+ const overlay = banner?.overlay !== false;
63
+ const hostInsetEdge = !overlay && pushLayoutEdge ? pushLayoutEdge : null;
64
+ const safeAreaEdge = hostInsetEdge ?? overlaySafeEdge ?? null;
65
+ const onPushChromeLayout = usePushBannerHostInsetRegistration(
66
+ hostInsetEdge,
67
+ session.id,
68
+ hostInsetEdge != null,
69
+ );
70
+ const fadeIn = banner?.animate === true && hostInsetEdge == null;
71
+ const opacity = React.useRef(new Animated.Value(fadeIn ? 0 : 1)).current;
54
72
 
55
73
  React.useEffect(() => {
56
- if (!banner?.animate) {
74
+ if (!fadeIn) {
57
75
  opacity.setValue(1);
58
76
  return;
59
77
  }
@@ -67,73 +85,93 @@ export function BannerBar({
67
85
  return () => {
68
86
  animation.stop();
69
87
  };
70
- }, [banner?.animate, opacity, session.id]);
88
+ }, [fadeIn, opacity, session.id]);
71
89
 
72
90
  if (!banner) return null;
73
91
 
74
- const overlay = banner.overlay !== false;
75
92
  const frameStyle = bannerFrameStyle(banner);
93
+ const rootStyle = bannerBarRootStyle(banner);
76
94
  const innerStyle = bannerInnerStyle(banner);
77
95
  const closePressedBackground = mixThemeColors(backgroundColor, foregroundColor, 0.12);
78
96
  const overlayEdge = banner.embedMode === BannerEmbedMode.BODY_LAST ? 'bottom' : 'top';
79
97
  const barRadius = resolveBannerBarBorderRadius(banner);
98
+ const insetChrome = safeAreaEdge
99
+ ? bannerPushLayoutChrome(safeAreaEdge, insets, backgroundColor)
100
+ : null;
101
+ const insetChromeOutsideFade = insetChrome != null && fadeIn;
80
102
 
81
- return (
82
- <View testID="userflow-banner-frame" style={frameStyle}>
83
- <Animated.View style={[{ opacity }, overlay ? bannerOverlayShadowStyle(overlayEdge) : null]}>
84
- <View
85
- testID="userflow-banner"
86
- style={[
87
- styles.root,
88
- { backgroundColor },
89
- barRadius != null ? { borderRadius: barRadius, overflow: 'hidden' as const } : null,
90
- ]}
91
- >
92
- <View style={styles.startSpacer} />
93
- <View style={innerStyle}>
94
- <View style={styles.content}>
95
- <RichContentRenderer
96
- content={banner.content}
97
- buttons={[]}
103
+ const bar = (
104
+ <View
105
+ testID="userflow-banner"
106
+ style={[
107
+ styles.root,
108
+ rootStyle,
109
+ { backgroundColor },
110
+ barRadius != null ? { borderRadius: barRadius, overflow: 'hidden' as const } : null,
111
+ ]}
112
+ >
113
+ <View style={styles.startSpacer} />
114
+ <View style={innerStyle}>
115
+ <View style={styles.content}>
116
+ <RichContentRenderer
117
+ content={banner.content}
118
+ buttons={[]}
119
+ theme={bannerTheme}
120
+ sessionData={session.data}
121
+ lookupAttribute={lookupAttribute}
122
+ onActions={onActions}
123
+ />
124
+ </View>
125
+ {banner.buttons.length > 0 ? (
126
+ <View style={styles.buttons}>
127
+ {banner.buttons.map((button) => (
128
+ <BannerStepButton
129
+ key={button.id}
130
+ button={button}
98
131
  theme={bannerTheme}
99
132
  sessionData={session.data}
100
- lookupAttribute={lookupAttribute}
101
133
  onActions={onActions}
102
134
  />
103
- </View>
104
- {banner.buttons.length > 0 ? (
105
- <View style={styles.buttons}>
106
- {banner.buttons.map((button) => (
107
- <BannerStepButton
108
- key={button.id}
109
- button={button}
110
- theme={bannerTheme}
111
- sessionData={session.data}
112
- onActions={onActions}
113
- />
114
- ))}
115
- </View>
116
- ) : null}
135
+ ))}
117
136
  </View>
118
- {session.version.closeDisabled ? (
119
- <View style={styles.startSpacer} />
120
- ) : (
121
- <Pressable
122
- testID="userflow-banner-dismiss"
123
- accessibilityRole="button"
124
- accessibilityLabel="Dismiss"
125
- hitSlop={8}
126
- onPress={() => onDismiss(EndFlowReasonInput.USER_CLOSED)}
127
- style={({ pressed }) => [
128
- styles.close,
129
- pressed ? { backgroundColor: closePressedBackground } : null,
130
- ]}
131
- >
132
- <Text style={[styles.dismissIcon, { color: foregroundColor }]}>×</Text>
133
- </Pressable>
134
- )}
135
- </View>
136
- </Animated.View>
137
+ ) : null}
138
+ </View>
139
+ {session.version.closeDisabled ? (
140
+ <View style={styles.startSpacer} />
141
+ ) : (
142
+ <Pressable
143
+ testID="userflow-banner-dismiss"
144
+ accessibilityRole="button"
145
+ accessibilityLabel="Dismiss"
146
+ hitSlop={8}
147
+ onPress={() => onDismiss(EndFlowReasonInput.USER_CLOSED)}
148
+ style={({ pressed }) => [
149
+ styles.close,
150
+ pressed ? { backgroundColor: closePressedBackground } : null,
151
+ ]}
152
+ >
153
+ <Text style={[styles.dismissIcon, { color: foregroundColor }]}>×</Text>
154
+ </Pressable>
155
+ )}
156
+ </View>
157
+ );
158
+
159
+ const animatedBar = (
160
+ <Animated.View
161
+ onLayout={hostInsetEdge ? onPushChromeLayout : undefined}
162
+ style={[
163
+ { opacity },
164
+ overlay ? bannerOverlayShadowStyle(overlayEdge) : null,
165
+ insetChromeOutsideFade ? null : insetChrome,
166
+ ]}
167
+ >
168
+ {bar}
169
+ </Animated.View>
170
+ );
171
+
172
+ return (
173
+ <View testID="userflow-banner-frame" style={frameStyle}>
174
+ {insetChromeOutsideFade ? <View style={insetChrome}>{animatedBar}</View> : animatedBar}
137
175
  </View>
138
176
  );
139
177
  }
@@ -15,6 +15,10 @@ import { bannerNeedsWebView } from './bannerWebView/bannerNeedsWebView';
15
15
 
16
16
  export interface BannerSurfaceProps {
17
17
  session: BannerSessionFragment;
18
+ /** When set, the banner pushes host layout and fills the safe-area inset on this edge. */
19
+ pushLayoutEdge?: 'top' | 'bottom' | null;
20
+ /** Overlay host edge — paints safe-area padding without pushing layout. */
21
+ overlaySafeEdge?: 'top' | 'bottom' | null;
18
22
  onActions: (actions: FlowActionFragment[]) => void | Promise<void>;
19
23
  onDismiss: (endReason: EndFlowReasonInput) => void;
20
24
  }
@@ -27,6 +31,8 @@ let warnedMissingWebView = false;
27
31
  */
28
32
  export function BannerSurface({
29
33
  session,
34
+ pushLayoutEdge = null,
35
+ overlaySafeEdge = null,
30
36
  onActions,
31
37
  onDismiss,
32
38
  }: BannerSurfaceProps): React.ReactElement | null {
@@ -35,6 +41,34 @@ export function BannerSurface({
35
41
  const needsWebView = banner != null && bannerNeedsWebView(banner, theme);
36
42
  const webView = needsWebView ? getEvalJsWebView() : null;
37
43
 
44
+ React.useEffect(() => {
45
+ if (!banner || !__DEV__) {
46
+ return;
47
+ }
48
+ // eslint-disable-next-line no-console -- intentional development diagnostic
49
+ console.log('[Userflow] banner shown', {
50
+ flowId: session.flow.id,
51
+ flowName: session.flow.name,
52
+ bannerSessionId: session.id,
53
+ versionId: session.version.id,
54
+ embedMode: banner.embedMode,
55
+ overlay: banner.overlay !== false,
56
+ webView: needsWebView,
57
+ pushLayoutEdge: pushLayoutEdge ?? null,
58
+ overlaySafeEdge: overlaySafeEdge ?? null,
59
+ hostAsOverlay: banner.overlay !== false || needsWebView,
60
+ });
61
+ }, [
62
+ banner,
63
+ needsWebView,
64
+ overlaySafeEdge,
65
+ pushLayoutEdge,
66
+ session.flow.id,
67
+ session.flow.name,
68
+ session.id,
69
+ session.version.id,
70
+ ]);
71
+
38
72
  React.useLayoutEffect(() => {
39
73
  if (!needsWebView || !webView) {
40
74
  return;
@@ -69,13 +103,35 @@ export function BannerSurface({
69
103
  );
70
104
  }
71
105
  }
72
- return <BannerBar session={session} onActions={onActions} onDismiss={onDismiss} />;
106
+ return (
107
+ <BannerBar
108
+ session={session}
109
+ pushLayoutEdge={pushLayoutEdge}
110
+ overlaySafeEdge={overlaySafeEdge}
111
+ onActions={onActions}
112
+ onDismiss={onDismiss}
113
+ />
114
+ );
73
115
  }
74
116
  return (
75
- <BannerWebViewBar session={session} onActions={onActions} onDismiss={dismissWebViewBanner} />
117
+ <BannerWebViewBar
118
+ session={session}
119
+ pushLayoutEdge={pushLayoutEdge}
120
+ overlaySafeEdge={overlaySafeEdge}
121
+ onActions={onActions}
122
+ onDismiss={dismissWebViewBanner}
123
+ />
76
124
  );
77
125
  }
78
- return <BannerBar session={session} onActions={onActions} onDismiss={onDismiss} />;
126
+ return (
127
+ <BannerBar
128
+ session={session}
129
+ pushLayoutEdge={pushLayoutEdge}
130
+ overlaySafeEdge={overlaySafeEdge}
131
+ onActions={onActions}
132
+ onDismiss={onDismiss}
133
+ />
134
+ );
79
135
  }
80
136
 
81
137
  /** @internal */
@@ -1,9 +1,10 @@
1
1
  import type { FlowSessionEngine } from '@userflow-tech/core';
2
- import type {
3
- EndFlowReasonInput,
4
- FlowSessionFragment,
5
- FlowStepFragment,
6
- ThemeFragment,
2
+ import {
3
+ decodeMobileAutoData,
4
+ type EndFlowReasonInput,
5
+ type FlowSessionFragment,
6
+ type FlowStepFragment,
7
+ type ThemeFragment,
7
8
  } from '@userflow-tech/protocol';
8
9
  import * as React from 'react';
9
10
  import {
@@ -20,6 +21,7 @@ import {
20
21
  import { anchorRegistry } from '../anchors/AnchorRegistry';
21
22
  import { autoAnchorRegistry } from '../anchors/AutoAnchorRegistry';
22
23
  import { addAnimationFrameCallback, removeAnimationFrameCallback } from '../anchors/animationFrame';
24
+ import { isAppActive, subscribeAppActive } from '../anchors/appActive';
23
25
  import { fiberScanRoot } from '../anchors/fiber/FiberScanRoot';
24
26
  import type { AnyFiber } from '../anchors/fiber/fiberInternals';
25
27
  import { clipToScrollViewports } from '../anchors/fiber/visibleRect';
@@ -27,6 +29,7 @@ import {
27
29
  clearFiberAnchorCache,
28
30
  invalidateFiberAnchor,
29
31
  resolveAnchorHost,
32
+ subscribeAnchorScreenChange,
30
33
  } from '../anchors/resolveAnchorHost';
31
34
  import { resolveAutoAnchorHost } from '../anchors/resolveAutoAnchorHost';
32
35
  import { resolveManualAnchorId } from '../anchors/resolveManualAnchorId';
@@ -42,6 +45,7 @@ import { MADE_WITH_USERFLOW_GAP, MadeWithUserflow } from './MadeWithUserflow';
42
45
  import { ProgressBar } from './ProgressBar';
43
46
  import { ABSOLUTE_FILL } from './absoluteFill';
44
47
  import { useCardTransitionPhase } from './cardTransition';
48
+ import { stepTrapsHostAccessibility } from './hostAccessibilityTrap';
45
49
  import { parseBackdropOpacity } from './parseBackdropOpacity';
46
50
  import { resolveStepProgress } from './progress';
47
51
  import { resolveCloseButtonColor } from './resolveCloseButtonColor';
@@ -77,6 +81,7 @@ import {
77
81
  tooltipSideGap,
78
82
  tooltipVerticalGap,
79
83
  } from './tooltip/resolveSideCardWidth';
84
+ import { TargetSearchSchedule } from './tooltip/targetSearchSchedule';
80
85
 
81
86
  export interface TooltipSurfaceProps {
82
87
  engine: FlowSessionEngine;
@@ -94,10 +99,6 @@ export interface TooltipSurfaceProps {
94
99
 
95
100
  type MissingMode = 'waiting' | 'reported';
96
101
 
97
- /** Interval for re-resolving a missing / remounted anchor (not for live measure). */
98
- const RESOLVE_MS = 250;
99
- /** Slower resolve poll after FLAG/ERROR report so a late-mounting target can recover. */
100
- const RESOLVE_SLOW_MS = 1000;
101
102
  /**
102
103
  * One-frame delay before the first resolve after a step/reset so dialog
103
104
  * teardown cannot arm the missing-target timer with a 0-size measure.
@@ -162,6 +163,11 @@ export function TooltipSurface({
162
163
  const selector = currentStep.selector;
163
164
  const isAuto = (selector?.type as string | null) === 'AUTO';
164
165
  const autoData = selector?.autoData ?? null;
166
+ // Decoded once per step rather than on every search.
167
+ const decodedAutoData = React.useMemo(
168
+ () => (isAuto ? decodeMobileAutoData(autoData) : null),
169
+ [isAuto, autoData],
170
+ );
165
171
  const anchorId = isAuto ? null : resolveManualAnchorId(selector?.css);
166
172
  const targetKey = isAuto ? `auto:${autoData ?? ''}` : (anchorId ?? '');
167
173
  const highlight = currentStep.backdrop ? resolveHighlightChrome(theme, backdropPadding) : null;
@@ -191,7 +197,6 @@ export function TooltipSurface({
191
197
  desiredPlacement ?? 'BELOW',
192
198
  );
193
199
  const [missingMode, setMissingMode] = React.useState<MissingMode>('waiting');
194
- const [registryTick, setRegistryTick] = React.useState(0);
195
200
  /** True after at least one *failed* measure for the current step/anchor. */
196
201
  const [measureAttempted, setMeasureAttempted] = React.useState(false);
197
202
  /**
@@ -272,18 +277,6 @@ export function TooltipSurface({
272
277
  };
273
278
  }, []);
274
279
 
275
- React.useEffect(() => {
276
- const bump = () => setRegistryTick((n) => n + 1);
277
- const unsubRegistry = anchorRegistry.subscribe(bump);
278
- const unsubAuto = autoAnchorRegistry.subscribe(bump);
279
- const unsubScan = fiberScanRoot.subscribe(bump);
280
- return () => {
281
- unsubRegistry();
282
- unsubAuto();
283
- unsubScan();
284
- };
285
- }, []);
286
-
287
280
  React.useEffect(() => {
288
281
  reportedRef.current = false;
289
282
  scrollAttemptsRef.current = 0;
@@ -426,14 +419,14 @@ export function TooltipSurface({
426
419
  );
427
420
 
428
421
  /**
429
- * Resolve the anchor host (registry / fiber scan). Used by the interval and
430
- * on registry ticks — intentionally NOT on every animation frame.
422
+ * Resolve the anchor host (registry / fiber scan). Driven by
423
+ * {@link TargetSearchSchedule} — intentionally NOT on every animation frame.
431
424
  */
432
425
  const resolveTarget = React.useCallback(() => {
433
426
  if (!mountedRef.current) return;
434
427
 
435
428
  const resolution = isAuto
436
- ? resolveAutoAnchorHost(autoData)
429
+ ? resolveAutoAnchorHost(decodedAutoData)
437
430
  : anchorId
438
431
  ? resolveAnchorHost(anchorId)
439
432
  : null;
@@ -474,7 +467,7 @@ export function TooltipSurface({
474
467
  maybeScrollIntoView(rect);
475
468
  applyTarget(rect);
476
469
  });
477
- }, [anchorId, applyTarget, autoData, dropHost, isAuto, maybeScrollIntoView]);
470
+ }, [anchorId, applyTarget, decodedAutoData, dropHost, isAuto, maybeScrollIntoView]);
478
471
 
479
472
  /**
480
473
  * Live re-measure of an already-resolved host. Driven by the shared rAF hub
@@ -509,45 +502,60 @@ export function TooltipSurface({
509
502
  });
510
503
  }, [applyTarget, dropHost, maybeScrollIntoView]);
511
504
 
505
+ const resolveTargetRef = React.useRef(resolveTarget);
506
+ resolveTargetRef.current = resolveTarget;
507
+ const searchRef = React.useRef<TargetSearchSchedule | null>(null);
508
+ const searchAllowsRecovery =
509
+ behavior === 'FLAG' || behavior === 'ERROR' || (behavior === 'END' && session.draftMode);
510
+ const searchStopped = missingMode === 'reported' && !searchAllowsRecovery;
511
+
512
+ // Target search: steady poll while anchored, backoff while missing, nothing
513
+ // while the app is in the background. Live measurement is on rAF. A new step
514
+ // or target restarts it right away; a same-step restart (`resetNonce`) goes
515
+ // through `resolveArmed`, after the first-resolve delay.
512
516
  React.useEffect(() => {
513
- if (!resolveArmed) return;
514
- resolveTarget();
515
- }, [resolveTarget, registryTick, resolveArmed]);
516
-
517
- // Resolve poll: recover missing / remounted anchors. Live measurement is on rAF.
518
- React.useEffect(() => {
519
- if (!resolveArmed) {
517
+ if (!resolveArmed || searchStopped) {
520
518
  return;
521
519
  }
522
- const allowsRecovery =
523
- behavior === 'FLAG' || behavior === 'ERROR' || (behavior === 'END' && session.draftMode);
524
- const pauseAfterReport = missingMode === 'reported' && !allowsRecovery;
525
-
526
- if (pauseAfterReport) {
527
- return;
528
- }
529
-
530
- resolveTarget();
531
- const intervalMs = missingMode === 'reported' ? RESOLVE_SLOW_MS : RESOLVE_MS;
532
- const handle = setInterval(resolveTarget, intervalMs);
533
- const dimSub = Dimensions.addEventListener('change', resolveTarget);
520
+ const search = new TargetSearchSchedule(() => resolveTargetRef.current());
521
+ searchRef.current = search;
522
+ search.setMode(targetRef.current ? 'anchored' : 'missing');
523
+
524
+ const trigger = () => search.trigger();
525
+ const unsubscribers = [
526
+ anchorRegistry.subscribe(trigger),
527
+ autoAnchorRegistry.subscribe(trigger),
528
+ fiberScanRoot.subscribe(trigger),
529
+ subscribeAnchorScreenChange(() => search.rearm()),
530
+ subscribeAppActive((active) => (active ? search.resume() : search.pause())),
531
+ ];
532
+ if (!isAppActive()) search.pause();
533
+ search.runNow();
534
+
535
+ const dimSub = Dimensions.addEventListener('change', () => search.runNow());
534
536
  const onKeyboardShow = (event: { endCoordinates?: { height?: number } }) => {
535
537
  setKeyboardBottomInset(Math.max(0, event.endCoordinates?.height ?? 0));
536
- resolveTarget();
538
+ search.runNow();
537
539
  };
538
540
  const onKeyboardHide = () => {
539
541
  setKeyboardBottomInset(0);
540
- resolveTarget();
542
+ search.runNow();
541
543
  };
542
544
  const kbShow = Keyboard.addListener('keyboardDidShow', onKeyboardShow);
543
545
  const kbHide = Keyboard.addListener('keyboardDidHide', onKeyboardHide);
544
546
  return () => {
545
- clearInterval(handle);
547
+ for (const unsubscribe of unsubscribers) unsubscribe();
546
548
  dimSub.remove();
547
549
  kbShow.remove();
548
550
  kbHide.remove();
551
+ search.dispose();
552
+ if (searchRef.current === search) searchRef.current = null;
549
553
  };
550
- }, [resolveTarget, missingMode, behavior, session.draftMode, resolveArmed]);
554
+ }, [resolveArmed, searchStopped, currentStep.id, targetKey]);
555
+
556
+ React.useEffect(() => {
557
+ searchRef.current?.setMode(target ? 'anchored' : 'missing');
558
+ }, [target]);
551
559
 
552
560
  // rAF tracking while a host is resolved (and we are not paused after report).
553
561
  React.useEffect(() => {
@@ -864,6 +872,9 @@ export function TooltipSurface({
864
872
 
865
873
  const showBranding = session.flow.company.flowBranding;
866
874
  const contentPadding = chrome.contentPadding;
875
+ // Modal only while the host is trapped too. Otherwise VoiceOver would skip
876
+ // the host app, including the target this step asks the user to press.
877
+ const cardIsModal = stepTrapsHostAccessibility(currentStep);
867
878
 
868
879
  // Still pending the first measure attempt — only the blocker shows.
869
880
  if (isMissing && missingMode === 'waiting' && !measureAttempted) {
@@ -877,7 +888,7 @@ export function TooltipSurface({
877
888
  <View style={[styles.center, centerPaddingStyle]} pointerEvents="box-none">
878
889
  <CardFade
879
890
  testID="userflow-tooltip-waiting"
880
- accessibilityViewIsModal
891
+ accessibilityViewIsModal={cardIsModal}
881
892
  pointerEvents="auto"
882
893
  style={[
883
894
  chrome.shadowStyle,
@@ -990,7 +1001,7 @@ export function TooltipSurface({
990
1001
  <View style={[styles.center, centerPaddingStyle]} pointerEvents="box-none">
991
1002
  <CardFade
992
1003
  testID="userflow-tooltip-card"
993
- accessibilityViewIsModal
1004
+ accessibilityViewIsModal={cardIsModal}
994
1005
  pointerEvents="auto"
995
1006
  onLayout={onCardLayout}
996
1007
  style={[
@@ -1032,6 +1043,7 @@ export function TooltipSurface({
1032
1043
  onLayout={onCardLayout}
1033
1044
  maxHeight={layout.maxCardHeight}
1034
1045
  showNotch={layout.showNotch}
1046
+ accessibilityViewIsModal={cardIsModal}
1035
1047
  style={{
1036
1048
  position: 'absolute',
1037
1049
  left: layout.cardPosition.x,
@@ -3,10 +3,12 @@ import { BannerContentLayout } from '@userflow-tech/protocol';
3
3
  import type { ViewStyle } from 'react-native';
4
4
 
5
5
  import {
6
+ BANNER_CLOSE_SLOT_SIZE,
6
7
  BANNER_INNER_COLUMN_GAP,
7
8
  BANNER_INNER_MARGIN_HORIZONTAL,
8
9
  BANNER_INNER_ROW_GAP,
9
10
  BANNER_OVERLAY_SCREEN_INSET_HORIZONTAL,
11
+ BANNER_ROOT_PADDING,
10
12
  } from './bannerBarTokens';
11
13
  import type { EdgeInsets } from './safeArea';
12
14
 
@@ -16,13 +18,50 @@ function optionalMargin(value: number | null | undefined): number | undefined {
16
18
  return value == null ? undefined : value;
17
19
  }
18
20
 
21
+ /** Fixed horizontal chrome inside the embed width (padding, close/spacer slots, inner margins). */
22
+ export function bannerHorizontalChromeWidth(): number {
23
+ return BANNER_ROOT_PADDING * 2 + BANNER_CLOSE_SLOT_SIZE * 2 + BANNER_INNER_MARGIN_HORIZONTAL * 2;
24
+ }
25
+
26
+ /** Outer bar cap — max embed width only (max content width limits the inner row on web). */
27
+ export function resolveBannerFrameMaxWidth(banner: BannerFragment): number | null {
28
+ return banner.maxWidth;
29
+ }
30
+
31
+ export function bannerHasMaxContentWidth(banner: BannerFragment): boolean {
32
+ return banner.maxContentWidth != null;
33
+ }
34
+
35
+ /**
36
+ * Cap for the native inner row and WebView HTML `.userflowjs-banner-inner` only.
37
+ * Returns null when max content width is unset. May be 0 when embed width is narrower than chrome.
38
+ */
39
+ export function resolveBannerInnerMaxWidth(banner: BannerFragment): number | null {
40
+ if (!bannerHasMaxContentWidth(banner)) {
41
+ return null;
42
+ }
43
+ let cap = banner.maxContentWidth!;
44
+ if (banner.maxWidth != null) {
45
+ const embedInnerCap = Math.max(0, banner.maxWidth - bannerHorizontalChromeWidth());
46
+ cap = Math.min(cap, embedInnerCap);
47
+ }
48
+ return cap;
49
+ }
50
+
19
51
  /** Outer frame: max embed width and builder outer margin (web `userflowjs-banner-frame`). */
20
52
  export function bannerFrameStyle(banner: BannerFragment): ViewStyle {
21
- const hasMaxWidth = banner.maxWidth != null;
53
+ const frameMaxWidth = resolveBannerFrameMaxWidth(banner);
54
+ const hasFrameCap = frameMaxWidth != null;
22
55
 
23
56
  return {
24
- alignSelf: hasMaxWidth ? 'center' : 'stretch',
25
- ...(hasMaxWidth ? { maxWidth: banner.maxWidth! } : null),
57
+ alignSelf: hasFrameCap ? 'center' : 'stretch',
58
+ ...(hasFrameCap
59
+ ? {
60
+ maxWidth: frameMaxWidth!,
61
+ width: '100%' as const,
62
+ overflow: 'hidden' as const,
63
+ }
64
+ : null),
26
65
  marginTop: optionalMargin(banner.marginTop),
27
66
  marginRight: optionalMargin(banner.marginRight),
28
67
  marginBottom: optionalMargin(banner.marginBottom),
@@ -56,12 +95,45 @@ export function bannerInnerJustifyContent(
56
95
  }
57
96
  }
58
97
 
98
+ /**
99
+ * Push-layout chrome: safe-area pad + bar background on the same node as the
100
+ * fade so an animated banner does not leave a visible status-bar strip at opacity 0.
101
+ */
102
+ export function bannerPushLayoutChrome(
103
+ edge: 'top' | 'bottom',
104
+ insets: EdgeInsets,
105
+ backgroundColor: string,
106
+ ): ViewStyle {
107
+ return {
108
+ backgroundColor,
109
+ paddingTop: edge === 'top' ? insets.top : 0,
110
+ paddingBottom: edge === 'bottom' ? insets.bottom : 0,
111
+ };
112
+ }
113
+
114
+ /** Matches web `.userflowjs-banner-inner { flex(1000000) }` vs start-spacer `flex(1)`. */
115
+ const BANNER_INNER_FLEX_GROW = 1_000_000;
116
+
117
+ /** Painted bar (`userflow-banner`) — fill the frame so max embed width clips chrome, not just margins. */
118
+ export function bannerBarRootStyle(banner: BannerFragment): ViewStyle {
119
+ if (resolveBannerFrameMaxWidth(banner) == null) {
120
+ return {};
121
+ }
122
+ return {
123
+ width: '100%',
124
+ maxWidth: '100%',
125
+ };
126
+ }
127
+
59
128
  export function bannerInnerStyle(banner: BannerFragment): ViewStyle {
60
- const hasMaxContentWidth = banner.maxContentWidth != null;
129
+ const hasMaxContentWidth = bannerHasMaxContentWidth(banner);
130
+ const innerMaxWidth = resolveBannerInnerMaxWidth(banner);
61
131
  return {
62
- flexGrow: 1,
132
+ // Web `.userflowjs-banner-inner { flex(1000000) }` — grow with shrink so text wraps and close stays visible.
133
+ flexGrow: BANNER_INNER_FLEX_GROW,
63
134
  flexShrink: 1,
64
- flexBasis: 0,
135
+ // `auto` avoids the old `flexBasis: 0` + inner `width: 100%` sliver; `minWidth: 0` lets shrink wrap text.
136
+ flexBasis: hasMaxContentWidth ? ('auto' as const) : 0,
65
137
  flexDirection: 'row',
66
138
  flexWrap: 'wrap',
67
139
  alignItems: 'center',
@@ -69,6 +141,7 @@ export function bannerInnerStyle(banner: BannerFragment): ViewStyle {
69
141
  rowGap: BANNER_INNER_ROW_GAP,
70
142
  marginHorizontal: BANNER_INNER_MARGIN_HORIZONTAL,
71
143
  justifyContent: bannerInnerJustifyContent(banner.contentLayout),
72
- ...(hasMaxContentWidth ? { maxWidth: banner.maxContentWidth!, width: '100%' } : null),
144
+ minWidth: hasMaxContentWidth ? 0 : undefined,
145
+ ...(innerMaxWidth != null && innerMaxWidth > 0 ? { maxWidth: innerMaxWidth } : null),
73
146
  };
74
147
  }