@userflow-tech/react-native 0.4.5 → 0.6.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 (502) hide show
  1. package/README.md +31 -2
  2. package/docs/anchoring.md +6 -1
  3. package/docs/custom-fonts.md +36 -0
  4. package/docs/troubleshooting.md +33 -6
  5. package/lib/commonjs/anchors/AutoAnchorRegistry.js +144 -34
  6. package/lib/commonjs/anchors/AutoAnchorRegistry.js.map +1 -1
  7. package/lib/commonjs/anchors/animationFrame.js +29 -1
  8. package/lib/commonjs/anchors/animationFrame.js.map +1 -1
  9. package/lib/commonjs/anchors/appActive.js +61 -0
  10. package/lib/commonjs/anchors/appActive.js.map +1 -0
  11. package/lib/commonjs/anchors/elementConditionEvaluator.js +25 -8
  12. package/lib/commonjs/anchors/elementConditionEvaluator.js.map +1 -1
  13. package/lib/commonjs/anchors/elementTapMonitor.js +67 -12
  14. package/lib/commonjs/anchors/elementTapMonitor.js.map +1 -1
  15. package/lib/commonjs/anchors/elementTargetMatch.js +33 -7
  16. package/lib/commonjs/anchors/elementTargetMatch.js.map +1 -1
  17. package/lib/commonjs/anchors/fiber/FiberScanRootStore.js +8 -0
  18. package/lib/commonjs/anchors/fiber/FiberScanRootStore.js.map +1 -1
  19. package/lib/commonjs/anchors/fiber/detectAppFontFamily.js +341 -0
  20. package/lib/commonjs/anchors/fiber/detectAppFontFamily.js.map +1 -0
  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/fiber/visibleScreens.js +6 -0
  28. package/lib/commonjs/anchors/fiber/visibleScreens.js.map +1 -1
  29. package/lib/commonjs/anchors/fiberAnchorCache.js +20 -0
  30. package/lib/commonjs/anchors/fiberAnchorCache.js.map +1 -1
  31. package/lib/commonjs/anchors/gestureHandlerHandoff.js +304 -0
  32. package/lib/commonjs/anchors/gestureHandlerHandoff.js.map +1 -0
  33. package/lib/commonjs/anchors/resolveAnchorHost.js +6 -0
  34. package/lib/commonjs/anchors/resolveAnchorHost.js.map +1 -1
  35. package/lib/commonjs/anchors/resolveAutoAnchorHost.js +11 -6
  36. package/lib/commonjs/anchors/resolveAutoAnchorHost.js.map +1 -1
  37. package/lib/commonjs/anchors/resolveElementTargets.js +63 -8
  38. package/lib/commonjs/anchors/resolveElementTargets.js.map +1 -1
  39. package/lib/commonjs/anchors/semanticMatcher.js +43 -21
  40. package/lib/commonjs/anchors/semanticMatcher.js.map +1 -1
  41. package/lib/commonjs/anchors/tapSlop.js +16 -0
  42. package/lib/commonjs/anchors/tapSlop.js.map +1 -0
  43. package/lib/commonjs/anchors/targetPressMonitor.js +66 -12
  44. package/lib/commonjs/anchors/targetPressMonitor.js.map +1 -1
  45. package/lib/commonjs/authoring/fingerprintFromInstance.js +5 -9
  46. package/lib/commonjs/authoring/fingerprintFromInstance.js.map +1 -1
  47. package/lib/commonjs/content/BannerStepButton.js +2 -1
  48. package/lib/commonjs/content/BannerStepButton.js.map +1 -1
  49. package/lib/commonjs/content/RichContentRenderer.js +20 -14
  50. package/lib/commonjs/content/RichContentRenderer.js.map +1 -1
  51. package/lib/commonjs/content/StepButton.js +2 -1
  52. package/lib/commonjs/content/StepButton.js.map +1 -1
  53. package/lib/commonjs/content/openSafeUrl.js +3 -1
  54. package/lib/commonjs/content/openSafeUrl.js.map +1 -1
  55. package/lib/commonjs/content/resolveContentFontFamily.js +96 -0
  56. package/lib/commonjs/content/resolveContentFontFamily.js.map +1 -0
  57. package/lib/commonjs/expo-router/useUserflowExpoRouter.js +6 -0
  58. package/lib/commonjs/expo-router/useUserflowExpoRouter.js.map +1 -1
  59. package/lib/commonjs/index.js +21 -5
  60. package/lib/commonjs/index.js.map +1 -1
  61. package/lib/commonjs/navigation/navigateUrlUtils.js +105 -0
  62. package/lib/commonjs/navigation/navigateUrlUtils.js.map +1 -0
  63. package/lib/commonjs/navigation/openExternalNavigateUrl.js +14 -0
  64. package/lib/commonjs/navigation/openExternalNavigateUrl.js.map +1 -0
  65. package/lib/commonjs/navigation/openFlowNavigateUrl.js +47 -41
  66. package/lib/commonjs/navigation/openFlowNavigateUrl.js.map +1 -1
  67. package/lib/commonjs/pairing/PairingController.js +3 -1
  68. package/lib/commonjs/pairing/PairingController.js.map +1 -1
  69. package/lib/commonjs/runtime/BannerHost.js +38 -29
  70. package/lib/commonjs/runtime/BannerHost.js.map +1 -1
  71. package/lib/commonjs/runtime/TargetPressBridge.js +55 -7
  72. package/lib/commonjs/runtime/TargetPressBridge.js.map +1 -1
  73. package/lib/commonjs/runtime/UserflowOverlay.js +5 -11
  74. package/lib/commonjs/runtime/UserflowOverlay.js.map +1 -1
  75. package/lib/commonjs/runtime/UserflowProvider.js +66 -40
  76. package/lib/commonjs/runtime/UserflowProvider.js.map +1 -1
  77. package/lib/commonjs/runtime/bannerEngine.js +8 -2
  78. package/lib/commonjs/runtime/bannerEngine.js.map +1 -1
  79. package/lib/commonjs/runtime/bannerFlowSpacer.js +71 -0
  80. package/lib/commonjs/runtime/bannerFlowSpacer.js.map +1 -0
  81. package/lib/commonjs/runtime/flowEngine.js +11 -0
  82. package/lib/commonjs/runtime/flowEngine.js.map +1 -1
  83. package/lib/commonjs/runtime/previewStart.js +39 -0
  84. package/lib/commonjs/runtime/previewStart.js.map +1 -0
  85. package/lib/commonjs/runtime/pushBannerLayoutContext.js +101 -0
  86. package/lib/commonjs/runtime/pushBannerLayoutContext.js.map +1 -0
  87. package/lib/commonjs/screenTracker.js +12 -0
  88. package/lib/commonjs/screenTracker.js.map +1 -1
  89. package/lib/commonjs/surfaces/BannerBar.js +79 -61
  90. package/lib/commonjs/surfaces/BannerBar.js.map +1 -1
  91. package/lib/commonjs/surfaces/BannerSurface.js +28 -6
  92. package/lib/commonjs/surfaces/BannerSurface.js.map +1 -1
  93. package/lib/commonjs/surfaces/ProgressBar.js +2 -1
  94. package/lib/commonjs/surfaces/ProgressBar.js.map +1 -1
  95. package/lib/commonjs/surfaces/TooltipSurface.js +48 -41
  96. package/lib/commonjs/surfaces/TooltipSurface.js.map +1 -1
  97. package/lib/commonjs/surfaces/bannerBarLayout.js +77 -10
  98. package/lib/commonjs/surfaces/bannerBarLayout.js.map +1 -1
  99. package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js +114 -63
  100. package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
  101. package/lib/commonjs/surfaces/bannerWebView/bannerNeedsWebView.js +3 -33
  102. package/lib/commonjs/surfaces/bannerWebView/bannerNeedsWebView.js.map +1 -1
  103. package/lib/commonjs/surfaces/bannerWebView/bannerThemeStyles.js +2 -1
  104. package/lib/commonjs/surfaces/bannerWebView/bannerThemeStyles.js.map +1 -1
  105. package/lib/commonjs/surfaces/bannerWebView/bannerWebViewHeight.js +33 -0
  106. package/lib/commonjs/surfaces/bannerWebView/bannerWebViewHeight.js.map +1 -0
  107. package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js +104 -23
  108. package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
  109. package/lib/commonjs/surfaces/hostAccessibilityTrap.js +28 -0
  110. package/lib/commonjs/surfaces/hostAccessibilityTrap.js.map +1 -0
  111. package/lib/commonjs/surfaces/resolveBannerTheme.js +6 -3
  112. package/lib/commonjs/surfaces/resolveBannerTheme.js.map +1 -1
  113. package/lib/commonjs/surfaces/tooltip/TooltipBubble.js +3 -2
  114. package/lib/commonjs/surfaces/tooltip/TooltipBubble.js.map +1 -1
  115. package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js +134 -0
  116. package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
  117. package/lib/commonjs/version.js +1 -1
  118. package/lib/module/anchors/AutoAnchorRegistry.js +145 -35
  119. package/lib/module/anchors/AutoAnchorRegistry.js.map +1 -1
  120. package/lib/module/anchors/animationFrame.js +29 -2
  121. package/lib/module/anchors/animationFrame.js.map +1 -1
  122. package/lib/module/anchors/appActive.js +57 -0
  123. package/lib/module/anchors/appActive.js.map +1 -0
  124. package/lib/module/anchors/elementConditionEvaluator.js +26 -9
  125. package/lib/module/anchors/elementConditionEvaluator.js.map +1 -1
  126. package/lib/module/anchors/elementTapMonitor.js +67 -12
  127. package/lib/module/anchors/elementTapMonitor.js.map +1 -1
  128. package/lib/module/anchors/elementTargetMatch.js +32 -7
  129. package/lib/module/anchors/elementTargetMatch.js.map +1 -1
  130. package/lib/module/anchors/fiber/FiberScanRootStore.js +8 -0
  131. package/lib/module/anchors/fiber/FiberScanRootStore.js.map +1 -1
  132. package/lib/module/anchors/fiber/detectAppFontFamily.js +330 -0
  133. package/lib/module/anchors/fiber/detectAppFontFamily.js.map +1 -0
  134. package/lib/module/anchors/fiber/fiberInternals.js +65 -0
  135. package/lib/module/anchors/fiber/fiberInternals.js.map +1 -1
  136. package/lib/module/anchors/fiber/indexHostFibers.js +4 -3
  137. package/lib/module/anchors/fiber/indexHostFibers.js.map +1 -1
  138. package/lib/module/anchors/fiber/resolveFiberPath.js +30 -15
  139. package/lib/module/anchors/fiber/resolveFiberPath.js.map +1 -1
  140. package/lib/module/anchors/fiber/visibleScreens.js +5 -0
  141. package/lib/module/anchors/fiber/visibleScreens.js.map +1 -1
  142. package/lib/module/anchors/fiberAnchorCache.js +19 -0
  143. package/lib/module/anchors/fiberAnchorCache.js.map +1 -1
  144. package/lib/module/anchors/gestureHandlerHandoff.js +298 -0
  145. package/lib/module/anchors/gestureHandlerHandoff.js.map +1 -0
  146. package/lib/module/anchors/resolveAnchorHost.js +1 -0
  147. package/lib/module/anchors/resolveAnchorHost.js.map +1 -1
  148. package/lib/module/anchors/resolveAutoAnchorHost.js +11 -6
  149. package/lib/module/anchors/resolveAutoAnchorHost.js.map +1 -1
  150. package/lib/module/anchors/resolveElementTargets.js +63 -8
  151. package/lib/module/anchors/resolveElementTargets.js.map +1 -1
  152. package/lib/module/anchors/semanticMatcher.js +43 -21
  153. package/lib/module/anchors/semanticMatcher.js.map +1 -1
  154. package/lib/module/anchors/tapSlop.js +11 -0
  155. package/lib/module/anchors/tapSlop.js.map +1 -0
  156. package/lib/module/anchors/targetPressMonitor.js +53 -8
  157. package/lib/module/anchors/targetPressMonitor.js.map +1 -1
  158. package/lib/module/authoring/fingerprintFromInstance.js +6 -10
  159. package/lib/module/authoring/fingerprintFromInstance.js.map +1 -1
  160. package/lib/module/content/BannerStepButton.js +2 -1
  161. package/lib/module/content/BannerStepButton.js.map +1 -1
  162. package/lib/module/content/RichContentRenderer.js +20 -14
  163. package/lib/module/content/RichContentRenderer.js.map +1 -1
  164. package/lib/module/content/StepButton.js +2 -1
  165. package/lib/module/content/StepButton.js.map +1 -1
  166. package/lib/module/content/openSafeUrl.js +3 -1
  167. package/lib/module/content/openSafeUrl.js.map +1 -1
  168. package/lib/module/content/resolveContentFontFamily.js +89 -0
  169. package/lib/module/content/resolveContentFontFamily.js.map +1 -0
  170. package/lib/module/expo-router/useUserflowExpoRouter.js +7 -1
  171. package/lib/module/expo-router/useUserflowExpoRouter.js.map +1 -1
  172. package/lib/module/index.js +24 -7
  173. package/lib/module/index.js.map +1 -1
  174. package/lib/module/navigation/navigateUrlUtils.js +94 -0
  175. package/lib/module/navigation/navigateUrlUtils.js.map +1 -0
  176. package/lib/module/navigation/openExternalNavigateUrl.js +11 -0
  177. package/lib/module/navigation/openExternalNavigateUrl.js.map +1 -0
  178. package/lib/module/navigation/openFlowNavigateUrl.js +22 -36
  179. package/lib/module/navigation/openFlowNavigateUrl.js.map +1 -1
  180. package/lib/module/pairing/PairingController.js +3 -1
  181. package/lib/module/pairing/PairingController.js.map +1 -1
  182. package/lib/module/runtime/BannerHost.js +38 -29
  183. package/lib/module/runtime/BannerHost.js.map +1 -1
  184. package/lib/module/runtime/TargetPressBridge.js +56 -8
  185. package/lib/module/runtime/TargetPressBridge.js.map +1 -1
  186. package/lib/module/runtime/UserflowOverlay.js +5 -11
  187. package/lib/module/runtime/UserflowOverlay.js.map +1 -1
  188. package/lib/module/runtime/UserflowProvider.js +66 -40
  189. package/lib/module/runtime/UserflowProvider.js.map +1 -1
  190. package/lib/module/runtime/bannerEngine.js +7 -2
  191. package/lib/module/runtime/bannerEngine.js.map +1 -1
  192. package/lib/module/runtime/bannerFlowSpacer.js +65 -0
  193. package/lib/module/runtime/bannerFlowSpacer.js.map +1 -0
  194. package/lib/module/runtime/flowEngine.js +11 -0
  195. package/lib/module/runtime/flowEngine.js.map +1 -1
  196. package/lib/module/runtime/previewStart.js +35 -0
  197. package/lib/module/runtime/previewStart.js.map +1 -0
  198. package/lib/module/runtime/pushBannerLayoutContext.js +94 -0
  199. package/lib/module/runtime/pushBannerLayoutContext.js.map +1 -0
  200. package/lib/module/screenTracker.js +11 -0
  201. package/lib/module/screenTracker.js.map +1 -1
  202. package/lib/module/surfaces/BannerBar.js +80 -62
  203. package/lib/module/surfaces/BannerBar.js.map +1 -1
  204. package/lib/module/surfaces/BannerSurface.js +28 -6
  205. package/lib/module/surfaces/BannerSurface.js.map +1 -1
  206. package/lib/module/surfaces/ProgressBar.js +2 -1
  207. package/lib/module/surfaces/ProgressBar.js.map +1 -1
  208. package/lib/module/surfaces/TooltipSurface.js +49 -42
  209. package/lib/module/surfaces/TooltipSurface.js.map +1 -1
  210. package/lib/module/surfaces/bannerBarLayout.js +72 -11
  211. package/lib/module/surfaces/bannerBarLayout.js.map +1 -1
  212. package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js +115 -64
  213. package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
  214. package/lib/module/surfaces/bannerWebView/bannerNeedsWebView.js +3 -31
  215. package/lib/module/surfaces/bannerWebView/bannerNeedsWebView.js.map +1 -1
  216. package/lib/module/surfaces/bannerWebView/bannerThemeStyles.js +2 -1
  217. package/lib/module/surfaces/bannerWebView/bannerThemeStyles.js.map +1 -1
  218. package/lib/module/surfaces/bannerWebView/bannerWebViewHeight.js +29 -0
  219. package/lib/module/surfaces/bannerWebView/bannerWebViewHeight.js.map +1 -0
  220. package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js +104 -23
  221. package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
  222. package/lib/module/surfaces/hostAccessibilityTrap.js +24 -0
  223. package/lib/module/surfaces/hostAccessibilityTrap.js.map +1 -0
  224. package/lib/module/surfaces/resolveBannerTheme.js +6 -3
  225. package/lib/module/surfaces/resolveBannerTheme.js.map +1 -1
  226. package/lib/module/surfaces/tooltip/TooltipBubble.js +3 -2
  227. package/lib/module/surfaces/tooltip/TooltipBubble.js.map +1 -1
  228. package/lib/module/surfaces/tooltip/targetSearchSchedule.js +128 -0
  229. package/lib/module/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
  230. package/lib/module/version.js +1 -1
  231. package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts +36 -2
  232. package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
  233. package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts +0 -4
  234. package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts.map +1 -1
  235. package/lib/typescript/commonjs/src/anchors/appActive.d.ts +8 -0
  236. package/lib/typescript/commonjs/src/anchors/appActive.d.ts.map +1 -0
  237. package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts +17 -0
  238. package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts.map +1 -1
  239. package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts +7 -0
  240. package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts.map +1 -1
  241. package/lib/typescript/commonjs/src/anchors/fiber/FiberScanRootStore.d.ts +5 -0
  242. package/lib/typescript/commonjs/src/anchors/fiber/FiberScanRootStore.d.ts.map +1 -1
  243. package/lib/typescript/commonjs/src/anchors/fiber/detectAppFontFamily.d.ts +30 -0
  244. package/lib/typescript/commonjs/src/anchors/fiber/detectAppFontFamily.d.ts.map +1 -0
  245. package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts +7 -0
  246. package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
  247. package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts +3 -2
  248. package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
  249. package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
  250. package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
  251. package/lib/typescript/commonjs/src/anchors/fiber/visibleScreens.d.ts +2 -0
  252. package/lib/typescript/commonjs/src/anchors/fiber/visibleScreens.d.ts.map +1 -1
  253. package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts +5 -0
  254. package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts.map +1 -1
  255. package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts +70 -0
  256. package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
  257. package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts +1 -0
  258. package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts.map +1 -1
  259. package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
  260. package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
  261. package/lib/typescript/commonjs/src/anchors/resolveElementTargets.d.ts.map +1 -1
  262. package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts +2 -0
  263. package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts.map +1 -1
  264. package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts +9 -0
  265. package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts.map +1 -0
  266. package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts +13 -8
  267. package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts.map +1 -1
  268. package/lib/typescript/commonjs/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
  269. package/lib/typescript/commonjs/src/content/BannerStepButton.d.ts.map +1 -1
  270. package/lib/typescript/commonjs/src/content/RichContentRenderer.d.ts.map +1 -1
  271. package/lib/typescript/commonjs/src/content/StepButton.d.ts.map +1 -1
  272. package/lib/typescript/commonjs/src/content/openSafeUrl.d.ts.map +1 -1
  273. package/lib/typescript/commonjs/src/content/resolveContentFontFamily.d.ts +35 -0
  274. package/lib/typescript/commonjs/src/content/resolveContentFontFamily.d.ts.map +1 -0
  275. package/lib/typescript/commonjs/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
  276. package/lib/typescript/commonjs/src/index.d.ts +6 -5
  277. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  278. package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts +15 -0
  279. package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts.map +1 -0
  280. package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts +3 -0
  281. package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
  282. package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts +2 -5
  283. package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
  284. package/lib/typescript/commonjs/src/pairing/PairingController.d.ts +6 -0
  285. package/lib/typescript/commonjs/src/pairing/PairingController.d.ts.map +1 -1
  286. package/lib/typescript/commonjs/src/runtime/BannerHost.d.ts.map +1 -1
  287. package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts +3 -0
  288. package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts.map +1 -1
  289. package/lib/typescript/commonjs/src/runtime/UserflowOverlay.d.ts.map +1 -1
  290. package/lib/typescript/commonjs/src/runtime/UserflowProvider.d.ts.map +1 -1
  291. package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts +5 -1
  292. package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts.map +1 -1
  293. package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts +15 -0
  294. package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
  295. package/lib/typescript/commonjs/src/runtime/flowEngine.d.ts.map +1 -1
  296. package/lib/typescript/commonjs/src/runtime/previewStart.d.ts +13 -0
  297. package/lib/typescript/commonjs/src/runtime/previewStart.d.ts.map +1 -0
  298. package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts +22 -0
  299. package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
  300. package/lib/typescript/commonjs/src/screenTracker.d.ts +6 -0
  301. package/lib/typescript/commonjs/src/screenTracker.d.ts.map +1 -1
  302. package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts +3 -1
  303. package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts.map +1 -1
  304. package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts +6 -5
  305. package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts.map +1 -1
  306. package/lib/typescript/commonjs/src/surfaces/ProgressBar.d.ts.map +1 -1
  307. package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts +1 -1
  308. package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts.map +1 -1
  309. package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts +17 -0
  310. package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts.map +1 -1
  311. package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
  312. package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
  313. package/lib/typescript/commonjs/src/surfaces/bannerWebView/bannerNeedsWebView.d.ts +3 -12
  314. package/lib/typescript/commonjs/src/surfaces/bannerWebView/bannerNeedsWebView.d.ts.map +1 -1
  315. package/lib/typescript/commonjs/src/surfaces/bannerWebView/bannerThemeStyles.d.ts.map +1 -1
  316. package/lib/typescript/commonjs/src/surfaces/bannerWebView/bannerWebViewHeight.d.ts +13 -0
  317. package/lib/typescript/commonjs/src/surfaces/bannerWebView/bannerWebViewHeight.d.ts.map +1 -0
  318. package/lib/typescript/commonjs/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
  319. package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
  320. package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
  321. package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts +4 -1
  322. package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
  323. package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
  324. package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
  325. package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
  326. package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
  327. package/lib/typescript/commonjs/src/version.d.ts +1 -1
  328. package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts +36 -2
  329. package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
  330. package/lib/typescript/module/src/anchors/animationFrame.d.ts +0 -4
  331. package/lib/typescript/module/src/anchors/animationFrame.d.ts.map +1 -1
  332. package/lib/typescript/module/src/anchors/appActive.d.ts +8 -0
  333. package/lib/typescript/module/src/anchors/appActive.d.ts.map +1 -0
  334. package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts +17 -0
  335. package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts.map +1 -1
  336. package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts +7 -0
  337. package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts.map +1 -1
  338. package/lib/typescript/module/src/anchors/fiber/FiberScanRootStore.d.ts +5 -0
  339. package/lib/typescript/module/src/anchors/fiber/FiberScanRootStore.d.ts.map +1 -1
  340. package/lib/typescript/module/src/anchors/fiber/detectAppFontFamily.d.ts +30 -0
  341. package/lib/typescript/module/src/anchors/fiber/detectAppFontFamily.d.ts.map +1 -0
  342. package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts +7 -0
  343. package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
  344. package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts +3 -2
  345. package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
  346. package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
  347. package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
  348. package/lib/typescript/module/src/anchors/fiber/visibleScreens.d.ts +2 -0
  349. package/lib/typescript/module/src/anchors/fiber/visibleScreens.d.ts.map +1 -1
  350. package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts +5 -0
  351. package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts.map +1 -1
  352. package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts +70 -0
  353. package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
  354. package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts +1 -0
  355. package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts.map +1 -1
  356. package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
  357. package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
  358. package/lib/typescript/module/src/anchors/resolveElementTargets.d.ts.map +1 -1
  359. package/lib/typescript/module/src/anchors/semanticMatcher.d.ts +2 -0
  360. package/lib/typescript/module/src/anchors/semanticMatcher.d.ts.map +1 -1
  361. package/lib/typescript/module/src/anchors/tapSlop.d.ts +9 -0
  362. package/lib/typescript/module/src/anchors/tapSlop.d.ts.map +1 -0
  363. package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts +13 -8
  364. package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts.map +1 -1
  365. package/lib/typescript/module/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
  366. package/lib/typescript/module/src/content/BannerStepButton.d.ts.map +1 -1
  367. package/lib/typescript/module/src/content/RichContentRenderer.d.ts.map +1 -1
  368. package/lib/typescript/module/src/content/StepButton.d.ts.map +1 -1
  369. package/lib/typescript/module/src/content/openSafeUrl.d.ts.map +1 -1
  370. package/lib/typescript/module/src/content/resolveContentFontFamily.d.ts +35 -0
  371. package/lib/typescript/module/src/content/resolveContentFontFamily.d.ts.map +1 -0
  372. package/lib/typescript/module/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
  373. package/lib/typescript/module/src/index.d.ts +6 -5
  374. package/lib/typescript/module/src/index.d.ts.map +1 -1
  375. package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts +15 -0
  376. package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts.map +1 -0
  377. package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts +3 -0
  378. package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
  379. package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts +2 -5
  380. package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
  381. package/lib/typescript/module/src/pairing/PairingController.d.ts +6 -0
  382. package/lib/typescript/module/src/pairing/PairingController.d.ts.map +1 -1
  383. package/lib/typescript/module/src/runtime/BannerHost.d.ts.map +1 -1
  384. package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts +3 -0
  385. package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts.map +1 -1
  386. package/lib/typescript/module/src/runtime/UserflowOverlay.d.ts.map +1 -1
  387. package/lib/typescript/module/src/runtime/UserflowProvider.d.ts.map +1 -1
  388. package/lib/typescript/module/src/runtime/bannerEngine.d.ts +5 -1
  389. package/lib/typescript/module/src/runtime/bannerEngine.d.ts.map +1 -1
  390. package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts +15 -0
  391. package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
  392. package/lib/typescript/module/src/runtime/flowEngine.d.ts.map +1 -1
  393. package/lib/typescript/module/src/runtime/previewStart.d.ts +13 -0
  394. package/lib/typescript/module/src/runtime/previewStart.d.ts.map +1 -0
  395. package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts +22 -0
  396. package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
  397. package/lib/typescript/module/src/screenTracker.d.ts +6 -0
  398. package/lib/typescript/module/src/screenTracker.d.ts.map +1 -1
  399. package/lib/typescript/module/src/surfaces/BannerBar.d.ts +3 -1
  400. package/lib/typescript/module/src/surfaces/BannerBar.d.ts.map +1 -1
  401. package/lib/typescript/module/src/surfaces/BannerSurface.d.ts +6 -5
  402. package/lib/typescript/module/src/surfaces/BannerSurface.d.ts.map +1 -1
  403. package/lib/typescript/module/src/surfaces/ProgressBar.d.ts.map +1 -1
  404. package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts +1 -1
  405. package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts.map +1 -1
  406. package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts +17 -0
  407. package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts.map +1 -1
  408. package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
  409. package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
  410. package/lib/typescript/module/src/surfaces/bannerWebView/bannerNeedsWebView.d.ts +3 -12
  411. package/lib/typescript/module/src/surfaces/bannerWebView/bannerNeedsWebView.d.ts.map +1 -1
  412. package/lib/typescript/module/src/surfaces/bannerWebView/bannerThemeStyles.d.ts.map +1 -1
  413. package/lib/typescript/module/src/surfaces/bannerWebView/bannerWebViewHeight.d.ts +13 -0
  414. package/lib/typescript/module/src/surfaces/bannerWebView/bannerWebViewHeight.d.ts.map +1 -0
  415. package/lib/typescript/module/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
  416. package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
  417. package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
  418. package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts +4 -1
  419. package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
  420. package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
  421. package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
  422. package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
  423. package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
  424. package/lib/typescript/module/src/version.d.ts +1 -1
  425. package/package.json +1 -1
  426. package/src/anchors/AutoAnchorRegistry.ts +166 -35
  427. package/src/anchors/animationFrame.ts +31 -1
  428. package/src/anchors/appActive.ts +63 -0
  429. package/src/anchors/elementConditionEvaluator.ts +29 -12
  430. package/src/anchors/elementTapMonitor.ts +68 -10
  431. package/src/anchors/elementTargetMatch.ts +34 -7
  432. package/src/anchors/fiber/FiberScanRootStore.ts +8 -0
  433. package/src/anchors/fiber/detectAppFontFamily.ts +400 -0
  434. package/src/anchors/fiber/fiberInternals.ts +68 -0
  435. package/src/anchors/fiber/indexHostFibers.ts +8 -3
  436. package/src/anchors/fiber/resolveFiberPath.ts +25 -16
  437. package/src/anchors/fiber/visibleScreens.ts +5 -0
  438. package/src/anchors/fiberAnchorCache.ts +19 -0
  439. package/src/anchors/gestureHandlerHandoff.ts +356 -0
  440. package/src/anchors/resolveAnchorHost.ts +2 -0
  441. package/src/anchors/resolveAutoAnchorHost.ts +18 -8
  442. package/src/anchors/resolveElementTargets.ts +64 -7
  443. package/src/anchors/semanticMatcher.ts +48 -27
  444. package/src/anchors/tapSlop.ts +18 -0
  445. package/src/anchors/targetPressMonitor.ts +56 -19
  446. package/src/authoring/fingerprintFromInstance.ts +4 -5
  447. package/src/content/BannerStepButton.tsx +2 -1
  448. package/src/content/RichContentRenderer.tsx +19 -13
  449. package/src/content/StepButton.tsx +2 -1
  450. package/src/content/openSafeUrl.ts +3 -1
  451. package/src/content/resolveContentFontFamily.ts +119 -0
  452. package/src/expo-router/useUserflowExpoRouter.ts +10 -1
  453. package/src/index.ts +22 -9
  454. package/src/navigation/navigateUrlUtils.ts +100 -0
  455. package/src/navigation/openExternalNavigateUrl.ts +9 -0
  456. package/src/navigation/openFlowNavigateUrl.ts +36 -36
  457. package/src/pairing/PairingController.ts +9 -1
  458. package/src/runtime/BannerHost.tsx +41 -23
  459. package/src/runtime/TargetPressBridge.tsx +66 -9
  460. package/src/runtime/UserflowOverlay.tsx +7 -9
  461. package/src/runtime/UserflowProvider.tsx +56 -32
  462. package/src/runtime/bannerEngine.ts +10 -2
  463. package/src/runtime/bannerFlowSpacer.tsx +70 -0
  464. package/src/runtime/flowEngine.ts +11 -0
  465. package/src/runtime/previewStart.ts +49 -0
  466. package/src/runtime/pushBannerLayoutContext.tsx +126 -0
  467. package/src/screenTracker.ts +11 -0
  468. package/src/surfaces/BannerBar.tsx +93 -55
  469. package/src/surfaces/BannerSurface.tsx +61 -9
  470. package/src/surfaces/ProgressBar.tsx +2 -1
  471. package/src/surfaces/TooltipSurface.tsx +78 -53
  472. package/src/surfaces/bannerBarLayout.ts +80 -7
  473. package/src/surfaces/bannerWebView/BannerWebViewBar.tsx +151 -57
  474. package/src/surfaces/bannerWebView/bannerNeedsWebView.ts +4 -36
  475. package/src/surfaces/bannerWebView/bannerThemeStyles.ts +2 -1
  476. package/src/surfaces/bannerWebView/bannerWebViewHeight.ts +32 -0
  477. package/src/surfaces/bannerWebView/buildCustomBannerHtml.ts +111 -23
  478. package/src/surfaces/hostAccessibilityTrap.ts +25 -0
  479. package/src/surfaces/resolveBannerTheme.ts +14 -3
  480. package/src/surfaces/tooltip/TooltipBubble.tsx +8 -1
  481. package/src/surfaces/tooltip/targetSearchSchedule.ts +140 -0
  482. package/src/version.ts +1 -1
  483. package/vendor/core/dist/engine/BannerSessionEngine.d.ts +29 -2
  484. package/vendor/core/dist/engine/BannerSessionEngine.d.ts.map +1 -1
  485. package/vendor/core/dist/engine/BannerSessionEngine.js +87 -6
  486. package/vendor/core/dist/engine/BannerSessionEngine.js.map +1 -1
  487. package/vendor/core/dist/engine/ClientConditionEngine.d.ts.map +1 -1
  488. package/vendor/core/dist/engine/ClientConditionEngine.js +30 -7
  489. package/vendor/core/dist/engine/ClientConditionEngine.js.map +1 -1
  490. package/vendor/core/dist/engine/PairingEngine.d.ts +6 -1
  491. package/vendor/core/dist/engine/PairingEngine.d.ts.map +1 -1
  492. package/vendor/core/dist/engine/PairingEngine.js +23 -3
  493. package/vendor/core/dist/engine/PairingEngine.js.map +1 -1
  494. package/vendor/core/dist/engine/handleActions.d.ts.map +1 -1
  495. package/vendor/core/dist/engine/handleActions.js +67 -2
  496. package/vendor/core/dist/engine/handleActions.js.map +1 -1
  497. package/vendor/core/dist/version.d.ts +1 -1
  498. package/vendor/core/dist/version.js +1 -1
  499. package/vendor/core/package.json +1 -1
  500. package/vendor/protocol/dist/flows.d.ts +2 -0
  501. package/vendor/protocol/dist/flows.d.ts.map +1 -1
  502. package/vendor/protocol/package.json +1 -1
