@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.
- package/README.md +31 -2
- package/docs/anchoring.md +6 -1
- package/docs/custom-fonts.md +36 -0
- package/docs/troubleshooting.md +33 -6
- package/lib/commonjs/anchors/AutoAnchorRegistry.js +144 -34
- package/lib/commonjs/anchors/AutoAnchorRegistry.js.map +1 -1
- package/lib/commonjs/anchors/animationFrame.js +29 -1
- package/lib/commonjs/anchors/animationFrame.js.map +1 -1
- package/lib/commonjs/anchors/appActive.js +61 -0
- package/lib/commonjs/anchors/appActive.js.map +1 -0
- package/lib/commonjs/anchors/elementConditionEvaluator.js +25 -8
- package/lib/commonjs/anchors/elementConditionEvaluator.js.map +1 -1
- package/lib/commonjs/anchors/elementTapMonitor.js +67 -12
- package/lib/commonjs/anchors/elementTapMonitor.js.map +1 -1
- package/lib/commonjs/anchors/elementTargetMatch.js +33 -7
- package/lib/commonjs/anchors/elementTargetMatch.js.map +1 -1
- package/lib/commonjs/anchors/fiber/FiberScanRootStore.js +8 -0
- package/lib/commonjs/anchors/fiber/FiberScanRootStore.js.map +1 -1
- package/lib/commonjs/anchors/fiber/detectAppFontFamily.js +341 -0
- package/lib/commonjs/anchors/fiber/detectAppFontFamily.js.map +1 -0
- package/lib/commonjs/anchors/fiber/fiberInternals.js +66 -0
- package/lib/commonjs/anchors/fiber/fiberInternals.js.map +1 -1
- package/lib/commonjs/anchors/fiber/indexHostFibers.js +4 -3
- package/lib/commonjs/anchors/fiber/indexHostFibers.js.map +1 -1
- package/lib/commonjs/anchors/fiber/resolveFiberPath.js +30 -15
- package/lib/commonjs/anchors/fiber/resolveFiberPath.js.map +1 -1
- package/lib/commonjs/anchors/fiber/visibleScreens.js +6 -0
- package/lib/commonjs/anchors/fiber/visibleScreens.js.map +1 -1
- package/lib/commonjs/anchors/fiberAnchorCache.js +20 -0
- package/lib/commonjs/anchors/fiberAnchorCache.js.map +1 -1
- package/lib/commonjs/anchors/gestureHandlerHandoff.js +304 -0
- package/lib/commonjs/anchors/gestureHandlerHandoff.js.map +1 -0
- package/lib/commonjs/anchors/resolveAnchorHost.js +6 -0
- package/lib/commonjs/anchors/resolveAnchorHost.js.map +1 -1
- package/lib/commonjs/anchors/resolveAutoAnchorHost.js +11 -6
- package/lib/commonjs/anchors/resolveAutoAnchorHost.js.map +1 -1
- package/lib/commonjs/anchors/resolveElementTargets.js +63 -8
- package/lib/commonjs/anchors/resolveElementTargets.js.map +1 -1
- package/lib/commonjs/anchors/semanticMatcher.js +43 -21
- package/lib/commonjs/anchors/semanticMatcher.js.map +1 -1
- package/lib/commonjs/anchors/tapSlop.js +16 -0
- package/lib/commonjs/anchors/tapSlop.js.map +1 -0
- package/lib/commonjs/anchors/targetPressMonitor.js +66 -12
- package/lib/commonjs/anchors/targetPressMonitor.js.map +1 -1
- package/lib/commonjs/authoring/fingerprintFromInstance.js +5 -9
- package/lib/commonjs/authoring/fingerprintFromInstance.js.map +1 -1
- package/lib/commonjs/content/BannerStepButton.js +2 -1
- package/lib/commonjs/content/BannerStepButton.js.map +1 -1
- package/lib/commonjs/content/RichContentRenderer.js +20 -14
- package/lib/commonjs/content/RichContentRenderer.js.map +1 -1
- package/lib/commonjs/content/StepButton.js +2 -1
- package/lib/commonjs/content/StepButton.js.map +1 -1
- package/lib/commonjs/content/openSafeUrl.js +3 -1
- package/lib/commonjs/content/openSafeUrl.js.map +1 -1
- package/lib/commonjs/content/resolveContentFontFamily.js +96 -0
- package/lib/commonjs/content/resolveContentFontFamily.js.map +1 -0
- package/lib/commonjs/expo-router/useUserflowExpoRouter.js +6 -0
- package/lib/commonjs/expo-router/useUserflowExpoRouter.js.map +1 -1
- package/lib/commonjs/index.js +21 -5
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/navigation/navigateUrlUtils.js +105 -0
- package/lib/commonjs/navigation/navigateUrlUtils.js.map +1 -0
- package/lib/commonjs/navigation/openExternalNavigateUrl.js +14 -0
- package/lib/commonjs/navigation/openExternalNavigateUrl.js.map +1 -0
- package/lib/commonjs/navigation/openFlowNavigateUrl.js +47 -41
- package/lib/commonjs/navigation/openFlowNavigateUrl.js.map +1 -1
- package/lib/commonjs/pairing/PairingController.js +3 -1
- package/lib/commonjs/pairing/PairingController.js.map +1 -1
- package/lib/commonjs/runtime/BannerHost.js +38 -29
- package/lib/commonjs/runtime/BannerHost.js.map +1 -1
- package/lib/commonjs/runtime/TargetPressBridge.js +55 -7
- package/lib/commonjs/runtime/TargetPressBridge.js.map +1 -1
- package/lib/commonjs/runtime/UserflowOverlay.js +5 -11
- package/lib/commonjs/runtime/UserflowOverlay.js.map +1 -1
- package/lib/commonjs/runtime/UserflowProvider.js +66 -40
- package/lib/commonjs/runtime/UserflowProvider.js.map +1 -1
- package/lib/commonjs/runtime/bannerEngine.js +8 -2
- package/lib/commonjs/runtime/bannerEngine.js.map +1 -1
- package/lib/commonjs/runtime/bannerFlowSpacer.js +71 -0
- package/lib/commonjs/runtime/bannerFlowSpacer.js.map +1 -0
- package/lib/commonjs/runtime/flowEngine.js +11 -0
- package/lib/commonjs/runtime/flowEngine.js.map +1 -1
- package/lib/commonjs/runtime/previewStart.js +39 -0
- package/lib/commonjs/runtime/previewStart.js.map +1 -0
- package/lib/commonjs/runtime/pushBannerLayoutContext.js +101 -0
- package/lib/commonjs/runtime/pushBannerLayoutContext.js.map +1 -0
- package/lib/commonjs/screenTracker.js +12 -0
- package/lib/commonjs/screenTracker.js.map +1 -1
- package/lib/commonjs/surfaces/BannerBar.js +79 -61
- package/lib/commonjs/surfaces/BannerBar.js.map +1 -1
- package/lib/commonjs/surfaces/BannerSurface.js +28 -6
- package/lib/commonjs/surfaces/BannerSurface.js.map +1 -1
- package/lib/commonjs/surfaces/ProgressBar.js +2 -1
- package/lib/commonjs/surfaces/ProgressBar.js.map +1 -1
- package/lib/commonjs/surfaces/TooltipSurface.js +48 -41
- package/lib/commonjs/surfaces/TooltipSurface.js.map +1 -1
- package/lib/commonjs/surfaces/bannerBarLayout.js +77 -10
- package/lib/commonjs/surfaces/bannerBarLayout.js.map +1 -1
- package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js +114 -63
- package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
- package/lib/commonjs/surfaces/bannerWebView/bannerNeedsWebView.js +3 -33
- package/lib/commonjs/surfaces/bannerWebView/bannerNeedsWebView.js.map +1 -1
- package/lib/commonjs/surfaces/bannerWebView/bannerThemeStyles.js +2 -1
- package/lib/commonjs/surfaces/bannerWebView/bannerThemeStyles.js.map +1 -1
- package/lib/commonjs/surfaces/bannerWebView/bannerWebViewHeight.js +33 -0
- package/lib/commonjs/surfaces/bannerWebView/bannerWebViewHeight.js.map +1 -0
- package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js +104 -23
- package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
- package/lib/commonjs/surfaces/hostAccessibilityTrap.js +28 -0
- package/lib/commonjs/surfaces/hostAccessibilityTrap.js.map +1 -0
- package/lib/commonjs/surfaces/resolveBannerTheme.js +6 -3
- package/lib/commonjs/surfaces/resolveBannerTheme.js.map +1 -1
- package/lib/commonjs/surfaces/tooltip/TooltipBubble.js +3 -2
- package/lib/commonjs/surfaces/tooltip/TooltipBubble.js.map +1 -1
- package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js +134 -0
- package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/module/anchors/AutoAnchorRegistry.js +145 -35
- package/lib/module/anchors/AutoAnchorRegistry.js.map +1 -1
- package/lib/module/anchors/animationFrame.js +29 -2
- package/lib/module/anchors/animationFrame.js.map +1 -1
- package/lib/module/anchors/appActive.js +57 -0
- package/lib/module/anchors/appActive.js.map +1 -0
- package/lib/module/anchors/elementConditionEvaluator.js +26 -9
- package/lib/module/anchors/elementConditionEvaluator.js.map +1 -1
- package/lib/module/anchors/elementTapMonitor.js +67 -12
- package/lib/module/anchors/elementTapMonitor.js.map +1 -1
- package/lib/module/anchors/elementTargetMatch.js +32 -7
- package/lib/module/anchors/elementTargetMatch.js.map +1 -1
- package/lib/module/anchors/fiber/FiberScanRootStore.js +8 -0
- package/lib/module/anchors/fiber/FiberScanRootStore.js.map +1 -1
- package/lib/module/anchors/fiber/detectAppFontFamily.js +330 -0
- package/lib/module/anchors/fiber/detectAppFontFamily.js.map +1 -0
- package/lib/module/anchors/fiber/fiberInternals.js +65 -0
- package/lib/module/anchors/fiber/fiberInternals.js.map +1 -1
- package/lib/module/anchors/fiber/indexHostFibers.js +4 -3
- package/lib/module/anchors/fiber/indexHostFibers.js.map +1 -1
- package/lib/module/anchors/fiber/resolveFiberPath.js +30 -15
- package/lib/module/anchors/fiber/resolveFiberPath.js.map +1 -1
- package/lib/module/anchors/fiber/visibleScreens.js +5 -0
- package/lib/module/anchors/fiber/visibleScreens.js.map +1 -1
- package/lib/module/anchors/fiberAnchorCache.js +19 -0
- package/lib/module/anchors/fiberAnchorCache.js.map +1 -1
- package/lib/module/anchors/gestureHandlerHandoff.js +298 -0
- package/lib/module/anchors/gestureHandlerHandoff.js.map +1 -0
- package/lib/module/anchors/resolveAnchorHost.js +1 -0
- package/lib/module/anchors/resolveAnchorHost.js.map +1 -1
- package/lib/module/anchors/resolveAutoAnchorHost.js +11 -6
- package/lib/module/anchors/resolveAutoAnchorHost.js.map +1 -1
- package/lib/module/anchors/resolveElementTargets.js +63 -8
- package/lib/module/anchors/resolveElementTargets.js.map +1 -1
- package/lib/module/anchors/semanticMatcher.js +43 -21
- package/lib/module/anchors/semanticMatcher.js.map +1 -1
- package/lib/module/anchors/tapSlop.js +11 -0
- package/lib/module/anchors/tapSlop.js.map +1 -0
- package/lib/module/anchors/targetPressMonitor.js +53 -8
- package/lib/module/anchors/targetPressMonitor.js.map +1 -1
- package/lib/module/authoring/fingerprintFromInstance.js +6 -10
- package/lib/module/authoring/fingerprintFromInstance.js.map +1 -1
- package/lib/module/content/BannerStepButton.js +2 -1
- package/lib/module/content/BannerStepButton.js.map +1 -1
- package/lib/module/content/RichContentRenderer.js +20 -14
- package/lib/module/content/RichContentRenderer.js.map +1 -1
- package/lib/module/content/StepButton.js +2 -1
- package/lib/module/content/StepButton.js.map +1 -1
- package/lib/module/content/openSafeUrl.js +3 -1
- package/lib/module/content/openSafeUrl.js.map +1 -1
- package/lib/module/content/resolveContentFontFamily.js +89 -0
- package/lib/module/content/resolveContentFontFamily.js.map +1 -0
- package/lib/module/expo-router/useUserflowExpoRouter.js +7 -1
- package/lib/module/expo-router/useUserflowExpoRouter.js.map +1 -1
- package/lib/module/index.js +24 -7
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigation/navigateUrlUtils.js +94 -0
- package/lib/module/navigation/navigateUrlUtils.js.map +1 -0
- package/lib/module/navigation/openExternalNavigateUrl.js +11 -0
- package/lib/module/navigation/openExternalNavigateUrl.js.map +1 -0
- package/lib/module/navigation/openFlowNavigateUrl.js +22 -36
- package/lib/module/navigation/openFlowNavigateUrl.js.map +1 -1
- package/lib/module/pairing/PairingController.js +3 -1
- package/lib/module/pairing/PairingController.js.map +1 -1
- package/lib/module/runtime/BannerHost.js +38 -29
- package/lib/module/runtime/BannerHost.js.map +1 -1
- package/lib/module/runtime/TargetPressBridge.js +56 -8
- package/lib/module/runtime/TargetPressBridge.js.map +1 -1
- package/lib/module/runtime/UserflowOverlay.js +5 -11
- package/lib/module/runtime/UserflowOverlay.js.map +1 -1
- package/lib/module/runtime/UserflowProvider.js +66 -40
- package/lib/module/runtime/UserflowProvider.js.map +1 -1
- package/lib/module/runtime/bannerEngine.js +7 -2
- package/lib/module/runtime/bannerEngine.js.map +1 -1
- package/lib/module/runtime/bannerFlowSpacer.js +65 -0
- package/lib/module/runtime/bannerFlowSpacer.js.map +1 -0
- package/lib/module/runtime/flowEngine.js +11 -0
- package/lib/module/runtime/flowEngine.js.map +1 -1
- package/lib/module/runtime/previewStart.js +35 -0
- package/lib/module/runtime/previewStart.js.map +1 -0
- package/lib/module/runtime/pushBannerLayoutContext.js +94 -0
- package/lib/module/runtime/pushBannerLayoutContext.js.map +1 -0
- package/lib/module/screenTracker.js +11 -0
- package/lib/module/screenTracker.js.map +1 -1
- package/lib/module/surfaces/BannerBar.js +80 -62
- package/lib/module/surfaces/BannerBar.js.map +1 -1
- package/lib/module/surfaces/BannerSurface.js +28 -6
- package/lib/module/surfaces/BannerSurface.js.map +1 -1
- package/lib/module/surfaces/ProgressBar.js +2 -1
- package/lib/module/surfaces/ProgressBar.js.map +1 -1
- package/lib/module/surfaces/TooltipSurface.js +49 -42
- package/lib/module/surfaces/TooltipSurface.js.map +1 -1
- package/lib/module/surfaces/bannerBarLayout.js +72 -11
- package/lib/module/surfaces/bannerBarLayout.js.map +1 -1
- package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js +115 -64
- package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
- package/lib/module/surfaces/bannerWebView/bannerNeedsWebView.js +3 -31
- package/lib/module/surfaces/bannerWebView/bannerNeedsWebView.js.map +1 -1
- package/lib/module/surfaces/bannerWebView/bannerThemeStyles.js +2 -1
- package/lib/module/surfaces/bannerWebView/bannerThemeStyles.js.map +1 -1
- package/lib/module/surfaces/bannerWebView/bannerWebViewHeight.js +29 -0
- package/lib/module/surfaces/bannerWebView/bannerWebViewHeight.js.map +1 -0
- package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js +104 -23
- package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
- package/lib/module/surfaces/hostAccessibilityTrap.js +24 -0
- package/lib/module/surfaces/hostAccessibilityTrap.js.map +1 -0
- package/lib/module/surfaces/resolveBannerTheme.js +6 -3
- package/lib/module/surfaces/resolveBannerTheme.js.map +1 -1
- package/lib/module/surfaces/tooltip/TooltipBubble.js +3 -2
- package/lib/module/surfaces/tooltip/TooltipBubble.js.map +1 -1
- package/lib/module/surfaces/tooltip/targetSearchSchedule.js +128 -0
- package/lib/module/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts +36 -2
- package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts +0 -4
- package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/appActive.d.ts +8 -0
- package/lib/typescript/commonjs/src/anchors/appActive.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts +17 -0
- package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts +7 -0
- package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/fiber/FiberScanRootStore.d.ts +5 -0
- package/lib/typescript/commonjs/src/anchors/fiber/FiberScanRootStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/fiber/detectAppFontFamily.d.ts +30 -0
- package/lib/typescript/commonjs/src/anchors/fiber/detectAppFontFamily.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts +7 -0
- package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts +3 -2
- package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
- package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/fiber/visibleScreens.d.ts +2 -0
- package/lib/typescript/commonjs/src/anchors/fiber/visibleScreens.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts +5 -0
- package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts +70 -0
- package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts +1 -0
- package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
- package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/resolveElementTargets.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts +2 -0
- package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts +9 -0
- package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts +13 -8
- package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/content/BannerStepButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/content/RichContentRenderer.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/content/StepButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/content/openSafeUrl.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/content/resolveContentFontFamily.d.ts +35 -0
- package/lib/typescript/commonjs/src/content/resolveContentFontFamily.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +6 -5
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts +15 -0
- package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts +3 -0
- package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts +2 -5
- package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/pairing/PairingController.d.ts +6 -0
- package/lib/typescript/commonjs/src/pairing/PairingController.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/BannerHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts +3 -0
- package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/UserflowOverlay.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/UserflowProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts +5 -1
- package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts +15 -0
- package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/runtime/flowEngine.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/runtime/previewStart.d.ts +13 -0
- package/lib/typescript/commonjs/src/runtime/previewStart.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts +22 -0
- package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/screenTracker.d.ts +6 -0
- package/lib/typescript/commonjs/src/screenTracker.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts +3 -1
- package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts +6 -5
- package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/ProgressBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts +1 -1
- package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts +17 -0
- package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/bannerNeedsWebView.d.ts +3 -12
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/bannerNeedsWebView.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/bannerThemeStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/bannerWebViewHeight.d.ts +13 -0
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/bannerWebViewHeight.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
- package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts +4 -1
- package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
- package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
- package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/version.d.ts +1 -1
- package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts +36 -2
- package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/animationFrame.d.ts +0 -4
- package/lib/typescript/module/src/anchors/animationFrame.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/appActive.d.ts +8 -0
- package/lib/typescript/module/src/anchors/appActive.d.ts.map +1 -0
- package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts +17 -0
- package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts +7 -0
- package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/fiber/FiberScanRootStore.d.ts +5 -0
- package/lib/typescript/module/src/anchors/fiber/FiberScanRootStore.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/fiber/detectAppFontFamily.d.ts +30 -0
- package/lib/typescript/module/src/anchors/fiber/detectAppFontFamily.d.ts.map +1 -0
- package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts +7 -0
- package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts +3 -2
- package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
- package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/fiber/visibleScreens.d.ts +2 -0
- package/lib/typescript/module/src/anchors/fiber/visibleScreens.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts +5 -0
- package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts +70 -0
- package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
- package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts +1 -0
- package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
- package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/resolveElementTargets.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/semanticMatcher.d.ts +2 -0
- package/lib/typescript/module/src/anchors/semanticMatcher.d.ts.map +1 -1
- package/lib/typescript/module/src/anchors/tapSlop.d.ts +9 -0
- package/lib/typescript/module/src/anchors/tapSlop.d.ts.map +1 -0
- package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts +13 -8
- package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts.map +1 -1
- package/lib/typescript/module/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
- package/lib/typescript/module/src/content/BannerStepButton.d.ts.map +1 -1
- package/lib/typescript/module/src/content/RichContentRenderer.d.ts.map +1 -1
- package/lib/typescript/module/src/content/StepButton.d.ts.map +1 -1
- package/lib/typescript/module/src/content/openSafeUrl.d.ts.map +1 -1
- package/lib/typescript/module/src/content/resolveContentFontFamily.d.ts +35 -0
- package/lib/typescript/module/src/content/resolveContentFontFamily.d.ts.map +1 -0
- package/lib/typescript/module/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +6 -5
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts +15 -0
- package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts +3 -0
- package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
- package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts +2 -5
- package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
- package/lib/typescript/module/src/pairing/PairingController.d.ts +6 -0
- package/lib/typescript/module/src/pairing/PairingController.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/BannerHost.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts +3 -0
- package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/UserflowOverlay.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/UserflowProvider.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/bannerEngine.d.ts +5 -1
- package/lib/typescript/module/src/runtime/bannerEngine.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts +15 -0
- package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
- package/lib/typescript/module/src/runtime/flowEngine.d.ts.map +1 -1
- package/lib/typescript/module/src/runtime/previewStart.d.ts +13 -0
- package/lib/typescript/module/src/runtime/previewStart.d.ts.map +1 -0
- package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts +22 -0
- package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
- package/lib/typescript/module/src/screenTracker.d.ts +6 -0
- package/lib/typescript/module/src/screenTracker.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/BannerBar.d.ts +3 -1
- package/lib/typescript/module/src/surfaces/BannerBar.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/BannerSurface.d.ts +6 -5
- package/lib/typescript/module/src/surfaces/BannerSurface.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/ProgressBar.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts +1 -1
- package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts +17 -0
- package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
- package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/bannerWebView/bannerNeedsWebView.d.ts +3 -12
- package/lib/typescript/module/src/surfaces/bannerWebView/bannerNeedsWebView.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/bannerWebView/bannerThemeStyles.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/bannerWebView/bannerWebViewHeight.d.ts +13 -0
- package/lib/typescript/module/src/surfaces/bannerWebView/bannerWebViewHeight.d.ts.map +1 -0
- package/lib/typescript/module/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
- package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
- package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts +4 -1
- package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
- package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
- package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
- package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
- package/lib/typescript/module/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/anchors/AutoAnchorRegistry.ts +166 -35
- package/src/anchors/animationFrame.ts +31 -1
- package/src/anchors/appActive.ts +63 -0
- package/src/anchors/elementConditionEvaluator.ts +29 -12
- package/src/anchors/elementTapMonitor.ts +68 -10
- package/src/anchors/elementTargetMatch.ts +34 -7
- package/src/anchors/fiber/FiberScanRootStore.ts +8 -0
- package/src/anchors/fiber/detectAppFontFamily.ts +400 -0
- package/src/anchors/fiber/fiberInternals.ts +68 -0
- package/src/anchors/fiber/indexHostFibers.ts +8 -3
- package/src/anchors/fiber/resolveFiberPath.ts +25 -16
- package/src/anchors/fiber/visibleScreens.ts +5 -0
- package/src/anchors/fiberAnchorCache.ts +19 -0
- package/src/anchors/gestureHandlerHandoff.ts +356 -0
- package/src/anchors/resolveAnchorHost.ts +2 -0
- package/src/anchors/resolveAutoAnchorHost.ts +18 -8
- package/src/anchors/resolveElementTargets.ts +64 -7
- package/src/anchors/semanticMatcher.ts +48 -27
- package/src/anchors/tapSlop.ts +18 -0
- package/src/anchors/targetPressMonitor.ts +56 -19
- package/src/authoring/fingerprintFromInstance.ts +4 -5
- package/src/content/BannerStepButton.tsx +2 -1
- package/src/content/RichContentRenderer.tsx +19 -13
- package/src/content/StepButton.tsx +2 -1
- package/src/content/openSafeUrl.ts +3 -1
- package/src/content/resolveContentFontFamily.ts +119 -0
- package/src/expo-router/useUserflowExpoRouter.ts +10 -1
- package/src/index.ts +22 -9
- package/src/navigation/navigateUrlUtils.ts +100 -0
- package/src/navigation/openExternalNavigateUrl.ts +9 -0
- package/src/navigation/openFlowNavigateUrl.ts +36 -36
- package/src/pairing/PairingController.ts +9 -1
- package/src/runtime/BannerHost.tsx +41 -23
- package/src/runtime/TargetPressBridge.tsx +66 -9
- package/src/runtime/UserflowOverlay.tsx +7 -9
- package/src/runtime/UserflowProvider.tsx +56 -32
- package/src/runtime/bannerEngine.ts +10 -2
- package/src/runtime/bannerFlowSpacer.tsx +70 -0
- package/src/runtime/flowEngine.ts +11 -0
- package/src/runtime/previewStart.ts +49 -0
- package/src/runtime/pushBannerLayoutContext.tsx +126 -0
- package/src/screenTracker.ts +11 -0
- package/src/surfaces/BannerBar.tsx +93 -55
- package/src/surfaces/BannerSurface.tsx +61 -9
- package/src/surfaces/ProgressBar.tsx +2 -1
- package/src/surfaces/TooltipSurface.tsx +78 -53
- package/src/surfaces/bannerBarLayout.ts +80 -7
- package/src/surfaces/bannerWebView/BannerWebViewBar.tsx +151 -57
- package/src/surfaces/bannerWebView/bannerNeedsWebView.ts +4 -36
- package/src/surfaces/bannerWebView/bannerThemeStyles.ts +2 -1
- package/src/surfaces/bannerWebView/bannerWebViewHeight.ts +32 -0
- package/src/surfaces/bannerWebView/buildCustomBannerHtml.ts +111 -23
- package/src/surfaces/hostAccessibilityTrap.ts +25 -0
- package/src/surfaces/resolveBannerTheme.ts +14 -3
- package/src/surfaces/tooltip/TooltipBubble.tsx +8 -1
- package/src/surfaces/tooltip/targetSearchSchedule.ts +140 -0
- package/src/version.ts +1 -1
- package/vendor/core/dist/engine/BannerSessionEngine.d.ts +29 -2
- package/vendor/core/dist/engine/BannerSessionEngine.d.ts.map +1 -1
- package/vendor/core/dist/engine/BannerSessionEngine.js +87 -6
- package/vendor/core/dist/engine/BannerSessionEngine.js.map +1 -1
- package/vendor/core/dist/engine/ClientConditionEngine.d.ts.map +1 -1
- package/vendor/core/dist/engine/ClientConditionEngine.js +30 -7
- package/vendor/core/dist/engine/ClientConditionEngine.js.map +1 -1
- package/vendor/core/dist/engine/PairingEngine.d.ts +6 -1
- package/vendor/core/dist/engine/PairingEngine.d.ts.map +1 -1
- package/vendor/core/dist/engine/PairingEngine.js +23 -3
- package/vendor/core/dist/engine/PairingEngine.js.map +1 -1
- package/vendor/core/dist/engine/handleActions.d.ts.map +1 -1
- package/vendor/core/dist/engine/handleActions.js +67 -2
- package/vendor/core/dist/engine/handleActions.js.map +1 -1
- package/vendor/core/dist/version.d.ts +1 -1
- package/vendor/core/dist/version.js +1 -1
- package/vendor/core/package.json +1 -1
- package/vendor/protocol/dist/flows.d.ts +2 -0
- package/vendor/protocol/dist/flows.d.ts.map +1 -1
- package/vendor/protocol/package.json +1 -1
|
@@ -26,19 +26,46 @@ export function anchorIdsAboveTap(tap: ElementTap): ReadonlySet<string> {
|
|
|
26
26
|
const ids = new Set<string>();
|
|
27
27
|
let current: AnyFiber = tap.targetFiber;
|
|
28
28
|
for (let depth = 0; current && depth < MAX_ANCESTOR_WALK; depth += 1) {
|
|
29
|
-
|
|
30
|
-
if (props) {
|
|
31
|
-
for (const tier of ANCHOR_PROP_TIERS) {
|
|
32
|
-
const value = props[tier];
|
|
33
|
-
if (typeof value === 'string') ids.add(value);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
29
|
+
collectAnchorIds(current, ids);
|
|
36
30
|
current = getReturn(current);
|
|
37
31
|
}
|
|
38
32
|
anchorIdsByTap.set(tap, ids);
|
|
39
33
|
return ids;
|
|
40
34
|
}
|
|
41
35
|
|
|
36
|
+
/**
|
|
37
|
+
* The fiber on `start` (or an ancestor) that carries `anchorId`.
|
|
38
|
+
*
|
|
39
|
+
* The touch-start fiber is kept after the press navigates away, so a click can
|
|
40
|
+
* still match when a live scan of the new screen no longer finds the id.
|
|
41
|
+
*/
|
|
42
|
+
export function fiberBearingAnchorId(start: AnyFiber, anchorId: string): AnyFiber | null {
|
|
43
|
+
let current: AnyFiber = start;
|
|
44
|
+
for (let depth = 0; current && depth < MAX_ANCESTOR_WALK; depth += 1) {
|
|
45
|
+
if (fiberHasAnchorId(current, anchorId)) return current;
|
|
46
|
+
current = getReturn(current);
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function fiberHasAnchorId(fiber: AnyFiber, anchorId: string): boolean {
|
|
52
|
+
const props = getMemoizedProps(fiber);
|
|
53
|
+
if (!props) return false;
|
|
54
|
+
for (const tier of ANCHOR_PROP_TIERS) {
|
|
55
|
+
if (props[tier] === anchorId) return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function collectAnchorIds(fiber: AnyFiber, ids: Set<string>): void {
|
|
61
|
+
const props = getMemoizedProps(fiber);
|
|
62
|
+
if (!props) return;
|
|
63
|
+
for (const tier of ANCHOR_PROP_TIERS) {
|
|
64
|
+
const value = props[tier];
|
|
65
|
+
if (typeof value === 'string') ids.add(value);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
42
69
|
export function sameFiber(a: AnyFiber, b: AnyFiber): boolean {
|
|
43
70
|
if (a == null || b == null) return false;
|
|
44
71
|
if (a === b) return true;
|
|
@@ -28,6 +28,14 @@ class FiberScanRootStore {
|
|
|
28
28
|
return resolveCurrentFiber(fiber);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Mounted scan-root instance. Font detection caches against this identity
|
|
33
|
+
* so a remount scans again and an update of the same root does not.
|
|
34
|
+
*/
|
|
35
|
+
getInstance(): unknown {
|
|
36
|
+
return this.instance;
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
subscribe(listener: Listener): () => void {
|
|
32
40
|
this.listeners.add(listener);
|
|
33
41
|
return () => {
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-app font used when a theme is still on the default CSS stack.
|
|
3
|
+
*
|
|
4
|
+
* React Native does not inherit `fontFamily` from a parent view, so the family
|
|
5
|
+
* actually in use is the one set on `Text`. The primary font is whichever
|
|
6
|
+
* family appears on the most visible host text under the scan root (the SDK
|
|
7
|
+
* overlay is a sibling and is not walked). Embedded banners are inside that
|
|
8
|
+
* root; their hosts use a `userflow-` testID and are not votes, or the bar's
|
|
9
|
+
* own text would win. Text with no family counts as the platform font. Icon
|
|
10
|
+
* faces are not content. Nested text with no family does not vote: the
|
|
11
|
+
* ancestor text already does, and a child run must not add another vote for
|
|
12
|
+
* that family or for the platform font.
|
|
13
|
+
*
|
|
14
|
+
* A finished walk is cached until the visible screen changes, the reported
|
|
15
|
+
* screen changes, or `Userflow.reset()`. A walk that hits the node cap is not
|
|
16
|
+
* cached. Failures return null.
|
|
17
|
+
*/
|
|
18
|
+
import { getCurrentScreenLocation } from '../../screenTracker';
|
|
19
|
+
|
|
20
|
+
import { fiberScanRoot } from './FiberScanRootStore';
|
|
21
|
+
import {
|
|
22
|
+
type AnyFiber,
|
|
23
|
+
MAX_ANCESTOR_WALK,
|
|
24
|
+
getHostTypeName,
|
|
25
|
+
getLiveHostProps,
|
|
26
|
+
getReturn,
|
|
27
|
+
getStateNode,
|
|
28
|
+
} from './fiberInternals';
|
|
29
|
+
import { isFiberOnVisibleScreen, isScreenHostFiber } from './visibleScreens';
|
|
30
|
+
import { walkVisibleFibers } from './walkVisibleFibers';
|
|
31
|
+
|
|
32
|
+
const TEXT_HOST_NAMES = new Set(['Text', 'RCTText', 'RCTVirtualText']);
|
|
33
|
+
|
|
34
|
+
/** SDK hosts inside the scan root (embedded banners). Not the app's font. */
|
|
35
|
+
const SDK_HOST_TEST_ID_PREFIX = 'userflow-';
|
|
36
|
+
|
|
37
|
+
/** Cycle guard for nested-text inheritance. Real trees are only a few levels deep. */
|
|
38
|
+
const MAX_FONT_ANCESTORS = 50;
|
|
39
|
+
|
|
40
|
+
const CSS_GENERIC_FAMILIES = new Set([
|
|
41
|
+
'sans-serif',
|
|
42
|
+
'serif',
|
|
43
|
+
'monospace',
|
|
44
|
+
'system-ui',
|
|
45
|
+
'ui-sans-serif',
|
|
46
|
+
'ui-serif',
|
|
47
|
+
'ui-monospace',
|
|
48
|
+
'ui-rounded',
|
|
49
|
+
'cursive',
|
|
50
|
+
'fantasy',
|
|
51
|
+
'emoji',
|
|
52
|
+
'math',
|
|
53
|
+
'fangsong',
|
|
54
|
+
'inherit',
|
|
55
|
+
'initial',
|
|
56
|
+
'unset',
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
/** Short vector-icon families. Matched after stripping spaces and punctuation. */
|
|
60
|
+
const ICON_FONT_EXACT = new Set([
|
|
61
|
+
'feather',
|
|
62
|
+
'antdesign',
|
|
63
|
+
'entypo',
|
|
64
|
+
'fontisto',
|
|
65
|
+
'simplelineicons',
|
|
66
|
+
'octicons',
|
|
67
|
+
'zocial',
|
|
68
|
+
'foundation',
|
|
69
|
+
'evilicons',
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
interface FontCache {
|
|
73
|
+
instance: object;
|
|
74
|
+
/** null: countable text voted for the platform font, or nothing voted. */
|
|
75
|
+
family: string | null;
|
|
76
|
+
/** Visible screens this tally belongs to. Empty when the tree has none. */
|
|
77
|
+
screens: AnyFiber[];
|
|
78
|
+
/** `updateScreen` / router identity at tally time. Null before the first report. */
|
|
79
|
+
screenKey: string | null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let cache: FontCache | null = null;
|
|
83
|
+
|
|
84
|
+
/** Drop the cached app font. Called from `Userflow.reset()`. */
|
|
85
|
+
export function resetDetectedAppFont(): void {
|
|
86
|
+
cache = null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Family to apply, or null when the app font is the platform face or could
|
|
91
|
+
* not be read. Never throws.
|
|
92
|
+
*/
|
|
93
|
+
export function detectAppFontFamily(): string | null {
|
|
94
|
+
try {
|
|
95
|
+
const instance = fiberScanRoot.getInstance();
|
|
96
|
+
if (!instance || typeof instance !== 'object') return null;
|
|
97
|
+
|
|
98
|
+
const root = fiberScanRoot.getFiber();
|
|
99
|
+
if (!root) return null;
|
|
100
|
+
if (cache?.instance === instance && cacheStillCurrent(cache, root)) return cache.family;
|
|
101
|
+
|
|
102
|
+
const tally = tallyHostText(root);
|
|
103
|
+
if (tally.truncated) {
|
|
104
|
+
if (!tally.voted) return null;
|
|
105
|
+
return pickMajority(tally.counts);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const family = tally.voted ? pickMajority(tally.counts) : null;
|
|
109
|
+
cache = { instance, family, screens: tally.screens, screenKey: reportedScreenKey() };
|
|
110
|
+
return family;
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Faces that appear in the builder's default system stack. A comma-separated
|
|
118
|
+
* list made only of these and generic keywords is that default, not a custom
|
|
119
|
+
* font. A single concrete name is still kept, including `Roboto`.
|
|
120
|
+
*/
|
|
121
|
+
const DEFAULT_STACK_FACES = new Set([
|
|
122
|
+
'blinkmacsystemfont',
|
|
123
|
+
'segoe ui',
|
|
124
|
+
'roboto',
|
|
125
|
+
'helvetica',
|
|
126
|
+
'arial',
|
|
127
|
+
'apple color emoji',
|
|
128
|
+
'segoe ui emoji',
|
|
129
|
+
'segoe ui symbol',
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* A single React Native family name. CSS stacks and generic keywords are not
|
|
134
|
+
* usable as a host `fontFamily` — RN treats the whole string as one face.
|
|
135
|
+
* Theme stacks are parsed by {@link themeConcreteFontFamily}.
|
|
136
|
+
*/
|
|
137
|
+
export function concreteFontFamily(value: unknown): string | undefined {
|
|
138
|
+
if (typeof value !== 'string') return undefined;
|
|
139
|
+
const name = unwrapFontName(value);
|
|
140
|
+
if (!name || name.includes(',') || name.startsWith('-')) return undefined;
|
|
141
|
+
if (CSS_GENERIC_FAMILIES.has(name.toLowerCase())) return undefined;
|
|
142
|
+
return name;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Family a theme author chose. A single concrete name is kept. A comma-separated
|
|
147
|
+
* list keeps the first family that is not a generic or a default-stack face,
|
|
148
|
+
* so `"Source Sans Pro", sans-serif` stays Source Sans Pro and the system
|
|
149
|
+
* stack still resolves to undefined.
|
|
150
|
+
*/
|
|
151
|
+
export function themeConcreteFontFamily(value: unknown): string | undefined {
|
|
152
|
+
if (typeof value !== 'string') return undefined;
|
|
153
|
+
const parts = splitCssFamilies(value);
|
|
154
|
+
if (parts.length === 0) return undefined;
|
|
155
|
+
if (parts.length === 1) return concreteFontFamily(parts[0]);
|
|
156
|
+
for (const part of parts) {
|
|
157
|
+
const name = concreteFontFamily(part);
|
|
158
|
+
if (!name || DEFAULT_STACK_FACES.has(name.toLowerCase())) continue;
|
|
159
|
+
return name;
|
|
160
|
+
}
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Split a CSS `font-family` list. A quote that opens a family is wrapping and
|
|
166
|
+
* is removed; a quote inside a name stays, so `Foo"Bar` is one family.
|
|
167
|
+
* Commas inside wrapping quotes are not separators.
|
|
168
|
+
*/
|
|
169
|
+
export function splitCssFamilies(value: string): string[] {
|
|
170
|
+
const parts: string[] = [];
|
|
171
|
+
let current = '';
|
|
172
|
+
let quote: '"' | "'" | null = null;
|
|
173
|
+
for (const char of value) {
|
|
174
|
+
if (quote) {
|
|
175
|
+
if (char === quote) quote = null;
|
|
176
|
+
else current += char;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if ((char === '"' || char === "'") && current.trim() === '') {
|
|
180
|
+
quote = char;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (char === ',') {
|
|
184
|
+
const name = current.trim();
|
|
185
|
+
if (name) parts.push(name);
|
|
186
|
+
current = '';
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
current += char;
|
|
190
|
+
}
|
|
191
|
+
const name = current.trim();
|
|
192
|
+
if (name) parts.push(name);
|
|
193
|
+
return parts;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Generic keywords and vendor-prefixed faces stay unquoted in CSS. */
|
|
197
|
+
export function isBareCssFamily(name: string): boolean {
|
|
198
|
+
return name.startsWith('-') || CSS_GENERIC_FAMILIES.has(name.toLowerCase());
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function isIconFontFamily(name: string): boolean {
|
|
202
|
+
const key = name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
203
|
+
if (ICON_FONT_EXACT.has(key)) return true;
|
|
204
|
+
return (
|
|
205
|
+
key.startsWith('fontawesome') ||
|
|
206
|
+
key.startsWith('ionicons') ||
|
|
207
|
+
key.startsWith('materialcommunityicons') ||
|
|
208
|
+
key.startsWith('materialicons') ||
|
|
209
|
+
key.startsWith('materialsymbols')
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
interface Tally {
|
|
214
|
+
voted: boolean;
|
|
215
|
+
truncated: boolean;
|
|
216
|
+
screens: AnyFiber[];
|
|
217
|
+
counts: Map<string | null, number>;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function tallyHostText(root: AnyFiber): Tally {
|
|
221
|
+
const counts = new Map<string | null, number>();
|
|
222
|
+
const screens: AnyFiber[] = [];
|
|
223
|
+
let voted = false;
|
|
224
|
+
const { truncated } = walkVisibleFibers(root, (fiber) => {
|
|
225
|
+
try {
|
|
226
|
+
if (isScreenHostFiber(fiber)) screens.push(fiber);
|
|
227
|
+
if (!isTextHost(fiber)) return;
|
|
228
|
+
const vote = voteFor(fiber, root);
|
|
229
|
+
if (vote === 'skip') return;
|
|
230
|
+
voted = true;
|
|
231
|
+
counts.set(vote, (counts.get(vote) ?? 0) + 1);
|
|
232
|
+
} catch {
|
|
233
|
+
// One unreadable node is not a vote.
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
return { voted, truncated, screens, counts };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** Usable family, null for the platform font, or skip for an icon face. */
|
|
240
|
+
function voteFor(fiber: AnyFiber, root: AnyFiber): string | null | 'skip' {
|
|
241
|
+
if (isUnderSdkHost(fiber, root)) return 'skip';
|
|
242
|
+
const own = classifyFamily(readFontFamily(fiber));
|
|
243
|
+
if (own.kind === 'icon') return 'skip';
|
|
244
|
+
if (own.kind === 'family') return own.name;
|
|
245
|
+
if (own.kind === 'unusable') return null;
|
|
246
|
+
// No family of its own. The ancestor text node casts the vote for this run.
|
|
247
|
+
if (hasAncestorText(fiber, root)) return 'skip';
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function hasAncestorText(fiber: AnyFiber, root: AnyFiber): boolean {
|
|
252
|
+
let parent = getReturn(fiber);
|
|
253
|
+
for (let depth = 0; parent && parent !== root && depth < MAX_FONT_ANCESTORS; depth += 1) {
|
|
254
|
+
if (isTextHost(parent)) return true;
|
|
255
|
+
parent = getReturn(parent);
|
|
256
|
+
}
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function cacheStillCurrent(cached: FontCache, root: AnyFiber): boolean {
|
|
261
|
+
if (cached.screenKey !== reportedScreenKey()) return false;
|
|
262
|
+
if (cached.screens.length === 0) return true;
|
|
263
|
+
return cached.screens.every((screen) => screenStillCurrent(screen, root));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function screenStillCurrent(screen: AnyFiber, root: AnyFiber): boolean {
|
|
267
|
+
if (!isFiberOnVisibleScreen(screen)) return false;
|
|
268
|
+
let current: AnyFiber = screen;
|
|
269
|
+
for (let depth = 0; current && depth < MAX_ANCESTOR_WALK; depth += 1) {
|
|
270
|
+
if (current === root) return true;
|
|
271
|
+
current = getReturn(current);
|
|
272
|
+
}
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function reportedScreenKey(): string | null {
|
|
277
|
+
const location = getCurrentScreenLocation();
|
|
278
|
+
if (!location) return null;
|
|
279
|
+
const key = 'path' in location ? location.path : location.screen;
|
|
280
|
+
return key || null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
type Classified =
|
|
284
|
+
| { kind: 'absent' }
|
|
285
|
+
| { kind: 'icon' }
|
|
286
|
+
| { kind: 'family'; name: string }
|
|
287
|
+
| { kind: 'unusable' };
|
|
288
|
+
|
|
289
|
+
function classifyFamily(raw: string | undefined): Classified {
|
|
290
|
+
if (!raw) return { kind: 'absent' };
|
|
291
|
+
if (isIconFontFamily(raw)) return { kind: 'icon' };
|
|
292
|
+
const concrete = concreteFontFamily(raw);
|
|
293
|
+
if (!concrete) return { kind: 'unusable' };
|
|
294
|
+
return { kind: 'family', name: concrete };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function pickMajority(counts: Map<string | null, number>): string | null {
|
|
298
|
+
const platform = counts.get(null) ?? 0;
|
|
299
|
+
let bestName: string | null = null;
|
|
300
|
+
let bestCount = 0;
|
|
301
|
+
for (const [name, count] of counts) {
|
|
302
|
+
if (name == null) continue;
|
|
303
|
+
if (bestName == null || count > bestCount || (count === bestCount && name < bestName)) {
|
|
304
|
+
bestName = name;
|
|
305
|
+
bestCount = count;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (bestName == null || platform > bestCount) return null;
|
|
309
|
+
return bestName;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function isUnderSdkHost(fiber: AnyFiber, root: AnyFiber): boolean {
|
|
313
|
+
let parent = getReturn(fiber);
|
|
314
|
+
for (let depth = 0; parent && parent !== root && depth < MAX_FONT_ANCESTORS; depth += 1) {
|
|
315
|
+
try {
|
|
316
|
+
const testID = getLiveHostProps(parent)?.testID;
|
|
317
|
+
if (typeof testID === 'string' && testID.startsWith(SDK_HOST_TEST_ID_PREFIX)) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
} catch {
|
|
321
|
+
// An unreadable ancestor is not an SDK host.
|
|
322
|
+
}
|
|
323
|
+
parent = getReturn(parent);
|
|
324
|
+
}
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function isTextHost(fiber: AnyFiber): boolean {
|
|
329
|
+
const typeName = getHostTypeName(fiber);
|
|
330
|
+
if (typeName && TEXT_HOST_NAMES.has(typeName)) return true;
|
|
331
|
+
const elementType = fiber?.elementType;
|
|
332
|
+
if (typeof elementType === 'string' && TEXT_HOST_NAMES.has(elementType)) return true;
|
|
333
|
+
try {
|
|
334
|
+
const node = getStateNode(fiber) as {
|
|
335
|
+
viewConfig?: { uiViewClassName?: unknown };
|
|
336
|
+
__viewConfig?: { uiViewClassName?: unknown };
|
|
337
|
+
} | null;
|
|
338
|
+
const className = node?.viewConfig?.uiViewClassName ?? node?.__viewConfig?.uiViewClassName;
|
|
339
|
+
return typeof className === 'string' && TEXT_HOST_NAMES.has(className);
|
|
340
|
+
} catch {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function readFontFamily(fiber: AnyFiber): string | undefined {
|
|
346
|
+
try {
|
|
347
|
+
const flat = flattenFontStyle(getLiveHostProps(fiber)?.style);
|
|
348
|
+
const family = flat?.fontFamily;
|
|
349
|
+
if (typeof family !== 'string') return undefined;
|
|
350
|
+
const trimmed = family.trim();
|
|
351
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
352
|
+
} catch {
|
|
353
|
+
return undefined;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function flattenFontStyle(style: unknown): { fontFamily?: unknown } | null {
|
|
358
|
+
if (style == null || style === false) return null;
|
|
359
|
+
if (typeof style === 'number') return flattenRegisteredStyle(style);
|
|
360
|
+
if (Array.isArray(style)) {
|
|
361
|
+
let family: unknown;
|
|
362
|
+
for (const entry of style) {
|
|
363
|
+
const flat = flattenFontStyle(entry);
|
|
364
|
+
if (flat && flat.fontFamily != null) family = flat.fontFamily;
|
|
365
|
+
}
|
|
366
|
+
return { fontFamily: family };
|
|
367
|
+
}
|
|
368
|
+
if (typeof style === 'object') return style as { fontFamily?: unknown };
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* StyleSheet ids are numbers. Resolve them only when one shows up so tests
|
|
374
|
+
* that pass plain style objects do not load react-native.
|
|
375
|
+
*/
|
|
376
|
+
function flattenRegisteredStyle(styleId: number): { fontFamily?: unknown } | null {
|
|
377
|
+
try {
|
|
378
|
+
// Lazy require so unit tests that pass plain style objects do not load RN.
|
|
379
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
380
|
+
const { StyleSheet } = require('react-native') as {
|
|
381
|
+
StyleSheet?: { flatten?: (style: number) => { fontFamily?: unknown } | null | undefined };
|
|
382
|
+
};
|
|
383
|
+
const flat = StyleSheet?.flatten?.(styleId);
|
|
384
|
+
return flat ?? null;
|
|
385
|
+
} catch {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function unwrapFontName(value: string): string {
|
|
391
|
+
const trimmed = value.trim();
|
|
392
|
+
if (trimmed.length >= 2) {
|
|
393
|
+
const first = trimmed[0];
|
|
394
|
+
const last = trimmed[trimmed.length - 1];
|
|
395
|
+
if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
|
|
396
|
+
return trimmed.slice(1, -1).trim();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return trimmed;
|
|
400
|
+
}
|
|
@@ -319,6 +319,74 @@ export function resolveCurrentFiber(fiber: AnyFiber): AnyFiber {
|
|
|
319
319
|
return fiber;
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
/** Where a fiber sits: its tree's current fiber, and whether it is under it. */
|
|
323
|
+
interface TreePlace {
|
|
324
|
+
current: AnyFiber;
|
|
325
|
+
inTree: boolean;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* {@link resolveCurrentFiber} for many fibers in one synchronous pass. Where
|
|
330
|
+
* each ancestor sits is remembered, so the pass walks every ancestor once
|
|
331
|
+
* instead of once per fiber below it. Discard it when the pass ends: a render
|
|
332
|
+
* moves `return` pointers.
|
|
333
|
+
*/
|
|
334
|
+
export function createCurrentFiberResolver(): (fiber: AnyFiber) => AnyFiber {
|
|
335
|
+
const placeOf = new Map<object, TreePlace | null>();
|
|
336
|
+
|
|
337
|
+
const locate = (fiber: AnyFiber): TreePlace | null => {
|
|
338
|
+
const path: AnyFiber[] = [];
|
|
339
|
+
let above: TreePlace | null = null;
|
|
340
|
+
let cur: AnyFiber = fiber;
|
|
341
|
+
for (let depth = 0; cur && depth < MAX_ANCESTOR_WALK; depth += 1) {
|
|
342
|
+
const known = placeOf.get(cur);
|
|
343
|
+
if (known !== undefined) {
|
|
344
|
+
above = known;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
path.push(cur);
|
|
348
|
+
// Only a HostRoot (or a detached fiber) has no parent, so the tag is
|
|
349
|
+
// read once, at the top, not at every step.
|
|
350
|
+
const parent = getReturn(cur);
|
|
351
|
+
if (!parent) {
|
|
352
|
+
const rootState =
|
|
353
|
+
getFiberTag(cur) === HOST_ROOT_TAG
|
|
354
|
+
? (getStateNode(cur) as { current?: AnyFiber } | null)
|
|
355
|
+
: null;
|
|
356
|
+
above = rootState?.current ? { current: rootState.current, inTree: false } : null;
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
cur = parent;
|
|
360
|
+
}
|
|
361
|
+
// Top down: a fiber is in the current tree when the current fiber is it
|
|
362
|
+
// or one of its ancestors.
|
|
363
|
+
let place = above;
|
|
364
|
+
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
365
|
+
if (place && !place.inTree && path[i] === place.current) {
|
|
366
|
+
place = { current: place.current, inTree: true };
|
|
367
|
+
}
|
|
368
|
+
placeOf.set(path[i], place);
|
|
369
|
+
}
|
|
370
|
+
return place;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
return (fiber) => {
|
|
374
|
+
if (!fiber) return fiber;
|
|
375
|
+
try {
|
|
376
|
+
const alternate = getAlternate(fiber);
|
|
377
|
+
if (!alternate) return fiber;
|
|
378
|
+
const own = locate(fiber);
|
|
379
|
+
if (!own || own.inTree) return fiber;
|
|
380
|
+
const other = locate(alternate);
|
|
381
|
+
// Two trees for one pair only happens on a corrupt graph.
|
|
382
|
+
if (!other || other.current !== own.current) return resolveCurrentFiber(fiber);
|
|
383
|
+
return other.inTree ? alternate : fiber;
|
|
384
|
+
} catch {
|
|
385
|
+
return fiber;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
322
390
|
function isInTree(fiber: AnyFiber, root: AnyFiber): boolean {
|
|
323
391
|
let cur: AnyFiber = fiber;
|
|
324
392
|
for (let depth = 0; cur && depth < MAX_ANCESTOR_WALK; depth += 1) {
|
|
@@ -111,13 +111,18 @@ export function createLazyHostFiberIndex(
|
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
113
|
* Fiber for a public instance: direct pointer first, then the scan-root index
|
|
114
|
-
* by identity / native tag.
|
|
114
|
+
* by identity / native tag. Pass a `createCurrentFiberResolver()` resolver when
|
|
115
|
+
* looking up many instances in one pass.
|
|
115
116
|
*/
|
|
116
|
-
export function lookupHostFiber(
|
|
117
|
+
export function lookupHostFiber(
|
|
118
|
+
index: HostFiberIndexSource,
|
|
119
|
+
instance: unknown,
|
|
120
|
+
resolveCurrent: (fiber: AnyFiber) => AnyFiber = resolveCurrentFiber,
|
|
121
|
+
): AnyFiber | null {
|
|
117
122
|
const direct = fiberFromPublicInstance(instance);
|
|
118
123
|
if (direct) {
|
|
119
124
|
try {
|
|
120
|
-
return
|
|
125
|
+
return resolveCurrent(direct);
|
|
121
126
|
} catch {
|
|
122
127
|
return direct;
|
|
123
128
|
}
|
|
@@ -31,10 +31,13 @@ import { walkVisibleFibers } from './walkVisibleFibers';
|
|
|
31
31
|
const MIN_SCORE = 40;
|
|
32
32
|
const UNIQUENESS_MARGIN = 12;
|
|
33
33
|
|
|
34
|
+
/** Why a fiber path did not resolve. Element conditions map these differently. */
|
|
35
|
+
export type FiberPathMissReason = 'empty' | 'no-root' | 'no-match';
|
|
36
|
+
|
|
34
37
|
export type FiberPathResolution =
|
|
35
38
|
| { status: 'found'; fiber: AnyFiber; cacheId: string; textDisagreed?: boolean }
|
|
36
39
|
| { status: 'ambiguous' }
|
|
37
|
-
| { status: 'unknown' };
|
|
40
|
+
| { status: 'unknown'; reason: FiberPathMissReason };
|
|
38
41
|
|
|
39
42
|
function stringProp(value: unknown): string | undefined {
|
|
40
43
|
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
@@ -50,18 +53,13 @@ function sameFiber(a: AnyFiber, b: AnyFiber): boolean {
|
|
|
50
53
|
return alt != null && alt === b;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
/** The live segment without its text, which needs a subtree walk. */
|
|
57
|
+
function liveIdentity(fiber: AnyFiber): MobileFiberSegment {
|
|
54
58
|
const props = getLiveHostProps(fiber);
|
|
55
59
|
const role = stringProp(props?.accessibilityRole) ?? stringProp(props?.role);
|
|
56
60
|
const testID = stringProp(props?.testID);
|
|
57
61
|
const accessibilityLabel =
|
|
58
62
|
stringProp(props?.accessibilityLabel) ?? stringProp(props?.['aria-label']);
|
|
59
|
-
let text: string | undefined;
|
|
60
|
-
try {
|
|
61
|
-
text = readHostText(fiber, 1);
|
|
62
|
-
} catch {
|
|
63
|
-
text = undefined;
|
|
64
|
-
}
|
|
65
63
|
const hostKind = getHostTypeName(fiber);
|
|
66
64
|
return {
|
|
67
65
|
displayName: typeof fiber?.type === 'string' ? fiber.type : '',
|
|
@@ -69,10 +67,19 @@ function liveSegment(fiber: AnyFiber): MobileFiberSegment {
|
|
|
69
67
|
...(role ? { role } : {}),
|
|
70
68
|
...(testID ? { testID } : {}),
|
|
71
69
|
...(accessibilityLabel ? { accessibilityLabel } : {}),
|
|
72
|
-
...(text ? { text } : {}),
|
|
73
70
|
};
|
|
74
71
|
}
|
|
75
72
|
|
|
73
|
+
function liveSegment(fiber: AnyFiber): MobileFiberSegment {
|
|
74
|
+
let text: string | undefined;
|
|
75
|
+
try {
|
|
76
|
+
text = readHostText(fiber, 1);
|
|
77
|
+
} catch {
|
|
78
|
+
text = undefined;
|
|
79
|
+
}
|
|
80
|
+
return { ...liveIdentity(fiber), ...(text ? { text } : {}) };
|
|
81
|
+
}
|
|
82
|
+
|
|
76
83
|
function ancestorsOf(fiber: AnyFiber): AnyFiber[] {
|
|
77
84
|
const chain: AnyFiber[] = [];
|
|
78
85
|
let current: AnyFiber | null = fiber;
|
|
@@ -90,7 +97,8 @@ function scoreAlignment(expected: MobileFiberSegment[], actual: AnyFiber[]): num
|
|
|
90
97
|
let kindAligned = 0;
|
|
91
98
|
for (let i = 0; i < n; i += 1) {
|
|
92
99
|
const exp = expected[expected.length - 1 - i]!;
|
|
93
|
-
|
|
100
|
+
// Alignment never compares text, so skip the subtree walk for every ancestor.
|
|
101
|
+
const live = liveIdentity(actual[actual.length - 1 - i]!);
|
|
94
102
|
const expKind = exp.hostKind ?? exp.displayName;
|
|
95
103
|
const liveKind = live.hostKind ?? live.displayName;
|
|
96
104
|
if (expKind && liveKind && expKind !== liveKind) break;
|
|
@@ -107,13 +115,14 @@ function scoreHost(
|
|
|
107
115
|
path: MobileFiberPath,
|
|
108
116
|
fiber: AnyFiber,
|
|
109
117
|
): { score: number; textDisagreed: boolean } {
|
|
110
|
-
const live = liveSegment(fiber);
|
|
111
118
|
const leaf = path.segments[path.segments.length - 1];
|
|
112
119
|
const leafKind = leaf?.hostKind ?? path.leafKind;
|
|
113
|
-
|
|
120
|
+
// Kind first: most hosts fail here, before any props or text are read.
|
|
121
|
+
const liveKind = getHostTypeName(fiber) ?? '';
|
|
114
122
|
if (leafKind && liveKind && leafKind !== liveKind) {
|
|
115
123
|
return { score: -Infinity, textDisagreed: false };
|
|
116
124
|
}
|
|
125
|
+
const live = liveSegment(fiber);
|
|
117
126
|
|
|
118
127
|
let score = 20;
|
|
119
128
|
if (leaf?.role && live.role === leaf.role) score += 15;
|
|
@@ -151,15 +160,15 @@ function scoreHost(
|
|
|
151
160
|
|
|
152
161
|
function resolveScored(path: MobileFiberPath, root: AnyFiber): FiberPathResolution {
|
|
153
162
|
const scored: Array<{ fiber: AnyFiber; score: number; textDisagreed: boolean }> = [];
|
|
163
|
+
// The walk already skips covered screens, so no per-host visibility walk.
|
|
154
164
|
walkVisibleFibers(root, (fiber) => {
|
|
155
165
|
if (!isHostFiber(fiber)) return;
|
|
156
|
-
if (!isFiberOnVisibleScreen(fiber)) return;
|
|
157
166
|
const { score, textDisagreed } = scoreHost(path, fiber);
|
|
158
167
|
if (score > -Infinity) scored.push({ fiber, score, textDisagreed });
|
|
159
168
|
});
|
|
160
169
|
scored.sort((a, b) => b.score - a.score);
|
|
161
170
|
const best = scored[0];
|
|
162
|
-
if (!best || best.score < MIN_SCORE) return { status: 'unknown' };
|
|
171
|
+
if (!best || best.score < MIN_SCORE) return { status: 'unknown', reason: 'no-match' };
|
|
163
172
|
const second = scored[1];
|
|
164
173
|
if (second && best.score - second.score < UNIQUENESS_MARGIN) return { status: 'ambiguous' };
|
|
165
174
|
return {
|
|
@@ -175,7 +184,7 @@ function resolveScored(path: MobileFiberPath, root: AnyFiber): FiberPathResoluti
|
|
|
175
184
|
* and reuses the manual fiber scan.
|
|
176
185
|
*/
|
|
177
186
|
export function resolveFiberPath(path: MobileFiberPath | null | undefined): FiberPathResolution {
|
|
178
|
-
if (!path || path.segments.length === 0) return { status: 'unknown' };
|
|
187
|
+
if (!path || path.segments.length === 0) return { status: 'unknown', reason: 'empty' };
|
|
179
188
|
|
|
180
189
|
const leaf = path.segments[path.segments.length - 1];
|
|
181
190
|
const exactId = leaf?.testID;
|
|
@@ -200,7 +209,7 @@ export function resolveFiberPath(path: MobileFiberPath | null | undefined): Fibe
|
|
|
200
209
|
}
|
|
201
210
|
|
|
202
211
|
const root = fiberScanRoot.getFiber();
|
|
203
|
-
if (!root) return { status: 'unknown' };
|
|
212
|
+
if (!root) return { status: 'unknown', reason: 'no-root' };
|
|
204
213
|
|
|
205
214
|
const resolved = resolveScored(path, root);
|
|
206
215
|
if (resolved.status === 'found') {
|
|
@@ -64,6 +64,11 @@ function isScreenFiber(fiber: AnyFiber): boolean {
|
|
|
64
64
|
return type !== undefined && SCREEN_TYPES.has(type);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/** Host screen the walk can key a cache on (`RNSScreen`, `RNSModalScreen`). */
|
|
68
|
+
export function isScreenHostFiber(fiber: AnyFiber): boolean {
|
|
69
|
+
return isScreenFiber(fiber);
|
|
70
|
+
}
|
|
71
|
+
|
|
67
72
|
function isStackContainer(fiber: AnyFiber): boolean {
|
|
68
73
|
const type = getHostTypeName(fiber);
|
|
69
74
|
return type !== undefined && STACK_CONTAINER_TYPES.has(type);
|