@buoy-gg/highlight-updates 7.0.26 → 7.0.28
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/lib/commonjs/highlight-updates/HighlightUpdatesOverlay.js +4 -4
- package/lib/commonjs/highlight-updates/components/HighlightFilterView.js +110 -87
- package/lib/commonjs/highlight-updates/components/HighlightUpdatesModal.js +12 -8
- package/lib/commonjs/highlight-updates/components/IdentifierBadge.js +2 -2
- package/lib/commonjs/highlight-updates/components/IsolatedRenderList.js +6 -6
- package/lib/commonjs/highlight-updates/components/ModalHeaderContent.js +17 -17
- package/lib/commonjs/highlight-updates/components/RenderCauseBadge.js +22 -20
- package/lib/commonjs/highlight-updates/components/RenderDetailView.js +53 -49
- package/lib/commonjs/highlight-updates/components/RenderHistoryViewer.js +62 -60
- package/lib/commonjs/highlight-updates/components/RenderListItem.js +7 -7
- package/lib/commonjs/highlight-updates/components/RendersCopySettingsView.js +18 -18
- package/lib/commonjs/highlight-updates/components/StatsDisplay.js +4 -4
- package/lib/commonjs/highlight-updates/utils/HighlightUpdatesController.js +55 -3
- package/lib/commonjs/highlight-updates/utils/RenderTracker.js +6 -1
- package/lib/commonjs/highlight-updates/utils/namedComposite.js +41 -0
- package/lib/commonjs/highlight-updates/utils/screenElements.js +50 -25
- package/lib/commonjs/highlight-updates/utils/tapMatch.js +58 -0
- package/lib/module/highlight-updates/HighlightUpdatesOverlay.js +5 -5
- package/lib/module/highlight-updates/components/HighlightFilterView.js +111 -88
- package/lib/module/highlight-updates/components/HighlightUpdatesModal.js +13 -9
- package/lib/module/highlight-updates/components/IdentifierBadge.js +3 -3
- package/lib/module/highlight-updates/components/IsolatedRenderList.js +7 -7
- package/lib/module/highlight-updates/components/ModalHeaderContent.js +18 -18
- package/lib/module/highlight-updates/components/RenderCauseBadge.js +23 -21
- package/lib/module/highlight-updates/components/RenderDetailView.js +54 -50
- package/lib/module/highlight-updates/components/RenderHistoryViewer.js +63 -61
- package/lib/module/highlight-updates/components/RenderListItem.js +8 -8
- package/lib/module/highlight-updates/components/RendersCopySettingsView.js +19 -19
- package/lib/module/highlight-updates/components/StatsDisplay.js +5 -5
- package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +55 -3
- package/lib/module/highlight-updates/utils/RenderTracker.js +6 -1
- package/lib/module/highlight-updates/utils/namedComposite.js +36 -0
- package/lib/module/highlight-updates/utils/screenElements.js +50 -25
- package/lib/module/highlight-updates/utils/tapMatch.js +52 -0
- package/lib/typescript/highlight-updates/components/HighlightFilterView.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/components/HighlightUpdatesModal.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/components/RenderDetailView.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/components/RendersCopySettingsView.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts +6 -0
- package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/utils/namedComposite.d.ts +17 -0
- package/lib/typescript/highlight-updates/utils/namedComposite.d.ts.map +1 -0
- package/lib/typescript/highlight-updates/utils/screenElements.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/utils/tapMatch.d.ts +42 -0
- package/lib/typescript/highlight-updates/utils/tapMatch.d.ts.map +1 -0
- package/package.json +7 -7
|
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
14
14
|
exports.default = exports.RenderTracker = void 0;
|
|
15
15
|
var _ViewTypeMapper = require("./ViewTypeMapper");
|
|
16
16
|
var _PerformanceLogger = require("./PerformanceLogger");
|
|
17
|
+
var _namedComposite = require("./namedComposite");
|
|
17
18
|
/**
|
|
18
19
|
* Debug logging levels for render cause detection.
|
|
19
20
|
* Controls verbosity of console output for debugging "Why Did You Render" feature.
|
|
@@ -72,7 +73,8 @@ class RenderTrackerSingleton {
|
|
|
72
73
|
// Debug settings
|
|
73
74
|
debugLogLevel: "off",
|
|
74
75
|
// Dev tools visibility - exclude by default
|
|
75
|
-
excludeDevTools: true
|
|
76
|
+
excludeDevTools: true,
|
|
77
|
+
showHostViews: false
|
|
76
78
|
};
|
|
77
79
|
|
|
78
80
|
// Batch mode: defer notifyListeners until endBatch is called
|
|
@@ -421,6 +423,9 @@ class RenderTrackerSingleton {
|
|
|
421
423
|
*/
|
|
422
424
|
getFilteredRenders(searchText = "") {
|
|
423
425
|
let renders = this.getRenders();
|
|
426
|
+
if (!this.settings.showHostViews) {
|
|
427
|
+
renders = renders.filter(_namedComposite.isNamedComposite);
|
|
428
|
+
}
|
|
424
429
|
|
|
425
430
|
// Apply search filter
|
|
426
431
|
if (searchText.trim()) {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isHostViewName = isHostViewName;
|
|
7
|
+
exports.isNamedComposite = isNamedComposite;
|
|
8
|
+
/**
|
|
9
|
+
* Host views (RCTView / View / Text) drown the Highlight Updates list.
|
|
10
|
+
* Named composites are what you actually want when asking "what re-rendered?"
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const HOST_NAMES = new Set(["View", "RCTView", "Text", "RCTText", "RawText", "RCTRawText", "VirtualText", "RCTVirtualText", "ScrollView", "RCTScrollView", "RCTScrollContentView", "SafeAreaView", "RCTSafeAreaView", "Pressable", "TouchableOpacity", "Image", "RCTImageView"]);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Fiber names that look like identity but are list chrome / RN internals.
|
|
17
|
+
* Only provably-anonymous names belong here — real components (CopyButton,
|
|
18
|
+
* a user's `Copy`…) must stay visible. "Comp Copy"-style truncated
|
|
19
|
+
* placeholders are handled by the regex in isNamedComposite instead.
|
|
20
|
+
*/
|
|
21
|
+
const PLACEHOLDER_NAMES = new Set(["Unknown", "Anonymous", "CellRenderer", "NativeComponent"]);
|
|
22
|
+
/**
|
|
23
|
+
* True when this row is a named user composite (or has a testID). Host views
|
|
24
|
+
* and anonymous "Comp Copy" wrappers return false.
|
|
25
|
+
*/
|
|
26
|
+
function isNamedComposite(r) {
|
|
27
|
+
if (r.testID && r.testID.trim()) return true;
|
|
28
|
+
const name = (r.componentName ?? "").trim();
|
|
29
|
+
if (!name) return false;
|
|
30
|
+
if (HOST_NAMES.has(name)) return false;
|
|
31
|
+
if (PLACEHOLDER_NAMES.has(name)) return false;
|
|
32
|
+
const view = (r.viewType ?? "").trim();
|
|
33
|
+
if (view && HOST_NAMES.has(view) && name === view) return false;
|
|
34
|
+
// "Comp Copy" / "1x" style placeholders — no real component identity.
|
|
35
|
+
if (/^comp(\s|$)/i.test(name)) return false;
|
|
36
|
+
if (name.startsWith("_")) return false;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
function isHostViewName(name) {
|
|
40
|
+
return !!name && HOST_NAMES.has(name);
|
|
41
|
+
}
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.describeScreen = describeScreen;
|
|
7
7
|
exports.tapElement = tapElement;
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
|
+
var _tapMatch = require("./tapMatch");
|
|
9
10
|
/**
|
|
10
11
|
* On-device screen element discovery + in-JS tap.
|
|
11
12
|
*
|
|
@@ -499,9 +500,10 @@ async function describeScreen() {
|
|
|
499
500
|
if (r) {
|
|
500
501
|
const cx = r.x + r.width / 2;
|
|
501
502
|
const cy = r.y + r.height / 2;
|
|
502
|
-
//
|
|
503
|
+
// Drop previous Expo Router screens (centers well outside the window).
|
|
504
|
+
// Keep a 10% slack so the floating menu / home-indicator-clipped rows stay.
|
|
503
505
|
if (cy < -screenH * 0.1 || cy > screenH * 1.1 || cx < -screenW * 0.1 || cx > screenW * 1.1) {
|
|
504
|
-
|
|
506
|
+
continue;
|
|
505
507
|
}
|
|
506
508
|
tap = {
|
|
507
509
|
x: round2(cx / screenW),
|
|
@@ -554,20 +556,24 @@ function round2(n) {
|
|
|
554
556
|
|
|
555
557
|
// --- tapElement -----------------------------------------------------------
|
|
556
558
|
|
|
559
|
+
function fieldsOf(c) {
|
|
560
|
+
return {
|
|
561
|
+
nativeTag: c.nativeTag,
|
|
562
|
+
testID: c.testID,
|
|
563
|
+
label: c.label,
|
|
564
|
+
text: c.text,
|
|
565
|
+
name: c.name
|
|
566
|
+
};
|
|
567
|
+
}
|
|
557
568
|
function matchScore(c, params) {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
// testID exact match should win over a fuzzy name hit.
|
|
567
|
-
if (params.testID && c.testID && c.testID.toLowerCase() === params.testID.toLowerCase()) {
|
|
568
|
-
best = Math.max(best, 200);
|
|
569
|
-
}
|
|
570
|
-
return best;
|
|
569
|
+
return (0, _tapMatch.scoreTapMatch)(fieldsOf(c), params);
|
|
570
|
+
}
|
|
571
|
+
function tapPoint(rect, screenW, screenH) {
|
|
572
|
+
if (!rect || !screenW || !screenH) return undefined;
|
|
573
|
+
return {
|
|
574
|
+
x: (rect.x + rect.width / 2) / screenW,
|
|
575
|
+
y: (rect.y + rect.height / 2) / screenH
|
|
576
|
+
};
|
|
571
577
|
}
|
|
572
578
|
function makeSyntheticPressEvent(rect) {
|
|
573
579
|
const pageX = rect ? rect.x + rect.width / 2 : 0;
|
|
@@ -623,7 +629,28 @@ async function tapElement(params) {
|
|
|
623
629
|
}))
|
|
624
630
|
};
|
|
625
631
|
}
|
|
626
|
-
|
|
632
|
+
|
|
633
|
+
// Measure the top matches so an on-screen DialIcon beats an off-screen nav
|
|
634
|
+
// chip whose testID happens to contain the same substring.
|
|
635
|
+
const win = _reactNative.Dimensions.get("window");
|
|
636
|
+
const screenW = win.width || 1;
|
|
637
|
+
const screenH = win.height || 1;
|
|
638
|
+
const top = ranked.slice(0, 15);
|
|
639
|
+
await Promise.all(top.map(async r => {
|
|
640
|
+
if (r.c.instance && !r.c.rect) r.c.rect = await measureInstance(r.c.instance);
|
|
641
|
+
}));
|
|
642
|
+
for (const r of top) {
|
|
643
|
+
r.score = (0, _tapMatch.scoreTapMatch)({
|
|
644
|
+
...fieldsOf(r.c),
|
|
645
|
+
tap: tapPoint(r.c.rect, screenW, screenH)
|
|
646
|
+
}, params);
|
|
647
|
+
}
|
|
648
|
+
top.sort((a, b) => b.score - a.score);
|
|
649
|
+
|
|
650
|
+
// Fall back to the FULL ranked list for an interactive match — the top-15
|
|
651
|
+
// re-score is only a tiebreak, and on a busy screen the pressable match can
|
|
652
|
+
// sit below 15 text-score peers.
|
|
653
|
+
const target = top.find(r => isInteractive(r.c)) ?? ranked.find(r => isInteractive(r.c)) ?? top[0];
|
|
627
654
|
const c = target.c;
|
|
628
655
|
if (!isInteractive(c)) {
|
|
629
656
|
return {
|
|
@@ -642,20 +669,18 @@ async function tapElement(params) {
|
|
|
642
669
|
|
|
643
670
|
// Measure the target so we can decide whether to scroll it on-screen (and,
|
|
644
671
|
// for a press, give the synthetic event real coordinates).
|
|
645
|
-
if (c.instance) c.rect = await measureInstance(c.instance);
|
|
672
|
+
if (c.instance && !c.rect) c.rect = await measureInstance(c.instance);
|
|
646
673
|
|
|
647
674
|
// Bring it into view if it's off-screen and scrolling is wanted. The action
|
|
648
675
|
// doesn't NEED this (handlers are invoked directly), but it keeps the element
|
|
649
676
|
// visible for the human watching / a follow-up describe.
|
|
650
677
|
let scrolled = false;
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if (scrolled) c.rect = await measureInstance(c.instance); // fresh coords
|
|
658
|
-
}
|
|
678
|
+
// A failed measure (rect == null) counts as off-screen: the node may be in
|
|
679
|
+
// a virtualized list that hasn't materialized it, and scrolling can help.
|
|
680
|
+
const offScreen = c.rect == null || !(0, _tapMatch.isRectCenterOnScreen)(c.rect, screenW, screenH);
|
|
681
|
+
if (offScreen && params.scrollIntoView !== false && c.instance) {
|
|
682
|
+
scrolled = await scrollIntoView(c.fiber, c.instance, c.stateNode, 80);
|
|
683
|
+
if (scrolled) c.rect = await measureInstance(c.instance); // fresh coords
|
|
659
684
|
}
|
|
660
685
|
const base = {
|
|
661
686
|
nativeTag: c.nativeTag,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isOnScreenTap = isOnScreenTap;
|
|
7
|
+
exports.isRectCenterOnScreen = isRectCenterOnScreen;
|
|
8
|
+
exports.scoreTapMatch = scoreTapMatch;
|
|
9
|
+
/**
|
|
10
|
+
* Shared tap-target scoring for describe_screen / tap_element.
|
|
11
|
+
*
|
|
12
|
+
* Ranking used to be substring-on-testID only, so `tap: "ASSETS"` preferred
|
|
13
|
+
* `nav-assets-tool-test` (score 25) over a visible dial icon, and off-screen
|
|
14
|
+
* Expo Router screens (y > 1) stayed in the candidate list.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** True when the measured center is inside the window (10% slack for home-indicator / portal hosts). */
|
|
18
|
+
function isOnScreenTap(tap) {
|
|
19
|
+
if (!tap) return false;
|
|
20
|
+
return tap.x >= -0.1 && tap.x <= 1.1 && tap.y >= -0.1 && tap.y <= 1.1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Point-in-window test in *points* (measureInWindow coords), with slack so
|
|
25
|
+
* the floating menu and rows clipped at the home indicator still count.
|
|
26
|
+
* Previous Expo Router screens typically measure at y > 1.2× window height.
|
|
27
|
+
*/
|
|
28
|
+
function isRectCenterOnScreen(rect, screenW, screenH) {
|
|
29
|
+
if (!rect || screenW <= 0 || screenH <= 0) return false;
|
|
30
|
+
const cx = rect.x + rect.width / 2;
|
|
31
|
+
const cy = rect.y + rect.height / 2;
|
|
32
|
+
const slackX = screenW * 0.1;
|
|
33
|
+
const slackY = screenH * 0.1;
|
|
34
|
+
return cx >= -slackX && cx <= screenW + slackX && cy >= -slackY && cy <= screenH + slackY;
|
|
35
|
+
}
|
|
36
|
+
function scoreTapMatch(c, params) {
|
|
37
|
+
if (params.nativeTag != null && c.nativeTag === params.nativeTag) return 1000;
|
|
38
|
+
const q = (params.query ?? params.testID ?? "").trim().toLowerCase();
|
|
39
|
+
if (!q) return 0;
|
|
40
|
+
const fields = [c.testID, c.label, c.text, c.name].filter(Boolean).map(s => s.toLowerCase());
|
|
41
|
+
let best = 0;
|
|
42
|
+
for (const f of fields) {
|
|
43
|
+
if (f === q) best = Math.max(best, 100);else if (f.startsWith(q)) best = Math.max(best, 50);else if (f.includes(q)) best = Math.max(best, 25);
|
|
44
|
+
}
|
|
45
|
+
if (params.testID && c.testID && c.testID.toLowerCase() === params.testID.toLowerCase()) {
|
|
46
|
+
best = Math.max(best, 200);
|
|
47
|
+
}
|
|
48
|
+
if (best === 0) return 0;
|
|
49
|
+
const id = (c.testID ?? "").toLowerCase();
|
|
50
|
+
// Dial icons are the thing agents mean when they tap a tool name. (No
|
|
51
|
+
// penalties for app testID patterns — this ships in the library, and we
|
|
52
|
+
// don't know consumer apps' naming conventions.)
|
|
53
|
+
if (id.startsWith("buoy-dial-")) best += 40;
|
|
54
|
+
if (c.tap) {
|
|
55
|
+
if (isOnScreenTap(c.tap)) best += 20;else best -= 40;
|
|
56
|
+
}
|
|
57
|
+
return best;
|
|
58
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import React, { useEffect, useState, useRef, useCallback, useLayoutEffect } from "react";
|
|
12
12
|
import { View, Text, StyleSheet } from "react-native";
|
|
13
|
-
import {
|
|
13
|
+
import { night } from "@buoy-gg/shared-ui";
|
|
14
14
|
import HighlightUpdatesController from "./utils/HighlightUpdatesController";
|
|
15
15
|
import { PerformanceLogger, markOverlayRendered } from "./utils/PerformanceLogger";
|
|
16
16
|
import { RenderTracker } from "./utils/RenderTracker";
|
|
@@ -252,20 +252,20 @@ const styles = StyleSheet.create({
|
|
|
252
252
|
position: "absolute",
|
|
253
253
|
borderWidth: 2,
|
|
254
254
|
borderStyle: "dashed",
|
|
255
|
-
borderColor:
|
|
255
|
+
borderColor: night.color.accent,
|
|
256
256
|
backgroundColor: "transparent"
|
|
257
257
|
},
|
|
258
258
|
spotlightLabel: {
|
|
259
259
|
position: "absolute",
|
|
260
|
-
backgroundColor:
|
|
260
|
+
backgroundColor: night.color.surface,
|
|
261
261
|
borderWidth: 1,
|
|
262
|
-
borderColor:
|
|
262
|
+
borderColor: night.color.accent,
|
|
263
263
|
paddingHorizontal: 8,
|
|
264
264
|
paddingVertical: 3,
|
|
265
265
|
borderRadius: 4
|
|
266
266
|
},
|
|
267
267
|
spotlightLabelText: {
|
|
268
|
-
color:
|
|
268
|
+
color: night.color.accent,
|
|
269
269
|
fontSize: 11,
|
|
270
270
|
fontWeight: "600",
|
|
271
271
|
fontFamily: "monospace"
|