@@ -23,11 +23,14 @@ import { getEvalJsWebView, type EvalJsWebViewHandle } from '../../evalJs/getWebV
23
23
  import { injectEvalPendingIntoWebView } from '../../evalJs/injectEvalPending';
24
24
  import type { EvalJsContext } from '../../evalJs/types';
25
25
  import { useEvalJsWebViewInjection } from '../../evalJs/useEvalJsWebViewInjection';
26
+ import { usePushBannerHostInsetRegistration } from '../../runtime/pushBannerLayoutContext';
27
+ import { setBannerFlowSpacerHeight } from '../../runtime/bannerFlowSpacer';
28
+ import { warnBannerOnce } from '../../runtime/bannerPlacement';
26
29
  import { getUserflowCore } from '../../runtime/userflowCore';
27
30
  import { getCurrentScreenLocation, subscribeScreenLocation } from '../../screenTracker';
28
31
  import {
29
32
  bannerFrameStyle,
30
- bannerInnerStyle,
33
+ bannerPushLayoutChrome,
31
34
  resolveBannerBarBorderRadius,
32
35
  } from '../bannerBarLayout';
33
36
  import { bannerOverlayShadowStyle } from '../bannerBarTokens';
@@ -39,6 +42,8 @@ import { buildCustomBannerBodyHtml } from './buildCustomBannerHtml';
39
42
 
