execbro 0.0.1-reserve → 1.10.12
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/LICENSE +32 -0
- package/README.md +496 -0
- package/build/__tests__/helpers/fake-cdp-server.d.ts +56 -0
- package/build/__tests__/helpers/fake-cdp-server.d.ts.map +1 -0
- package/build/__tests__/helpers/fake-cdp-server.js +120 -0
- package/build/__tests__/helpers/fake-cdp-server.js.map +1 -0
- package/build/__tests__/helpers/tap-test-helpers.d.ts +36 -0
- package/build/__tests__/helpers/tap-test-helpers.d.ts.map +1 -0
- package/build/__tests__/helpers/tap-test-helpers.js +130 -0
- package/build/__tests__/helpers/tap-test-helpers.js.map +1 -0
- package/build/__tests__/integration/auto-reconnect.test.d.ts +2 -0
- package/build/__tests__/integration/auto-reconnect.test.d.ts.map +1 -0
- package/build/__tests__/integration/auto-reconnect.test.js +118 -0
- package/build/__tests__/integration/auto-reconnect.test.js.map +1 -0
- package/build/__tests__/integration/connection-health.test.d.ts +2 -0
- package/build/__tests__/integration/connection-health.test.d.ts.map +1 -0
- package/build/__tests__/integration/connection-health.test.js +157 -0
- package/build/__tests__/integration/connection-health.test.js.map +1 -0
- package/build/__tests__/integration/execute-in-app.test.d.ts +2 -0
- package/build/__tests__/integration/execute-in-app.test.d.ts.map +1 -0
- package/build/__tests__/integration/execute-in-app.test.js +124 -0
- package/build/__tests__/integration/execute-in-app.test.js.map +1 -0
- package/build/__tests__/integration/per-call-timeout.test.d.ts +2 -0
- package/build/__tests__/integration/per-call-timeout.test.d.ts.map +1 -0
- package/build/__tests__/integration/per-call-timeout.test.js +103 -0
- package/build/__tests__/integration/per-call-timeout.test.js.map +1 -0
- package/build/__tests__/integration/tap-strategies.test.d.ts +2 -0
- package/build/__tests__/integration/tap-strategies.test.d.ts.map +1 -0
- package/build/__tests__/integration/tap-strategies.test.js +330 -0
- package/build/__tests__/integration/tap-strategies.test.js.map +1 -0
- package/build/__tests__/integration/tools.test.d.ts +2 -0
- package/build/__tests__/integration/tools.test.d.ts.map +1 -0
- package/build/__tests__/integration/tools.test.js +227 -0
- package/build/__tests__/integration/tools.test.js.map +1 -0
- package/build/__tests__/setup.d.ts +2 -0
- package/build/__tests__/setup.d.ts.map +1 -0
- package/build/__tests__/setup.js +15 -0
- package/build/__tests__/setup.js.map +1 -0
- package/build/__tests__/unit/androidEmulatorAvds.test.d.ts +2 -0
- package/build/__tests__/unit/androidEmulatorAvds.test.d.ts.map +1 -0
- package/build/__tests__/unit/androidEmulatorAvds.test.js +98 -0
- package/build/__tests__/unit/androidEmulatorAvds.test.js.map +1 -0
- package/build/__tests__/unit/buildToken.test.d.ts +2 -0
- package/build/__tests__/unit/buildToken.test.d.ts.map +1 -0
- package/build/__tests__/unit/buildToken.test.js +22 -0
- package/build/__tests__/unit/buildToken.test.js.map +1 -0
- package/build/__tests__/unit/bundle.test.d.ts +2 -0
- package/build/__tests__/unit/bundle.test.d.ts.map +1 -0
- package/build/__tests__/unit/bundle.test.js +53 -0
- package/build/__tests__/unit/bundle.test.js.map +1 -0
- package/build/__tests__/unit/burst.test.d.ts +2 -0
- package/build/__tests__/unit/burst.test.d.ts.map +1 -0
- package/build/__tests__/unit/burst.test.js +54 -0
- package/build/__tests__/unit/burst.test.js.map +1 -0
- package/build/__tests__/unit/connection-health.test.d.ts +2 -0
- package/build/__tests__/unit/connection-health.test.d.ts.map +1 -0
- package/build/__tests__/unit/connection-health.test.js +38 -0
- package/build/__tests__/unit/connection-health.test.js.map +1 -0
- package/build/__tests__/unit/debugGlobals.test.d.ts +2 -0
- package/build/__tests__/unit/debugGlobals.test.d.ts.map +1 -0
- package/build/__tests__/unit/debugGlobals.test.js +14 -0
- package/build/__tests__/unit/debugGlobals.test.js.map +1 -0
- package/build/__tests__/unit/deviceDiscovery.test.d.ts +2 -0
- package/build/__tests__/unit/deviceDiscovery.test.d.ts.map +1 -0
- package/build/__tests__/unit/deviceDiscovery.test.js +119 -0
- package/build/__tests__/unit/deviceDiscovery.test.js.map +1 -0
- package/build/__tests__/unit/deviceResolver.test.d.ts +2 -0
- package/build/__tests__/unit/deviceResolver.test.d.ts.map +1 -0
- package/build/__tests__/unit/deviceResolver.test.js +261 -0
- package/build/__tests__/unit/deviceResolver.test.js.map +1 -0
- package/build/__tests__/unit/escapeNonAscii.test.d.ts +2 -0
- package/build/__tests__/unit/escapeNonAscii.test.d.ts.map +1 -0
- package/build/__tests__/unit/escapeNonAscii.test.js +76 -0
- package/build/__tests__/unit/escapeNonAscii.test.js.map +1 -0
- package/build/__tests__/unit/exec.test.d.ts +2 -0
- package/build/__tests__/unit/exec.test.d.ts.map +1 -0
- package/build/__tests__/unit/exec.test.js +54 -0
- package/build/__tests__/unit/exec.test.js.map +1 -0
- package/build/__tests__/unit/executor.test.d.ts +2 -0
- package/build/__tests__/unit/executor.test.d.ts.map +1 -0
- package/build/__tests__/unit/executor.test.js +220 -0
- package/build/__tests__/unit/executor.test.js.map +1 -0
- package/build/__tests__/unit/failureArtifact.test.d.ts +2 -0
- package/build/__tests__/unit/failureArtifact.test.d.ts.map +1 -0
- package/build/__tests__/unit/failureArtifact.test.js +207 -0
- package/build/__tests__/unit/failureArtifact.test.js.map +1 -0
- package/build/__tests__/unit/fastRefreshRecorder.test.d.ts +2 -0
- package/build/__tests__/unit/fastRefreshRecorder.test.d.ts.map +1 -0
- package/build/__tests__/unit/fastRefreshRecorder.test.js +68 -0
- package/build/__tests__/unit/fastRefreshRecorder.test.js.map +1 -0
- package/build/__tests__/unit/fastRefreshTools.test.d.ts +2 -0
- package/build/__tests__/unit/fastRefreshTools.test.d.ts.map +1 -0
- package/build/__tests__/unit/fastRefreshTools.test.js +112 -0
- package/build/__tests__/unit/fastRefreshTools.test.js.map +1 -0
- package/build/__tests__/unit/fiberMatch.test.d.ts +2 -0
- package/build/__tests__/unit/fiberMatch.test.d.ts.map +1 -0
- package/build/__tests__/unit/fiberMatch.test.js +114 -0
- package/build/__tests__/unit/fiberMatch.test.js.map +1 -0
- package/build/__tests__/unit/fingerprint.test.d.ts +2 -0
- package/build/__tests__/unit/fingerprint.test.d.ts.map +1 -0
- package/build/__tests__/unit/fingerprint.test.js +99 -0
- package/build/__tests__/unit/fingerprint.test.js.map +1 -0
- package/build/__tests__/unit/focusedInput.test.d.ts +2 -0
- package/build/__tests__/unit/focusedInput.test.d.ts.map +1 -0
- package/build/__tests__/unit/focusedInput.test.js +58 -0
- package/build/__tests__/unit/focusedInput.test.js.map +1 -0
- package/build/__tests__/unit/focusedInputTools.test.d.ts +2 -0
- package/build/__tests__/unit/focusedInputTools.test.d.ts.map +1 -0
- package/build/__tests__/unit/focusedInputTools.test.js +79 -0
- package/build/__tests__/unit/focusedInputTools.test.js.map +1 -0
- package/build/__tests__/unit/iconSemantics.test.d.ts +2 -0
- package/build/__tests__/unit/iconSemantics.test.d.ts.map +1 -0
- package/build/__tests__/unit/iconSemantics.test.js +80 -0
- package/build/__tests__/unit/iconSemantics.test.js.map +1 -0
- package/build/__tests__/unit/imageBuffer.test.d.ts +2 -0
- package/build/__tests__/unit/imageBuffer.test.d.ts.map +1 -0
- package/build/__tests__/unit/imageBuffer.test.js +128 -0
- package/build/__tests__/unit/imageBuffer.test.js.map +1 -0
- package/build/__tests__/unit/logs.test.d.ts +2 -0
- package/build/__tests__/unit/logs.test.d.ts.map +1 -0
- package/build/__tests__/unit/logs.test.js +90 -0
- package/build/__tests__/unit/logs.test.js.map +1 -0
- package/build/__tests__/unit/measureComponent.test.d.ts +2 -0
- package/build/__tests__/unit/measureComponent.test.d.ts.map +1 -0
- package/build/__tests__/unit/measureComponent.test.js +66 -0
- package/build/__tests__/unit/measureComponent.test.js.map +1 -0
- package/build/__tests__/unit/measureComponentTools.test.d.ts +2 -0
- package/build/__tests__/unit/measureComponentTools.test.d.ts.map +1 -0
- package/build/__tests__/unit/measureComponentTools.test.js +94 -0
- package/build/__tests__/unit/measureComponentTools.test.js.map +1 -0
- package/build/__tests__/unit/metro.test.d.ts +2 -0
- package/build/__tests__/unit/metro.test.d.ts.map +1 -0
- package/build/__tests__/unit/metro.test.js +61 -0
- package/build/__tests__/unit/metro.test.js.map +1 -0
- package/build/__tests__/unit/network.test.d.ts +2 -0
- package/build/__tests__/unit/network.test.d.ts.map +1 -0
- package/build/__tests__/unit/network.test.js +102 -0
- package/build/__tests__/unit/network.test.js.map +1 -0
- package/build/__tests__/unit/networkInterceptor.test.d.ts +2 -0
- package/build/__tests__/unit/networkInterceptor.test.d.ts.map +1 -0
- package/build/__tests__/unit/networkInterceptor.test.js +104 -0
- package/build/__tests__/unit/networkInterceptor.test.js.map +1 -0
- package/build/__tests__/unit/probe.test.d.ts +2 -0
- package/build/__tests__/unit/probe.test.d.ts.map +1 -0
- package/build/__tests__/unit/probe.test.js +62 -0
- package/build/__tests__/unit/probe.test.js.map +1 -0
- package/build/__tests__/unit/rnGlobalsBootstrap.test.d.ts +2 -0
- package/build/__tests__/unit/rnGlobalsBootstrap.test.d.ts.map +1 -0
- package/build/__tests__/unit/rnGlobalsBootstrap.test.js +29 -0
- package/build/__tests__/unit/rnGlobalsBootstrap.test.js.map +1 -0
- package/build/__tests__/unit/screenState.test.d.ts +2 -0
- package/build/__tests__/unit/screenState.test.d.ts.map +1 -0
- package/build/__tests__/unit/screenState.test.js +186 -0
- package/build/__tests__/unit/screenState.test.js.map +1 -0
- package/build/__tests__/unit/screenshot-diff.test.d.ts +2 -0
- package/build/__tests__/unit/screenshot-diff.test.d.ts.map +1 -0
- package/build/__tests__/unit/screenshot-diff.test.js +97 -0
- package/build/__tests__/unit/screenshot-diff.test.js.map +1 -0
- package/build/__tests__/unit/tap-evidence.test.d.ts +2 -0
- package/build/__tests__/unit/tap-evidence.test.d.ts.map +1 -0
- package/build/__tests__/unit/tap-evidence.test.js +89 -0
- package/build/__tests__/unit/tap-evidence.test.js.map +1 -0
- package/build/__tests__/unit/tap.test.d.ts +2 -0
- package/build/__tests__/unit/tap.test.d.ts.map +1 -0
- package/build/__tests__/unit/tap.test.js +769 -0
- package/build/__tests__/unit/tap.test.js.map +1 -0
- package/build/__tests__/unit/timeoutClamp.test.d.ts +2 -0
- package/build/__tests__/unit/timeoutClamp.test.d.ts.map +1 -0
- package/build/__tests__/unit/timeoutClamp.test.js +17 -0
- package/build/__tests__/unit/timeoutClamp.test.js.map +1 -0
- package/build/__tests__/unit/toolDescriptions.test.d.ts +2 -0
- package/build/__tests__/unit/toolDescriptions.test.d.ts.map +1 -0
- package/build/__tests__/unit/toolDescriptions.test.js +145 -0
- package/build/__tests__/unit/toolDescriptions.test.js.map +1 -0
- package/build/__tests__/unit/transportClassify.test.d.ts +2 -0
- package/build/__tests__/unit/transportClassify.test.d.ts.map +1 -0
- package/build/__tests__/unit/transportClassify.test.js +60 -0
- package/build/__tests__/unit/transportClassify.test.js.map +1 -0
- package/build/__tests__/unit/verifyAction.test.d.ts +2 -0
- package/build/__tests__/unit/verifyAction.test.d.ts.map +1 -0
- package/build/__tests__/unit/verifyAction.test.js +26 -0
- package/build/__tests__/unit/verifyAction.test.js.map +1 -0
- package/build/core/android.d.ts +299 -0
- package/build/core/android.d.ts.map +1 -0
- package/build/core/android.js +1582 -0
- package/build/core/android.js.map +1 -0
- package/build/core/appDetection.d.ts +20 -0
- package/build/core/appDetection.d.ts.map +1 -0
- package/build/core/appDetection.js +167 -0
- package/build/core/appDetection.js.map +1 -0
- package/build/core/bundle.d.ts +49 -0
- package/build/core/bundle.d.ts.map +1 -0
- package/build/core/bundle.js +356 -0
- package/build/core/bundle.js.map +1 -0
- package/build/core/componentSearch.d.ts +41 -0
- package/build/core/componentSearch.d.ts.map +1 -0
- package/build/core/componentSearch.js +588 -0
- package/build/core/componentSearch.js.map +1 -0
- package/build/core/componentTree.d.ts +13 -0
- package/build/core/componentTree.d.ts.map +1 -0
- package/build/core/componentTree.js +275 -0
- package/build/core/componentTree.js.map +1 -0
- package/build/core/config.d.ts +9 -0
- package/build/core/config.d.ts.map +1 -0
- package/build/core/config.js +28 -0
- package/build/core/config.js.map +1 -0
- package/build/core/connection.d.ts +102 -0
- package/build/core/connection.d.ts.map +1 -0
- package/build/core/connection.js +1710 -0
- package/build/core/connection.js.map +1 -0
- package/build/core/connectionState.d.ts +122 -0
- package/build/core/connectionState.d.ts.map +1 -0
- package/build/core/connectionState.js +313 -0
- package/build/core/connectionState.js.map +1 -0
- package/build/core/debugGlobals.d.ts +6 -0
- package/build/core/debugGlobals.d.ts.map +1 -0
- package/build/core/debugGlobals.js +370 -0
- package/build/core/debugGlobals.js.map +1 -0
- package/build/core/deviceDiscovery.d.ts +52 -0
- package/build/core/deviceDiscovery.d.ts.map +1 -0
- package/build/core/deviceDiscovery.js +74 -0
- package/build/core/deviceDiscovery.js.map +1 -0
- package/build/core/deviceResolver.d.ts +44 -0
- package/build/core/deviceResolver.d.ts.map +1 -0
- package/build/core/deviceResolver.js +225 -0
- package/build/core/deviceResolver.js.map +1 -0
- package/build/core/errorScreenParser.d.ts +30 -0
- package/build/core/errorScreenParser.d.ts.map +1 -0
- package/build/core/errorScreenParser.js +198 -0
- package/build/core/errorScreenParser.js.map +1 -0
- package/build/core/errors.d.ts +14 -0
- package/build/core/errors.d.ts.map +1 -0
- package/build/core/errors.js +17 -0
- package/build/core/errors.js.map +1 -0
- package/build/core/escapeNonAscii.d.ts +18 -0
- package/build/core/escapeNonAscii.d.ts.map +1 -0
- package/build/core/escapeNonAscii.js +352 -0
- package/build/core/escapeNonAscii.js.map +1 -0
- package/build/core/exec.d.ts +22 -0
- package/build/core/exec.d.ts.map +1 -0
- package/build/core/exec.js +54 -0
- package/build/core/exec.js.map +1 -0
- package/build/core/executor.d.ts +9 -0
- package/build/core/executor.d.ts.map +1 -0
- package/build/core/executor.js +12 -0
- package/build/core/executor.js.map +1 -0
- package/build/core/failureArtifact.d.ts +120 -0
- package/build/core/failureArtifact.d.ts.map +1 -0
- package/build/core/failureArtifact.js +113 -0
- package/build/core/failureArtifact.js.map +1 -0
- package/build/core/fastRefreshRecorder.d.ts +23 -0
- package/build/core/fastRefreshRecorder.d.ts.map +1 -0
- package/build/core/fastRefreshRecorder.js +83 -0
- package/build/core/fastRefreshRecorder.js.map +1 -0
- package/build/core/fastRefreshTools.d.ts +19 -0
- package/build/core/fastRefreshTools.d.ts.map +1 -0
- package/build/core/fastRefreshTools.js +71 -0
- package/build/core/fastRefreshTools.js.map +1 -0
- package/build/core/feedback.d.ts +18 -0
- package/build/core/feedback.d.ts.map +1 -0
- package/build/core/feedback.js +73 -0
- package/build/core/feedback.js.map +1 -0
- package/build/core/fiberMatch.d.ts +25 -0
- package/build/core/fiberMatch.d.ts.map +1 -0
- package/build/core/fiberMatch.js +35 -0
- package/build/core/fiberMatch.js.map +1 -0
- package/build/core/fingerprint.d.ts +4 -0
- package/build/core/fingerprint.d.ts.map +1 -0
- package/build/core/fingerprint.js +47 -0
- package/build/core/fingerprint.js.map +1 -0
- package/build/core/focusedInput.d.ts +27 -0
- package/build/core/focusedInput.d.ts.map +1 -0
- package/build/core/focusedInput.js +110 -0
- package/build/core/focusedInput.js.map +1 -0
- package/build/core/focusedInputTools.d.ts +21 -0
- package/build/core/focusedInputTools.d.ts.map +1 -0
- package/build/core/focusedInputTools.js +51 -0
- package/build/core/focusedInputTools.js.map +1 -0
- package/build/core/format.d.ts +8 -0
- package/build/core/format.d.ts.map +1 -0
- package/build/core/format.js +34 -0
- package/build/core/format.js.map +1 -0
- package/build/core/guides.d.ts +21 -0
- package/build/core/guides.d.ts.map +1 -0
- package/build/core/guides.js +397 -0
- package/build/core/guides.js.map +1 -0
- package/build/core/iconSemantics.d.ts +18 -0
- package/build/core/iconSemantics.d.ts.map +1 -0
- package/build/core/iconSemantics.js +114 -0
- package/build/core/iconSemantics.js.map +1 -0
- package/build/core/imageBuffer.d.ts +37 -0
- package/build/core/imageBuffer.d.ts.map +1 -0
- package/build/core/imageBuffer.js +58 -0
- package/build/core/imageBuffer.js.map +1 -0
- package/build/core/index.d.ts +37 -0
- package/build/core/index.d.ts.map +1 -0
- package/build/core/index.js +66 -0
- package/build/core/index.js.map +1 -0
- package/build/core/inspector.d.ts +55 -0
- package/build/core/inspector.d.ts.map +1 -0
- package/build/core/inspector.js +603 -0
- package/build/core/inspector.js.map +1 -0
- package/build/core/ios.d.ts +263 -0
- package/build/core/ios.d.ts.map +1 -0
- package/build/core/ios.js +1705 -0
- package/build/core/ios.js.map +1 -0
- package/build/core/jsExecute.d.ts +62 -0
- package/build/core/jsExecute.d.ts.map +1 -0
- package/build/core/jsExecute.js +696 -0
- package/build/core/jsExecute.js.map +1 -0
- package/build/core/license.d.ts +48 -0
- package/build/core/license.d.ts.map +1 -0
- package/build/core/license.js +267 -0
- package/build/core/license.js.map +1 -0
- package/build/core/logbox.d.ts +58 -0
- package/build/core/logbox.d.ts.map +1 -0
- package/build/core/logbox.js +330 -0
- package/build/core/logbox.js.map +1 -0
- package/build/core/logs.d.ts +44 -0
- package/build/core/logs.d.ts.map +1 -0
- package/build/core/logs.js +149 -0
- package/build/core/logs.js.map +1 -0
- package/build/core/measureComponent.d.ts +19 -0
- package/build/core/measureComponent.d.ts.map +1 -0
- package/build/core/measureComponent.js +94 -0
- package/build/core/measureComponent.js.map +1 -0
- package/build/core/measureComponentTools.d.ts +6 -0
- package/build/core/measureComponentTools.d.ts.map +1 -0
- package/build/core/measureComponentTools.js +43 -0
- package/build/core/measureComponentTools.js.map +1 -0
- package/build/core/metro.d.ts +31 -0
- package/build/core/metro.d.ts.map +1 -0
- package/build/core/metro.js +124 -0
- package/build/core/metro.js.map +1 -0
- package/build/core/nativeOnlyHints.d.ts +14 -0
- package/build/core/nativeOnlyHints.d.ts.map +1 -0
- package/build/core/nativeOnlyHints.js +58 -0
- package/build/core/nativeOnlyHints.js.map +1 -0
- package/build/core/network.d.ts +43 -0
- package/build/core/network.d.ts.map +1 -0
- package/build/core/network.js +217 -0
- package/build/core/network.js.map +1 -0
- package/build/core/networkInterceptor.d.ts +30 -0
- package/build/core/networkInterceptor.d.ts.map +1 -0
- package/build/core/networkInterceptor.js +211 -0
- package/build/core/networkInterceptor.js.map +1 -0
- package/build/core/ocr.d.ts +87 -0
- package/build/core/ocr.d.ts.map +1 -0
- package/build/core/ocr.js +335 -0
- package/build/core/ocr.js.map +1 -0
- package/build/core/paths.d.ts +2 -0
- package/build/core/paths.d.ts.map +1 -0
- package/build/core/paths.js +28 -0
- package/build/core/paths.js.map +1 -0
- package/build/core/posthog.d.ts +5 -0
- package/build/core/posthog.d.ts.map +1 -0
- package/build/core/posthog.js +52 -0
- package/build/core/posthog.js.map +1 -0
- package/build/core/pressables.d.ts +46 -0
- package/build/core/pressables.d.ts.map +1 -0
- package/build/core/pressables.js +1719 -0
- package/build/core/pressables.js.map +1 -0
- package/build/core/probe.d.ts +12 -0
- package/build/core/probe.d.ts.map +1 -0
- package/build/core/probe.js +56 -0
- package/build/core/probe.js.map +1 -0
- package/build/core/redux.d.ts +22 -0
- package/build/core/redux.d.ts.map +1 -0
- package/build/core/redux.js +116 -0
- package/build/core/redux.js.map +1 -0
- package/build/core/register.d.ts +8 -0
- package/build/core/register.d.ts.map +1 -0
- package/build/core/register.js +202 -0
- package/build/core/register.js.map +1 -0
- package/build/core/rnGlobalsBootstrap.d.ts +16 -0
- package/build/core/rnGlobalsBootstrap.d.ts.map +1 -0
- package/build/core/rnGlobalsBootstrap.js +116 -0
- package/build/core/rnGlobalsBootstrap.js.map +1 -0
- package/build/core/screenLayout.d.ts +53 -0
- package/build/core/screenLayout.d.ts.map +1 -0
- package/build/core/screenLayout.js +878 -0
- package/build/core/screenLayout.js.map +1 -0
- package/build/core/screenState.d.ts +98 -0
- package/build/core/screenState.d.ts.map +1 -0
- package/build/core/screenState.js +1121 -0
- package/build/core/screenState.js.map +1 -0
- package/build/core/sdkBridge.d.ts +67 -0
- package/build/core/sdkBridge.d.ts.map +1 -0
- package/build/core/sdkBridge.js +148 -0
- package/build/core/sdkBridge.js.map +1 -0
- package/build/core/state.d.ts +37 -0
- package/build/core/state.d.ts.map +1 -0
- package/build/core/state.js +124 -0
- package/build/core/state.js.map +1 -0
- package/build/core/telemetry.d.ts +50 -0
- package/build/core/telemetry.d.ts.map +1 -0
- package/build/core/telemetry.js +653 -0
- package/build/core/telemetry.js.map +1 -0
- package/build/core/toolHelpers.d.ts +24 -0
- package/build/core/toolHelpers.d.ts.map +1 -0
- package/build/core/toolHelpers.js +118 -0
- package/build/core/toolHelpers.js.map +1 -0
- package/build/core/types.d.ts +162 -0
- package/build/core/types.d.ts.map +1 -0
- package/build/core/types.js +2 -0
- package/build/core/types.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +132 -0
- package/build/index.js.map +1 -0
- package/build/pro/screenshot-diff.d.ts +10 -0
- package/build/pro/screenshot-diff.d.ts.map +1 -0
- package/build/pro/screenshot-diff.js +38 -0
- package/build/pro/screenshot-diff.js.map +1 -0
- package/build/pro/tap.d.ts +295 -0
- package/build/pro/tap.d.ts.map +1 -0
- package/build/pro/tap.js +1937 -0
- package/build/pro/tap.js.map +1 -0
- package/build/pro/verifyAction.d.ts +50 -0
- package/build/pro/verifyAction.d.ts.map +1 -0
- package/build/pro/verifyAction.js +285 -0
- package/build/pro/verifyAction.js.map +1 -0
- package/build/tools/_deviceArg.d.ts +45 -0
- package/build/tools/_deviceArg.d.ts.map +1 -0
- package/build/tools/_deviceArg.js +58 -0
- package/build/tools/_deviceArg.js.map +1 -0
- package/build/tools/accountTools.d.ts +46 -0
- package/build/tools/accountTools.d.ts.map +1 -0
- package/build/tools/accountTools.js +223 -0
- package/build/tools/accountTools.js.map +1 -0
- package/build/tools/bundleTools.d.ts +3 -0
- package/build/tools/bundleTools.d.ts.map +1 -0
- package/build/tools/bundleTools.js +375 -0
- package/build/tools/bundleTools.js.map +1 -0
- package/build/tools/componentTools.d.ts +3 -0
- package/build/tools/componentTools.d.ts.map +1 -0
- package/build/tools/componentTools.js +745 -0
- package/build/tools/componentTools.js.map +1 -0
- package/build/tools/connectionTools.d.ts +3 -0
- package/build/tools/connectionTools.d.ts.map +1 -0
- package/build/tools/connectionTools.js +625 -0
- package/build/tools/connectionTools.js.map +1 -0
- package/build/tools/deviceTools.d.ts +3 -0
- package/build/tools/deviceTools.d.ts.map +1 -0
- package/build/tools/deviceTools.js +423 -0
- package/build/tools/deviceTools.js.map +1 -0
- package/build/tools/executionTools.d.ts +3 -0
- package/build/tools/executionTools.d.ts.map +1 -0
- package/build/tools/executionTools.js +243 -0
- package/build/tools/executionTools.js.map +1 -0
- package/build/tools/interactionTools.d.ts +3 -0
- package/build/tools/interactionTools.d.ts.map +1 -0
- package/build/tools/interactionTools.js +539 -0
- package/build/tools/interactionTools.js.map +1 -0
- package/build/tools/logTools.d.ts +3 -0
- package/build/tools/logTools.d.ts.map +1 -0
- package/build/tools/logTools.js +371 -0
- package/build/tools/logTools.js.map +1 -0
- package/build/tools/metaTools.d.ts +7 -0
- package/build/tools/metaTools.d.ts.map +1 -0
- package/build/tools/metaTools.js +186 -0
- package/build/tools/metaTools.js.map +1 -0
- package/build/tools/networkTools.d.ts +3 -0
- package/build/tools/networkTools.d.ts.map +1 -0
- package/build/tools/networkTools.js +478 -0
- package/build/tools/networkTools.js.map +1 -0
- package/build/tools/reduxTools.d.ts +3 -0
- package/build/tools/reduxTools.d.ts.map +1 -0
- package/build/tools/reduxTools.js +83 -0
- package/build/tools/reduxTools.js.map +1 -0
- package/build/tools/screenshotTools.d.ts +3 -0
- package/build/tools/screenshotTools.d.ts.map +1 -0
- package/build/tools/screenshotTools.js +703 -0
- package/build/tools/screenshotTools.js.map +1 -0
- package/package.json +63 -2
|
@@ -0,0 +1,878 @@
|
|
|
1
|
+
import { executeInApp, delay } from "./jsExecute.js";
|
|
2
|
+
export function formatSummaryToTonl(components, total) {
|
|
3
|
+
const lines = [`#summary total=${total}`];
|
|
4
|
+
for (const c of components) {
|
|
5
|
+
lines.push(`${c.component}:${c.count}`);
|
|
6
|
+
}
|
|
7
|
+
return lines.join("\n");
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Classify each root as "screen" or "overlay".
|
|
11
|
+
*
|
|
12
|
+
* A root is a **screen** if its subtree contains a navigation screen marker
|
|
13
|
+
* (Route(...), *Screen, *Page). Falls back to the largest root by area
|
|
14
|
+
* if no navigation markers are found. Everything else is an overlay.
|
|
15
|
+
*/
|
|
16
|
+
function classifyRoots(roots, elements, childrenMap) {
|
|
17
|
+
const labels = [];
|
|
18
|
+
// Navigation screen markers: Route(...) wrapper or user-defined *Screen/*Page
|
|
19
|
+
const screenMarker = /^(Route\(|.*Screen\(|.*Page\(|.*Screen$|.*Page$)/;
|
|
20
|
+
function subtreeHasScreen(idx, depth) {
|
|
21
|
+
if (depth > 30)
|
|
22
|
+
return false;
|
|
23
|
+
if (screenMarker.test(elements[idx].component))
|
|
24
|
+
return true;
|
|
25
|
+
const kids = childrenMap.get(idx);
|
|
26
|
+
if (kids) {
|
|
27
|
+
for (const kid of kids) {
|
|
28
|
+
if (subtreeHasScreen(kid, depth + 1))
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
// First pass: check which roots contain a navigation screen
|
|
35
|
+
const hasScreen = roots.map(ri => subtreeHasScreen(ri, 0));
|
|
36
|
+
const anyScreenFound = hasScreen.some(Boolean);
|
|
37
|
+
if (anyScreenFound) {
|
|
38
|
+
for (let i = 0; i < roots.length; i++) {
|
|
39
|
+
labels.push(hasScreen[i] ? "screen" : "overlay");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Fallback: largest root by area is the screen
|
|
44
|
+
let maxArea = 0;
|
|
45
|
+
let maxIdx = 0;
|
|
46
|
+
for (let i = 0; i < roots.length; i++) {
|
|
47
|
+
const f = elements[roots[i]].frame;
|
|
48
|
+
if (f && f.width * f.height > maxArea) {
|
|
49
|
+
maxArea = f.width * f.height;
|
|
50
|
+
maxIdx = i;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (let i = 0; i < roots.length; i++) {
|
|
54
|
+
labels.push(i === maxIdx ? "screen" : "overlay");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { labels, hasOverlays: labels.some(l => l === "overlay") };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build, collapse, classify, and render a layout tree.
|
|
61
|
+
*
|
|
62
|
+
* Shared by both get_screen_layout (points, no tap coords) and
|
|
63
|
+
* the screenshot layout enrichment (pixels, with tap coords).
|
|
64
|
+
*
|
|
65
|
+
* @param elements - flat list of layout nodes with parentIndex linkage
|
|
66
|
+
* @param renderLine - callback that produces the output line for a node,
|
|
67
|
+
* given (element, indent level, whether it's a leaf)
|
|
68
|
+
*/
|
|
69
|
+
function formatLayoutTree(elements, renderLine) {
|
|
70
|
+
// Build index: originalIndex -> element index in the filtered array
|
|
71
|
+
const indexMap = new Map();
|
|
72
|
+
for (let i = 0; i < elements.length; i++) {
|
|
73
|
+
if (elements[i].originalIndex !== undefined) {
|
|
74
|
+
indexMap.set(elements[i].originalIndex, i);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Build children lists
|
|
78
|
+
const children = new Map();
|
|
79
|
+
const roots = [];
|
|
80
|
+
for (let i = 0; i < elements.length; i++) {
|
|
81
|
+
const el = elements[i];
|
|
82
|
+
const parentOrigIdx = el.parentIndex;
|
|
83
|
+
if (parentOrigIdx === undefined || parentOrigIdx === -1 || !indexMap.has(parentOrigIdx)) {
|
|
84
|
+
roots.push(i);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const parentIdx = indexMap.get(parentOrigIdx);
|
|
88
|
+
if (!children.has(parentIdx))
|
|
89
|
+
children.set(parentIdx, []);
|
|
90
|
+
children.get(parentIdx).push(i);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Collapse wrapper chains: if a child has the same frame as its parent,
|
|
94
|
+
// it's just a wrapper — promote its children and text to the parent
|
|
95
|
+
function sameFrame(a, b) {
|
|
96
|
+
if (!a.frame || !b.frame)
|
|
97
|
+
return false;
|
|
98
|
+
return Math.abs(a.frame.x - b.frame.x) < 1 &&
|
|
99
|
+
Math.abs(a.frame.y - b.frame.y) < 1 &&
|
|
100
|
+
Math.abs(a.frame.width - b.frame.width) < 1 &&
|
|
101
|
+
Math.abs(a.frame.height - b.frame.height) < 1;
|
|
102
|
+
}
|
|
103
|
+
function collapseNode(parentIdx) {
|
|
104
|
+
const kids = children.get(parentIdx);
|
|
105
|
+
if (!kids)
|
|
106
|
+
return;
|
|
107
|
+
const newKids = [];
|
|
108
|
+
for (const kidIdx of kids) {
|
|
109
|
+
if (sameFrame(elements[parentIdx], elements[kidIdx])) {
|
|
110
|
+
if (!elements[parentIdx].text && elements[kidIdx].text) {
|
|
111
|
+
elements[parentIdx].text = elements[kidIdx].text;
|
|
112
|
+
}
|
|
113
|
+
const grandKids = children.get(kidIdx);
|
|
114
|
+
if (grandKids) {
|
|
115
|
+
newKids.push(...grandKids);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
newKids.push(kidIdx);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (newKids.length > 0) {
|
|
123
|
+
children.set(parentIdx, newKids);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
children.delete(parentIdx);
|
|
127
|
+
}
|
|
128
|
+
const updatedKids = children.get(parentIdx);
|
|
129
|
+
if (updatedKids) {
|
|
130
|
+
for (const kid of updatedKids) {
|
|
131
|
+
collapseNode(kid);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
for (const root of roots) {
|
|
136
|
+
collapseNode(root);
|
|
137
|
+
}
|
|
138
|
+
// Render tree
|
|
139
|
+
const lines = [];
|
|
140
|
+
function printNode(idx, indent, parent) {
|
|
141
|
+
const isLeaf = !children.has(idx);
|
|
142
|
+
lines.push(renderLine(elements[idx], indent, isLeaf, parent));
|
|
143
|
+
const kids = children.get(idx);
|
|
144
|
+
if (kids) {
|
|
145
|
+
for (const kid of kids) {
|
|
146
|
+
printNode(kid, indent + 1, elements[idx]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Classify roots and emit layer headers
|
|
151
|
+
const { labels, hasOverlays } = classifyRoots(roots, elements, children);
|
|
152
|
+
if (hasOverlays) {
|
|
153
|
+
let prevLabel = "";
|
|
154
|
+
for (let ri = 0; ri < roots.length; ri++) {
|
|
155
|
+
const label = labels[ri];
|
|
156
|
+
if (label === "overlay" || label !== prevLabel) {
|
|
157
|
+
if (lines.length > 0)
|
|
158
|
+
lines.push("");
|
|
159
|
+
lines.push(`[${label}]`);
|
|
160
|
+
}
|
|
161
|
+
prevLabel = label;
|
|
162
|
+
printNode(roots[ri], 0, null);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
for (const root of roots) {
|
|
167
|
+
printNode(root, 0, null);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return lines.join("\n");
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Compute per-side overflow of a child frame relative to its parent.
|
|
174
|
+
* Returns null if neither has a frame, or overflow is within 1pt of rounding noise.
|
|
175
|
+
*/
|
|
176
|
+
function computeOverflow(child, parent) {
|
|
177
|
+
if (!child.frame || !parent.frame)
|
|
178
|
+
return null;
|
|
179
|
+
const cf = child.frame, pf = parent.frame;
|
|
180
|
+
const sides = {};
|
|
181
|
+
const dLeft = pf.x - cf.x;
|
|
182
|
+
const dRight = (cf.x + cf.width) - (pf.x + pf.width);
|
|
183
|
+
const dTop = pf.y - cf.y;
|
|
184
|
+
const dBottom = (cf.y + cf.height) - (pf.y + pf.height);
|
|
185
|
+
if (dLeft > 1)
|
|
186
|
+
sides.left = dLeft;
|
|
187
|
+
if (dRight > 1)
|
|
188
|
+
sides.right = dRight;
|
|
189
|
+
if (dTop > 1)
|
|
190
|
+
sides.top = dTop;
|
|
191
|
+
if (dBottom > 1)
|
|
192
|
+
sides.bottom = dBottom;
|
|
193
|
+
const entries = Object.entries(sides);
|
|
194
|
+
if (entries.length === 0)
|
|
195
|
+
return null;
|
|
196
|
+
let max = 0, dominantSide = "";
|
|
197
|
+
for (const [k, v] of entries) {
|
|
198
|
+
if (v > max) {
|
|
199
|
+
max = v;
|
|
200
|
+
dominantSide = k;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return { sides, max, dominantSide };
|
|
204
|
+
}
|
|
205
|
+
export function formatScreenLayoutTree(elements, extended = false, offScreen) {
|
|
206
|
+
const overflows = [];
|
|
207
|
+
const tree = formatLayoutTree(elements, (el, indent, isLeaf, parent) => {
|
|
208
|
+
const prefix = " ".repeat(indent);
|
|
209
|
+
const frame = el.frame
|
|
210
|
+
? ` (${Math.round(el.frame.x)},${Math.round(el.frame.y)} ${Math.round(el.frame.width)}x${Math.round(el.frame.height)})`
|
|
211
|
+
: "";
|
|
212
|
+
const id = el.identifiers?.testID || el.identifiers?.accessibilityLabel || "";
|
|
213
|
+
const idStr = id ? ` [${id}]` : "";
|
|
214
|
+
const textStr = el.text && isLeaf ? ` "${el.text}"` : "";
|
|
215
|
+
const layoutStr = extended && el.layout
|
|
216
|
+
? ` {${Object.entries(el.layout).map(([k, v]) => `${k}:${v}`).join("; ")}}`
|
|
217
|
+
: "";
|
|
218
|
+
let overflowStr = "";
|
|
219
|
+
if (parent) {
|
|
220
|
+
const ovf = computeOverflow(el, parent);
|
|
221
|
+
if (ovf) {
|
|
222
|
+
overflowStr = ` ⚠ overflows parent by ${Math.round(ovf.max)}pt (${ovf.dominantSide})`;
|
|
223
|
+
overflows.push({ child: el.component, parent: parent.component, max: ovf.max, dominantSide: ovf.dominantSide });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return `${prefix}${el.component}${frame}${idStr}${textStr}${layoutStr}${overflowStr}`;
|
|
227
|
+
});
|
|
228
|
+
const suffix = [];
|
|
229
|
+
if (overflows.length > 0) {
|
|
230
|
+
const sorted = overflows.slice().sort((a, b) => b.max - a.max);
|
|
231
|
+
const lines = [`[overflows] ${sorted.length} node${sorted.length === 1 ? "" : "s"} extend beyond parent (largest first):`];
|
|
232
|
+
for (const o of sorted) {
|
|
233
|
+
lines.push(` ${o.child} overflows ${o.parent} by ${Math.round(o.max)}pt (${o.dominantSide})`);
|
|
234
|
+
}
|
|
235
|
+
suffix.push(lines.join("\n"));
|
|
236
|
+
}
|
|
237
|
+
if (offScreen?.offScreenAbove?.length) {
|
|
238
|
+
suffix.push(formatOffScreenLine(offScreen.offScreenAbove, "above fold"));
|
|
239
|
+
}
|
|
240
|
+
if (offScreen?.offScreenBelow?.length) {
|
|
241
|
+
suffix.push(formatOffScreenLine(offScreen.offScreenBelow, "below fold"));
|
|
242
|
+
}
|
|
243
|
+
return suffix.length > 0 ? `${tree}\n\n${suffix.join("\n")}` : tree;
|
|
244
|
+
}
|
|
245
|
+
function formatOffScreenLine(names, position) {
|
|
246
|
+
const total = names.length;
|
|
247
|
+
const CAP = 10;
|
|
248
|
+
if (total <= CAP) {
|
|
249
|
+
return `[... ${total} component${total === 1 ? "" : "s"} ${position}: ${names.join(", ")}]`;
|
|
250
|
+
}
|
|
251
|
+
const shown = names.slice(0, CAP).join(", ");
|
|
252
|
+
return `[... ${total} components ${position}: ${shown}, ... +${total - CAP} more]`;
|
|
253
|
+
}
|
|
254
|
+
export async function getScreenLayout(options = {}) {
|
|
255
|
+
const { extended = false, summary = false, device, raw = false, timeoutMs } = options;
|
|
256
|
+
const maxDepth = 5000;
|
|
257
|
+
const componentsOnly = true;
|
|
258
|
+
const shortPath = true;
|
|
259
|
+
// --- Step 1: walk fiber tree + dispatch measureInWindow calls ---
|
|
260
|
+
const dispatchExpression = `
|
|
261
|
+
(function() {
|
|
262
|
+
var hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
263
|
+
if (!hook) return { error: 'React DevTools hook not found.' };
|
|
264
|
+
|
|
265
|
+
var roots = [];
|
|
266
|
+
if (hook.getFiberRoots) {
|
|
267
|
+
roots = Array.from(hook.getFiberRoots(1) || []);
|
|
268
|
+
}
|
|
269
|
+
if (roots.length === 0 && hook.renderers) {
|
|
270
|
+
for (var entry of hook.renderers) {
|
|
271
|
+
var r = Array.from(hook.getFiberRoots ? (hook.getFiberRoots(entry[0]) || []) : []);
|
|
272
|
+
if (r.length > 0) { roots = r; break; }
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (roots.length === 0) return { error: 'No fiber roots found.' };
|
|
276
|
+
|
|
277
|
+
function getMeasurable(fiber) {
|
|
278
|
+
var sn = fiber.stateNode;
|
|
279
|
+
if (!sn) return null;
|
|
280
|
+
if (typeof sn.measureInWindow === 'function') return sn;
|
|
281
|
+
if (sn.canonical && sn.canonical.publicInstance &&
|
|
282
|
+
typeof sn.canonical.publicInstance.measureInWindow === 'function') {
|
|
283
|
+
return sn.canonical.publicInstance;
|
|
284
|
+
}
|
|
285
|
+
// Fabric leaf nodes like RCTText have no publicInstance. Measure via
|
|
286
|
+
// the native Fabric UIManager using the shadow node instead — needed
|
|
287
|
+
// for text bounds that are tight around the glyphs (not the scroll
|
|
288
|
+
// container the text happens to live inside).
|
|
289
|
+
if (sn.node && globalThis.nativeFabricUIManager &&
|
|
290
|
+
typeof globalThis.nativeFabricUIManager.measureInWindow === 'function') {
|
|
291
|
+
var node = sn.node;
|
|
292
|
+
return {
|
|
293
|
+
measureInWindow: function(cb) {
|
|
294
|
+
try { globalThis.nativeFabricUIManager.measureInWindow(node, cb); } catch(e) {}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Collect host fibers with their metadata
|
|
302
|
+
var hostFibers = [];
|
|
303
|
+
var fiberMeta = [];
|
|
304
|
+
var componentsOnlyMode = ${componentsOnly};
|
|
305
|
+
|
|
306
|
+
// RN internals and primitives — skip these when looking for meaningful component names
|
|
307
|
+
// Only filter internal components that can't be written as JSX.
|
|
308
|
+
// Keep all user-facing components (View, Text, ScrollView, Modal, etc.)
|
|
309
|
+
var RN_PRIMITIVES = /^(Animated\\(.*|withAnimated.*|AnimatedComponent.*|ForwardRef.*|memo\\(.*|Context\\.Consumer|Context\\.Provider|ScrollViewContext(Base)?|VirtualizedListContext(Resetter)?|TextInputContext|KeyboardAvoidingViewContext|RCT.*|RNS.*|RNC.*|ViewManagerAdapter_.*|VirtualizedList.*|CellRenderer.*|FrameSizeProvider.*|MaybeScreenContainer|MaybeScreen|Navigation.*|Screen$|ScreenStack|ScreenContainer|ScreenContentWrapper|SceneView|DelayedFreeze|Freeze|Suspender|DebugContainer|StaticContainer|SafeAreaProvider.*|SafeAreaFrameContext|SafeAreaInsetsContext|ExpoRoot|ExpoRootComponent|GestureHandler.*|NativeViewGestureHandler|GestureDetector|PanGestureHandler|Reanimated.*|BottomTabNavigator|TabLayout|RouteNode|Route$|KeyboardProvider|PortalProviderComponent|BottomSheetModalProviderWrapper|ThemeContext|ThemeProvider|TextAncestorContext|PressabilityDebugView|TouchableHighlightImpl|StatusBarOverlay|BottomSheetHostingContainerComponent|BottomSheetGestureHandlersProvider|BottomSheetBackdropContainerComponent|BottomSheetContainerComponent|BottomSheetDraggableViewComponent|BottomSheetHandleContainerComponent|BottomSheetBackgroundContainerComponent|DebuggingOverlay|InspectorDeferred|Inspector|InspectorOverlay|InspectorPanel|StyleInspector|BoxInspector|BoxContainer|ElementBox|BorderBox|InspectorPanelButton)$/;
|
|
310
|
+
|
|
311
|
+
function getComponentName(fiber) {
|
|
312
|
+
if (!fiber || !fiber.type) return null;
|
|
313
|
+
if (typeof fiber.type === 'string') return fiber.type;
|
|
314
|
+
return fiber.type.displayName || fiber.type.name || null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Find the first measurable host descendant of a fiber
|
|
318
|
+
function findFirstHost(fiber, depth) {
|
|
319
|
+
if (!fiber || depth > 20) return null;
|
|
320
|
+
if (typeof fiber.type === 'string' && getMeasurable(fiber)) return fiber;
|
|
321
|
+
var child = fiber.child;
|
|
322
|
+
while (child) {
|
|
323
|
+
var found = findFirstHost(child, depth + 1);
|
|
324
|
+
if (found) return found;
|
|
325
|
+
child = child.sibling;
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Extract text content from a fiber subtree.
|
|
331
|
+
// When a fiber has a string child, return it without recursing
|
|
332
|
+
// (avoids duplication from Text > RCTText having the same string).
|
|
333
|
+
function collectText(fiber, d) {
|
|
334
|
+
if (!fiber || d > 30) return '';
|
|
335
|
+
var props = fiber.memoizedProps;
|
|
336
|
+
if (props) {
|
|
337
|
+
var ch = props.children;
|
|
338
|
+
// Leaf text — return without recursing into children fibers
|
|
339
|
+
if (typeof ch === 'string') return ch;
|
|
340
|
+
if (typeof ch === 'number') return String(ch);
|
|
341
|
+
if (Array.isArray(ch)) {
|
|
342
|
+
var inline = [];
|
|
343
|
+
for (var ci = 0; ci < ch.length; ci++) {
|
|
344
|
+
if (typeof ch[ci] === 'string') inline.push(ch[ci]);
|
|
345
|
+
else if (typeof ch[ci] === 'number') inline.push(String(ch[ci]));
|
|
346
|
+
}
|
|
347
|
+
if (inline.length > 0) return inline.join('');
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// No direct text — collect from child fibers (siblings = adjacent elements)
|
|
351
|
+
var parts = [];
|
|
352
|
+
var child = fiber.child;
|
|
353
|
+
while (child) {
|
|
354
|
+
var t = collectText(child, d + 1);
|
|
355
|
+
if (t) parts.push(t);
|
|
356
|
+
child = child.sibling;
|
|
357
|
+
}
|
|
358
|
+
return parts.join(' ').trim();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (componentsOnlyMode) {
|
|
362
|
+
// componentsOnly: walk tree looking for meaningful custom components,
|
|
363
|
+
// measure their first host child, track parent for tree output
|
|
364
|
+
function walkComponents(fiber, depth, path, parentIdx, ancestors) {
|
|
365
|
+
if (!fiber || depth > ${maxDepth}) return;
|
|
366
|
+
var name = getComponentName(fiber);
|
|
367
|
+
var isHost = typeof fiber.type === 'string';
|
|
368
|
+
|
|
369
|
+
// Skip inactive screens (react-native-screens MaybeScreen with active=0)
|
|
370
|
+
// active: 0 = inactive/detached, 1 = transitioning, 2 = active
|
|
371
|
+
if (name === 'MaybeScreen' && fiber.memoizedProps && fiber.memoizedProps.active === 0) return;
|
|
372
|
+
|
|
373
|
+
// Skip unfocused screens in NativeStackNavigator (SceneView with focused=false)
|
|
374
|
+
if (name === 'SceneView' && fiber.memoizedProps && fiber.memoizedProps.focused === false) return;
|
|
375
|
+
|
|
376
|
+
var isMeaningful = name && !isHost && !RN_PRIMITIVES.test(name);
|
|
377
|
+
|
|
378
|
+
var myIdx = parentIdx;
|
|
379
|
+
var nextAncestors = ancestors;
|
|
380
|
+
if (isMeaningful) {
|
|
381
|
+
var host = findFirstHost(fiber, 0);
|
|
382
|
+
if (host) {
|
|
383
|
+
myIdx = hostFibers.length;
|
|
384
|
+
// Extract text from this component's subtree
|
|
385
|
+
var text = collectText(fiber, 0);
|
|
386
|
+
hostFibers.push(host);
|
|
387
|
+
fiberMeta.push({
|
|
388
|
+
hostName: typeof host.type === 'string' ? host.type : '',
|
|
389
|
+
customName: name,
|
|
390
|
+
depth: depth,
|
|
391
|
+
path: path.concat([name]),
|
|
392
|
+
parentIndex: parentIdx,
|
|
393
|
+
ancestorIndices: ancestors.slice(),
|
|
394
|
+
text: text ? text.slice(0, 80) : null
|
|
395
|
+
});
|
|
396
|
+
// Build the ancestor chain for descendants: [myIdx, ...previous ancestors]
|
|
397
|
+
nextAncestors = [myIdx].concat(ancestors);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
var child = fiber.child;
|
|
402
|
+
while (child) {
|
|
403
|
+
var childName = getComponentName(child);
|
|
404
|
+
walkComponents(child, depth + 1, childName ? path.concat([childName]) : path, myIdx, nextAncestors);
|
|
405
|
+
child = child.sibling;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
for (var ri = 0; ri < roots.length; ri++) {
|
|
409
|
+
walkComponents(roots[ri].current, 0, [], -1, []);
|
|
410
|
+
}
|
|
411
|
+
} else {
|
|
412
|
+
// Default mode: collect all host fibers with ancestor info
|
|
413
|
+
function walkFibers(fiber, depth, path) {
|
|
414
|
+
if (!fiber || depth > ${maxDepth}) return;
|
|
415
|
+
var name = getComponentName(fiber);
|
|
416
|
+
var isHost = typeof fiber.type === 'string';
|
|
417
|
+
|
|
418
|
+
// Skip inactive screens (react-native-screens MaybeScreen with active=0)
|
|
419
|
+
if (name === 'MaybeScreen' && fiber.memoizedProps && fiber.memoizedProps.active === 0) return;
|
|
420
|
+
|
|
421
|
+
// Skip unfocused screens in NativeStackNavigator (SceneView with focused=false)
|
|
422
|
+
if (name === 'SceneView' && fiber.memoizedProps && fiber.memoizedProps.focused === false) return;
|
|
423
|
+
|
|
424
|
+
if (name && isHost && getMeasurable(fiber)) {
|
|
425
|
+
// Find nearest meaningful custom component ancestor for display
|
|
426
|
+
var customName = null;
|
|
427
|
+
var fallbackName = null;
|
|
428
|
+
var cur = fiber.return;
|
|
429
|
+
while (cur) {
|
|
430
|
+
if (cur.type && typeof cur.type !== 'string') {
|
|
431
|
+
var cName = cur.type.displayName || cur.type.name || null;
|
|
432
|
+
if (cName) {
|
|
433
|
+
if (!fallbackName) fallbackName = cName;
|
|
434
|
+
if (!RN_PRIMITIVES.test(cName)) {
|
|
435
|
+
customName = cName;
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
cur = cur.return;
|
|
441
|
+
}
|
|
442
|
+
if (!customName) customName = fallbackName;
|
|
443
|
+
|
|
444
|
+
hostFibers.push(fiber);
|
|
445
|
+
fiberMeta.push({
|
|
446
|
+
hostName: name,
|
|
447
|
+
customName: customName,
|
|
448
|
+
depth: depth,
|
|
449
|
+
path: path.slice()
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
var child = fiber.child;
|
|
454
|
+
while (child) {
|
|
455
|
+
var childName = getComponentName(child);
|
|
456
|
+
walkFibers(child, depth + 1, childName ? path.concat([childName]) : path);
|
|
457
|
+
child = child.sibling;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
for (var ri = 0; ri < roots.length; ri++) {
|
|
461
|
+
walkFibers(roots[ri].current, 0, []);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (hostFibers.length === 0) return { error: 'No measurable host components found.' };
|
|
466
|
+
|
|
467
|
+
// Store fibers and metadata globally for step 2
|
|
468
|
+
globalThis.__layoutFibers = hostFibers;
|
|
469
|
+
globalThis.__layoutMeta = fiberMeta;
|
|
470
|
+
globalThis.__layoutMeasurements = new Array(hostFibers.length).fill(null);
|
|
471
|
+
|
|
472
|
+
// Dispatch measureInWindow on all host fibers
|
|
473
|
+
for (var i = 0; i < hostFibers.length; i++) {
|
|
474
|
+
try {
|
|
475
|
+
(function(idx) {
|
|
476
|
+
getMeasurable(hostFibers[idx]).measureInWindow(function(fx, fy, fw, fh) {
|
|
477
|
+
globalThis.__layoutMeasurements[idx] = { x: fx, y: fy, width: fw, height: fh };
|
|
478
|
+
});
|
|
479
|
+
})(i);
|
|
480
|
+
} catch(e) {}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return { count: hostFibers.length };
|
|
484
|
+
})()
|
|
485
|
+
`;
|
|
486
|
+
let dispatchResult = await executeInApp(dispatchExpression, false, { timeoutMs: timeoutMs ?? 30000, originatingToolName: "get_screen_layout" }, device);
|
|
487
|
+
if (!dispatchResult.success)
|
|
488
|
+
return dispatchResult;
|
|
489
|
+
let dispatchError;
|
|
490
|
+
try {
|
|
491
|
+
const parsed = JSON.parse(dispatchResult.result || "{}");
|
|
492
|
+
if (parsed.error)
|
|
493
|
+
dispatchError = parsed.error;
|
|
494
|
+
}
|
|
495
|
+
catch {
|
|
496
|
+
/* ignore */
|
|
497
|
+
}
|
|
498
|
+
// Retry once on the early-startup race where the React DevTools hook
|
|
499
|
+
// isn't registered yet. Production / non-__DEV__ builds will still fail
|
|
500
|
+
// after the retry — surface an actionable error pointing at OCR/screenshot.
|
|
501
|
+
if (dispatchError && /React DevTools hook not found/.test(dispatchError)) {
|
|
502
|
+
await delay(400);
|
|
503
|
+
dispatchResult = await executeInApp(dispatchExpression, false, { timeoutMs: timeoutMs ?? 30000, originatingToolName: "get_screen_layout" }, device);
|
|
504
|
+
if (!dispatchResult.success)
|
|
505
|
+
return dispatchResult;
|
|
506
|
+
dispatchError = undefined;
|
|
507
|
+
try {
|
|
508
|
+
const parsed = JSON.parse(dispatchResult.result || "{}");
|
|
509
|
+
if (parsed.error)
|
|
510
|
+
dispatchError = parsed.error;
|
|
511
|
+
}
|
|
512
|
+
catch {
|
|
513
|
+
/* ignore */
|
|
514
|
+
}
|
|
515
|
+
if (dispatchError && /React DevTools hook not found/.test(dispatchError)) {
|
|
516
|
+
return {
|
|
517
|
+
success: false,
|
|
518
|
+
error: "React DevTools hook not registered (likely a production / non-__DEV__ build). " +
|
|
519
|
+
"Fiber-based layout is unavailable. Use ocr_screenshot for text + tap coordinates, " +
|
|
520
|
+
"or ios_screenshot / android_screenshot for a visual snapshot.",
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (dispatchError)
|
|
525
|
+
return { success: false, error: dispatchError };
|
|
526
|
+
// Wait for measureInWindow callbacks
|
|
527
|
+
await delay(300);
|
|
528
|
+
// --- Step 2: read measurements, filter visible, build results ---
|
|
529
|
+
const resolveExpression = `
|
|
530
|
+
(function() {
|
|
531
|
+
var fibers = globalThis.__layoutFibers;
|
|
532
|
+
var meta = globalThis.__layoutMeta;
|
|
533
|
+
var measurements = globalThis.__layoutMeasurements;
|
|
534
|
+
globalThis.__layoutFibers = null;
|
|
535
|
+
globalThis.__layoutMeta = null;
|
|
536
|
+
globalThis.__layoutMeasurements = null;
|
|
537
|
+
|
|
538
|
+
if (!fibers || !measurements || !meta) {
|
|
539
|
+
return { error: 'No measurement data. Run get_screen_layout again.' };
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
var componentsOnly = ${componentsOnly};
|
|
543
|
+
var shortPath = ${shortPath};
|
|
544
|
+
var summaryMode = ${summary};
|
|
545
|
+
var pathSegments = 3;
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
// Get viewport dimensions from the first root view measurement
|
|
549
|
+
// Accept elements starting at x=0 even with negative y (safe area extensions)
|
|
550
|
+
var viewportW = 9999, viewportH = 9999;
|
|
551
|
+
for (var v = 0; v < measurements.length; v++) {
|
|
552
|
+
if (measurements[v] && measurements[v].x === 0 && measurements[v].y <= 0 &&
|
|
553
|
+
measurements[v].width > 0 && measurements[v].height > 0) {
|
|
554
|
+
viewportW = measurements[v].width;
|
|
555
|
+
// For wrappers extending behind safe area, the visible viewport height
|
|
556
|
+
// is the total height minus the negative offset
|
|
557
|
+
viewportH = measurements[v].height + measurements[v].y;
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function formatPath(pathArray) {
|
|
563
|
+
if (!shortPath || pathArray.length <= pathSegments) {
|
|
564
|
+
return pathArray.join(' > ');
|
|
565
|
+
}
|
|
566
|
+
return '... > ' + pathArray.slice(-pathSegments).join(' > ');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function extractLayoutStyles(style) {
|
|
570
|
+
try {
|
|
571
|
+
if (!style) return null;
|
|
572
|
+
var merged = Array.isArray(style)
|
|
573
|
+
? Object.assign.apply(null, [{}].concat(style.filter(Boolean).map(function(s) {
|
|
574
|
+
try { return typeof s === 'object' ? s : {}; }
|
|
575
|
+
catch(e) { return {}; }
|
|
576
|
+
})))
|
|
577
|
+
: (typeof style === 'object' ? style : {});
|
|
578
|
+
|
|
579
|
+
var layout = {};
|
|
580
|
+
var layoutKeys = [
|
|
581
|
+
'padding', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight',
|
|
582
|
+
'paddingHorizontal', 'paddingVertical',
|
|
583
|
+
'margin', 'marginTop', 'marginBottom', 'marginLeft', 'marginRight',
|
|
584
|
+
'marginHorizontal', 'marginVertical',
|
|
585
|
+
'width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight',
|
|
586
|
+
'flex', 'flexDirection', 'flexWrap', 'flexGrow', 'flexShrink',
|
|
587
|
+
'justifyContent', 'alignItems', 'alignSelf', 'alignContent',
|
|
588
|
+
'position', 'top', 'bottom', 'left', 'right',
|
|
589
|
+
'gap', 'rowGap', 'columnGap',
|
|
590
|
+
'borderWidth', 'borderTopWidth', 'borderBottomWidth', 'borderLeftWidth', 'borderRightWidth',
|
|
591
|
+
'backgroundColor', 'borderColor', 'borderRadius',
|
|
592
|
+
'zIndex', 'elevation'
|
|
593
|
+
];
|
|
594
|
+
|
|
595
|
+
for (var k = 0; k < layoutKeys.length; k++) {
|
|
596
|
+
if (merged[layoutKeys[k]] !== undefined) layout[layoutKeys[k]] = merged[layoutKeys[k]];
|
|
597
|
+
}
|
|
598
|
+
return Object.keys(layout).length > 0 ? layout : null;
|
|
599
|
+
} catch(e) { return null; }
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
var elements = [];
|
|
603
|
+
var offScreenBelow = [];
|
|
604
|
+
var offScreenAbove = [];
|
|
605
|
+
|
|
606
|
+
for (var i = 0; i < measurements.length; i++) {
|
|
607
|
+
var m = measurements[i];
|
|
608
|
+
if (!m) continue;
|
|
609
|
+
|
|
610
|
+
// Filter: zero-size
|
|
611
|
+
if (m.width <= 0 || m.height <= 0) continue;
|
|
612
|
+
|
|
613
|
+
// Vertical: track off-screen above/below for the summary line
|
|
614
|
+
if (m.y + m.height < 0) {
|
|
615
|
+
if (meta[i] && meta[i].customName) offScreenAbove.push({ idx: i, y: m.y });
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
if (m.y > viewportH) {
|
|
619
|
+
if (meta[i] && meta[i].customName) offScreenBelow.push({ idx: i, y: m.y });
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Horizontal: center must be inside the viewport (drops phantom slides at x=-411 etc.)
|
|
624
|
+
var centerX = m.x + m.width / 2;
|
|
625
|
+
if (centerX < 0 || centerX > viewportW) continue;
|
|
626
|
+
|
|
627
|
+
var fiber = fibers[i];
|
|
628
|
+
var info = meta[i];
|
|
629
|
+
|
|
630
|
+
var displayName = componentsOnly ? info.customName : info.hostName;
|
|
631
|
+
if (!displayName) continue;
|
|
632
|
+
if (componentsOnly && !info.customName) continue;
|
|
633
|
+
|
|
634
|
+
var style = null;
|
|
635
|
+
try { style = fiber.memoizedProps ? fiber.memoizedProps.style : null; } catch {}
|
|
636
|
+
var layout = extractLayoutStyles(style);
|
|
637
|
+
|
|
638
|
+
// Get text content — use pre-collected text from step 1, or fall back to host fiber
|
|
639
|
+
var textContent = info.text || null;
|
|
640
|
+
if (!textContent && (info.hostName === 'RCTText' || info.hostName === 'Text')) {
|
|
641
|
+
var children = null;
|
|
642
|
+
try { children = fiber.memoizedProps ? fiber.memoizedProps.children : null; } catch {}
|
|
643
|
+
if (typeof children === 'string') textContent = children;
|
|
644
|
+
else if (typeof children === 'number') textContent = String(children);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
var element = {
|
|
648
|
+
component: displayName,
|
|
649
|
+
path: formatPath(info.path),
|
|
650
|
+
depth: info.depth,
|
|
651
|
+
frame: { x: m.x, y: m.y, width: m.width, height: m.height },
|
|
652
|
+
originalIndex: i
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
if (info.parentIndex !== undefined) element.parentIndex = info.parentIndex;
|
|
656
|
+
if (layout) element.layout = layout;
|
|
657
|
+
if (textContent) element.text = textContent.slice(0, 100);
|
|
658
|
+
|
|
659
|
+
// Identifiers
|
|
660
|
+
if (fiber.memoizedProps) {
|
|
661
|
+
var identifiers = {};
|
|
662
|
+
if (fiber.memoizedProps.testID) identifiers.testID = fiber.memoizedProps.testID;
|
|
663
|
+
if (fiber.memoizedProps.accessibilityLabel) identifiers.accessibilityLabel = fiber.memoizedProps.accessibilityLabel;
|
|
664
|
+
if (fiber.memoizedProps.nativeID) identifiers.nativeID = fiber.memoizedProps.nativeID;
|
|
665
|
+
if (fiber.key) identifiers.key = fiber.key;
|
|
666
|
+
if (Object.keys(identifiers).length > 0) element.identifiers = identifiers;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
elements.push(element);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// In componentsOnly mode, remove full-screen wrapper components
|
|
673
|
+
// and re-parent their children to collapse the wrapper chain
|
|
674
|
+
if (componentsOnly && elements.length > 0) {
|
|
675
|
+
var filtered = [];
|
|
676
|
+
// Map: originalIndex -> resolved parent for skipped wrappers
|
|
677
|
+
var reparent = {};
|
|
678
|
+
|
|
679
|
+
for (var fi = 0; fi < elements.length; fi++) {
|
|
680
|
+
var el = elements[fi];
|
|
681
|
+
var fr = el.frame;
|
|
682
|
+
// A wrapper is full-screen if it covers the entire viewport or extends beyond it
|
|
683
|
+
// (e.g., y=-119 wrappers that extend behind the safe area)
|
|
684
|
+
var isFullScreen = fr && fr.x <= 0 && fr.y <= 0 &&
|
|
685
|
+
(fr.width >= viewportW - 2) && (fr.y + fr.height >= viewportH - 2);
|
|
686
|
+
|
|
687
|
+
if (isFullScreen) {
|
|
688
|
+
// Skip this wrapper, map its originalIndex to its parent
|
|
689
|
+
reparent[el.originalIndex] = el.parentIndex;
|
|
690
|
+
} else {
|
|
691
|
+
// Resolve parent through any skipped wrappers
|
|
692
|
+
var resolvedParent = el.parentIndex;
|
|
693
|
+
while (resolvedParent !== undefined && resolvedParent !== -1 && reparent[resolvedParent] !== undefined) {
|
|
694
|
+
resolvedParent = reparent[resolvedParent];
|
|
695
|
+
}
|
|
696
|
+
el.parentIndex = resolvedParent;
|
|
697
|
+
filtered.push(el);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
elements = filtered;
|
|
701
|
+
|
|
702
|
+
// Second pass: rewrite orphans whose parentIndex points at a non-surviving element.
|
|
703
|
+
// This happens when an intermediate ancestor was dropped by the viewport filter
|
|
704
|
+
// (not the full-screen wrapper path). Walk ancestorIndices to the nearest survivor.
|
|
705
|
+
var surviving = {};
|
|
706
|
+
for (var si = 0; si < elements.length; si++) {
|
|
707
|
+
surviving[elements[si].originalIndex] = true;
|
|
708
|
+
}
|
|
709
|
+
for (var oi = 0; oi < elements.length; oi++) {
|
|
710
|
+
var e2 = elements[oi];
|
|
711
|
+
if (e2.parentIndex === -1 || e2.parentIndex === undefined) continue;
|
|
712
|
+
if (surviving[e2.parentIndex]) continue;
|
|
713
|
+
var anc = meta[e2.originalIndex] && meta[e2.originalIndex].ancestorIndices;
|
|
714
|
+
if (!anc) { e2.parentIndex = -1; continue; }
|
|
715
|
+
var found = -1;
|
|
716
|
+
for (var ai = 0; ai < anc.length; ai++) {
|
|
717
|
+
if (surviving[anc[ai]]) { found = anc[ai]; break; }
|
|
718
|
+
}
|
|
719
|
+
e2.parentIndex = found;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (summaryMode) {
|
|
724
|
+
var counts = {};
|
|
725
|
+
for (var j = 0; j < elements.length; j++) {
|
|
726
|
+
counts[elements[j].component] = (counts[elements[j].component] || 0) + 1;
|
|
727
|
+
}
|
|
728
|
+
var sorted = Object.keys(counts).map(function(name) {
|
|
729
|
+
return { component: name, count: counts[name] };
|
|
730
|
+
}).sort(function(a, b) { return b.count - a.count; });
|
|
731
|
+
return {
|
|
732
|
+
totalElements: elements.length,
|
|
733
|
+
uniqueComponents: sorted.length,
|
|
734
|
+
components: sorted
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return {
|
|
739
|
+
viewport: { width: viewportW, height: viewportH },
|
|
740
|
+
totalElements: elements.length,
|
|
741
|
+
elements: elements,
|
|
742
|
+
offScreenBelow: (function() {
|
|
743
|
+
var seen = {}, out = [];
|
|
744
|
+
offScreenBelow.sort(function(a, b) { return a.y - b.y; });
|
|
745
|
+
for (var k = 0; k < offScreenBelow.length; k++) {
|
|
746
|
+
var n = meta[offScreenBelow[k].idx].customName;
|
|
747
|
+
if (n && !seen[n]) { seen[n] = true; out.push(n); }
|
|
748
|
+
}
|
|
749
|
+
return out;
|
|
750
|
+
})(),
|
|
751
|
+
offScreenAbove: (function() {
|
|
752
|
+
var seen = {}, out = [];
|
|
753
|
+
offScreenAbove.sort(function(a, b) { return b.y - a.y; });
|
|
754
|
+
for (var k = 0; k < offScreenAbove.length; k++) {
|
|
755
|
+
var n = meta[offScreenAbove[k].idx].customName;
|
|
756
|
+
if (n && !seen[n]) { seen[n] = true; out.push(n); }
|
|
757
|
+
}
|
|
758
|
+
return out;
|
|
759
|
+
})()
|
|
760
|
+
};
|
|
761
|
+
})()
|
|
762
|
+
`;
|
|
763
|
+
const result = await executeInApp(resolveExpression, false, { timeoutMs: timeoutMs ?? 30000, originatingToolName: "get_screen_layout" }, device);
|
|
764
|
+
// Format output as tree
|
|
765
|
+
if (result.success && result.result) {
|
|
766
|
+
try {
|
|
767
|
+
const parsed = JSON.parse(result.result);
|
|
768
|
+
if (parsed.components) {
|
|
769
|
+
// Summary mode
|
|
770
|
+
const tonl = formatSummaryToTonl(parsed.components, parsed.totalElements);
|
|
771
|
+
return { success: true, result: tonl };
|
|
772
|
+
}
|
|
773
|
+
else if (parsed.elements) {
|
|
774
|
+
if (raw) {
|
|
775
|
+
return {
|
|
776
|
+
success: true,
|
|
777
|
+
result: result.result,
|
|
778
|
+
parsedElements: parsed.elements,
|
|
779
|
+
viewport: parsed.viewport,
|
|
780
|
+
offScreenBelow: Array.isArray(parsed.offScreenBelow) ? parsed.offScreenBelow : [],
|
|
781
|
+
offScreenAbove: Array.isArray(parsed.offScreenAbove) ? parsed.offScreenAbove : []
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
const tree = formatScreenLayoutTree(parsed.elements, extended, {
|
|
785
|
+
offScreenBelow: Array.isArray(parsed.offScreenBelow) ? parsed.offScreenBelow : [],
|
|
786
|
+
offScreenAbove: Array.isArray(parsed.offScreenAbove) ? parsed.offScreenAbove : []
|
|
787
|
+
});
|
|
788
|
+
return { success: true, result: tree };
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
catch {
|
|
792
|
+
// If parsing fails, return original result
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return result;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Format enriched elements as an indented tree with tap coordinates in pixels.
|
|
799
|
+
* Same tree structure as get_screen_layout but with tap:(x,y) per node.
|
|
800
|
+
*/
|
|
801
|
+
function formatEnrichedLayoutTree(elements, offScreen) {
|
|
802
|
+
const tree = formatLayoutTree(elements, (el, indent, isLeaf, _parent) => {
|
|
803
|
+
const prefix = " ".repeat(indent);
|
|
804
|
+
const frame = `(${Math.round(el.frame.x)},${Math.round(el.frame.y)} ${Math.round(el.frame.width)}x${Math.round(el.frame.height)})`;
|
|
805
|
+
const tap = ` tap:(${el.tapX},${el.tapY})`;
|
|
806
|
+
const id = el.identifiers?.testID || el.identifiers?.accessibilityLabel || "";
|
|
807
|
+
const idStr = id ? ` [${id}]` : "";
|
|
808
|
+
const textStr = el.text && isLeaf ? ` "${el.text}"` : "";
|
|
809
|
+
return `${prefix}${el.component} ${frame}${tap}${idStr}${textStr}`;
|
|
810
|
+
});
|
|
811
|
+
const suffix = [];
|
|
812
|
+
if (offScreen?.offScreenAbove?.length) {
|
|
813
|
+
suffix.push(formatOffScreenLine(offScreen.offScreenAbove, "above fold"));
|
|
814
|
+
}
|
|
815
|
+
if (offScreen?.offScreenBelow?.length) {
|
|
816
|
+
suffix.push(formatOffScreenLine(offScreen.offScreenBelow, "below fold"));
|
|
817
|
+
}
|
|
818
|
+
return suffix.length > 0 ? `${tree}\n\n${suffix.join("\n")}` : tree;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Enrich screen layout data with tap-ready pixel coordinates for bundling with screenshots.
|
|
822
|
+
* Converts points/dp frame coordinates to pixels using the device pixel ratio,
|
|
823
|
+
* computes center-point tapX/tapY for each element.
|
|
824
|
+
*
|
|
825
|
+
* @param pixelRatio - device pixel ratio (e.g., 3 for @3x iPhone)
|
|
826
|
+
* @param screenshotScaleFactor - if the screenshot image was scaled down, this factor adjusts coordinates
|
|
827
|
+
* @param device - optional target device name
|
|
828
|
+
* @returns formatted tree string with pixel coordinates, or null if unavailable
|
|
829
|
+
*/
|
|
830
|
+
export async function enrichScreenshotWithLayout(pixelRatio, screenshotScaleFactor, device, safeAreaTopPoints = 0) {
|
|
831
|
+
try {
|
|
832
|
+
const result = await getScreenLayout({ extended: false, summary: false, device, raw: true });
|
|
833
|
+
if (!result.success || !result.parsedElements || result.parsedElements.length === 0)
|
|
834
|
+
return null;
|
|
835
|
+
const elements = result.parsedElements.map((el) => {
|
|
836
|
+
const frame = el.frame || { x: 0, y: 0, width: 0, height: 0 };
|
|
837
|
+
// React-native-screens modal/sheet presentations on iOS cause measureInWindow to return y
|
|
838
|
+
// relative to the screen's content origin (below safe-area inset), not the window origin.
|
|
839
|
+
// An element whose center y sits inside the safe-area band is physically impossible for a
|
|
840
|
+
// visible interactive target — treat that as a sign of the shifted space and add the inset.
|
|
841
|
+
const centerXPoints = frame.x + frame.width / 2;
|
|
842
|
+
let centerYPoints = frame.y + frame.height / 2;
|
|
843
|
+
let yPoints = frame.y;
|
|
844
|
+
if (safeAreaTopPoints > 0 && centerYPoints < safeAreaTopPoints) {
|
|
845
|
+
centerYPoints += safeAreaTopPoints;
|
|
846
|
+
yPoints += safeAreaTopPoints;
|
|
847
|
+
}
|
|
848
|
+
const tapX = Math.round((centerXPoints * pixelRatio) / screenshotScaleFactor);
|
|
849
|
+
const tapY = Math.round((centerYPoints * pixelRatio) / screenshotScaleFactor);
|
|
850
|
+
const pixelFrame = {
|
|
851
|
+
x: Math.round((frame.x * pixelRatio) / screenshotScaleFactor),
|
|
852
|
+
y: Math.round((yPoints * pixelRatio) / screenshotScaleFactor),
|
|
853
|
+
width: Math.round((frame.width * pixelRatio) / screenshotScaleFactor),
|
|
854
|
+
height: Math.round((frame.height * pixelRatio) / screenshotScaleFactor),
|
|
855
|
+
};
|
|
856
|
+
return {
|
|
857
|
+
component: el.component,
|
|
858
|
+
frame: pixelFrame,
|
|
859
|
+
tapX,
|
|
860
|
+
tapY,
|
|
861
|
+
text: el.text,
|
|
862
|
+
identifiers: el.identifiers,
|
|
863
|
+
parentIndex: el.parentIndex,
|
|
864
|
+
originalIndex: el.originalIndex,
|
|
865
|
+
depth: el.depth,
|
|
866
|
+
path: el.path,
|
|
867
|
+
};
|
|
868
|
+
});
|
|
869
|
+
return formatEnrichedLayoutTree(elements, {
|
|
870
|
+
offScreenBelow: result.offScreenBelow,
|
|
871
|
+
offScreenAbove: result.offScreenAbove,
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
catch {
|
|
875
|
+
return null; // Non-fatal: screenshot works without layout
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
//# sourceMappingURL=screenLayout.js.map
|