react-native-ai-debugger 1.4.40 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/__tests__/integration/tap-strategies.test.js +4 -3
- package/build/__tests__/integration/tap-strategies.test.js.map +1 -1
- package/build/__tests__/unit/executor.test.js +63 -1
- package/build/__tests__/unit/executor.test.js.map +1 -1
- package/build/core/connection.d.ts +14 -0
- package/build/core/connection.d.ts.map +1 -1
- package/build/core/connection.js +49 -0
- package/build/core/connection.js.map +1 -1
- package/build/core/executor.d.ts +9 -1
- package/build/core/executor.d.ts.map +1 -1
- package/build/core/executor.js +380 -42
- package/build/core/executor.js.map +1 -1
- package/build/core/index.d.ts +2 -2
- package/build/core/index.d.ts.map +1 -1
- package/build/core/index.js +2 -2
- package/build/core/index.js.map +1 -1
- package/build/core/ios.d.ts +1 -0
- package/build/core/ios.d.ts.map +1 -1
- package/build/core/ios.js +43 -1
- package/build/core/ios.js.map +1 -1
- package/build/index.js +218 -81
- package/build/index.js.map +1 -1
- package/build/pro/tap.d.ts +8 -0
- package/build/pro/tap.d.ts.map +1 -1
- package/build/pro/tap.js +188 -21
- package/build/pro/tap.js.map +1 -1
- package/package.json +1 -1
package/build/core/executor.js
CHANGED
|
@@ -761,8 +761,8 @@ function formatLayoutTree(elements, renderLine) {
|
|
|
761
761
|
}
|
|
762
762
|
return lines.join("\n");
|
|
763
763
|
}
|
|
764
|
-
function formatScreenLayoutTree(elements, extended = false) {
|
|
765
|
-
|
|
764
|
+
export function formatScreenLayoutTree(elements, extended = false, offScreen) {
|
|
765
|
+
const tree = formatLayoutTree(elements, (el, indent, isLeaf) => {
|
|
766
766
|
const prefix = " ".repeat(indent);
|
|
767
767
|
const frame = el.frame
|
|
768
768
|
? ` (${Math.round(el.frame.x)},${Math.round(el.frame.y)} ${Math.round(el.frame.width)}x${Math.round(el.frame.height)})`
|
|
@@ -775,6 +775,23 @@ function formatScreenLayoutTree(elements, extended = false) {
|
|
|
775
775
|
: "";
|
|
776
776
|
return `${prefix}${el.component}${frame}${idStr}${textStr}${layoutStr}`;
|
|
777
777
|
});
|
|
778
|
+
const suffix = [];
|
|
779
|
+
if (offScreen?.offScreenAbove?.length) {
|
|
780
|
+
suffix.push(formatOffScreenLine(offScreen.offScreenAbove, "above fold"));
|
|
781
|
+
}
|
|
782
|
+
if (offScreen?.offScreenBelow?.length) {
|
|
783
|
+
suffix.push(formatOffScreenLine(offScreen.offScreenBelow, "below fold"));
|
|
784
|
+
}
|
|
785
|
+
return suffix.length > 0 ? `${tree}\n\n${suffix.join("\n")}` : tree;
|
|
786
|
+
}
|
|
787
|
+
function formatOffScreenLine(names, position) {
|
|
788
|
+
const total = names.length;
|
|
789
|
+
const CAP = 10;
|
|
790
|
+
if (total <= CAP) {
|
|
791
|
+
return `[... ${total} component${total === 1 ? "" : "s"} ${position}: ${names.join(", ")}]`;
|
|
792
|
+
}
|
|
793
|
+
const shown = names.slice(0, CAP).join(", ");
|
|
794
|
+
return `[... ${total} components ${position}: ${shown}, ... +${total - CAP} more]`;
|
|
778
795
|
}
|
|
779
796
|
function formatFoundComponentsToTonl(components) {
|
|
780
797
|
const lines = ["#found{component,path,depth,key,layout}"];
|
|
@@ -1075,6 +1092,19 @@ export async function getScreenLayout(options = {}) {
|
|
|
1075
1092
|
typeof sn.canonical.publicInstance.measureInWindow === 'function') {
|
|
1076
1093
|
return sn.canonical.publicInstance;
|
|
1077
1094
|
}
|
|
1095
|
+
// Fabric leaf nodes like RCTText have no publicInstance. Measure via
|
|
1096
|
+
// the native Fabric UIManager using the shadow node instead — needed
|
|
1097
|
+
// for text bounds that are tight around the glyphs (not the scroll
|
|
1098
|
+
// container the text happens to live inside).
|
|
1099
|
+
if (sn.node && globalThis.nativeFabricUIManager &&
|
|
1100
|
+
typeof globalThis.nativeFabricUIManager.measureInWindow === 'function') {
|
|
1101
|
+
var node = sn.node;
|
|
1102
|
+
return {
|
|
1103
|
+
measureInWindow: function(cb) {
|
|
1104
|
+
try { globalThis.nativeFabricUIManager.measureInWindow(node, cb); } catch(e) {}
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1078
1108
|
return null;
|
|
1079
1109
|
}
|
|
1080
1110
|
|
|
@@ -1086,7 +1116,7 @@ export async function getScreenLayout(options = {}) {
|
|
|
1086
1116
|
// RN internals and primitives — skip these when looking for meaningful component names
|
|
1087
1117
|
// Only filter internal components that can't be written as JSX.
|
|
1088
1118
|
// Keep all user-facing components (View, Text, ScrollView, Modal, etc.)
|
|
1089
|
-
var RN_PRIMITIVES = /^(Animated\\(.*|withAnimated.*|AnimatedComponent.*|ForwardRef.*|memo\\(.*|Context\\.Consumer|Context\\.Provider|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)$/;
|
|
1119
|
+
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)$/;
|
|
1090
1120
|
|
|
1091
1121
|
function getComponentName(fiber) {
|
|
1092
1122
|
if (!fiber || !fiber.type) return null;
|
|
@@ -1141,7 +1171,7 @@ export async function getScreenLayout(options = {}) {
|
|
|
1141
1171
|
if (componentsOnlyMode) {
|
|
1142
1172
|
// componentsOnly: walk tree looking for meaningful custom components,
|
|
1143
1173
|
// measure their first host child, track parent for tree output
|
|
1144
|
-
function walkComponents(fiber, depth, path, parentIdx) {
|
|
1174
|
+
function walkComponents(fiber, depth, path, parentIdx, ancestors) {
|
|
1145
1175
|
if (!fiber || depth > ${maxDepth}) return;
|
|
1146
1176
|
var name = getComponentName(fiber);
|
|
1147
1177
|
var isHost = typeof fiber.type === 'string';
|
|
@@ -1156,6 +1186,7 @@ export async function getScreenLayout(options = {}) {
|
|
|
1156
1186
|
var isMeaningful = name && !isHost && !RN_PRIMITIVES.test(name);
|
|
1157
1187
|
|
|
1158
1188
|
var myIdx = parentIdx;
|
|
1189
|
+
var nextAncestors = ancestors;
|
|
1159
1190
|
if (isMeaningful) {
|
|
1160
1191
|
var host = findFirstHost(fiber, 0);
|
|
1161
1192
|
if (host) {
|
|
@@ -1169,19 +1200,24 @@ export async function getScreenLayout(options = {}) {
|
|
|
1169
1200
|
depth: depth,
|
|
1170
1201
|
path: path.concat([name]),
|
|
1171
1202
|
parentIndex: parentIdx,
|
|
1203
|
+
ancestorIndices: ancestors.slice(),
|
|
1172
1204
|
text: text ? text.slice(0, 80) : null
|
|
1173
1205
|
});
|
|
1206
|
+
// Build the ancestor chain for descendants: [myIdx, ...previous ancestors]
|
|
1207
|
+
nextAncestors = [myIdx].concat(ancestors);
|
|
1174
1208
|
}
|
|
1175
1209
|
}
|
|
1176
1210
|
|
|
1177
1211
|
var child = fiber.child;
|
|
1178
1212
|
while (child) {
|
|
1179
1213
|
var childName = getComponentName(child);
|
|
1180
|
-
walkComponents(child, depth + 1, childName ? path.concat([childName]) : path, myIdx);
|
|
1214
|
+
walkComponents(child, depth + 1, childName ? path.concat([childName]) : path, myIdx, nextAncestors);
|
|
1181
1215
|
child = child.sibling;
|
|
1182
1216
|
}
|
|
1183
1217
|
}
|
|
1184
|
-
|
|
1218
|
+
for (var ri = 0; ri < roots.length; ri++) {
|
|
1219
|
+
walkComponents(roots[ri].current, 0, [], -1, []);
|
|
1220
|
+
}
|
|
1185
1221
|
} else {
|
|
1186
1222
|
// Default mode: collect all host fibers with ancestor info
|
|
1187
1223
|
function walkFibers(fiber, depth, path) {
|
|
@@ -1231,7 +1267,9 @@ export async function getScreenLayout(options = {}) {
|
|
|
1231
1267
|
child = child.sibling;
|
|
1232
1268
|
}
|
|
1233
1269
|
}
|
|
1234
|
-
|
|
1270
|
+
for (var ri = 0; ri < roots.length; ri++) {
|
|
1271
|
+
walkFibers(roots[ri].current, 0, []);
|
|
1272
|
+
}
|
|
1235
1273
|
}
|
|
1236
1274
|
|
|
1237
1275
|
if (hostFibers.length === 0) return { error: 'No measurable host components found.' };
|
|
@@ -1343,15 +1381,29 @@ export async function getScreenLayout(options = {}) {
|
|
|
1343
1381
|
}
|
|
1344
1382
|
|
|
1345
1383
|
var elements = [];
|
|
1384
|
+
var offScreenBelow = [];
|
|
1385
|
+
var offScreenAbove = [];
|
|
1346
1386
|
|
|
1347
1387
|
for (var i = 0; i < measurements.length; i++) {
|
|
1348
1388
|
var m = measurements[i];
|
|
1349
1389
|
if (!m) continue;
|
|
1350
1390
|
|
|
1351
|
-
// Filter:
|
|
1391
|
+
// Filter: zero-size
|
|
1352
1392
|
if (m.width <= 0 || m.height <= 0) continue;
|
|
1353
|
-
|
|
1354
|
-
|
|
1393
|
+
|
|
1394
|
+
// Vertical: track off-screen above/below for the summary line
|
|
1395
|
+
if (m.y + m.height < 0) {
|
|
1396
|
+
if (meta[i] && meta[i].customName) offScreenAbove.push({ idx: i, y: m.y });
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
if (m.y > viewportH) {
|
|
1400
|
+
if (meta[i] && meta[i].customName) offScreenBelow.push({ idx: i, y: m.y });
|
|
1401
|
+
continue;
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// Horizontal: center must be inside the viewport (drops phantom slides at x=-411 etc.)
|
|
1405
|
+
var centerX = m.x + m.width / 2;
|
|
1406
|
+
if (centerX < 0 || centerX > viewportW) continue;
|
|
1355
1407
|
|
|
1356
1408
|
var fiber = fibers[i];
|
|
1357
1409
|
var info = meta[i];
|
|
@@ -1427,6 +1479,26 @@ export async function getScreenLayout(options = {}) {
|
|
|
1427
1479
|
}
|
|
1428
1480
|
}
|
|
1429
1481
|
elements = filtered;
|
|
1482
|
+
|
|
1483
|
+
// Second pass: rewrite orphans whose parentIndex points at a non-surviving element.
|
|
1484
|
+
// This happens when an intermediate ancestor was dropped by the viewport filter
|
|
1485
|
+
// (not the full-screen wrapper path). Walk ancestorIndices to the nearest survivor.
|
|
1486
|
+
var surviving = {};
|
|
1487
|
+
for (var si = 0; si < elements.length; si++) {
|
|
1488
|
+
surviving[elements[si].originalIndex] = true;
|
|
1489
|
+
}
|
|
1490
|
+
for (var oi = 0; oi < elements.length; oi++) {
|
|
1491
|
+
var e2 = elements[oi];
|
|
1492
|
+
if (e2.parentIndex === -1 || e2.parentIndex === undefined) continue;
|
|
1493
|
+
if (surviving[e2.parentIndex]) continue;
|
|
1494
|
+
var anc = meta[e2.originalIndex] && meta[e2.originalIndex].ancestorIndices;
|
|
1495
|
+
if (!anc) { e2.parentIndex = -1; continue; }
|
|
1496
|
+
var found = -1;
|
|
1497
|
+
for (var ai = 0; ai < anc.length; ai++) {
|
|
1498
|
+
if (surviving[anc[ai]]) { found = anc[ai]; break; }
|
|
1499
|
+
}
|
|
1500
|
+
e2.parentIndex = found;
|
|
1501
|
+
}
|
|
1430
1502
|
}
|
|
1431
1503
|
|
|
1432
1504
|
if (summaryMode) {
|
|
@@ -1447,7 +1519,25 @@ export async function getScreenLayout(options = {}) {
|
|
|
1447
1519
|
return {
|
|
1448
1520
|
viewport: { width: viewportW, height: viewportH },
|
|
1449
1521
|
totalElements: elements.length,
|
|
1450
|
-
elements: elements
|
|
1522
|
+
elements: elements,
|
|
1523
|
+
offScreenBelow: (function() {
|
|
1524
|
+
var seen = {}, out = [];
|
|
1525
|
+
offScreenBelow.sort(function(a, b) { return a.y - b.y; });
|
|
1526
|
+
for (var k = 0; k < offScreenBelow.length; k++) {
|
|
1527
|
+
var n = meta[offScreenBelow[k].idx].customName;
|
|
1528
|
+
if (n && !seen[n]) { seen[n] = true; out.push(n); }
|
|
1529
|
+
}
|
|
1530
|
+
return out;
|
|
1531
|
+
})(),
|
|
1532
|
+
offScreenAbove: (function() {
|
|
1533
|
+
var seen = {}, out = [];
|
|
1534
|
+
offScreenAbove.sort(function(a, b) { return b.y - a.y; });
|
|
1535
|
+
for (var k = 0; k < offScreenAbove.length; k++) {
|
|
1536
|
+
var n = meta[offScreenAbove[k].idx].customName;
|
|
1537
|
+
if (n && !seen[n]) { seen[n] = true; out.push(n); }
|
|
1538
|
+
}
|
|
1539
|
+
return out;
|
|
1540
|
+
})()
|
|
1451
1541
|
};
|
|
1452
1542
|
})()
|
|
1453
1543
|
`;
|
|
@@ -1467,10 +1557,15 @@ export async function getScreenLayout(options = {}) {
|
|
|
1467
1557
|
success: true,
|
|
1468
1558
|
result: result.result,
|
|
1469
1559
|
parsedElements: parsed.elements,
|
|
1470
|
-
viewport: parsed.viewport
|
|
1560
|
+
viewport: parsed.viewport,
|
|
1561
|
+
offScreenBelow: Array.isArray(parsed.offScreenBelow) ? parsed.offScreenBelow : [],
|
|
1562
|
+
offScreenAbove: Array.isArray(parsed.offScreenAbove) ? parsed.offScreenAbove : []
|
|
1471
1563
|
};
|
|
1472
1564
|
}
|
|
1473
|
-
const tree = formatScreenLayoutTree(parsed.elements, extended
|
|
1565
|
+
const tree = formatScreenLayoutTree(parsed.elements, extended, {
|
|
1566
|
+
offScreenBelow: Array.isArray(parsed.offScreenBelow) ? parsed.offScreenBelow : [],
|
|
1567
|
+
offScreenAbove: Array.isArray(parsed.offScreenAbove) ? parsed.offScreenAbove : []
|
|
1568
|
+
});
|
|
1474
1569
|
return { success: true, result: tree };
|
|
1475
1570
|
}
|
|
1476
1571
|
}
|
|
@@ -1508,6 +1603,19 @@ export async function getPressableElements(options = {}) {
|
|
|
1508
1603
|
typeof sn.canonical.publicInstance.measureInWindow === 'function') {
|
|
1509
1604
|
return sn.canonical.publicInstance;
|
|
1510
1605
|
}
|
|
1606
|
+
// Fabric leaf nodes like RCTText have no publicInstance. Measure via
|
|
1607
|
+
// the native Fabric UIManager using the shadow node instead — needed
|
|
1608
|
+
// for text bounds that are tight around the glyphs (not the scroll
|
|
1609
|
+
// container the text happens to live inside).
|
|
1610
|
+
if (sn.node && globalThis.nativeFabricUIManager &&
|
|
1611
|
+
typeof globalThis.nativeFabricUIManager.measureInWindow === 'function') {
|
|
1612
|
+
var node = sn.node;
|
|
1613
|
+
return {
|
|
1614
|
+
measureInWindow: function(cb) {
|
|
1615
|
+
try { globalThis.nativeFabricUIManager.measureInWindow(node, cb); } catch(e) {}
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1511
1619
|
return null;
|
|
1512
1620
|
}
|
|
1513
1621
|
|
|
@@ -1574,10 +1682,12 @@ export async function getPressableElements(options = {}) {
|
|
|
1574
1682
|
return fiber.type.displayName || fiber.type.name || null;
|
|
1575
1683
|
}
|
|
1576
1684
|
|
|
1577
|
-
var RN_PRIMITIVES = /^(Animated\\(.*|withAnimated.*|AnimatedComponent.*|ForwardRef.*|memo\\(.*|Context\\.Consumer|Context\\.Provider|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)$/;
|
|
1685
|
+
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)$/;
|
|
1578
1686
|
|
|
1579
1687
|
var hostFibers = [];
|
|
1580
1688
|
var fiberMeta = [];
|
|
1689
|
+
var textFibers = [];
|
|
1690
|
+
var textContents = [];
|
|
1581
1691
|
|
|
1582
1692
|
function findMeaningfulAncestorName(fiber) {
|
|
1583
1693
|
var cur = fiber.return;
|
|
@@ -1595,6 +1705,30 @@ export async function getPressableElements(options = {}) {
|
|
|
1595
1705
|
return fallbackName;
|
|
1596
1706
|
}
|
|
1597
1707
|
|
|
1708
|
+
// Layout-only and touch-wrapper components that should be skipped when scanning
|
|
1709
|
+
// children for a meaningful icon/content component name.
|
|
1710
|
+
var SKIP_IN_CHILD_SCAN = /^(View|Text|Image|ImageBackground|ScrollView|FlatList|SectionList|KeyboardAvoidingView|SafeAreaView|TouchableOpacity|TouchableHighlight|TouchableWithoutFeedback|TouchableNativeFeedback|Pressable|TextInput|ActivityIndicator|Switch|Modal|StatusBar|VirtualizedList|RefreshControl|Animated\\(.*|withAnimated.*|AnimatedComponent.*)$/;
|
|
1711
|
+
|
|
1712
|
+
// Scan children of a pressable for a meaningful component name (e.g. SvgChevronBack inside a View).
|
|
1713
|
+
// Skips generic layout/touch wrappers to find the actual icon or content component.
|
|
1714
|
+
function findMeaningfulChildName(fiber) {
|
|
1715
|
+
function scan(f, d) {
|
|
1716
|
+
if (!f || d > 12) return null;
|
|
1717
|
+
var n = getComponentName(f);
|
|
1718
|
+
if (n && typeof f.type !== 'string' && !RN_PRIMITIVES.test(n) && !SKIP_IN_CHILD_SCAN.test(n)) return n;
|
|
1719
|
+
var c = f.child;
|
|
1720
|
+
while (c) {
|
|
1721
|
+
var r = scan(c, d + 1);
|
|
1722
|
+
if (r) return r;
|
|
1723
|
+
c = c.sibling;
|
|
1724
|
+
}
|
|
1725
|
+
return null;
|
|
1726
|
+
}
|
|
1727
|
+
return scan(fiber.child, 0);
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
var GENERIC_COMPONENT = /^(View|TouchableOpacity|TouchableHighlight|TouchableWithoutFeedback|Pressable|TouchableNativeFeedback|Text|RCTView|RCTText|Unknown)$/;
|
|
1731
|
+
|
|
1598
1732
|
function buildPath(fiber) {
|
|
1599
1733
|
var parts = [];
|
|
1600
1734
|
var cur = fiber;
|
|
@@ -1634,6 +1768,12 @@ export async function getPressableElements(options = {}) {
|
|
|
1634
1768
|
if (hostsForThis.length > 0) {
|
|
1635
1769
|
var text = collectText(fiber, 0, true);
|
|
1636
1770
|
var componentName = findMeaningfulAncestorName(fiber) || name || 'Unknown';
|
|
1771
|
+
// If name is generic (e.g. View, TouchableOpacity), scan children for a
|
|
1772
|
+
// meaningful name like SvgChevronBack so icon-only buttons are identifiable.
|
|
1773
|
+
if (GENERIC_COMPONENT.test(componentName)) {
|
|
1774
|
+
var childName = findMeaningfulChildName(fiber);
|
|
1775
|
+
if (childName) componentName = childName;
|
|
1776
|
+
}
|
|
1637
1777
|
var path = buildPath(fiber);
|
|
1638
1778
|
var testID = (props && (props.testID || props.nativeID)) || null;
|
|
1639
1779
|
var accessibilityLabel = (props && props.accessibilityLabel) || null;
|
|
@@ -1664,6 +1804,76 @@ export async function getPressableElements(options = {}) {
|
|
|
1664
1804
|
|
|
1665
1805
|
walkPressables(roots[0].current, 0);
|
|
1666
1806
|
|
|
1807
|
+
// Collect text fibers that are NOT inside a pressable — used later to attach
|
|
1808
|
+
// spatial 'nearbyText' hints to icon-only pressables.
|
|
1809
|
+
function extractTextString(fiber) {
|
|
1810
|
+
var p = fiber.memoizedProps;
|
|
1811
|
+
if (!p) return '';
|
|
1812
|
+
var ch = p.children;
|
|
1813
|
+
if (typeof ch === 'string') return ch;
|
|
1814
|
+
if (typeof ch === 'number') return String(ch);
|
|
1815
|
+
if (Array.isArray(ch)) {
|
|
1816
|
+
var parts = [];
|
|
1817
|
+
for (var k = 0; k < ch.length; k++) {
|
|
1818
|
+
if (typeof ch[k] === 'string') parts.push(ch[k]);
|
|
1819
|
+
else if (typeof ch[k] === 'number') parts.push(String(ch[k]));
|
|
1820
|
+
}
|
|
1821
|
+
if (parts.length > 0) return parts.join('');
|
|
1822
|
+
}
|
|
1823
|
+
return '';
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
function walkTexts(fiber, depth, insidePressable, inHidden) {
|
|
1827
|
+
if (!fiber || depth > 5000) return;
|
|
1828
|
+
var name = getComponentName(fiber);
|
|
1829
|
+
var props = fiber.memoizedProps;
|
|
1830
|
+
|
|
1831
|
+
// Track hidden-screen ancestry without early return — a MaybeScreen wrapper
|
|
1832
|
+
// can enclose BOTH the active and inactive siblings, so we must keep walking
|
|
1833
|
+
// to find the active one while still skipping the inactive subtree's texts.
|
|
1834
|
+
var nextHidden = inHidden;
|
|
1835
|
+
if (name === 'MaybeScreen' && props && props.active === 0) nextHidden = true;
|
|
1836
|
+
if (name === 'SceneView' && props && props.focused === false) nextHidden = true;
|
|
1837
|
+
if (name === 'RNSScreen' && props && props['aria-hidden'] === true) nextHidden = true;
|
|
1838
|
+
|
|
1839
|
+
var hasOnPress = props && typeof props.onPress === 'function';
|
|
1840
|
+
var isInputHere = props && (typeof props.onChangeText === 'function' || typeof props.onFocus === 'function');
|
|
1841
|
+
var nextInside = insidePressable || !!hasOnPress || !!isInputHere;
|
|
1842
|
+
|
|
1843
|
+
// Record standalone text when outside any pressable. Detects both stock
|
|
1844
|
+
// Text and custom wrappers (CustomText, ThemedText, etc.) by checking for
|
|
1845
|
+
// direct string/number children. Fabric RCTText has no publicInstance, so
|
|
1846
|
+
// we walk up the fiber chain to the nearest measurable host (the View
|
|
1847
|
+
// enclosing the text) and use its frame as the text's proxy bounds.
|
|
1848
|
+
if (!insidePressable && !nextHidden && name !== 'RCTText' && typeof fiber.type !== 'string') {
|
|
1849
|
+
var str = extractTextString(fiber);
|
|
1850
|
+
if (str && str.length > 0 && str.length <= 120) {
|
|
1851
|
+
var up = fiber;
|
|
1852
|
+
var upDepth = 0;
|
|
1853
|
+
var measurable = null;
|
|
1854
|
+
while (up && upDepth < 20) {
|
|
1855
|
+
if (typeof up.type === 'string' && getMeasurable(up)) {
|
|
1856
|
+
measurable = up;
|
|
1857
|
+
break;
|
|
1858
|
+
}
|
|
1859
|
+
up = up.return;
|
|
1860
|
+
upDepth++;
|
|
1861
|
+
}
|
|
1862
|
+
if (measurable) {
|
|
1863
|
+
textFibers.push(measurable);
|
|
1864
|
+
textContents.push(str);
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
var child = fiber.child;
|
|
1870
|
+
while (child) {
|
|
1871
|
+
walkTexts(child, depth + 1, nextInside, nextHidden);
|
|
1872
|
+
child = child.sibling;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
walkTexts(roots[0].current, 0, false, false);
|
|
1876
|
+
|
|
1667
1877
|
if (hostFibers.length === 0) return { error: 'No pressable elements found on screen.' };
|
|
1668
1878
|
|
|
1669
1879
|
// Also measure the root view for viewport detection (appended; tracked by explicit index to preserve hostIndices)
|
|
@@ -1689,7 +1899,19 @@ export async function getPressableElements(options = {}) {
|
|
|
1689
1899
|
} catch(e) {}
|
|
1690
1900
|
}
|
|
1691
1901
|
|
|
1692
|
-
|
|
1902
|
+
globalThis.__pressableTextContents = textContents;
|
|
1903
|
+
globalThis.__pressableTextMeasurements = new Array(textFibers.length).fill(null);
|
|
1904
|
+
for (var ti = 0; ti < textFibers.length; ti++) {
|
|
1905
|
+
try {
|
|
1906
|
+
(function(idx) {
|
|
1907
|
+
getMeasurable(textFibers[idx]).measureInWindow(function(fx, fy, fw, fh) {
|
|
1908
|
+
globalThis.__pressableTextMeasurements[idx] = { x: fx, y: fy, width: fw, height: fh };
|
|
1909
|
+
});
|
|
1910
|
+
})(ti);
|
|
1911
|
+
} catch(e) {}
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
return { count: hostFibers.length, textCount: textFibers.length };
|
|
1693
1915
|
})()
|
|
1694
1916
|
`;
|
|
1695
1917
|
const dispatchResult = await executeInApp(dispatchExpression, false, { timeoutMs: 30000 }, device);
|
|
@@ -1712,10 +1934,14 @@ export async function getPressableElements(options = {}) {
|
|
|
1712
1934
|
var meta = globalThis.__pressableMeta;
|
|
1713
1935
|
var measurements = globalThis.__pressableMeasurements;
|
|
1714
1936
|
var rootIdx = globalThis.__pressableRootIdx;
|
|
1937
|
+
var textContents = globalThis.__pressableTextContents || [];
|
|
1938
|
+
var textMeasurements = globalThis.__pressableTextMeasurements || [];
|
|
1715
1939
|
globalThis.__pressableFibers = null;
|
|
1716
1940
|
globalThis.__pressableMeta = null;
|
|
1717
1941
|
globalThis.__pressableMeasurements = null;
|
|
1718
1942
|
globalThis.__pressableRootIdx = null;
|
|
1943
|
+
globalThis.__pressableTextContents = null;
|
|
1944
|
+
globalThis.__pressableTextMeasurements = null;
|
|
1719
1945
|
|
|
1720
1946
|
if (!fibers || !measurements || !meta) {
|
|
1721
1947
|
return { error: 'No measurement data. Run get_pressable_elements again.' };
|
|
@@ -1854,6 +2080,65 @@ export async function getPressableElements(options = {}) {
|
|
|
1854
2080
|
}
|
|
1855
2081
|
}
|
|
1856
2082
|
|
|
2083
|
+
// Build a normalized list of visible standalone texts (content + frame)
|
|
2084
|
+
var textBoxes = [];
|
|
2085
|
+
for (var tmi = 0; tmi < textMeasurements.length; tmi++) {
|
|
2086
|
+
var tm = textMeasurements[tmi];
|
|
2087
|
+
var tc = textContents[tmi];
|
|
2088
|
+
if (!tm || !tc) continue;
|
|
2089
|
+
if (tm.width <= 0 || tm.height <= 0) continue;
|
|
2090
|
+
if (tm.x + tm.width < 0 || tm.y + tm.height < 0) continue;
|
|
2091
|
+
if (tm.x > viewportW || tm.y > viewportH) continue;
|
|
2092
|
+
textBoxes.push({
|
|
2093
|
+
text: tc,
|
|
2094
|
+
x: tm.x, y: tm.y, width: tm.width, height: tm.height,
|
|
2095
|
+
cx: tm.x + tm.width / 2, cy: tm.y + tm.height / 2
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
// Humanize component name → intent (strip Svg/Icon prefixes, split camelCase)
|
|
2100
|
+
function humanize(name) {
|
|
2101
|
+
if (!name) return '';
|
|
2102
|
+
var n = String(name);
|
|
2103
|
+
n = n.replace(/^(Svg|Icon)/, '').replace(/(Svg|Icon)$/, '');
|
|
2104
|
+
n = n.replace(/([a-z0-9])([A-Z])/g, '$1 $2');
|
|
2105
|
+
n = n.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2');
|
|
2106
|
+
return n.trim().toLowerCase();
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
// Attach nearbyText for pressables that lack their own text/label.
|
|
2110
|
+
// Nearest text by center-to-center distance, within a reasonable radius.
|
|
2111
|
+
var NEARBY_RADIUS = 120; // points
|
|
2112
|
+
for (var ei = 0; ei < elements.length; ei++) {
|
|
2113
|
+
var pe = elements[ei];
|
|
2114
|
+
pe.intent = humanize(pe.component);
|
|
2115
|
+
// Skip when the pressable already has its own human-readable text or a11y label.
|
|
2116
|
+
// testID alone doesn't suppress nearbyText — the testID may be cryptic and the
|
|
2117
|
+
// surrounding visible text is still a useful handle for an agent.
|
|
2118
|
+
if (pe.hasLabel || pe.accessibilityLabel) continue;
|
|
2119
|
+
if (textBoxes.length === 0) continue;
|
|
2120
|
+
|
|
2121
|
+
var pcx = pe.frame.x + pe.frame.width / 2;
|
|
2122
|
+
var pcy = pe.frame.y + pe.frame.height / 2;
|
|
2123
|
+
var best = null;
|
|
2124
|
+
var bestDist = Infinity;
|
|
2125
|
+
for (var tbi = 0; tbi < textBoxes.length; tbi++) {
|
|
2126
|
+
var tb = textBoxes[tbi];
|
|
2127
|
+
// Skip texts contained inside this pressable (shouldn't happen — walkTexts excludes them — but defensive)
|
|
2128
|
+
if (tb.x >= pe.frame.x && tb.y >= pe.frame.y &&
|
|
2129
|
+
tb.x + tb.width <= pe.frame.x + pe.frame.width &&
|
|
2130
|
+
tb.y + tb.height <= pe.frame.y + pe.frame.height) continue;
|
|
2131
|
+
var dx = tb.cx - pcx;
|
|
2132
|
+
var dy = tb.cy - pcy;
|
|
2133
|
+
var d = Math.sqrt(dx * dx + dy * dy);
|
|
2134
|
+
if (d < bestDist && d <= NEARBY_RADIUS) {
|
|
2135
|
+
bestDist = d;
|
|
2136
|
+
best = tb;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
if (best) pe.nearbyText = best.text;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
1857
2142
|
// Sort top-to-bottom, left-to-right
|
|
1858
2143
|
elements.sort(function(a, b) {
|
|
1859
2144
|
if (a.center.y !== b.center.y) return a.center.y - b.center.y;
|
|
@@ -1885,7 +2170,11 @@ export async function getPressableElements(options = {}) {
|
|
|
1885
2170
|
for (let i = 0; i < pressableElements.length; i++) {
|
|
1886
2171
|
const el = pressableElements[i];
|
|
1887
2172
|
const num = i + 1;
|
|
1888
|
-
const label = el.hasLabel
|
|
2173
|
+
const label = el.hasLabel
|
|
2174
|
+
? `"${el.text}"`
|
|
2175
|
+
: el.intent
|
|
2176
|
+
? `(${el.intent} icon)`
|
|
2177
|
+
: "(icon/image)";
|
|
1889
2178
|
const ids = [];
|
|
1890
2179
|
if (el.testID)
|
|
1891
2180
|
ids.push(`testID="${el.testID}"`);
|
|
@@ -1894,7 +2183,8 @@ export async function getPressableElements(options = {}) {
|
|
|
1894
2183
|
const idStr = ids.length > 0 ? ` [${ids.join(", ")}]` : "";
|
|
1895
2184
|
const inputStr = el.isInput ? " (input)" : "";
|
|
1896
2185
|
const wrapperStr = el.isWrapper ? " [wrapper — skip unless dismissing keyboard]" : "";
|
|
1897
|
-
|
|
2186
|
+
const nearPart = el.nearbyText ? ` near "${el.nearbyText}"` : "";
|
|
2187
|
+
lines.push(`${num}. ${el.component} ${label}${nearPart} — center:(${el.center.x},${el.center.y}) frame:(${el.frame.x},${el.frame.y} ${el.frame.width}x${el.frame.height})${idStr}${inputStr}${wrapperStr}`);
|
|
1898
2188
|
if (el.path)
|
|
1899
2189
|
lines.push(` path: ${el.path}`);
|
|
1900
2190
|
}
|
|
@@ -1914,8 +2204,8 @@ export async function getPressableElements(options = {}) {
|
|
|
1914
2204
|
* Format enriched elements as an indented tree with tap coordinates in pixels.
|
|
1915
2205
|
* Same tree structure as get_screen_layout but with tap:(x,y) per node.
|
|
1916
2206
|
*/
|
|
1917
|
-
function formatEnrichedLayoutTree(elements) {
|
|
1918
|
-
|
|
2207
|
+
function formatEnrichedLayoutTree(elements, offScreen) {
|
|
2208
|
+
const tree = formatLayoutTree(elements, (el, indent, isLeaf) => {
|
|
1919
2209
|
const prefix = " ".repeat(indent);
|
|
1920
2210
|
const frame = `(${Math.round(el.frame.x)},${Math.round(el.frame.y)} ${Math.round(el.frame.width)}x${Math.round(el.frame.height)})`;
|
|
1921
2211
|
const tap = ` tap:(${el.tapX},${el.tapY})`;
|
|
@@ -1924,6 +2214,14 @@ function formatEnrichedLayoutTree(elements) {
|
|
|
1924
2214
|
const textStr = el.text && isLeaf ? ` "${el.text}"` : "";
|
|
1925
2215
|
return `${prefix}${el.component} ${frame}${tap}${idStr}${textStr}`;
|
|
1926
2216
|
});
|
|
2217
|
+
const suffix = [];
|
|
2218
|
+
if (offScreen?.offScreenAbove?.length) {
|
|
2219
|
+
suffix.push(formatOffScreenLine(offScreen.offScreenAbove, "above fold"));
|
|
2220
|
+
}
|
|
2221
|
+
if (offScreen?.offScreenBelow?.length) {
|
|
2222
|
+
suffix.push(formatOffScreenLine(offScreen.offScreenBelow, "below fold"));
|
|
2223
|
+
}
|
|
2224
|
+
return suffix.length > 0 ? `${tree}\n\n${suffix.join("\n")}` : tree;
|
|
1927
2225
|
}
|
|
1928
2226
|
/**
|
|
1929
2227
|
* Enrich screen layout data with tap-ready pixel coordinates for bundling with screenshots.
|
|
@@ -1935,24 +2233,29 @@ function formatEnrichedLayoutTree(elements) {
|
|
|
1935
2233
|
* @param device - optional target device name
|
|
1936
2234
|
* @returns formatted tree string with pixel coordinates, or null if unavailable
|
|
1937
2235
|
*/
|
|
1938
|
-
export async function enrichScreenshotWithLayout(pixelRatio, screenshotScaleFactor, device) {
|
|
2236
|
+
export async function enrichScreenshotWithLayout(pixelRatio, screenshotScaleFactor, device, safeAreaTopPoints = 0) {
|
|
1939
2237
|
try {
|
|
1940
2238
|
const result = await getScreenLayout({ extended: false, summary: false, device, raw: true });
|
|
1941
2239
|
if (!result.success || !result.parsedElements || result.parsedElements.length === 0)
|
|
1942
2240
|
return null;
|
|
1943
2241
|
const elements = result.parsedElements.map((el) => {
|
|
1944
2242
|
const frame = el.frame || { x: 0, y: 0, width: 0, height: 0 };
|
|
1945
|
-
//
|
|
1946
|
-
//
|
|
1947
|
-
//
|
|
2243
|
+
// React-native-screens modal/sheet presentations on iOS cause measureInWindow to return y
|
|
2244
|
+
// relative to the screen's content origin (below safe-area inset), not the window origin.
|
|
2245
|
+
// An element whose center y sits inside the safe-area band is physically impossible for a
|
|
2246
|
+
// visible interactive target — treat that as a sign of the shifted space and add the inset.
|
|
1948
2247
|
const centerXPoints = frame.x + frame.width / 2;
|
|
1949
|
-
|
|
2248
|
+
let centerYPoints = frame.y + frame.height / 2;
|
|
2249
|
+
let yPoints = frame.y;
|
|
2250
|
+
if (safeAreaTopPoints > 0 && centerYPoints < safeAreaTopPoints) {
|
|
2251
|
+
centerYPoints += safeAreaTopPoints;
|
|
2252
|
+
yPoints += safeAreaTopPoints;
|
|
2253
|
+
}
|
|
1950
2254
|
const tapX = Math.round((centerXPoints * pixelRatio) / screenshotScaleFactor);
|
|
1951
2255
|
const tapY = Math.round((centerYPoints * pixelRatio) / screenshotScaleFactor);
|
|
1952
|
-
// Convert frame to pixels too
|
|
1953
2256
|
const pixelFrame = {
|
|
1954
2257
|
x: Math.round((frame.x * pixelRatio) / screenshotScaleFactor),
|
|
1955
|
-
y: Math.round((
|
|
2258
|
+
y: Math.round((yPoints * pixelRatio) / screenshotScaleFactor),
|
|
1956
2259
|
width: Math.round((frame.width * pixelRatio) / screenshotScaleFactor),
|
|
1957
2260
|
height: Math.round((frame.height * pixelRatio) / screenshotScaleFactor),
|
|
1958
2261
|
};
|
|
@@ -1969,7 +2272,10 @@ export async function enrichScreenshotWithLayout(pixelRatio, screenshotScaleFact
|
|
|
1969
2272
|
path: el.path,
|
|
1970
2273
|
};
|
|
1971
2274
|
});
|
|
1972
|
-
return formatEnrichedLayoutTree(elements
|
|
2275
|
+
return formatEnrichedLayoutTree(elements, {
|
|
2276
|
+
offScreenBelow: result.offScreenBelow,
|
|
2277
|
+
offScreenAbove: result.offScreenAbove,
|
|
2278
|
+
});
|
|
1973
2279
|
}
|
|
1974
2280
|
catch {
|
|
1975
2281
|
return null; // Non-fatal: screenshot works without layout
|
|
@@ -2412,27 +2718,35 @@ export async function pressElement(options) {
|
|
|
2412
2718
|
return fiber.type.displayName || fiber.type.name || null;
|
|
2413
2719
|
}
|
|
2414
2720
|
|
|
2721
|
+
// When a fiber holds a string/number child via memoizedProps.children, return it
|
|
2722
|
+
// without recursing — Text > RCTText > NativeText all carry the same string,
|
|
2723
|
+
// and walking through every layer duplicates it (e.g. "CircularsCircularsCirculars").
|
|
2415
2724
|
function extractText(fiber, depth) {
|
|
2416
2725
|
if (!fiber || depth > 5000) return '';
|
|
2417
|
-
var parts = [];
|
|
2418
2726
|
var props = fiber.memoizedProps;
|
|
2419
2727
|
if (props) {
|
|
2420
2728
|
var ch = props.children;
|
|
2421
|
-
if (typeof ch === 'string')
|
|
2422
|
-
|
|
2423
|
-
|
|
2729
|
+
if (typeof ch === 'string') return ch;
|
|
2730
|
+
if (typeof ch === 'number') return String(ch);
|
|
2731
|
+
if (Array.isArray(ch)) {
|
|
2732
|
+
var allPrimitive = ch.length > 0;
|
|
2733
|
+
var inline = [];
|
|
2424
2734
|
for (var i = 0; i < ch.length; i++) {
|
|
2425
|
-
if (typeof ch[i] === 'string')
|
|
2426
|
-
else if (typeof ch[i] === 'number')
|
|
2735
|
+
if (typeof ch[i] === 'string') inline.push(ch[i]);
|
|
2736
|
+
else if (typeof ch[i] === 'number') inline.push(String(ch[i]));
|
|
2737
|
+
else { allPrimitive = false; }
|
|
2427
2738
|
}
|
|
2739
|
+
if (allPrimitive && inline.length > 0) return inline.join('');
|
|
2428
2740
|
}
|
|
2429
2741
|
}
|
|
2742
|
+
var parts = [];
|
|
2430
2743
|
var child = fiber.child;
|
|
2431
2744
|
while (child) {
|
|
2432
|
-
|
|
2745
|
+
var t = extractText(child, depth + 1);
|
|
2746
|
+
if (t) parts.push(t);
|
|
2433
2747
|
child = child.sibling;
|
|
2434
2748
|
}
|
|
2435
|
-
return parts.join('');
|
|
2749
|
+
return parts.join(' ');
|
|
2436
2750
|
}
|
|
2437
2751
|
|
|
2438
2752
|
function getMeasurable(fiber) {
|
|
@@ -2443,10 +2757,23 @@ export async function pressElement(options) {
|
|
|
2443
2757
|
typeof sn.canonical.publicInstance.measureInWindow === 'function') {
|
|
2444
2758
|
return sn.canonical.publicInstance;
|
|
2445
2759
|
}
|
|
2760
|
+
// Fabric leaf nodes like RCTText have no publicInstance. Measure via
|
|
2761
|
+
// the native Fabric UIManager using the shadow node instead — needed
|
|
2762
|
+
// for text bounds that are tight around the glyphs (not the scroll
|
|
2763
|
+
// container the text happens to live inside).
|
|
2764
|
+
if (sn.node && globalThis.nativeFabricUIManager &&
|
|
2765
|
+
typeof globalThis.nativeFabricUIManager.measureInWindow === 'function') {
|
|
2766
|
+
var node = sn.node;
|
|
2767
|
+
return {
|
|
2768
|
+
measureInWindow: function(cb) {
|
|
2769
|
+
try { globalThis.nativeFabricUIManager.measureInWindow(node, cb); } catch(e) {}
|
|
2770
|
+
}
|
|
2771
|
+
};
|
|
2772
|
+
}
|
|
2446
2773
|
return null;
|
|
2447
2774
|
}
|
|
2448
2775
|
|
|
2449
|
-
var RN_PRIMITIVES = /^(Animated\\(.*|withAnimated.*|AnimatedComponent.*|ForwardRef.*|memo\\(.*|Context\\.Consumer|Context\\.Provider|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)$/;
|
|
2776
|
+
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)$/;
|
|
2450
2777
|
|
|
2451
2778
|
function isScreenHidden(name, props) {
|
|
2452
2779
|
if (!props) return false;
|
|
@@ -2941,21 +3268,19 @@ export async function pressElement(options) {
|
|
|
2941
3268
|
|
|
2942
3269
|
var target = matches[targetIndex];
|
|
2943
3270
|
var result = {
|
|
2944
|
-
|
|
3271
|
+
needsNativeTap: true,
|
|
3272
|
+
nativeTapTarget: { x: target.x, y: target.y, unit: 'points' },
|
|
2945
3273
|
pressed: target.name,
|
|
2946
3274
|
matchIndex: targetIndex,
|
|
2947
3275
|
totalMatches: matches.length,
|
|
2948
3276
|
text: target.text,
|
|
2949
3277
|
testID: target.testID,
|
|
2950
3278
|
path: target.path,
|
|
2951
|
-
isInput: target.isInput
|
|
2952
|
-
x: target.x,
|
|
2953
|
-
y: target.y,
|
|
2954
|
-
unit: 'points'
|
|
3279
|
+
isInput: target.isInput
|
|
2955
3280
|
};
|
|
2956
3281
|
if (matches.length > 1) {
|
|
2957
3282
|
result.allMatches = matches.map(function(m, i) {
|
|
2958
|
-
return { index: i, component: m.name, text: m.text, testID: m.testID };
|
|
3283
|
+
return { index: i, component: m.name, text: m.text, testID: m.testID, x: m.x, y: m.y };
|
|
2959
3284
|
});
|
|
2960
3285
|
}
|
|
2961
3286
|
return result;
|
|
@@ -3156,6 +3481,19 @@ export async function inspectAtPoint(x, y, options = {}) {
|
|
|
3156
3481
|
typeof sn.canonical.publicInstance.measureInWindow === 'function') {
|
|
3157
3482
|
return sn.canonical.publicInstance;
|
|
3158
3483
|
}
|
|
3484
|
+
// Fabric leaf nodes like RCTText have no publicInstance. Measure via
|
|
3485
|
+
// the native Fabric UIManager using the shadow node instead — needed
|
|
3486
|
+
// for text bounds that are tight around the glyphs (not the scroll
|
|
3487
|
+
// container the text happens to live inside).
|
|
3488
|
+
if (sn.node && globalThis.nativeFabricUIManager &&
|
|
3489
|
+
typeof globalThis.nativeFabricUIManager.measureInWindow === 'function') {
|
|
3490
|
+
var node = sn.node;
|
|
3491
|
+
return {
|
|
3492
|
+
measureInWindow: function(cb) {
|
|
3493
|
+
try { globalThis.nativeFabricUIManager.measureInWindow(node, cb); } catch(e) {}
|
|
3494
|
+
}
|
|
3495
|
+
};
|
|
3496
|
+
}
|
|
3159
3497
|
return null;
|
|
3160
3498
|
}
|
|
3161
3499
|
|