40
43
  export interface BannerWebViewBarProps {
41
44
  session: BannerSessionFragment;
45
+ pushLayoutEdge?: 'top' | 'bottom' | null;
46
+ overlaySafeEdge?: 'top' | 'bottom' | null;
42
47
  onActions: (actions: FlowActionFragment[]) => void | Promise<void>;
43
48
  onDismiss: (endReason: EndFlowReasonInput) => void;
44
49
  }
@@ -68,14 +73,27 @@ function useEvalJsScreen(): { name?: string; path?: string } {
68
73
 
69
74
  export function BannerWebViewBar({
70
75
  session,
76
+ pushLayoutEdge = null,
77
+ overlaySafeEdge = null,
71
78
  onActions,
72
79
  onDismiss,
73
80
  }: BannerWebViewBarProps): React.ReactElement | null {
74
81
  const banner = session.version.banner;
82
+ const overlay = banner?.overlay !== false;
83
+ const hostInsetEdge = banner && !overlay && pushLayoutEdge ? pushLayoutEdge : null;
84
+ const pushLayoutRegistrationEdge =
85
+ hostInsetEdge ?? (!overlay && overlaySafeEdge ? overlaySafeEdge : null);
86
+ const safeAreaEdge = hostInsetEdge ?? overlaySafeEdge ?? null;
87
+ const onPushChromeLayout = usePushBannerHostInsetRegistration(
88
+ pushLayoutRegistrationEdge,
89
+ session.id,
90
+ pushLayoutRegistrationEdge != null,
91
+ );
75
92
  const WebView = getEvalJsWebView();
76
93
  const webRef = React.useRef<EvalJsWebViewHandle>(null);
77
94
  const [pageReady, setPageReady] = React.useState(false);
78
95
  const [contentHeight, setContentHeight] = React.useState(56);
96
+ const [webLayoutHeight, setWebLayoutHeight] = React.useState<number | null>(null);
79
97
  const colorScheme = useColorScheme();
80
98
  const viewport = useWindowDimensions();
81
99
  const insets = useUserflowSafeAreaInsets();
@@ -104,18 +122,18 @@ export function BannerWebViewBar({
104
122
  const serverEndpoint = getUserflowCore().getServerEndpoint() ?? DEFAULT_SERVER_ENDPOINT;
105
123
 
106
124
  const skipAutoRunEval = shouldSkipBannerAutoRunEval(session.id);
107
- const initialContext = React.useRef(context);
108
125
  const html = React.useMemo(() => {
109
126
  const body = buildCustomBannerBodyHtml(session, lookupAttribute, serverEndpoint, {
110
127
  skipAutoRunEval,
111
128
  });
112
- return buildBootstrapHtml(initialContext.current, body);
113
- }, [lookupAttribute, serverEndpoint, session, skipAutoRunEval]);
129
+ return buildBootstrapHtml(context, body);
130
+ }, [context, lookupAttribute, serverEndpoint, session, skipAutoRunEval]);
114
131
 
115
- const opacity = React.useRef(new Animated.Value(banner?.animate ? 0 : 1)).current;
132
+ const fadeIn = banner?.animate === true && hostInsetEdge == null;
133
+ const opacity = React.useRef(new Animated.Value(fadeIn ? 0 : 1)).current;
116
134
 
117
135
  React.useEffect(() => {
118
- if (!banner?.animate) {
136
+ if (!fadeIn) {
119
137
  opacity.setValue(1);
120
138
  return;
121
139
  }
@@ -129,13 +147,39 @@ export function BannerWebViewBar({
129
147
  return () => {
130
148
  animation.stop();
131
149
  };
132
- }, [banner?.animate, opacity, session.id]);
150
+ }, [fadeIn, opacity, session.id]);
133
151
 
134
152
  React.useEffect(() => {
135
153
  setPageReady(false);
136
154
  setContentHeight(56);
155
+ setWebLayoutHeight(null);
137
156
  }, [session.id, html]);
138
157
 
158
+ const reserveFlowSpace = !overlay && overlaySafeEdge != null;
159
+ React.useEffect(() => {
160
+ if (!reserveFlowSpace || !overlaySafeEdge) {
161
+ return;
162
+ }
163
+ return () => {
164
+ setBannerFlowSpacerHeight(overlaySafeEdge, 0);
165
+ };
166
+ }, [overlaySafeEdge, reserveFlowSpace, session.id]);
167
+
168
+ React.useEffect(() => {
169
+ if (pushLayoutRegistrationEdge == null) {
170
+ return;
171
+ }
172
+ const timer = setTimeout(() => {
173
+ if (webLayoutHeight == null) {
174
+ warnBannerOnce(
175
+ `push-webview-layout:${session.id}`,
176
+ 'Userflow: push-layout WebView banner did not report a layout height; check custom CSS / WebView logs',
177
+ );
178
+ }
179
+ }, 4000);
180
+ return () => clearTimeout(timer);
181
+ }, [pushLayoutRegistrationEdge, session.id, webLayoutHeight]);
182
+
139
183
  React.useEffect(() => {
140
184
  if (!pageReady || skipAutoRunEval || !banner) {
141
185
  return;
@@ -177,17 +221,23 @@ export function BannerWebViewBar({
177
221
  const maxHeight = Math.floor(viewport.height * 0.92);
178
222
  const next = Math.min(Math.ceil(height), maxHeight);
179
223
  setContentHeight((prev) => (Math.abs(next - prev) <= 1 ? prev : next));
224
+ setWebLayoutHeight(next);
180
225
  }
181
226
  return;
182
227
  }
183
228
  if (record.__userflow === 1 && record.type === 'bannerButton') {
184
229
  const buttonId = record.buttonId;
185
230
  if (typeof buttonId === 'string' && banner) {
186
- const button = banner.buttons.find((entry) => entry.id === buttonId);
231
+ const button =
232
+ banner.buttons.find((entry) => entry.id === buttonId) ??
233
+ banner.buttons.find((entry) => entry.cvid === buttonId);
187
234
  if (button) {
188
235
  void Promise.resolve(onActions(button.actions)).then(() => {
189
236
  injectEvalPendingIntoWebView(webRef.current);
190
237
  });
238
+ } else if (__DEV__) {
239
+ // eslint-disable-next-line no-console -- intentional development diagnostic
240
+ console.warn('[Userflow] banner button not found for id', buttonId);
191
241
  }
192
242
  }
193
243
  return;
@@ -241,72 +291,116 @@ export function BannerWebViewBar({
241
291
  return;
242
292
  }
243
293
  },
244
- [banner, onActions, onDismiss, viewport.height],
294
+ [
295
+ banner,
296
+ insets.bottom,
297
+ insets.top,
298
+ onActions,
299
+ onDismiss,
300
+ overlaySafeEdge,
301
+ reserveFlowSpace,
302
+ viewport.height,
303
+ ],
245
304
  );
246
305
 
247
306
  if (!banner || !WebView) {
248
307
  return null;
249
308
  }
250
309
 
251
- const overlay = banner.overlay !== false;
252
310
  const frameStyle = bannerFrameStyle(banner);
253
- const innerStyle = bannerInnerStyle(banner);
254
311
  const overlayEdge = banner.embedMode === BannerEmbedMode.BODY_LAST ? 'bottom' : 'top';
255
312
  const barRadius = resolveBannerBarBorderRadius(banner);
313
+ // Custom CSS paints the card, often with its own radius. The theme color
314
+ // (brand blue when banner tokens are unset) would otherwise fill the
315
+ // status-bar pad and the corners outside that radius.
316
+ const shellBackground = 'transparent';
256
317
  const webStyle: ViewStyle = {
257
318
  width: '100%',
258
319
  height: contentHeight,
259
- backgroundColor: 'transparent',
320
+ backgroundColor: shellBackground,
260
321
  };
261
322
  const debugging = typeof __DEV__ !== 'undefined' && __DEV__ ? true : undefined;
323
+ const insetChrome = safeAreaEdge
324
+ ? bannerPushLayoutChrome(safeAreaEdge, insets, shellBackground)
325
+ : null;
326
+ const insetChromeOutsideFade = insetChrome != null && fadeIn;
327
+
328
+ const webViewBar = (
329
+ <View
330
+ testID="userflow-banner-webview"
331
+ pointerEvents="auto"
332
+ style={[{ borderRadius: barRadius ?? 0, overflow: 'hidden' as const }]}
333
+ >
334
+ <WebView
335
+ key={session.id}
336
+ ref={webRef}
337
+ source={{ html, baseUrl: EVAL_JS_BASE_URL }}
338
+ style={webStyle}
339
+ javaScriptEnabled
340
+ domStorageEnabled
341
+ scrollEnabled={false}
342
+ bounces={false}
343
+ showsHorizontalScrollIndicator={false}
344
+ showsVerticalScrollIndicator={false}
345
+ originWhitelist={['*']}
346
+ setSupportMultipleWindows={false}
347
+ androidLayerType="hardware"
348
+ {...(debugging !== undefined ? { webviewDebuggingEnabled: debugging } : {})}
349
+ onMessage={onMessage}
350
+ onLoadEnd={() => {
351
+ setPageReady(true);
352
+ webRef.current?.injectJavaScript(
353
+ 'window.__userflowBannerLayout&&window.__userflowBannerLayout();true;',
354
+ );
355
+ }}
356
+ onShouldStartLoadWithRequest={(request) => {
357
+ if (isBootstrapUrl(request.url)) {
358
+ return true;
359
+ }
360
+ openExternal(request.url);
361
+ return false;
362
+ }}
363
+ onOpenWindow={(event) => {
364
+ const url = event.nativeEvent.targetUrl;
365
+ if (url) {
366
+ openExternal(url);
367
+ }
368
+ }}
369
+ />
370
+ </View>
371
+ );
372
+
373
+ const animatedBar = (
374
+ <Animated.View
375
+ style={[
376
+ { opacity },
377
+ overlay ? bannerOverlayShadowStyle(overlayEdge) : null,
378
+ insetChromeOutsideFade ? null : insetChrome,
379
+ ]}
380
+ >
381
+ {webViewBar}
382
+ </Animated.View>
383
+ );
262
384
 
263
385
  return (
264
- <View testID="userflow-banner-frame" style={frameStyle}>
265
- <Animated.View style={[{ opacity }, overlay ? bannerOverlayShadowStyle(overlayEdge) : null]}>
266
- <View
267
- testID="userflow-banner-webview"
268
- style={[
269
- { borderRadius: barRadius ?? 0, overflow: 'hidden' as const },
270
- innerStyle.maxWidth != null ? { maxWidth: innerStyle.maxWidth } : null,
271
- ]}
272
- >
273
- <WebView
274
- ref={webRef}
275
- source={{ html, baseUrl: EVAL_JS_BASE_URL }}
276
- style={webStyle}
277
- javaScriptEnabled
278
- domStorageEnabled
279
- scrollEnabled={false}
280
- bounces={false}
281
- showsHorizontalScrollIndicator={false}
282
- showsVerticalScrollIndicator={false}
283
- originWhitelist={['*']}
284
- setSupportMultipleWindows={false}
285
- androidLayerType="hardware"
286
- {...(debugging !== undefined ? { webviewDebuggingEnabled: debugging } : {})}
287
- onMessage={onMessage}
288
- onLoadEnd={() => {
289
- setPageReady(true);
290
- webRef.current?.injectJavaScript(
291
- 'window.__userflowBannerLayout&&window.__userflowBannerLayout();true;',
292
- );
293
- }}
294
- onShouldStartLoadWithRequest={(request) => {
295
- if (isBootstrapUrl(request.url)) {
296
- return true;
386
+ <View
387
+ testID="userflow-banner-frame"
388
+ style={frameStyle}
389
+ onLayout={
390
+ reserveFlowSpace && overlaySafeEdge
391
+ ? (event) => {
392
+ onPushChromeLayout(event);
393
+ const h = event.nativeEvent.layout.height;
394
+ if (h > 0) {
395
+ setBannerFlowSpacerHeight(overlaySafeEdge, h);
297
396
  }
298
- openExternal(request.url);
299
- return false;
300
- }}
301
- onOpenWindow={(event) => {
302
- const url = event.nativeEvent.targetUrl;
303
- if (url) {
304
- openExternal(url);
305
- }
306
- }}
307
- />
308
- </View>
309
- </Animated.View>
397
+ }
398
+ : pushLayoutRegistrationEdge
399
+ ? onPushChromeLayout
400
+ : undefined
401
+ }
402
+ >
403
+ {insetChromeOutsideFade ? <View style={insetChrome}>{animatedBar}</View> : animatedBar}
310
404
  </View>
311
405
  );
312
406
  }
@@ -1,38 +1,6 @@
1
- import type { BannerFragment, ThemeFragment } from '@userflow-tech/protocol';
2
- import { FlowActionType } from '@userflow-tech/protocol';
1
+ import type { BannerFragment } from '@userflow-tech/protocol';
3
2
 
4
- import { themeHasPublishedCustomCss } from './resolveCdnHost';
5
-
6
- let debugForceWebViewBanner = false;
7
-
8
- /** POC / local testing when the builder flag is not on the wire yet. */
9
- export function setDebugForceWebViewBanner(enabled: boolean): void {
10
- debugForceWebViewBanner = enabled;
11
- }
12
-
13
- /** @internal */
14
- export function resetDebugForceWebViewBannerForTests(): void {
15
- debugForceWebViewBanner = false;
16
- }
17
-
18
- function bannerHasEvalJsAction(banner: BannerFragment): boolean {
19
- return banner.buttons.some((button) =>
20
- button.actions.some((action) => action.type === FlowActionType.EVAL_JS),
21
- );
22
- }
23
-
24
- /**
25
- * Use the WebView banner renderer when any button runs Evaluate JavaScript
26
- * (needs the shared eval page), when the theme has published custom CSS
27
- * (`.userflow-custom-container-*` / `.userflow-custom-button-*` via CDN),
28
- * or when {@link setDebugForceWebViewBanner} is true.
29
- */
30
- export function bannerNeedsWebView(banner: BannerFragment, theme?: ThemeFragment | null): boolean {
31
- if (debugForceWebViewBanner) {
32
- return true;
33
- }
34
- if (theme != null && themeHasPublishedCustomCss(theme)) {
35
- return true;
36
- }
37
- return bannerHasEvalJsAction(banner);
3
+ /** Use the WebView banner renderer when the builder enabled custom banners. */
4
+ export function bannerNeedsWebView(banner: BannerFragment): boolean {
5
+ return banner.customBannerEnabled === true;
38
6
  }
@@ -1,5 +1,6 @@
1
1
  import type { ThemeFragment } from '@userflow-tech/protocol';
2
2
 
3
+ import { contentFontFamilyCss } from '../../content/resolveContentFontFamily';
3
4
  import { resolveBannerBackgroundColor, resolveBannerForegroundColor } from '../resolveBannerTheme';
4
5
 
5
6
  function readString(theme: ThemeFragment, key: string, fallback: string): string {
@@ -68,7 +69,7 @@ export function bannerThemeInlineStyles(theme: ThemeFragment): Record<string, st
68
69
  'bannerActiveForegroundColor',
69
70
  bannerFg,
70
71
  ),
71
- '--userflow-font-family': readString(theme, 'fontFamily', 'system-ui, sans-serif'),
72
+ '--userflow-font-family': contentFontFamilyCss(theme),
72
73
  '--userflow-font-size': `${fontSize}px`,
73
74
  '--userflow-line-height': `${lineHeight}px`,
74
75
  '--userflow-button-height': readCssLength(theme, 'buttonHeight', 32),
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Height the native WebView should use for a custom banner.
3
+ *
4
+ * Custom CSS often sizes the card with `min-height` and takes the copy and
5
+ * buttons out of flow (`position: absolute`). The WebView is created shorter
6
+ * than that card, so a root that only looks as tall as the viewport because of
7
+ * `min-height` must not be replaced with the close button's box.
8
+ *
9
+ * When the root really is stretching to fill a taller viewport (`height: 100%`),
10
+ * fall back to in-flow content, and never below `min-height`.
11
+ */
12
+ export function resolveBannerWebViewHeight(
13
+ rectHeight: number,
14
+ scrollHeight: number,
15
+ viewportHeight: number,
16
+ minHeight: number,
17
+ contentHeight: number,
18
+ ): number {
19
+ const rect = rectHeight > 0 ? rectHeight : 0;
20
+ const scroll = scrollHeight > 0 ? scrollHeight : 0;
21
+ let height = Math.ceil(Math.max(rect, scroll));
22
+ const floor = minHeight > 0 ? Math.ceil(minHeight) : 0;
23
+ if (viewportHeight > 0 && height >= viewportHeight - 2 && height > floor + 1) {
24
+ if (contentHeight > 0 && contentHeight < height) {
25
+ height = Math.ceil(contentHeight > floor ? contentHeight : floor);
26
+ }
27
+ }
28
+ if (floor > height) {
29
+ height = floor;
30
+ }
31
+ return height;
32
+ }
@@ -2,11 +2,17 @@ import { interpolateVariables } from '@userflow-tech/core';
2
2
  import type { BannerFragment, BannerSessionFragment } from '@userflow-tech/protocol';
3
3
  import { BannerContentLayout } from '@userflow-tech/protocol';
4
4
 
5
+ import {
6
+ bannerContentFontLockCss,
7
+ contentFontFaceCss,
8
+ } from '../../content/resolveContentFontFamily';
5
9
  import { buildBootstrapHtml, wrapCode } from '../../evalJs/bootstrap';
6
10
  import type { EvalJsContext } from '../../evalJs/types';
7
11
 
8
12
  import { collectBannerAutoRunEvalCodes } from './bannerAutoRunEvalJs';
9
13
  import { bannerThemeStyleAttribute } from './bannerThemeStyles';
14
+ import { resolveBannerInnerMaxWidth } from '../bannerBarLayout';
15
+
10
16
  import { BANNER_WEB_FRAME_CSS } from './bannerWebFrameCss';
11
17
  import { buttonClassName } from './buttonClassName';
12
18
  import { resolveCustomStylesheetUrl } from './resolveCdnHost';
@@ -25,26 +31,45 @@ function contentLayoutClass(layout: BannerContentLayout): string {
25
31
  }
26
32
 
27
33
  function layoutMeasureScript(): string {
34
+ // Inlined on purpose. Hermes' Function#toString is not the source text, so
35
+ // interpolating resolveBannerWebViewHeight.toString() ships a dead function.
36
+ // Keep this body in sync with resolveBannerWebViewHeight.
28
37
  return `
29
38
  (function(){
39
+ function resolveBannerWebViewHeight(rectHeight,scrollHeight,viewportHeight,minHeight,contentHeight){
40
+ var rect=rectHeight>0?rectHeight:0;
41
+ var scroll=scrollHeight>0?scrollHeight:0;
42
+ var height=Math.ceil(Math.max(rect,scroll));
43
+ var floor=minHeight>0?Math.ceil(minHeight):0;
44
+ if(viewportHeight>0&&height>=viewportHeight-2&&height>floor+1){
45
+ if(contentHeight>0&&contentHeight<height){
46
+ height=Math.ceil(contentHeight>floor?contentHeight:floor);
47
+ }
48
+ }
49
+ if(floor>height) height=floor;
50
+ return height;
51
+ }
30
52
  function postLayout(){
31
53
  var root=document.querySelector('.userflowjs-banner-root');
32
54
  if(!root||!window.ReactNativeWebView||!window.ReactNativeWebView.postMessage)return;
33
55
  var rectH=root.getBoundingClientRect().height;
34
56
  var scrollH=root.scrollHeight;
35
- var h=Math.ceil(Math.max(scrollH,rectH));
57
+ var minH=parseFloat(window.getComputedStyle(root).minHeight);
58
+ if(!(minH>0)) minH=0;
36
59
  var vh=window.innerHeight||0;
37
- if(vh>0&&h>=vh-2){
38
- var sum=0;
39
- for(var i=0;i<root.childNodes.length;i++){
40
- var node=root.childNodes[i];
41
- if(node&&node.nodeType===1){
42
- var el=node;
43
- sum+=el.scrollHeight||el.offsetHeight||0;
44
- }
45
- }
46
- if(sum>0&&sum<h) h=Math.ceil(sum);
60
+ var inFlow=0;
61
+ var all=0;
62
+ for(var i=0;i<root.childNodes.length;i++){
63
+ var node=root.childNodes[i];
64
+ if(!node||node.nodeType!==1) continue;
65
+ var box=node.scrollHeight||node.offsetHeight||0;
66
+ all+=box;
67
+ var cs=window.getComputedStyle(node);
68
+ if(cs.display==='none'||cs.position==='absolute'||cs.position==='fixed') continue;
69
+ inFlow+=box;
47
70
  }
71
+ var content=inFlow>0?inFlow:all;
72
+ var h=resolveBannerWebViewHeight(rectH,scrollH,vh,minH,content);
48
73
  window.ReactNativeWebView.postMessage(JSON.stringify({__userflow:1,type:'bannerLayout',height:h}));
49
74
  }
50
75
  window.__userflowBannerLayout=postLayout;
@@ -52,36 +77,87 @@ function layoutMeasureScript(): string {
52
77
  var root=document.querySelector('.userflowjs-banner-root');
53
78
  if(root){new ResizeObserver(postLayout).observe(root);}
54
79
  }
80
+ var links=document.querySelectorAll('link[rel="stylesheet"]');
81
+ for(var s=0;s<links.length;s++){
82
+ links[s].addEventListener('load',postLayout);
83
+ links[s].addEventListener('error',postLayout);
84
+ }
55
85
  window.addEventListener('load',postLayout);
86
+ // Custom CSS is often still applying on the first pass. A short WebView
87
+ // then clamps used height, so min-height never changes the box and
88
+ // ResizeObserver does not fire. Measure again after the stylesheet can apply.
56
89
  setTimeout(postLayout,0);
90
+ setTimeout(postLayout,60);
91
+ setTimeout(postLayout,250);
57
92
  })();
58
93
  `;
59
94
  }
60
95
 
61
- function bannerInteractionScript(): string {
96
+ function bannerInteractionScript(buttonsJson: string): string {
62
97
  return `
63
98
  (function(){
99
+ var __userflowBannerButtons=${buttonsJson};
64
100
  function post(msg){
65
101
  if(window.ReactNativeWebView&&window.ReactNativeWebView.postMessage){
66
102
  window.ReactNativeWebView.postMessage(JSON.stringify(Object.assign({__userflow:1},msg)));
67
103
  }
68
104
  }
69
- document.addEventListener('click',function(ev){
105
+ function resolveButtonId(el){
106
+ if(!el||!el.closest)return null;
107
+ var tagged=el.closest('[data-userflow-banner-button]');
108
+ if(tagged){
109
+ return tagged.getAttribute('data-userflow-banner-button');
110
+ }
111
+ function hasClass(el,token){
112
+ if(!token)return false;
113
+ var cls=el.className||'';
114
+ var parts=cls.split(/\\s+/);
115
+ for(var p=0;p<parts.length;p++){
116
+ if(parts[p]===token)return true;
117
+ }
118
+ return false;
119
+ }
120
+ function appearanceClass(appearance){
121
+ if(!appearance)return '';
122
+ var value=appearance;
123
+ var known=value==='primary'||value==='PRIMARY'||value==='default'||value==='DEFAULT'||value==='banner-primary'||value==='banner-secondary';
124
+ return known?'userflowjs-bubble-button--'+value.toLowerCase():value;
125
+ }
126
+ var bubble=el.closest('.userflowjs-bubble-button,button.userflowjs-bubble-button');
127
+ if(bubble){
128
+ var id=bubble.getAttribute('data-userflow-banner-button');
129
+ if(id)return id;
130
+ for(var i=0;i<__userflowBannerButtons.length;i++){
131
+ var meta=__userflowBannerButtons[i];
132
+ if(meta.appearance&&hasClass(bubble,appearanceClass(meta.appearance)))return meta.id;
133
+ }
134
+ }
135
+ var custom=el.closest('[class*="userflow-custom-button"]');
136
+ if(custom){
137
+ for(var j=0;j<__userflowBannerButtons.length;j++){
138
+ var entry=__userflowBannerButtons[j];
139
+ if(entry.appearance&&hasClass(custom,appearanceClass(entry.appearance)))return entry.id;
140
+ }
141
+ }
142
+ return null;
143
+ }
144
+ function onActivate(ev){
70
145
  var t=ev.target;
71
146
  if(!t||!t.closest)return;
72
- var btn=t.closest('[data-userflow-banner-button]');
73
- if(btn){
74
- ev.preventDefault();
75
- post({type:'bannerButton',buttonId:btn.getAttribute('data-userflow-banner-button')});
76
- return;
77
- }
78
147
  var close=t.closest('[data-userflow-banner-dismiss]');
79
148
  if(close){
80
149
  ev.preventDefault();
81
150
  window.__userflowBannerEvalDismissed=true;
82
151
  post({type:'bannerDismiss'});
152
+ return;
153
+ }
154
+ var buttonId=resolveButtonId(t);
155
+ if(buttonId){
156
+ ev.preventDefault();
157
+ post({type:'bannerButton',buttonId:buttonId});
83
158
  }
84
- });
159
+ }
160
+ document.addEventListener('click',onActivate,true);
85
161
  })();
86
162
  `;
87
163
  }
@@ -128,9 +204,12 @@ export function buildCustomBannerBodyHtml(
128
204
  const theme = session.version.theme;
129
205
  const layoutClass = contentLayoutClass(banner.contentLayout);
130
206
  const themeStyle = bannerThemeStyleAttribute(theme);
207
+ const fontFaceCss = contentFontFaceCss(theme);
208
+ const fontLockCss = bannerContentFontLockCss(theme);
131
209
  const customCssUrl = resolveCustomStylesheetUrl(serverEndpoint, theme);
210
+ const innerMaxWidth = resolveBannerInnerMaxWidth(banner);
132
211
  const maxInner =
133
- banner.maxContentWidth != null ? ` style="max-width:${banner.maxContentWidth}px"` : '';
212
+ innerMaxWidth != null && innerMaxWidth > 0 ? ` style="max-width:${innerMaxWidth}px"` : '';
134
213
  const contentHtml = richContentDocToHtml(banner.content, lookupAttribute);
135
214
  const buttonsHtml = banner.buttons
136
215
  .map((button) => {
@@ -143,13 +222,22 @@ export function buildCustomBannerBodyHtml(
143
222
  ? '<div class="userflowjs-banner-start-spacer"></div>'
144
223
  : '<button type="button" class="userflowjs-banner-close userflowjs-button-reset" data-userflow-banner-dismiss="1" aria-label="Dismiss">×</button>';
145
224
 
225
+ const buttonMetaJson = JSON.stringify(
226
+ banner.buttons.map((button) => ({
227
+ id: button.id,
228
+ cvid: button.cvid,
229
+ appearance: button.appearance ?? '',
230
+ })),
231
+ ).replace(/</g, '\\u003c');
232
+
146
233
  const customLink = customCssUrl
147
234
  ? `<link rel="stylesheet" href="${escapeAttr(customCssUrl)}" />`
148
235
  : '';
149
236
 
150
237
  return `
151
- <style>${BANNER_WEB_FRAME_CSS}</style>
238
+ <style>${fontFaceCss}${BANNER_WEB_FRAME_CSS}</style>
152
239
  ${customLink}
240
+ <style>${fontLockCss}</style>
153
241
  <div class="userflowjs-banner-root userflowjs-theme-root ${layoutClass}" style="${themeStyle}">
154
242
  <div class="userflowjs-banner-start-spacer"></div>
155
243
  <div class="userflowjs-banner-inner"${maxInner}>
@@ -158,7 +246,7 @@ ${customLink}
158
246
  </div>
159
247
  ${closeHtml}
160
248
  </div>
161
- <script>${layoutMeasureScript()}${bannerInteractionScript()}${options?.skipAutoRunEval ? '' : bannerAutoRunEvalScript(banner, lookupAttribute)}</script>
249
+ <script>${layoutMeasureScript()}${bannerInteractionScript(buttonMetaJson)}${options?.skipAutoRunEval ? '' : bannerAutoRunEvalScript(banner, lookupAttribute)}</script>
162
250
  `;
163
251
  }
164
252
 
@@ -0,0 +1,25 @@
1
+ import type { FlowStepFragment } from '@userflow-tech/protocol';
2
+
3
+ /**
4
+ * Whether a step hides host app content from the accessibility tree.
5
+ *
6
+ * Modals always do. A tooltip does only when `tooltipTargetBlocked` seals its
7
+ * target too — otherwise the target is the control the step asks the user to
8
+ * press, so a screen reader has to be able to reach it.
9
+ *
10
+ * `UserflowOverlay` traps the host with this, and tooltip cards gate
11
+ * `accessibilityViewIsModal` on it: on iOS that flag alone drops the host app,
12
+ * target included, out of VoiceOver's reach.
13
+ */
14
+ export function stepTrapsHostAccessibility(
15
+ step: Pick<FlowStepFragment, 'appearance' | 'tooltipTargetBlocked'>,
16
+ ): boolean {
17
+ const appearance = String(step.appearance).toUpperCase();
18
+ if (appearance === 'HIDDEN') {
19
+ return false;
20
+ }
21
+ if (appearance !== 'TOOLTIP') {
22
+ return true;
23
+ }
24
+ return step.tooltipTargetBlocked;
25
+ }
@@ -1,6 +1,9 @@
1
1
  import type { ThemeFragment } from '@userflow-tech/protocol';
2
2
 
3
- /** Default banner chrome when theme banner tokens are unset (matches product / web debugger palette). */
3
+ /**
4
+ * Last-resort banner chrome when neither banner nor brand tokens are set on the
5
+ * wire (web `applyThemeDefaults` falls back to brand first).
6
+ */
4
7
  export const DEFAULT_BANNER_BACKGROUND_COLOR = '#0A1A24';
5
8
  export const DEFAULT_BANNER_FOREGROUND_COLOR = '#FFFFFF';
6
9
 
@@ -10,9 +13,17 @@ function readThemeColor(theme: ThemeFragment, key: string): string | undefined {
10
13
  }
11
14
 
12
15
  export function resolveBannerBackgroundColor(theme: ThemeFragment): string {
13
- return readThemeColor(theme, 'bannerBackgroundColor') ?? DEFAULT_BANNER_BACKGROUND_COLOR;
16
+ return (
17
+ readThemeColor(theme, 'bannerBackgroundColor') ??
18
+ readThemeColor(theme, 'brandBackgroundColor') ??
19
+ DEFAULT_BANNER_BACKGROUND_COLOR
20
+ );
14
21
  }
15
22
 
16
23
  export function resolveBannerForegroundColor(theme: ThemeFragment): string {
17
- return readThemeColor(theme, 'bannerForegroundColor') ?? DEFAULT_BANNER_FOREGROUND_COLOR;
24
+ return (
25
+ readThemeColor(theme, 'bannerForegroundColor') ??
26
+ readThemeColor(theme, 'brandForegroundColor') ??
27
+ DEFAULT_BANNER_FOREGROUND_COLOR
28
+ );
18
29
  }