@buoy-gg/highlight-updates 7.0.21 → 7.0.23

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.
Files changed (26) hide show
  1. package/lib/commonjs/highlight-updates/components/IsolatedRenderList.js +10 -7
  2. package/lib/commonjs/highlight-updates/components/RenderDetailView.js +9 -9
  3. package/lib/commonjs/highlight-updates/components/RenderHistoryViewer.js +15 -18
  4. package/lib/commonjs/highlight-updates/utils/CommitProfiler.js +170 -8
  5. package/lib/commonjs/highlight-updates/utils/HighlightUpdatesController.js +6 -1
  6. package/lib/commonjs/highlight-updates/utils/devtoolsComponentNames.js +46 -5
  7. package/lib/commonjs/sync/highlightUpdatesSyncAdapter.js +43 -1
  8. package/lib/module/highlight-updates/components/IsolatedRenderList.js +10 -8
  9. package/lib/module/highlight-updates/components/RenderDetailView.js +10 -10
  10. package/lib/module/highlight-updates/components/RenderHistoryViewer.js +16 -20
  11. package/lib/module/highlight-updates/utils/CommitProfiler.js +171 -9
  12. package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +7 -2
  13. package/lib/module/highlight-updates/utils/devtoolsComponentNames.js +44 -4
  14. package/lib/module/sync/highlightUpdatesSyncAdapter.js +43 -1
  15. package/lib/typescript/highlight-updates/components/IsolatedRenderList.d.ts.map +1 -1
  16. package/lib/typescript/highlight-updates/components/RenderDetailView.d.ts.map +1 -1
  17. package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts +1 -1
  18. package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts.map +1 -1
  19. package/lib/typescript/highlight-updates/utils/CommitProfiler.d.ts +12 -2
  20. package/lib/typescript/highlight-updates/utils/CommitProfiler.d.ts.map +1 -1
  21. package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +1 -1
  22. package/lib/typescript/highlight-updates/utils/devtoolsComponentNames.d.ts +34 -0
  23. package/lib/typescript/highlight-updates/utils/devtoolsComponentNames.d.ts.map +1 -1
  24. package/lib/typescript/sync/highlightUpdatesSyncAdapter.d.ts +8 -0
  25. package/lib/typescript/sync/highlightUpdatesSyncAdapter.d.ts.map +1 -1
  26. package/package.json +8 -8
@@ -10,10 +10,7 @@
10
10
 
11
11
  import React, { useState, useCallback, useMemo } from "react";
12
12
  import { View, Text, TouchableOpacity, ScrollView, StyleSheet } from "react-native";
13
- import { Clock, GitBranch, Database, Lock, formatRelativeTime, SectionHeader, EventStepperFooter, buoyColors } from "@buoy-gg/shared-ui";
14
-
15
- // Free tier limit for render history events
16
- const FREE_TIER_EVENT_LIMIT = 3;
13
+ import { Clock, GitBranch, Database, Lock, formatRelativeTime, SectionHeader, EventStepperFooter, buoyColors, useLimit } from "@buoy-gg/shared-ui";
17
14
  import { TreeDiffViewer } from "@buoy-gg/shared-ui/dataViewer";
18
15
  import { EnhancedCauseDisplay, CAUSE_CONFIG } from "./RenderCauseBadge";
19
16
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -57,18 +54,16 @@ export function RenderHistoryViewer({
57
54
  return [...render.renderHistory].sort((a, b) => a.timestamp - b.timestamp);
58
55
  }, [render.renderHistory]);
59
56
 
60
- // Limit visible events for free tier (show most recent N events)
61
- const events = useMemo(() => {
62
- if (isPro) return allEvents;
63
- // Take the most recent events (last N in the sorted list)
64
- return allEvents.slice(-FREE_TIER_EVENT_LIMIT);
65
- }, [allEvents, isPro]);
57
+ // `isPro` stays a prop so callers can force the unlimited view; the tier cap
58
+ // applies otherwise. useLimit() already returns Infinity for Pro, so the two
59
+ // agree — this only preserves the prop contract for cross-package callers.
60
+ const tierLimit = useLimit("highlightUpdates.historyEvents");
61
+ const eventLimit = isPro ? Infinity : tierLimit;
66
62
 
67
- // Calculate how many events are locked
68
- const lockedEventCount = useMemo(() => {
69
- if (isPro) return 0;
70
- return Math.max(0, allEvents.length - FREE_TIER_EVENT_LIMIT);
71
- }, [allEvents.length, isPro]);
63
+ // Show the most recent N events (negative index clamps to 0, so an
64
+ // unlimited tier returns the whole list).
65
+ const events = useMemo(() => allEvents.slice(-eventLimit), [allEvents, eventLimit]);
66
+ const lockedEventCount = useMemo(() => Math.max(0, allEvents.length - eventLimit), [allEvents.length, eventLimit]);
72
67
  const totalEvents = events.length;
73
68
  const currentEvent = events[selectedEventIndex];
74
69
  const previousEvent = selectedEventIndex > 0 ? events[selectedEventIndex - 1] : null;
