@buoy-gg/highlight-updates 4.0.2 → 6.0.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/lib/commonjs/highlight-updates/HighlightUpdatesOverlay.js +11 -15
- package/lib/commonjs/highlight-updates/utils/CommitProfiler.js +279 -0
- package/lib/commonjs/highlight-updates/utils/HighlightUpdatesController.js +200 -174
- package/lib/commonjs/highlight-updates/utils/devtoolsComponentNames.js +127 -0
- package/lib/commonjs/index.js +67 -0
- package/lib/module/highlight-updates/HighlightUpdatesOverlay.js +12 -16
- package/lib/module/highlight-updates/utils/CommitProfiler.js +276 -0
- package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +196 -170
- package/lib/module/highlight-updates/utils/devtoolsComponentNames.js +119 -0
- package/lib/module/index.js +50 -0
- package/lib/typescript/highlight-updates/HighlightUpdatesOverlay.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/utils/CommitProfiler.d.ts +32 -0
- package/lib/typescript/highlight-updates/utils/CommitProfiler.d.ts.map +1 -0
- package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +1 -1
- package/lib/typescript/highlight-updates/utils/devtoolsComponentNames.d.ts +19 -0
- package/lib/typescript/highlight-updates/utils/devtoolsComponentNames.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +7 -7
|
@@ -37,6 +37,7 @@ var _RenderTracker = require("./RenderTracker");
|
|
|
37
37
|
var _PerformanceLogger = require("./PerformanceLogger");
|
|
38
38
|
var _RenderCauseDetector = require("./RenderCauseDetector");
|
|
39
39
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
40
|
+
var _devtoolsComponentNames = require("./devtoolsComponentNames");
|
|
40
41
|
// State
|
|
41
42
|
let globalEnabled = false; // User-controlled toggle for visual highlights
|
|
42
43
|
// When true, tracking + measurement + storage all run as normal, but the
|
|
@@ -118,6 +119,10 @@ function getNativeTag(instance) {
|
|
|
118
119
|
if (inst.canonical) {
|
|
119
120
|
if (inst.canonical.__nativeTag != null) return inst.canonical.__nativeTag;
|
|
120
121
|
if (inst.canonical._nativeTag != null) return inst.canonical._nativeTag;
|
|
122
|
+
// Modern Fabric instance handles: { node, canonical: { nativeTag, ... } }.
|
|
123
|
+
// Without this shape ~99% of traced nodes were dropped on New
|
|
124
|
+
// Architecture — highlights only appeared for tap-shaped legacy nodes.
|
|
125
|
+
if (inst.canonical.nativeTag != null) return inst.canonical.nativeTag;
|
|
121
126
|
}
|
|
122
127
|
return null;
|
|
123
128
|
}
|
|
@@ -324,48 +329,12 @@ function isOurOverlayTag(nativeTag) {
|
|
|
324
329
|
// 5. Cached property access to avoid repeated traversal
|
|
325
330
|
// ============================================================================
|
|
326
331
|
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
// to render callbacks via RenderTracker.onRenderEvent - tracking its renders
|
|
330
|
-
// would create an infinite callback loop).
|
|
331
|
-
const HIGHLIGHT_OVERLAY_COMPONENTS = new Set([
|
|
332
|
-
// Highlight Updates overlay components
|
|
333
|
-
"HighlightUpdatesOverlay", "HighlightUpdatesModal", "HighlightFilterView", "RenderDetailView", "RenderListItem", "RenderListItemInner", "RenderHistoryViewer", "RenderHistoryFooter", "RenderCauseBadge", "TwoLevelCauseBadge", "EnhancedCauseDisplay", "IsolatedRenderList", "IsolatedRenderListInner", "StatsDisplay", "StatsDisplayInner", "CurrentStateView", "EventStepperFooter",
|
|
334
|
-
// Events tool components - subscribes to onRenderEvent, tracking these causes infinite loops
|
|
335
|
-
"EventsModal", "UnifiedEventList", "UnifiedEventItem", "UnifiedEventDetail", "UnifiedEventFilters", "UnifiedEventViewer", "ReactQueryEventDetail", "EventsCopySettingsView"]);
|
|
332
|
+
// Component-name sets/prefixes live in devtoolsComponentNames.ts (shared with
|
|
333
|
+
// CommitProfiler). Native-ID sets stay here — only the node walk uses them.
|
|
336
334
|
|
|
337
335
|
// Native IDs that MUST always be excluded (highlight overlay)
|
|
338
336
|
const HIGHLIGHT_OVERLAY_NATIVE_IDS = new Set(["highlight-updates-overlay", "__rn_buoy__highlight-modal"]);
|
|
339
337
|
|
|
340
|
-
// Prefixes for always-excluded components (highlight overlay + events tool)
|
|
341
|
-
const HIGHLIGHT_OVERLAY_PREFIXES = ["HighlightUpdates", "RenderList", "RenderDetail", "RenderHistory", "RenderCause", "EventStepper",
|
|
342
|
-
// Events tool - subscribes to onRenderEvent, must always be excluded
|
|
343
|
-
"UnifiedEvent", "EventsModal", "EventsCopy"];
|
|
344
|
-
|
|
345
|
-
// Other dev tools components - can be toggled via excludeDevTools setting
|
|
346
|
-
const OTHER_DEV_TOOLS_COMPONENTS = new Set([
|
|
347
|
-
// React Buoy devtools components
|
|
348
|
-
"JsModalComponent", "JsModal", "TypePicker", "PatternInput", "PatternChip", "DetectedItemsSection", "DetectedCategoryBadge", "IdentifierBadge", "CategoryBadge", "AppRenderer", "AppOverlay", "FloatingTools", "DialDevTools", "DevToolsVisibilityProvider", "AppHostProvider", "MinimizedToolsProvider",
|
|
349
|
-
// Shared UI components used in modals
|
|
350
|
-
"ModalHeader", "TabSelector", "SectionHeader", "DraggableHeader", "WindowControls",
|
|
351
|
-
// Shared UI data viewer components (JSON diff viewers, etc.)
|
|
352
|
-
"TreeDiffViewer", "DataViewer", "SplitDiffViewer", "VirtualizedDataExplorer", "DiffSummary", "TypeLegend", "IndentGuides", "IndentGuidesOverlay", "CyberpunkInput", "DiffView", "AnswerCard", "DetailsSection", "DetailRow", "QuickActionsSection", "FilterOptionCard",
|
|
353
|
-
// Modal header sub-components
|
|
354
|
-
"SearchSection", "SearchSectionInner", "HeaderActions", "HeaderActionsInner", "MainListHeader", "FilterViewHeader", "DetailViewHeader",
|
|
355
|
-
// Floating menu components (minimized tools, dial, etc.)
|
|
356
|
-
"FloatingMenu", "FloatingDevTools", "MinimizedToolsStack", "MinimizedToolsContext", "GlitchToolButton", "ExpandablePopover", "CollapsedPeek", "ExpandedWrapper", "DialIcon", "DialMenu", "DialIconItem", "OnboardingTooltip", "MenuLauncherIcon", "DevToolsSettingsModal", "SettingsModal",
|
|
357
|
-
// Image Overlay components
|
|
358
|
-
"ImageOverlayModal", "ImageOverlayStandalone", "ToolCard", "ToolIcon", "GripVerticalIcon", "UserStatus", "Divider",
|
|
359
|
-
// Shared UI icon components (Chevrons, etc.)
|
|
360
|
-
"ChevronDown", "ChevronUp", "ChevronLeft", "ChevronRight", "ChevronDownIcon", "ChevronUpIcon", "ChevronLeftIcon", "ChevronRightIcon",
|
|
361
|
-
// React Native LogBox components (shown on reload/errors)
|
|
362
|
-
"LogBox", "LogBoxLog", "LogBoxLogNotification", "LogBoxNotificationContainer", "_LogBoxNotificationContainer", "LogBoxInspector", "LogBoxInspectorContainer", "LogBoxInspectorHeader", "LogBoxInspectorBody", "LogBoxInspectorFooter", "LogBoxInspectorMessageHeader", "LogBoxInspectorStackFrame", "LogBoxInspectorSection", "LogBoxButton", "LogBoxMessage"]);
|
|
363
|
-
|
|
364
|
-
// Other dev tools prefixes - can be toggled via excludeDevTools setting
|
|
365
|
-
const OTHER_DEV_TOOLS_PREFIXES = ["JsModal", "TreeDiff", "DataViewer", "DiffView", "DiffSummary", "Virtualized",
|
|
366
|
-
// Floating menu prefixes
|
|
367
|
-
"Floating", "Minimized", "Dial", "Expandable", "Chevron", "Glitch", "Settings", "Onboarding", "DevTools", "ImageOverlay"];
|
|
368
|
-
|
|
369
338
|
// Other dev tools native IDs - can be toggled
|
|
370
339
|
const OTHER_DEV_TOOLS_NATIVE_IDS = new Set(["jsmodal-root", "image-overlay-standalone",
|
|
371
340
|
// LogBox native IDs
|
|
@@ -524,8 +493,15 @@ function isOurOverlayNode(stateNode) {
|
|
|
524
493
|
// Get nativeTag for caching
|
|
525
494
|
const nativeTag = getNativeTag(stateNode) || getNativeTag(node?.canonical?.publicInstance);
|
|
526
495
|
|
|
527
|
-
// Check if we should exclude all dev tools or just highlight overlay
|
|
528
|
-
|
|
496
|
+
// Check if we should exclude all dev tools or just highlight overlay.
|
|
497
|
+
// Bench "profile our own tools" mode (the example app's /tool-bench page)
|
|
498
|
+
// forces this OFF so live highlight boxes flash on our tool UI too —
|
|
499
|
+
// except the perf-monitor HUD (checked separately in the walk below),
|
|
500
|
+
// whose 250ms sample-tick churn would flash constantly. Cache reads and
|
|
501
|
+
// writes are already gated on excludeDevTools, so bench mode bypasses
|
|
502
|
+
// the cache and can't poison it.
|
|
503
|
+
const benchMode = (0, _devtoolsComponentNames.isCapturingBuoyToolRenders)();
|
|
504
|
+
const excludeDevTools = !benchMode && _RenderTracker.RenderTracker.getSettings().excludeDevTools;
|
|
529
505
|
|
|
530
506
|
// Check cache first (O(1)) - but only for the current excludeDevTools setting
|
|
531
507
|
// When excludeDevTools is false, skip cache to re-evaluate all nodes
|
|
@@ -558,12 +534,12 @@ function isOurOverlayNode(stateNode) {
|
|
|
558
534
|
const name = getComponentName(current);
|
|
559
535
|
if (name) {
|
|
560
536
|
// Always check highlight overlay components (must always be excluded)
|
|
561
|
-
if (HIGHLIGHT_OVERLAY_COMPONENTS.has(name)) {
|
|
537
|
+
if (_devtoolsComponentNames.HIGHLIGHT_OVERLAY_COMPONENTS.has(name)) {
|
|
562
538
|
result = true;
|
|
563
539
|
break;
|
|
564
540
|
}
|
|
565
541
|
// Check highlight overlay prefixes
|
|
566
|
-
for (const prefix of HIGHLIGHT_OVERLAY_PREFIXES) {
|
|
542
|
+
for (const prefix of _devtoolsComponentNames.HIGHLIGHT_OVERLAY_PREFIXES) {
|
|
567
543
|
if (name.startsWith(prefix)) {
|
|
568
544
|
result = true;
|
|
569
545
|
break;
|
|
@@ -571,14 +547,20 @@ function isOurOverlayNode(stateNode) {
|
|
|
571
547
|
}
|
|
572
548
|
if (result) break;
|
|
573
549
|
|
|
550
|
+
// Bench mode: the perf-monitor HUD still never flashes.
|
|
551
|
+
if (benchMode && (0, _devtoolsComponentNames.isPerfMonitorComponentName)(name)) {
|
|
552
|
+
result = true;
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
|
|
574
556
|
// Only check other dev tools if excludeDevTools is enabled
|
|
575
557
|
if (excludeDevTools) {
|
|
576
|
-
if (OTHER_DEV_TOOLS_COMPONENTS.has(name)) {
|
|
558
|
+
if (_devtoolsComponentNames.OTHER_DEV_TOOLS_COMPONENTS.has(name)) {
|
|
577
559
|
result = true;
|
|
578
560
|
break;
|
|
579
561
|
}
|
|
580
562
|
// Check other dev tools prefixes
|
|
581
|
-
for (const prefix of OTHER_DEV_TOOLS_PREFIXES) {
|
|
563
|
+
for (const prefix of _devtoolsComponentNames.OTHER_DEV_TOOLS_PREFIXES) {
|
|
582
564
|
if (name.startsWith(prefix)) {
|
|
583
565
|
result = true;
|
|
584
566
|
break;
|
|
@@ -780,14 +762,33 @@ function handleTraceUpdates(nodes) {
|
|
|
780
762
|
// Use batch size from RenderTracker settings (default: 150)
|
|
781
763
|
// Note: batchSize already retrieved above for perfTimer
|
|
782
764
|
perfTimer.markMeasurementStart();
|
|
783
|
-
|
|
765
|
+
// Measure up to 4× batchSize: coalesced commits can exceed one batch and
|
|
766
|
+
// the old hard slice silently never highlighted the same tail components.
|
|
767
|
+
const measurePromises = nodesToDraw.slice(0, batchSize * 4).map(({
|
|
784
768
|
node: stateNode,
|
|
785
769
|
color,
|
|
786
770
|
count
|
|
787
771
|
}) => new Promise(resolve => {
|
|
772
|
+
// RN silently never fires measure callbacks for detached nodes;
|
|
773
|
+
// without this timeout one dead node hangs the whole batch's
|
|
774
|
+
// Promise.all (no boxes, no tracking, counts still climb).
|
|
775
|
+
let settled = false;
|
|
776
|
+
const settle = value => {
|
|
777
|
+
if (settled) return;
|
|
778
|
+
settled = true;
|
|
779
|
+
clearTimeout(timeoutId);
|
|
780
|
+
resolve(value);
|
|
781
|
+
};
|
|
782
|
+
const timeoutId = setTimeout(() => settle({
|
|
783
|
+
rect: null,
|
|
784
|
+
stateNode,
|
|
785
|
+
color,
|
|
786
|
+
count
|
|
787
|
+
}), 500);
|
|
788
788
|
const publicInstance = getPublicInstance(stateNode);
|
|
789
|
-
|
|
790
|
-
|
|
789
|
+
const nativeTag = getNativeTag(stateNode) || getNativeTag(publicInstance);
|
|
790
|
+
if (nativeTag == null) {
|
|
791
|
+
settle({
|
|
791
792
|
rect: null,
|
|
792
793
|
stateNode,
|
|
793
794
|
color,
|
|
@@ -795,44 +796,62 @@ function handleTraceUpdates(nodes) {
|
|
|
795
796
|
});
|
|
796
797
|
return;
|
|
797
798
|
}
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
799
|
+
const onMeasured = (x, y, width, height, pageX, pageY) => {
|
|
800
|
+
if (pageX == null || pageY == null || width == null || height == null) {
|
|
801
|
+
settle({
|
|
802
|
+
rect: null,
|
|
803
|
+
stateNode,
|
|
804
|
+
color,
|
|
805
|
+
count
|
|
806
|
+
});
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
settle({
|
|
810
|
+
rect: {
|
|
811
|
+
id: nativeTag,
|
|
812
|
+
x: pageX,
|
|
813
|
+
y: pageY,
|
|
814
|
+
width,
|
|
815
|
+
height,
|
|
816
|
+
color,
|
|
817
|
+
count
|
|
818
|
+
},
|
|
802
819
|
stateNode,
|
|
803
820
|
color,
|
|
804
821
|
count
|
|
805
822
|
});
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
823
|
+
};
|
|
824
|
+
if (!publicInstance) {
|
|
825
|
+
// New Architecture: canonical.publicInstance is often lazily
|
|
826
|
+
// ABSENT on traceUpdates wrappers — measure the wrapper's `node`
|
|
827
|
+
// (the Fabric shadow node) directly through FabricUIManager.
|
|
828
|
+
const fabricNode = stateNode.node;
|
|
829
|
+
const fabricUI = globalThis.nativeFabricUIManager;
|
|
830
|
+
if (fabricNode && fabricUI?.measure) {
|
|
831
|
+
try {
|
|
832
|
+
fabricUI.measure(fabricNode, onMeasured);
|
|
833
|
+
} catch (error) {
|
|
834
|
+
settle({
|
|
812
835
|
rect: null,
|
|
813
836
|
stateNode,
|
|
814
837
|
color,
|
|
815
838
|
count
|
|
816
839
|
});
|
|
817
|
-
return;
|
|
818
840
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
color,
|
|
827
|
-
count
|
|
828
|
-
},
|
|
829
|
-
stateNode,
|
|
830
|
-
color,
|
|
831
|
-
count
|
|
832
|
-
});
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
settle({
|
|
844
|
+
rect: null,
|
|
845
|
+
stateNode,
|
|
846
|
+
color,
|
|
847
|
+
count
|
|
833
848
|
});
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
publicInstance.measure(onMeasured);
|
|
834
853
|
} catch (error) {
|
|
835
|
-
|
|
854
|
+
settle({
|
|
836
855
|
rect: null,
|
|
837
856
|
stateNode,
|
|
838
857
|
color,
|
|
@@ -847,119 +866,126 @@ function handleTraceUpdates(nodes) {
|
|
|
847
866
|
// Mark measurement phase complete
|
|
848
867
|
perfTimer.markMeasurementComplete(validResults.length, results.length - validResults.length);
|
|
849
868
|
|
|
850
|
-
//
|
|
851
|
-
//
|
|
852
|
-
//
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
} catch (e) {
|
|
858
|
-
debugLog("registerTrackedNode batch failed", e);
|
|
869
|
+
// DRAW FIRST. With the render list open, tracking a big burst
|
|
870
|
+
// (store writes + cause detection + list row rebuilds) used to run
|
|
871
|
+
// BEFORE the highlight callback, so boxes painted ~0.7s later
|
|
872
|
+
// (user-timed: 1.3s list-closed vs 2s+ list-open, 2026-07-08).
|
|
873
|
+
// Boxes are the product — paint them, then bookkeep.
|
|
874
|
+
if (rects.length > 0 && highlightCallback && globalEnabled && !highlightsSuppressed) {
|
|
875
|
+
highlightCallback(rects);
|
|
859
876
|
}
|
|
860
877
|
|
|
861
|
-
//
|
|
862
|
-
//
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
rect
|
|
871
|
-
} of validResults) {
|
|
872
|
-
if (rect) {
|
|
873
|
-
batchNativeTags.add(rect.id);
|
|
878
|
+
// Tracking/list work runs in a macrotask so React commits the boxes
|
|
879
|
+
// before this heavier pass starts.
|
|
880
|
+
setTimeout(() => {
|
|
881
|
+
// Keep a live handle to each measured node so the desktop's "capture this
|
|
882
|
+
// component" flow can re-measure (and scroll to) it on demand later.
|
|
883
|
+
// Defensive: never let registration break the measurement/tracking batch.
|
|
884
|
+
try {
|
|
885
|
+
for (const r of validResults) {
|
|
886
|
+
if (r.rect) registerTrackedNode(r.rect.id, r.stateNode);
|
|
874
887
|
}
|
|
888
|
+
} catch (e) {
|
|
889
|
+
debugLog("registerTrackedNode batch failed", e);
|
|
875
890
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
//
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
const node = stateNode;
|
|
891
|
-
const fiber = node?.canonical?.internalInstanceHandle;
|
|
892
|
-
renderCause = (0, _RenderCauseDetector.detectRenderCause)(rect.id, fiber, batchNativeTags, settings.debugLogLevel);
|
|
891
|
+
|
|
892
|
+
// Track renders - behavior differs based on what's enabled:
|
|
893
|
+
// - globalEnabled (visual tool): full tracking with storage and list updates
|
|
894
|
+
// - backgroundTrackingEnabled only (Events tool): emit events only, no storage
|
|
895
|
+
const settings = _RenderTracker.RenderTracker.getSettings();
|
|
896
|
+
const trackCauses = settings.trackRenderCauses && settings.showRenderCount;
|
|
897
|
+
let batchNativeTags = null;
|
|
898
|
+
if (trackCauses) {
|
|
899
|
+
batchNativeTags = new Set();
|
|
900
|
+
for (const {
|
|
901
|
+
rect
|
|
902
|
+
} of validResults) {
|
|
903
|
+
if (rect) {
|
|
904
|
+
batchNativeTags.add(rect.id);
|
|
893
905
|
}
|
|
894
|
-
_RenderTracker.RenderTracker.trackRender({
|
|
895
|
-
nativeTag: rect.id,
|
|
896
|
-
viewType: componentInfo.viewType,
|
|
897
|
-
testID: componentInfo.testID,
|
|
898
|
-
nativeID: componentInfo.nativeID,
|
|
899
|
-
accessibilityLabel: componentInfo.accessibilityLabel,
|
|
900
|
-
componentName: componentInfo.componentName,
|
|
901
|
-
measurements: {
|
|
902
|
-
x: rect.x,
|
|
903
|
-
y: rect.y,
|
|
904
|
-
width: rect.width,
|
|
905
|
-
height: rect.height
|
|
906
|
-
},
|
|
907
|
-
color,
|
|
908
|
-
count,
|
|
909
|
-
renderCause
|
|
910
|
-
});
|
|
911
906
|
}
|
|
912
907
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
908
|
+
if (globalEnabled) {
|
|
909
|
+
// Visual tool is on - full tracking with storage and list notifications
|
|
910
|
+
_RenderTracker.RenderTracker.startBatch();
|
|
911
|
+
for (const {
|
|
912
|
+
rect,
|
|
913
|
+
stateNode,
|
|
914
|
+
color,
|
|
915
|
+
count
|
|
916
|
+
} of validResults) {
|
|
917
|
+
if (rect) {
|
|
918
|
+
const componentInfo = extractComponentInfo(stateNode);
|
|
919
|
+
let renderCause;
|
|
920
|
+
if (trackCauses && batchNativeTags) {
|
|
921
|
+
const node = stateNode;
|
|
922
|
+
const fiber = node?.canonical?.internalInstanceHandle;
|
|
923
|
+
renderCause = (0, _RenderCauseDetector.detectRenderCause)(rect.id, fiber, batchNativeTags, settings.debugLogLevel);
|
|
924
|
+
}
|
|
925
|
+
_RenderTracker.RenderTracker.trackRender({
|
|
926
|
+
nativeTag: rect.id,
|
|
927
|
+
viewType: componentInfo.viewType,
|
|
928
|
+
testID: componentInfo.testID,
|
|
929
|
+
nativeID: componentInfo.nativeID,
|
|
930
|
+
accessibilityLabel: componentInfo.accessibilityLabel,
|
|
931
|
+
componentName: componentInfo.componentName,
|
|
932
|
+
measurements: {
|
|
933
|
+
x: rect.x,
|
|
934
|
+
y: rect.y,
|
|
935
|
+
width: rect.width,
|
|
936
|
+
height: rect.height
|
|
937
|
+
},
|
|
938
|
+
color,
|
|
939
|
+
count,
|
|
940
|
+
renderCause
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
_RenderTracker.RenderTracker.endBatch();
|
|
945
|
+
} else {
|
|
946
|
+
// Only background tracking (Events tool) - emit events without storage
|
|
947
|
+
for (const {
|
|
948
|
+
rect,
|
|
949
|
+
stateNode,
|
|
950
|
+
color,
|
|
951
|
+
count
|
|
952
|
+
} of validResults) {
|
|
953
|
+
if (rect) {
|
|
954
|
+
const componentInfo = extractComponentInfo(stateNode);
|
|
955
|
+
let renderCause;
|
|
956
|
+
if (trackCauses && batchNativeTags) {
|
|
957
|
+
const node = stateNode;
|
|
958
|
+
const fiber = node?.canonical?.internalInstanceHandle;
|
|
959
|
+
renderCause = (0, _RenderCauseDetector.detectRenderCause)(rect.id, fiber, batchNativeTags, settings.debugLogLevel);
|
|
960
|
+
}
|
|
961
|
+
_RenderTracker.RenderTracker.emitRenderEvent({
|
|
962
|
+
nativeTag: rect.id,
|
|
963
|
+
viewType: componentInfo.viewType,
|
|
964
|
+
testID: componentInfo.testID,
|
|
965
|
+
nativeID: componentInfo.nativeID,
|
|
966
|
+
accessibilityLabel: componentInfo.accessibilityLabel,
|
|
967
|
+
componentName: componentInfo.componentName,
|
|
968
|
+
measurements: {
|
|
969
|
+
x: rect.x,
|
|
970
|
+
y: rect.y,
|
|
971
|
+
width: rect.width,
|
|
972
|
+
height: rect.height
|
|
973
|
+
},
|
|
974
|
+
color,
|
|
975
|
+
count,
|
|
976
|
+
renderCause
|
|
977
|
+
});
|
|
929
978
|
}
|
|
930
|
-
_RenderTracker.RenderTracker.emitRenderEvent({
|
|
931
|
-
nativeTag: rect.id,
|
|
932
|
-
viewType: componentInfo.viewType,
|
|
933
|
-
testID: componentInfo.testID,
|
|
934
|
-
nativeID: componentInfo.nativeID,
|
|
935
|
-
accessibilityLabel: componentInfo.accessibilityLabel,
|
|
936
|
-
componentName: componentInfo.componentName,
|
|
937
|
-
measurements: {
|
|
938
|
-
x: rect.x,
|
|
939
|
-
y: rect.y,
|
|
940
|
-
width: rect.width,
|
|
941
|
-
height: rect.height
|
|
942
|
-
},
|
|
943
|
-
color,
|
|
944
|
-
count,
|
|
945
|
-
renderCause
|
|
946
|
-
});
|
|
947
979
|
}
|
|
948
980
|
}
|
|
949
|
-
}
|
|
950
981
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
// Only show visual highlights when the visual tool is enabled (not just
|
|
955
|
-
// background tracking) AND not running in silent mode (screenshot tool).
|
|
956
|
-
if (rects.length > 0 && highlightCallback && globalEnabled && !highlightsSuppressed) {
|
|
957
|
-
highlightCallback(rects);
|
|
958
|
-
}
|
|
982
|
+
// Mark tracking phase complete
|
|
983
|
+
perfTimer.markTrackingComplete();
|
|
959
984
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
985
|
+
// Mark callback phase complete and finish timing
|
|
986
|
+
perfTimer.markCallbackComplete();
|
|
987
|
+
perfTimer.finish();
|
|
988
|
+
}, 0);
|
|
963
989
|
}).catch(error => {
|
|
964
990
|
console.error("[HighlightUpdates] Error in measurement pipeline:", error);
|
|
965
991
|
perfTimer.finish(); // Still log timing even on error
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.OTHER_DEV_TOOLS_PREFIXES = exports.OTHER_DEV_TOOLS_COMPONENTS = exports.HIGHLIGHT_OVERLAY_PREFIXES = exports.HIGHLIGHT_OVERLAY_COMPONENTS = void 0;
|
|
7
|
+
exports.isCapturingBuoyToolRenders = isCapturingBuoyToolRenders;
|
|
8
|
+
exports.isDevToolsComponentName = isDevToolsComponentName;
|
|
9
|
+
exports.isPerfMonitorComponentName = isPerfMonitorComponentName;
|
|
10
|
+
exports.setCaptureBuoyToolRenders = setCaptureBuoyToolRenders;
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Buoy devtools component-name sets, shared by HighlightUpdatesController's
|
|
13
|
+
// per-node exclusion walk and CommitProfiler's subtree pruning.
|
|
14
|
+
//
|
|
15
|
+
// Extracted from HighlightUpdatesController.ts verbatim — keep the split:
|
|
16
|
+
// HIGHLIGHT_* sets are ALWAYS excluded (tracking them causes infinite render
|
|
17
|
+
// loops); OTHER_DEV_TOOLS_* sets are toggleable via the excludeDevTools
|
|
18
|
+
// setting in the highlight flow (CommitProfiler always excludes both).
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
// Components that MUST always be excluded to prevent infinite loops.
|
|
22
|
+
// Includes the Highlight Updates overlay AND the Events tool (which subscribes
|
|
23
|
+
// to render callbacks via RenderTracker.onRenderEvent - tracking its renders
|
|
24
|
+
// would create an infinite callback loop).
|
|
25
|
+
const HIGHLIGHT_OVERLAY_COMPONENTS = exports.HIGHLIGHT_OVERLAY_COMPONENTS = new Set([
|
|
26
|
+
// Highlight Updates overlay components
|
|
27
|
+
"HighlightUpdatesOverlay", "HighlightUpdatesModal", "HighlightFilterView", "RenderDetailView", "RenderListItem", "RenderListItemInner", "RenderHistoryViewer", "RenderHistoryFooter", "RenderCauseBadge", "TwoLevelCauseBadge", "EnhancedCauseDisplay", "IsolatedRenderList", "IsolatedRenderListInner", "StatsDisplay", "StatsDisplayInner", "CurrentStateView", "EventStepperFooter",
|
|
28
|
+
// Events tool components - subscribes to onRenderEvent, tracking these causes infinite loops
|
|
29
|
+
"EventsModal", "UnifiedEventList", "UnifiedEventItem", "UnifiedEventDetail", "UnifiedEventFilters", "UnifiedEventViewer", "ReactQueryEventDetail", "EventsCopySettingsView"]);
|
|
30
|
+
|
|
31
|
+
// Prefixes for always-excluded components (highlight overlay + events tool)
|
|
32
|
+
const HIGHLIGHT_OVERLAY_PREFIXES = exports.HIGHLIGHT_OVERLAY_PREFIXES = ["HighlightUpdates", "RenderList", "RenderDetail", "RenderHistory", "RenderCause", "EventStepper",
|
|
33
|
+
// Events tool - subscribes to onRenderEvent, must always be excluded
|
|
34
|
+
"UnifiedEvent", "EventsModal", "EventsCopy"];
|
|
35
|
+
|
|
36
|
+
// Other dev tools components - can be toggled via excludeDevTools setting
|
|
37
|
+
const OTHER_DEV_TOOLS_COMPONENTS = exports.OTHER_DEV_TOOLS_COMPONENTS = new Set([
|
|
38
|
+
// React Buoy devtools components
|
|
39
|
+
"JsModalComponent", "JsModal", "TypePicker", "PatternInput", "PatternChip", "DetectedItemsSection", "DetectedCategoryBadge", "IdentifierBadge", "CategoryBadge", "AppRenderer", "AppOverlay", "FloatingTools", "DialDevTools", "DevToolsVisibilityProvider", "AppHostProvider", "MinimizedToolsProvider",
|
|
40
|
+
// Shared UI components used in modals
|
|
41
|
+
"ModalHeader", "TabSelector", "SectionHeader", "DraggableHeader", "WindowControls",
|
|
42
|
+
// Shared UI data viewer components (JSON diff viewers, etc.)
|
|
43
|
+
"TreeDiffViewer", "DataViewer", "SplitDiffViewer", "VirtualizedDataExplorer", "DiffSummary", "TypeLegend", "IndentGuides", "IndentGuidesOverlay", "CyberpunkInput", "DiffView", "AnswerCard", "DetailsSection", "DetailRow", "QuickActionsSection", "FilterOptionCard",
|
|
44
|
+
// Modal header sub-components
|
|
45
|
+
"SearchSection", "SearchSectionInner", "HeaderActions", "HeaderActionsInner", "MainListHeader", "FilterViewHeader", "DetailViewHeader",
|
|
46
|
+
// Floating menu components (minimized tools, dial, etc.)
|
|
47
|
+
"FloatingMenu", "FloatingDevTools", "MinimizedToolsStack", "MinimizedToolsContext", "GlitchToolButton", "ExpandablePopover", "CollapsedPeek", "ExpandedWrapper", "DialIcon", "DialMenu", "DialIconItem", "OnboardingTooltip", "MenuLauncherIcon", "DevToolsSettingsModal", "SettingsModal",
|
|
48
|
+
// Image Overlay components
|
|
49
|
+
"ImageOverlayModal", "ImageOverlayStandalone", "ToolCard", "ToolIcon", "GripVerticalIcon", "UserStatus", "Divider",
|
|
50
|
+
// Shared UI icon components (Chevrons, etc.)
|
|
51
|
+
"ChevronDown", "ChevronUp", "ChevronLeft", "ChevronRight", "ChevronDownIcon", "ChevronUpIcon", "ChevronLeftIcon", "ChevronRightIcon",
|
|
52
|
+
// React Native LogBox components (shown on reload/errors)
|
|
53
|
+
"LogBox", "LogBoxLog", "LogBoxLogNotification", "LogBoxNotificationContainer", "_LogBoxNotificationContainer", "LogBoxInspector", "LogBoxInspectorContainer", "LogBoxInspectorHeader", "LogBoxInspectorBody", "LogBoxInspectorFooter", "LogBoxInspectorMessageHeader", "LogBoxInspectorStackFrame", "LogBoxInspectorSection", "LogBoxButton", "LogBoxMessage",
|
|
54
|
+
// Perf-monitor components. The HUD re-renders on every 250ms sample tick
|
|
55
|
+
// during a benchmark recording, so leaving these in would dominate any
|
|
56
|
+
// render-capture result. NOTE: no blanket "Perf" prefix — user benchmark
|
|
57
|
+
// screens are commonly named Perf* (e.g. PerfBenchHeavy in the example app).
|
|
58
|
+
"PerfMonitorOverlay", "PerfMonitorModal", "PerfMonitorRoot", "PerfHudInline", "PerfDiagnostics", "MetricSparkline", "JsFallbackNotice", "AutomationConfigView", "AutomationProgressOverlay", "AutomationResumer", "SaveBenchmarkPrompt", "SavedCasesModal", "BatchBarReport", "BatchReportView",
|
|
59
|
+
// Perf HUD INNER components. The live-highlight exclusion walk climbs at
|
|
60
|
+
// most 30 fiber levels from a flagged host view — the HUD's metric
|
|
61
|
+
// TextInputs sit under deep TextInput/Animated wrappers, so the walk can
|
|
62
|
+
// miss the PerfMonitorOverlay root; near names are what it actually hits.
|
|
63
|
+
"PerfHudInlineInner", "PerfMonitorOverlayInner", "MiniMetric", "MetricRow", "MetricNumber", "MetricSparklineInner", "RecColumn", "PillBody", "FullBody", "TargetLine", "PagesSection", "ProbesSection"]);
|
|
64
|
+
|
|
65
|
+
// Other dev tools prefixes - can be toggled via excludeDevTools setting
|
|
66
|
+
const OTHER_DEV_TOOLS_PREFIXES = exports.OTHER_DEV_TOOLS_PREFIXES = ["JsModal", "TreeDiff", "DataViewer", "DiffView", "DiffSummary", "Virtualized",
|
|
67
|
+
// Floating menu prefixes
|
|
68
|
+
"Floating", "Minimized", "Dial", "Expandable", "Chevron", "Glitch", "Settings", "Onboarding", "DevTools", "ImageOverlay",
|
|
69
|
+
// Perf-monitor prefixes (safe: user code is Perf*, buoy code is
|
|
70
|
+
// PerfMonitor* / PerfHud*)
|
|
71
|
+
"PerfMonitor", "PerfHud"];
|
|
72
|
+
|
|
73
|
+
// ── Bench "profile our own tools" mode ──────────────────────────────────
|
|
74
|
+
// When enabled (by a dedicated benchmark screen, e.g. the example app's
|
|
75
|
+
// /tool-bench page), render capture INCLUDES Buoy's tool UI so we can
|
|
76
|
+
// benchmark our own devtools. Two groups stay excluded regardless:
|
|
77
|
+
// - highlight overlay + events tool (tracking them = infinite render loop)
|
|
78
|
+
// - perf-monitor UI (the HUD re-renders on every 250ms sample tick while
|
|
79
|
+
// recording, so its renders would dominate any capture result)
|
|
80
|
+
// Only consulted by `isDevToolsComponentName` (the CommitProfiler path) —
|
|
81
|
+
// the highlight flow's own excludeDevTools walk reads the raw sets above
|
|
82
|
+
// and is deliberately unaffected.
|
|
83
|
+
let captureBuoyToolRenders = false;
|
|
84
|
+
function setCaptureBuoyToolRenders(enabled) {
|
|
85
|
+
captureBuoyToolRenders = enabled;
|
|
86
|
+
}
|
|
87
|
+
function isCapturingBuoyToolRenders() {
|
|
88
|
+
return captureBuoyToolRenders;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Perf-monitor components without the "PerfMonitor" prefix (which is
|
|
92
|
+
// matched separately). Mirrors the perf entries in OTHER_DEV_TOOLS_*.
|
|
93
|
+
const PERF_MONITOR_ALWAYS_EXCLUDED = new Set(["PerfHudInline", "PerfHudInlineInner", "PerfDiagnostics", "MetricSparkline", "MetricSparklineInner", "JsFallbackNotice", "AutomationConfigView", "AutomationProgressOverlay", "AutomationResumer", "SaveBenchmarkPrompt", "SavedCasesModal", "BatchBarReport", "BatchReportView",
|
|
94
|
+
// HUD inner components — churn every 250ms sample tick while recording.
|
|
95
|
+
"MiniMetric", "MetricRow", "MetricNumber", "RecColumn", "PillBody", "FullBody", "TargetLine", "PagesSection", "ProbesSection"]);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* True when a name belongs to the perf-monitor UI. These stay excluded
|
|
99
|
+
* even in bench "profile our own tools" mode — the HUD re-renders on
|
|
100
|
+
* every 250ms sample tick while recording, so both capture results and
|
|
101
|
+
* live highlight boxes would be dominated by it.
|
|
102
|
+
*/
|
|
103
|
+
function isPerfMonitorComponentName(name) {
|
|
104
|
+
return name.startsWith("PerfMonitor") || name.startsWith("PerfHud") || PERF_MONITOR_ALWAYS_EXCLUDED.has(name);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* True when a component display name belongs to Buoy's own devtools UI (or
|
|
109
|
+
* RN's LogBox). Sets are checked first (O(1)), then prefixes.
|
|
110
|
+
*/
|
|
111
|
+
function isDevToolsComponentName(name) {
|
|
112
|
+
// Always excluded — tracking these causes infinite render loops.
|
|
113
|
+
if (HIGHLIGHT_OVERLAY_COMPONENTS.has(name)) return true;
|
|
114
|
+
for (let i = 0; i < HIGHLIGHT_OVERLAY_PREFIXES.length; i++) {
|
|
115
|
+
if (name.startsWith(HIGHLIGHT_OVERLAY_PREFIXES[i])) return true;
|
|
116
|
+
}
|
|
117
|
+
if (captureBuoyToolRenders) {
|
|
118
|
+
// Benchmarking our own tools: capture everything except the
|
|
119
|
+
// perf-monitor UI (its 250ms HUD churn would drown the result).
|
|
120
|
+
return isPerfMonitorComponentName(name);
|
|
121
|
+
}
|
|
122
|
+
if (OTHER_DEV_TOOLS_COMPONENTS.has(name)) return true;
|
|
123
|
+
for (let i = 0; i < OTHER_DEV_TOOLS_PREFIXES.length; i++) {
|
|
124
|
+
if (name.startsWith(OTHER_DEV_TOOLS_PREFIXES[i])) return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|