@@ -464,11 +459,12 @@ export function RenderHistoryFooter({
464
459
  return [...render.renderHistory].sort((a, b) => a.timestamp - b.timestamp);
465
460
  }, [render.renderHistory]);
466
461
 
467
- // Limit visible events for free tier (show most recent N events)
468
- const events = useMemo(() => {
469
- if (isPro) return allEvents;
470
- return allEvents.slice(-FREE_TIER_EVENT_LIMIT);
471
- }, [allEvents, isPro]);
462
+ // Same cap as RenderHistoryViewer above; `isPro` remains a caller override.
463
+ const tierLimit = useLimit("highlightUpdates.historyEvents");
464
+ const eventLimit = isPro ? Infinity : tierLimit;
465
+
466
+ // Most recent N (negative index clamps to 0, so unlimited returns all).
467
+ const events = useMemo(() => allEvents.slice(-eventLimit), [allEvents, eventLimit]);
472
468
  const totalEvents = events.length;
473
469
  const currentEvent = events[selectedEventIndex];
474
470
  const goToPrevious = useCallback(() => {
@@ -18,7 +18,7 @@
18
18
  * registry (see the registration in this package's index.tsx).
19
19
  */
20
20
 
21
- import { isDevToolsComponentName } from "./devtoolsComponentNames";
21
+ import { isDevToolsComponentName, isRenderLoopUnsafeName } from "./devtoolsComponentNames";
22
22
 
23
23
  // React work tags for components that render user code.
24
24
  const FunctionComponent = 0;
@@ -42,9 +42,11 @@ const TOP_COMPONENTS_CAP = 20;
42
42
  let hookInstalled = false;
43
43
  let active = false;
44
44
  let sawDurations = false;
45
+ let captureDetail = false;
45
46
  let totalCommits = 0;
46
47
  let totalRenders = 0;
47
48
  let totalRenderMs = 0;
49
+ let totalCauses = {};
48
50
  let buckets = new Map();
49
51
  function getHook() {
50
52
  if (typeof global === "undefined") return null;
@@ -116,13 +118,129 @@ function isFrameworkWrapperName(name) {
116
118
  }
117
119
  return false;
118
120
  }
119
- function recordRender(fiber) {
121
+
122
+ /** Bound on prop keys compared per render, so a fat props object can't stall a commit. */
123
+ const MAX_PROPS_COMPARED = 40;
124
+ /** Bound on distinct changed-prop key names remembered per component (detail tier). */
125
+ const MAX_CHANGED_KEYS = 5;
126
+
127
+ /**
128
+ * Are two values for the same prop key equivalent despite differing identity?
129
+ *
130
+ * Functions always count as equivalent: a fresh arrow per render is THE
131
+ * unstable-prop bug, and there's no way to compare behaviour anyway. Plain
132
+ * objects and arrays compare one level deep, which catches `style={{...}}`
133
+ * and `data={[...]}`. Anything else falls through to a real value change.
134
+ */
135
+ function isEquivalentValue(a, b) {
136
+ if (Object.is(a, b)) return true;
137
+ const ta = typeof a;
138
+ if (ta !== typeof b) return false;
139
+ if (ta === "function") return true;
140
+ if (a === null || b === null || ta !== "object") return false;
141
+ const aIsArray = Array.isArray(a);
142
+ if (aIsArray !== Array.isArray(b)) return false;
143
+ if (aIsArray) {
144
+ if (a.length !== b.length) return false;
145
+ for (let i = 0; i < a.length; i++) if (!Object.is(a[i], b[i])) return false;
146
+ return true;
147
+ }
148
+ const keysA = Object.keys(a);
149
+ const keysB = Object.keys(b);
150
+ if (keysA.length !== keysB.length) return false;
151
+ for (let i = 0; i < keysA.length; i++) {
152
+ const k = keysA[i];
153
+ if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
154
+ if (!Object.is(a[k], b[k])) return false;
155
+ }
156
+ return true;
157
+ }
158
+ /**
159
+ * Why did this fiber render?
160
+ *
161
+ * Simpler than the overlay's RenderCauseDetector, and deliberately so: that
162
+ * one carries WeakMap fallbacks and fiber-swap detection because traceUpdates
163
+ * hands it fibers at an ambiguous moment. Here we're inside onCommitFiberRoot
164
+ * walking the committed tree, so `fiber` is current and `fiber.alternate` is
165
+ * previous by definition. Porting the swap logic would be cargo-cult.
166
+ */
167
+ function detectCause(fiber, wantKeys) {
168
+ const prev = fiber.alternate;
169
+ if (!prev) return {
170
+ cause: "mount"
171
+ };
172
+ const nextProps = fiber.memoizedProps;
173
+ const prevProps = prev.memoizedProps;
174
+ if (nextProps !== prevProps && nextProps && prevProps) {
175
+ let sawChange = false;
176
+ let sawValueChange = false;
177
+ let changedKeys;
178
+ const keys = Object.keys(nextProps);
179
+ const limit = keys.length < MAX_PROPS_COMPARED ? keys.length : MAX_PROPS_COMPARED;
180
+ for (let i = 0; i < limit; i++) {
181
+ const k = keys[i];
182
+ const a = prevProps[k];
183
+ const b = nextProps[k];
184
+ if (Object.is(a, b)) continue;
185
+ sawChange = true;
186
+ if (wantKeys) {
187
+ if (!changedKeys) changedKeys = [];
188
+ if (changedKeys.length < MAX_CHANGED_KEYS) changedKeys.push(k);
189
+ }
190
+ if (!isEquivalentValue(a, b)) {
191
+ sawValueChange = true;
192
+ if (!wantKeys) break; // cause is settled; only keys need the full pass
193
+ }
194
+ }
195
+ // A removed key counts as a real change; cheap to detect via key count.
196
+ if (!sawValueChange && Object.keys(prevProps).length !== keys.length) {
197
+ sawChange = true;
198
+ sawValueChange = true;
199
+ }
200
+ if (sawChange) {
201
+ return {
202
+ cause: sawValueChange ? "props" : "propsUnstable",
203
+ changedKeys
204
+ };
205
+ }
206
+ }
207
+
208
+ // No prop change of substance. Own state/hooks next.
209
+ if (prev.memoizedState !== fiber.memoizedState) return {
210
+ cause: "hooks"
211
+ };
212
+ return {
213
+ cause: "parent"
214
+ };
215
+ }
216
+
217
+ /**
218
+ * Nearest ancestor that ALSO rendered this commit and is worth naming — i.e.
219
+ * skipping framework wrappers, which are never where a fix goes. Answers "who
220
+ * dragged this along", which is the actionable half of a cascade.
221
+ */
222
+ function nearestRenderedAncestorName(fiber) {
223
+ for (let p = fiber.return; p != null; p = p.return) {
224
+ if (!isCompositeTag(p.tag)) continue;
225
+ if ((getFlags(p) & PerformedWork) === 0) continue;
226
+ const name = getComponentName(p);
227
+ if (isFrameworkWrapperName(name)) continue;
228
+ return name;
229
+ }
230
+ return undefined;
231
+ }
232
+ function recordRender(fiber, parentName) {
120
233
  const name = getComponentName(fiber);
121
234
  const selfMs = computeSelfMs(fiber);
122
235
  if (selfMs !== null) sawDurations = true;
123
236
  const ms = selfMs ?? 0;
124
237
  totalRenders += 1;
125
238
  totalRenderMs += ms;
239
+ const {
240
+ cause,
241
+ changedKeys
242
+ } = detectCause(fiber, captureDetail);
243
+ totalCauses[cause] = (totalCauses[cause] ?? 0) + 1;
126
244
 
127
245
  // Framework wrappers count toward totals but never surface as a row.
128
246
  if (isFrameworkWrapperName(name)) return;
@@ -133,13 +251,27 @@ function recordRender(fiber) {
133
251
  bucket = {
134
252
  renders: 0,
135
253
  totalSelfMs: 0,
136
- maxSelfMs: 0
254
+ maxSelfMs: 0,
255
+ causes: {}
137
256
  };
138
257
  buckets.set(name, bucket);
139
258
  }
140
259
  bucket.renders += 1;
141
260
  bucket.totalSelfMs += ms;
142
261
  if (ms > bucket.maxSelfMs) bucket.maxSelfMs = ms;
262
+ bucket.causes[cause] = (bucket.causes[cause] ?? 0) + 1;
263
+ if (captureDetail) {
264
+ if (changedKeys) {
265
+ if (!bucket.changedProps) bucket.changedProps = new Set();
266
+ for (let i = 0; i < changedKeys.length; i++) {
267
+ if (bucket.changedProps.size >= MAX_CHANGED_KEYS) break;
268
+ bucket.changedProps.add(changedKeys[i]);
269
+ }
270
+ }
271
+ // First parent wins — a component rendered under several parents is rare,
272
+ // and the first is the one that showed up in the heaviest commit.
273
+ if (parentName && !bucket.parentName) bucket.parentName = parentName;
274
+ }
143
275
  }
144
276
 
145
277
  /**
@@ -158,13 +290,21 @@ function handleCommit(root) {
158
290
  let skipSubtree = false;
159
291
  const composite = isCompositeTag(fiber.tag);
160
292
  if (composite) {
161
- // Prune Buoy's own devtools UI wholesale.
162
- if (isDevToolsComponentName(getComponentName(fiber))) {
293
+ const compositeName = getComponentName(fiber);
294
+ // Only Buoy's render-tracking UI gets its SUBTREE pruned — walking it
295
+ // feeds its own renders back in and loops. Everything else that's merely
296
+ // devtools noise is skipped as a NODE and we keep descending: app
297
+ // components live underneath Buoy chrome, and pruning a subtree on a name
298
+ // match is what made a stray "Virtualized" prefix hide every FlatList in
299
+ // every app.
300
+ if (isRenderLoopUnsafeName(compositeName)) {
163
301
  skipSubtree = true;
302
+ } else if (isDevToolsComponentName(compositeName)) {
303
+ // skip this node, keep walking its children
164
304
  } else if ((getFlags(fiber) & PerformedWork) !== 0) {
165
305
  // PerformedWork is the only trustworthy "rendered this commit"
166
306
  // signal — actualDuration is stale on bailed-out fibers.
167
- recordRender(fiber);
307
+ recordRender(fiber, captureDetail ? nearestRenderedAncestorName(fiber) : undefined);
168
308
  }
169
309
  }
170
310
  if (!skipSubtree) {
@@ -227,14 +367,24 @@ function isCapturing() {
227
367
  return active;
228
368
  }
229
369
 
230
- /** Begin a capture window, discarding any previous unclaimed data. */
231
- function startCapture() {
370
+ /**
371
+ * Begin a capture window, discarding any previous unclaimed data.
372
+ *
373
+ * `detail` turns on the allocating tier — changed prop key names and parent
374
+ * component names. Causes themselves are always classified: it costs a shallow
375
+ * prop compare, against a `computeSelfMs` that already walks every direct
376
+ * child on every render, and the wasted-render total is the number most worth
377
+ * seeing without having to know to ask for it.
378
+ */
379
+ function startCapture(opts) {
232
380
  if (!ensureHookInstalled()) return;
233
381
  buckets = new Map();
234
382
  sawDurations = false;
383
+ captureDetail = opts?.detail === true;
235
384
  totalCommits = 0;
236
385
  totalRenders = 0;
237
386
  totalRenderMs = 0;
387
+ totalCauses = {};
238
388
  active = true;
239
389
  }
240
390
 
@@ -250,19 +400,31 @@ function stopCapture() {
250
400
  renders: b.renders,
251
401
  totalMs: round2(b.totalSelfMs),
252
402
  maxMs: round2(b.maxSelfMs),
253
- avgMs: b.renders > 0 ? round2(b.totalSelfMs / b.renders) : 0
403
+ avgMs: b.renders > 0 ? round2(b.totalSelfMs / b.renders) : 0,
404
+ causes: b.causes,
405
+ ...(b.changedProps && b.changedProps.size > 0 ? {
406
+ changedProps: Array.from(b.changedProps)
407
+ } : null),
408
+ ...(b.parentName ? {
409
+ parentName: b.parentName
410
+ } : null)
254
411
  }));
255
412
  const summary = {
256
413
  totalCommits,
257
414
  totalRenders,
258
415
  capturedDurations: sawDurations,
259
416
  totalRenderMs: round2(totalRenderMs),
417
+ // Parent cascades and identity-only prop churn are both removable without
418
+ // changing behaviour — together they're the recoverable share.
419
+ wastedRenders: (totalCauses.parent ?? 0) + (totalCauses.propsUnstable ?? 0),
420
+ causes: totalCauses,
260
421
  topComponents
261
422
  };
262
423
  buckets = new Map();
263
424
  totalCommits = 0;
264
425
  totalRenders = 0;
265
426
  totalRenderMs = 0;
427
+ totalCauses = {};
266
428
  return summary;
267
429
  }
268
430
  function round2(n) {
@@ -33,7 +33,7 @@ import { RenderTracker } from "./RenderTracker";
33
33
  import { PerformanceLogger, markEventReceived } from "./PerformanceLogger";
34
34
  import { detectRenderCause, clearRenderCauseState } from "./RenderCauseDetector";
35
35
  import { notifySubscriberCountChange } from "@buoy-gg/shared-ui";
36
- import { HIGHLIGHT_OVERLAY_COMPONENTS, HIGHLIGHT_OVERLAY_PREFIXES, OTHER_DEV_TOOLS_COMPONENTS, OTHER_DEV_TOOLS_PREFIXES, isCapturingBuoyToolRenders, isPerfMonitorComponentName } from "./devtoolsComponentNames";
36
+ import { HIGHLIGHT_OVERLAY_COMPONENTS, HIGHLIGHT_OVERLAY_PREFIXES, OTHER_DEV_TOOLS_COMPONENTS, OTHER_DEV_TOOLS_EXACT, OTHER_DEV_TOOLS_PREFIXES, isCapturingBuoyToolRenders, isPerfMonitorComponentName } from "./devtoolsComponentNames";
37
37
  // State
38
38
  let globalEnabled = false; // User-controlled toggle for visual highlights
39
39
  // When true, tracking + measurement + storage all run as normal, but the
@@ -551,7 +551,12 @@ function isOurOverlayNode(stateNode) {
551
551
 
552
552
  // Only check other dev tools if excludeDevTools is enabled
553
553
  if (excludeDevTools) {
554
- if (OTHER_DEV_TOOLS_COMPONENTS.has(name)) {
554
+ // Two sets, same meaning: COMPONENTS is the original list, EXACT
555
+ // holds the names that used to be matched by over-broad prefixes
556
+ // (the "Virtualized" that hid every FlatList, the "Dial" that hid
557
+ // every Dialog). Both paths must consult both, or Buoy's own chrome
558
+ // starts flashing highlight boxes in one of them and not the other.
559
+ if (OTHER_DEV_TOOLS_COMPONENTS.has(name) || OTHER_DEV_TOOLS_EXACT.has(name)) {
555
560
  result = true;
556
561
  break;
557
562
  }
@@ -57,16 +57,37 @@ export const OTHER_DEV_TOOLS_COMPONENTS = new Set([
57
57
  // separately). The modal/table re-render on every 250ms snapshot tick.
58
58
  "TaskTable", "TaskRow", "BusySparkline"]);
59
59
 
60
- // Other dev tools prefixes - can be toggled via excludeDevTools setting
61
- export const OTHER_DEV_TOOLS_PREFIXES = ["JsModal", "TreeDiff", "DataViewer", "DiffView", "DiffSummary", "Virtualized",
62
- // Floating menu prefixes
63
- "Floating", "Minimized", "Dial", "Expandable", "Chevron", "Glitch", "Settings", "Onboarding", "DevTools", "ImageOverlay",
60
+ /**
61
+ * Other dev-tools prefixes — noise reduction, toggleable via excludeDevTools.
62
+ *
63
+ * EVERY ENTRY HERE MUST BE BUOY-NAMESPACED. These are prefix matches, and a
64
+ * match hides the component from render reports. Generic English words do not
65
+ * belong in this list, because app components share them.
66
+ *
67
+ * This list used to contain `Virtualized`, meant for Buoy's own
68
+ * VirtualizedDataExplorer. React Native's FlatList renders a `VirtualizedList`,
69
+ * so the prefix matched — and because CommitProfiler pruned the whole subtree
70
+ * on a match, Buoy could not see inside ANY FlatList or SectionList in ANY
71
+ * app. Lists are the number-one source of render problems in React Native, and
72
+ * the render tools were blind to all of them. `Dial` was the same trap:
73
+ * "Dialog".startsWith("Dial") is true, so every Dialog vanished too.
74
+ *
75
+ * Names that are generic on their own now live in OTHER_DEV_TOOLS_COMPONENTS
76
+ * as exact matches instead.
77
+ */
78
+ export const OTHER_DEV_TOOLS_PREFIXES = ["JsModal", "ImageOverlay",
64
79
  // Perf-monitor prefixes (safe: user code is Perf*, buoy code is
65
80
  // PerfMonitor* / PerfHud*)
66
81
  "PerfMonitor", "PerfHud",
67
82
  // JS TOP (js-top package)
68
83
  "JsTop"];
69
84
 
85
+ /**
86
+ * Buoy component names that were previously matched by over-broad prefixes.
87
+ * Exact matches only — see the warning above.
88
+ */
89
+ export const OTHER_DEV_TOOLS_EXACT = new Set(["VirtualizedDataExplorer", "TreeDiffView", "TreeDiffRow", "DataViewer", "DiffView", "DiffSummary", "FloatingDevTools", "FloatingMenu", "FloatingBubble", "MinimizedDock", "DialMenu", "DialItem", "ExpandableSection", "ChevronIcon", "GlitchText", "SettingsModal", "SettingsRow", "OnboardingFlow", "DevToolsMenu"]);
90
+
70
91
  // ── Bench "profile our own tools" mode ──────────────────────────────────
71
92
  // When enabled (by a dedicated benchmark screen, e.g. the example app's
72
93
  // /tool-bench page), render capture INCLUDES Buoy's tool UI so we can
@@ -131,8 +152,27 @@ export function isDevToolsComponentName(name) {
131
152
  return isPerfMonitorComponentName(name) || isJsTopComponentName(name);
132
153
  }
133
154
  if (OTHER_DEV_TOOLS_COMPONENTS.has(name)) return true;
155
+ if (OTHER_DEV_TOOLS_EXACT.has(name)) return true;
134
156
  for (let i = 0; i < OTHER_DEV_TOOLS_PREFIXES.length; i++) {
135
157
  if (name.startsWith(OTHER_DEV_TOOLS_PREFIXES[i])) return true;
136
158
  }
137
159
  return false;
160
+ }
161
+
162
+ /**
163
+ * Is this name one whose SUBTREE must be skipped, not merely the node?
164
+ *
165
+ * Only the always-excluded group qualifies: walking Buoy's own render-tracking
166
+ * UI feeds its own renders back in and loops. Everything else in
167
+ * `isDevToolsComponentName` is noise reduction — hide the row, but keep
168
+ * walking, because app components live underneath Buoy chrome (a tool modal
169
+ * renders the host app behind it) and pruning on a NAME is how one bad prefix
170
+ * cost us every FlatList in every app.
171
+ */
172
+ export function isRenderLoopUnsafeName(name) {
173
+ if (HIGHLIGHT_OVERLAY_COMPONENTS.has(name)) return true;
174
+ for (let i = 0; i < HIGHLIGHT_OVERLAY_PREFIXES.length; i++) {
175
+ if (name.startsWith(HIGHLIGHT_OVERLAY_PREFIXES[i])) return true;
176
+ }
177
+ return false;
138
178
  }
@@ -3,6 +3,7 @@
3
3
  import HighlightUpdatesController from "../highlight-updates/utils/HighlightUpdatesController";
4
4
  import { RenderTracker } from "../highlight-updates/utils/RenderTracker";
5
5
  import { describeScreen, tapElement } from "../highlight-updates/utils/screenElements";
6
+ import { CommitProfiler } from "../highlight-updates/utils/CommitProfiler";
6
7
  function ensureInitialized() {
7
8
  if (!HighlightUpdatesController.isInitialized()) {
8
9
  HighlightUpdatesController.initialize();
@@ -60,6 +61,41 @@ function jsonSafeRender(render) {
60
61
  }
61
62
  }
62
63
 
64
+ /**
65
+ * Measurement window for automated callers (MCP `measure_renders`).
66
+ *
67
+ * Backed by CommitProfiler, NOT the RenderTracker that feeds the overlay, and
68
+ * the difference is the whole point. RenderTracker tracks HOST VIEWS: ask it
69
+ * why `DealRow` re-rendered and it answers about the native `View` inside it —
70
+ * "props changed: accessibilityState, accessibilityValue" — which is true and
71
+ * useless. CommitProfiler walks COMPOSITE fibers, so it answers about the
72
+ * component you named: which of ITS props changed, whether they changed by
73
+ * value or only by identity, and which parent dragged it along.
74
+ *
75
+ * It also touches none of the user's overlay state — no enabling, no clearing
76
+ * counters someone was collecting, nothing drawn on screen. A measurement is
77
+ * invisible.
78
+ */
79
+ function beginMeasurement() {
80
+ if (!CommitProfiler.isSupported()) {
81
+ return {
82
+ ok: false,
83
+ reason: "render capture needs a dev build with the React DevTools hook available"
84
+ };
85
+ }
86
+ CommitProfiler.startCapture({
87
+ detail: true
88
+ });
89
+ return {
90
+ ok: true
91
+ };
92
+ }
93
+ function endMeasurement() {
94
+ return {
95
+ summary: CommitProfiler.stopCapture()
96
+ };
97
+ }
98
+
63
99
  /**
64
100
  * Sync adapter for the highlight-updates tool, consumed by
65
101
  * @buoy-gg/external-sync's `useExternalSync` (structurally matches its
@@ -76,7 +112,9 @@ function jsonSafeRender(render) {
76
112
  * stay on the device. Dashboards fetch them via `getRenderDetail`.
77
113
  */
78
114
  export const highlightUpdatesSyncAdapter = {
79
- version: 2,
115
+ // v3: beginMeasurement / endMeasurement — a CommitProfiler capture window
116
+ // for automated callers, with per-component causes. Invisible to the user.
117
+ version: 3,
80
118
  getSnapshot: () => ({
81
119
  enabled: HighlightUpdatesController.isEnabled(),
82
120
  frozen: HighlightUpdatesController.getFrozen(),
@@ -98,6 +136,10 @@ export const highlightUpdatesSyncAdapter = {
98
136
  };
99
137
  },
100
138
  actions: {
139
+ // Capture window for automated callers (MCP `measure_renders`). Pairs:
140
+ // begin -> drive the UI -> end. `end` always restores.
141
+ beginMeasurement: () => beginMeasurement(),
142
+ endMeasurement: () => endMeasurement(),
101
143
  /**
102
144
  * On-demand detail: one component's real renderHistory + lastRenderCause
103
145
  * (including hookChanges). Wire-form snapshots omit those.
@@ -1 +1 @@
1
- {"version":3,"file":"IsolatedRenderList.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/components/IsolatedRenderList.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAkE,MAAM,OAAO,CAAC;AAEvF,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAO9F,UAAU,uBAAuB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,cAAc,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IAC5F,aAAa,EAAE,CAAC,KAAK,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClF,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IACrD,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,iBAAS,uBAAuB,CAAC,EAC/B,UAAU,EACV,OAAO,EACP,cAAc,EACd,aAAa,EACb,eAAe,EACf,UAAU,EACV,KAAa,GACd,EAAE,uBAAuB,qBAoGzB;AAGD,eAAO,MAAM,kBAAkB,2DAAgC,CAAC;AA0ChE,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"IsolatedRenderList.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/components/IsolatedRenderList.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAkE,MAAM,OAAO,CAAC;AAEvF,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAK9F,UAAU,uBAAuB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,cAAc,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IAC5F,aAAa,EAAE,CAAC,KAAK,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClF,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IACrD,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,iBAAS,uBAAuB,CAAC,EAC/B,UAAU,EACV,OAAO,EACP,cAAc,EACd,aAAa,EACb,eAAe,EACf,UAAU,EACV,KAAa,GACd,EAAE,uBAAuB,qBAwGzB;AAGD,eAAO,MAAM,kBAAkB,2DAAgC,CAAC;AA0ChE,eAAe,kBAAkB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"RenderDetailView.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/components/RenderDetailView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAY9D,OAAO,KAAK,EAAE,aAAa,EAAc,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAIvF,UAAU,qBAAqB;IAC7B,MAAM,EAAE,aAAa,CAAC;IACtB,iEAAiE;IACjE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,yDAAyD;IACzD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,4DAA4D;IAC5D,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,GAAG,SAAS,KAAK,IAAI,CAAC;IAC5E,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAMD,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,qBAA6B,EAC7B,kBAAkB,EAAE,aAAa,EACjC,kBAAkB,EAAE,gBAAgB,EACpC,WAAW,EACX,KAAa,GACd,EAAE,qBAAqB,qBAgIvB;AAmnBD,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"RenderDetailView.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/components/RenderDetailView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAa9D,OAAO,KAAK,EAAE,aAAa,EAAc,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAIvF,UAAU,qBAAqB;IAC7B,MAAM,EAAE,aAAa,CAAC;IACtB,iEAAiE;IACjE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,yDAAyD;IACzD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,4DAA4D;IAC5D,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,GAAG,SAAS,KAAK,IAAI,CAAC;IAC5E,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAID,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,qBAA6B,EAC7B,kBAAkB,EAAE,aAAa,EACjC,kBAAkB,EAAE,gBAAgB,EACpC,WAAW,EACX,KAAa,GACd,EAAE,qBAAqB,qBAqIvB;AAmnBD,eAAe,gBAAgB,CAAC"}
@@ -15,7 +15,7 @@ interface RenderHistoryViewerProps {
15
15
  selectedEventIndex?: number;
16
16
  /** Callback when event index changes */
17
17
  onEventIndexChange?: (index: number) => void;
18
- /** If true, show all events. Otherwise limit to FREE_TIER_EVENT_LIMIT */
18
+ /** If true, show all events. Otherwise the tier cap applies. */
19
19
  isPro?: boolean;
20
20
  }
21
21
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"RenderHistoryViewer.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/components/RenderHistoryViewer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAgB9D,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,wBAAwB,CAAC;AAGzE,UAAU,wBAAwB;IAChC,MAAM,EAAE,aAAa,CAAC;IACtB,uDAAuD;IACvD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,wCAAwC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wCAAwC;IACxC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,yEAAyE;IACzE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAcD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,qBAA6B,EAC7B,kBAAkB,EAAE,aAAa,EACjC,kBAAkB,EAAE,gBAAgB,EACpC,KAAa,GACd,EAAE,wBAAwB,qBA4L1B;AAkRD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,kBAAsB,EACtB,kBAA6B,EAC7B,KAAa,GACd,EAAE;IACD,MAAM,EAAE,aAAa,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,4BAyCA;AA6YD,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"RenderHistoryViewer.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/components/RenderHistoryViewer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAc9D,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,wBAAwB,CAAC;AAGzE,UAAU,wBAAwB;IAChC,MAAM,EAAE,aAAa,CAAC;IACtB,uDAAuD;IACvD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,wCAAwC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wCAAwC;IACxC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAcD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,qBAA6B,EAC7B,kBAAkB,EAAE,aAAa,EACjC,kBAAkB,EAAE,gBAAgB,EACpC,KAAa,GACd,EAAE,wBAAwB,qBAiM1B;AAkRD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,kBAAsB,EACtB,kBAA6B,EAC7B,KAAa,GACd,EAAE;IACD,MAAM,EAAE,aAAa,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,4BA6CA;AA6YD,eAAe,mBAAmB,CAAC"}
@@ -18,8 +18,18 @@
18
18
  import type { RenderCaptureSummary } from "@buoy-gg/shared-ui";
19
19
  declare function isSupported(): boolean;
20
20
  declare function isCapturing(): boolean;
21
- /** Begin a capture window, discarding any previous unclaimed data. */
22
- declare function startCapture(): void;
21
+ /**
22
+ * Begin a capture window, discarding any previous unclaimed data.
23
+ *
24
+ * `detail` turns on the allocating tier — changed prop key names and parent
25
+ * component names. Causes themselves are always classified: it costs a shallow
26
+ * prop compare, against a `computeSelfMs` that already walks every direct
27
+ * child on every render, and the wasted-render total is the number most worth
28
+ * seeing without having to know to ask for it.
29
+ */
30
+ declare function startCapture(opts?: {
31
+ detail?: boolean;
32
+ }): void;
23
33
  /** End the window and return the aggregate (null if never started). */
24
34
  declare function stopCapture(): RenderCaptureSummary | null;
25
35
  export declare const CommitProfiler: {
@@ -1 +1 @@
1
- {"version":3,"file":"CommitProfiler.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/utils/CommitProfiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,oBAAoB,CAAC;AA6Q5B,iBAAS,WAAW,IAAI,OAAO,CAI9B;AAED,iBAAS,WAAW,IAAI,OAAO,CAE9B;AAED,sEAAsE;AACtE,iBAAS,YAAY,IAAI,IAAI,CAQ5B;AAED,uEAAuE;AACvE,iBAAS,WAAW,IAAI,oBAAoB,GAAG,IAAI,CAkClD;AAMD,eAAO,MAAM,cAAc;;;;;CAK1B,CAAC"}
1
+ {"version":3,"file":"CommitProfiler.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/utils/CommitProfiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EACV,oBAAoB,EAGrB,MAAM,oBAAoB,CAAC;AA4a5B,iBAAS,WAAW,IAAI,OAAO,CAI9B;AAED,iBAAS,WAAW,IAAI,OAAO,CAE9B;AAED;;;;;;;;GAQG;AACH,iBAAS,YAAY,CAAC,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAUvD;AAED,uEAAuE;AACvE,iBAAS,WAAW,IAAI,oBAAoB,GAAG,IAAI,CA4ClD;AAMD,eAAO,MAAM,cAAc;;;;;CAK1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"HighlightUpdatesController.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/utils/HighlightUpdatesController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"HighlightUpdatesController.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/utils/HighlightUpdatesController.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,30 @@
1
1
  export declare const HIGHLIGHT_OVERLAY_COMPONENTS: Set<string>;
2
2
  export declare const HIGHLIGHT_OVERLAY_PREFIXES: string[];
3
3
  export declare const OTHER_DEV_TOOLS_COMPONENTS: Set<string>;
4
+ /**
5
+ * Other dev-tools prefixes — noise reduction, toggleable via excludeDevTools.
6
+ *
7
+ * EVERY ENTRY HERE MUST BE BUOY-NAMESPACED. These are prefix matches, and a
8
+ * match hides the component from render reports. Generic English words do not
9
+ * belong in this list, because app components share them.
10
+ *
11
+ * This list used to contain `Virtualized`, meant for Buoy's own
12
+ * VirtualizedDataExplorer. React Native's FlatList renders a `VirtualizedList`,
13
+ * so the prefix matched — and because CommitProfiler pruned the whole subtree
14
+ * on a match, Buoy could not see inside ANY FlatList or SectionList in ANY
15
+ * app. Lists are the number-one source of render problems in React Native, and
16
+ * the render tools were blind to all of them. `Dial` was the same trap:
17
+ * "Dialog".startsWith("Dial") is true, so every Dialog vanished too.
18
+ *
19
+ * Names that are generic on their own now live in OTHER_DEV_TOOLS_COMPONENTS
20
+ * as exact matches instead.
21
+ */
4
22
  export declare const OTHER_DEV_TOOLS_PREFIXES: string[];
23
+ /**
24
+ * Buoy component names that were previously matched by over-broad prefixes.
25
+ * Exact matches only — see the warning above.
26
+ */
27
+ export declare const OTHER_DEV_TOOLS_EXACT: Set<string>;
5
28
  export declare function setCaptureBuoyToolRenders(enabled: boolean): void;
6
29
  export declare function isCapturingBuoyToolRenders(): boolean;
7
30
  /**
@@ -22,4 +45,15 @@ export declare function isJsTopComponentName(name: string): boolean;
22
45
  * RN's LogBox). Sets are checked first (O(1)), then prefixes.
23
46
  */
24
47
  export declare function isDevToolsComponentName(name: string): boolean;
48
+ /**
49
+ * Is this name one whose SUBTREE must be skipped, not merely the node?
50
+ *
51
+ * Only the always-excluded group qualifies: walking Buoy's own render-tracking
52
+ * UI feeds its own renders back in and loops. Everything else in
53
+ * `isDevToolsComponentName` is noise reduction — hide the row, but keep
54
+ * walking, because app components live underneath Buoy chrome (a tool modal
55
+ * renders the host app behind it) and pruning on a NAME is how one bad prefix
56
+ * cost us every FlatList in every app.
57
+ */
58
+ export declare function isRenderLoopUnsafeName(name: string): boolean;
25
59
  //# sourceMappingURL=devtoolsComponentNames.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"devtoolsComponentNames.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/utils/devtoolsComponentNames.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,4BAA4B,aA4BvC,CAAC;AAGH,eAAO,MAAM,0BAA0B,UAWtC,CAAC;AAGF,eAAO,MAAM,0BAA0B,aAwIrC,CAAC;AAGH,eAAO,MAAM,wBAAwB,UAwBpC,CAAC;AAcF,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEhE;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AA8BD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMhE;AAUD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAiB7D"}
1
+ {"version":3,"file":"devtoolsComponentNames.d.ts","sourceRoot":"","sources":["../../../../src/highlight-updates/utils/devtoolsComponentNames.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,4BAA4B,aA4BvC,CAAC;AAGH,eAAO,MAAM,0BAA0B,UAWtC,CAAC;AAGF,eAAO,MAAM,0BAA0B,aAwIrC,CAAC;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,wBAAwB,UASpC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,aAoBhC,CAAC;AAcH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEhE;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AA8BD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMhE;AAUD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkB7D;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAM5D"}
@@ -1,4 +1,5 @@
1
1
  import type { TrackedRender } from "../highlight-updates/utils/RenderTracker";
2
+ import type { RenderCaptureSummary } from "@buoy-gg/shared-ui";
2
3
  /**
3
4
  * Sync adapter for the highlight-updates tool, consumed by
4
5
  * @buoy-gg/external-sync's `useExternalSync` (structurally matches its
@@ -28,6 +29,13 @@ export declare const highlightUpdatesSyncAdapter: {
28
29
  };
29
30
  subscribe: (onChange: () => void) => () => void;
30
31
  actions: {
32
+ beginMeasurement: () => {
33
+ ok: boolean;
34
+ reason?: string;
35
+ };
36
+ endMeasurement: () => {
37
+ summary: RenderCaptureSummary | null;
38
+ };
31
39
  /**
32
40
  * On-demand detail: one component's real renderHistory + lastRenderCause
33
41
  * (including hookChanges). Wire-form snapshots omit those.