@buoy-gg/highlight-updates 2.1.12 → 2.1.13

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 (64) hide show
  1. package/lib/commonjs/highlight-updates/HighlightUpdatesOverlay.js +1 -285
  2. package/lib/commonjs/highlight-updates/components/HighlightFilterView.js +1 -1371
  3. package/lib/commonjs/highlight-updates/components/HighlightUpdatesModal.js +1 -591
  4. package/lib/commonjs/highlight-updates/components/IdentifierBadge.js +1 -267
  5. package/lib/commonjs/highlight-updates/components/IsolatedRenderList.js +1 -178
  6. package/lib/commonjs/highlight-updates/components/ModalHeaderContent.js +1 -303
  7. package/lib/commonjs/highlight-updates/components/RenderCauseBadge.js +1 -500
  8. package/lib/commonjs/highlight-updates/components/RenderDetailView.js +1 -830
  9. package/lib/commonjs/highlight-updates/components/RenderHistoryViewer.js +1 -894
  10. package/lib/commonjs/highlight-updates/components/RenderListItem.js +1 -220
  11. package/lib/commonjs/highlight-updates/components/StatsDisplay.js +1 -70
  12. package/lib/commonjs/highlight-updates/components/index.js +1 -97
  13. package/lib/commonjs/highlight-updates/utils/HighlightUpdatesController.js +1 -1435
  14. package/lib/commonjs/highlight-updates/utils/PerformanceLogger.js +1 -359
  15. package/lib/commonjs/highlight-updates/utils/ProfilerInterceptor.js +1 -371
  16. package/lib/commonjs/highlight-updates/utils/RenderCauseDetector.js +1 -1828
  17. package/lib/commonjs/highlight-updates/utils/RenderTracker.js +1 -903
  18. package/lib/commonjs/highlight-updates/utils/ViewTypeMapper.js +1 -264
  19. package/lib/commonjs/highlight-updates/utils/renderExportFormatter.js +1 -58
  20. package/lib/commonjs/index.js +1 -311
  21. package/lib/commonjs/preset.js +1 -278
  22. package/lib/module/highlight-updates/HighlightUpdatesOverlay.js +1 -278
  23. package/lib/module/highlight-updates/components/HighlightFilterView.js +1 -1365
  24. package/lib/module/highlight-updates/components/HighlightUpdatesModal.js +1 -585
  25. package/lib/module/highlight-updates/components/IdentifierBadge.js +1 -259
  26. package/lib/module/highlight-updates/components/IsolatedRenderList.js +1 -174
  27. package/lib/module/highlight-updates/components/ModalHeaderContent.js +1 -298
  28. package/lib/module/highlight-updates/components/RenderCauseBadge.js +1 -491
  29. package/lib/module/highlight-updates/components/RenderDetailView.js +1 -826
  30. package/lib/module/highlight-updates/components/RenderHistoryViewer.js +1 -888
  31. package/lib/module/highlight-updates/components/RenderListItem.js +1 -215
  32. package/lib/module/highlight-updates/components/StatsDisplay.js +1 -67
  33. package/lib/module/highlight-updates/components/index.js +1 -16
  34. package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +1 -1431
  35. package/lib/module/highlight-updates/utils/PerformanceLogger.js +1 -353
  36. package/lib/module/highlight-updates/utils/ProfilerInterceptor.js +1 -358
  37. package/lib/module/highlight-updates/utils/RenderCauseDetector.js +1 -1818
  38. package/lib/module/highlight-updates/utils/RenderTracker.js +1 -900
  39. package/lib/module/highlight-updates/utils/ViewTypeMapper.js +1 -255
  40. package/lib/module/highlight-updates/utils/renderExportFormatter.js +1 -54
  41. package/lib/module/index.js +1 -71
  42. package/lib/module/preset.js +1 -272
  43. package/package.json +7 -7
  44. package/lib/typescript/highlight-updates/HighlightUpdatesOverlay.d.ts.map +0 -1
  45. package/lib/typescript/highlight-updates/components/HighlightFilterView.d.ts.map +0 -1
  46. package/lib/typescript/highlight-updates/components/HighlightUpdatesModal.d.ts.map +0 -1
  47. package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts.map +0 -1
  48. package/lib/typescript/highlight-updates/components/IsolatedRenderList.d.ts.map +0 -1
  49. package/lib/typescript/highlight-updates/components/ModalHeaderContent.d.ts.map +0 -1
  50. package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts.map +0 -1
  51. package/lib/typescript/highlight-updates/components/RenderDetailView.d.ts.map +0 -1
  52. package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts.map +0 -1
  53. package/lib/typescript/highlight-updates/components/RenderListItem.d.ts.map +0 -1
  54. package/lib/typescript/highlight-updates/components/StatsDisplay.d.ts.map +0 -1
  55. package/lib/typescript/highlight-updates/components/index.d.ts.map +0 -1
  56. package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +0 -1
  57. package/lib/typescript/highlight-updates/utils/PerformanceLogger.d.ts.map +0 -1
  58. package/lib/typescript/highlight-updates/utils/ProfilerInterceptor.d.ts.map +0 -1
  59. package/lib/typescript/highlight-updates/utils/RenderCauseDetector.d.ts.map +0 -1
  60. package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts.map +0 -1
  61. package/lib/typescript/highlight-updates/utils/ViewTypeMapper.d.ts.map +0 -1
  62. package/lib/typescript/highlight-updates/utils/renderExportFormatter.d.ts.map +0 -1
  63. package/lib/typescript/index.d.ts.map +0 -1
  64. package/lib/typescript/preset.d.ts.map +0 -1
@@ -1,585 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * HighlightUpdatesModal
5
- *
6
- * Main modal interface for the Highlight Updates dev tool.
7
- * Shows a list of tracked component renders with controls for
8
- * start/stop, clear, and filtering.
9
- *
10
- * PERFORMANCE OPTIMIZED:
11
- * - Uses isolated components to prevent parent re-renders
12
- * - Stats display is isolated (StatsDisplay)
13
- * - Render list is isolated (IsolatedRenderList)
14
- * - Header components are memoized
15
- * - Uses refs for values not displayed in UI
16
- */
17
-
18
- import React, { useState, useEffect, useRef, useCallback } from "react";
19
- import { View, Text, StyleSheet } from "react-native";
20
- import { Power, JsModal, devToolsStorageKeys, persistentStorage, buoyColors, TickProvider } from "@buoy-gg/shared-ui";
21
- import { useIsPro } from "@buoy-gg/license";
22
- import HighlightUpdatesController from "../utils/HighlightUpdatesController";
23
- import { RenderTracker } from "../utils/RenderTracker";
24
- import { RenderDetailView } from "./RenderDetailView";
25
- import { EventStepperFooter } from "@buoy-gg/shared-ui";
26
- import { HighlightFilterView } from "./HighlightFilterView";
27
- import { IsolatedRenderList } from "./IsolatedRenderList";
28
- import { MainListHeader, FilterViewHeader, DetailViewHeader } from "./ModalHeaderContent";
29
- import { RenderHistoryViewer, RenderHistoryFooter } from "./RenderHistoryViewer";
30
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
31
- // Disabled banner - memoized since props rarely change
32
- const DisabledBanner = /*#__PURE__*/React.memo(function DisabledBanner() {
33
- return /*#__PURE__*/_jsxs(View, {
34
- style: styles.disabledBanner,
35
- children: [/*#__PURE__*/_jsx(Power, {
36
- size: 14,
37
- color: buoyColors.warning
38
- }), /*#__PURE__*/_jsx(Text, {
39
- style: styles.disabledText,
40
- children: "Render tracking is disabled"
41
- })]
42
- });
43
- });
44
-
45
- /**
46
- * Format render data for copying to clipboard as JSON
47
- */
48
- function formatRenderDataForClipboard() {
49
- const renders = RenderTracker.getRenders();
50
- const exportData = {
51
- exportedAt: new Date().toISOString(),
52
- totalComponents: renders.length,
53
- totalRenders: renders.reduce((sum, r) => sum + r.renderCount, 0),
54
- components: renders.map(render => ({
55
- componentName: render.componentName || render.displayName,
56
- viewType: render.viewType,
57
- nativeTag: render.nativeTag,
58
- testID: render.testID,
59
- nativeID: render.nativeID,
60
- accessibilityLabel: render.accessibilityLabel,
61
- renderCount: render.renderCount,
62
- firstRenderTime: render.firstRenderTime,
63
- lastRenderTime: render.lastRenderTime,
64
- lastRenderCause: render.lastRenderCause ? {
65
- type: render.lastRenderCause.type,
66
- componentCause: render.lastRenderCause.componentCause,
67
- changedKeys: render.lastRenderCause.changedKeys,
68
- hookChanges: render.lastRenderCause.hookChanges?.map(h => ({
69
- type: h.type,
70
- index: h.index,
71
- description: h.description
72
- }))
73
- } : null,
74
- renderHistory: render.renderHistory?.map(event => ({
75
- renderNumber: event.renderNumber,
76
- timestamp: event.timestamp,
77
- cause: {
78
- type: event.cause.type,
79
- componentCause: event.cause.componentCause,
80
- changedKeys: event.cause.changedKeys
81
- }
82
- }))
83
- }))
84
- };
85
- return JSON.stringify(exportData, null, 2);
86
- }
87
- export function HighlightUpdatesModal({
88
- visible,
89
- onClose,
90
- onBack,
91
- onMinimize,
92
- enableSharedModalDimensions = false,
93
- initialNativeTag,
94
- onInitialNativeTagHandled
95
- }) {
96
- const isPro = useIsPro();
97
-
98
- // ============================================================================
99
- // TRACKING STATE - subscribed via isolated component
100
- // ============================================================================
101
- const [isTracking, setIsTracking] = useState(false);
102
- const [isFrozen, setIsFrozen] = useState(() => HighlightUpdatesController.getFrozen());
103
-
104
- // Track if there are renders for header clear button state
105
- const [hasRenders, setHasRenders] = useState(() => RenderTracker.getStats().totalComponents > 0);
106
-
107
- // ============================================================================
108
- // UI STATE - kept in parent for view switching
109
- // ============================================================================
110
- const [selectedRender, setSelectedRender] = useState(null);
111
- const [selectedRenderIndex, setSelectedRenderIndex] = useState(0);
112
- const [showFilterView, setShowFilterView] = useState(false);
113
- const [filterViewActiveTab, setFilterViewActiveTab] = useState("filters");
114
- const [detailViewActiveTab, setDetailViewActiveTab] = useState("details");
115
- const [historyEventIndex, setHistoryEventIndex] = useState(0);
116
- const [searchText, setSearchText] = useState("");
117
- const [isSearchActive, setIsSearchActive] = useState(false);
118
- const searchInputRef = useRef(null);
119
-
120
- // Track the renders list for navigation
121
- const rendersListRef = useRef([]);
122
-
123
- // ============================================================================
124
- // FILTER STATE - use ref for filter config, state for display count only
125
- // ============================================================================
126
- const filtersRef = useRef(RenderTracker.getFilters());
127
- const [activeFilterCount, setActiveFilterCount] = useState(() => {
128
- const filters = RenderTracker.getFilters();
129
- return filters.includePatterns.length + filters.excludePatterns.length;
130
- });
131
-
132
- // Expose filters via state for HighlightFilterView (it needs to display them)
133
- const [filters, setFilters] = useState(() => RenderTracker.getFilters());
134
-
135
- // ============================================================================
136
- // SETTINGS STATE
137
- // ============================================================================
138
- const [settings, setSettings] = useState(() => RenderTracker.getSettings());
139
-
140
- // ============================================================================
141
- // PERSISTENCE REFS - prevent saving on initial load
142
- // ============================================================================
143
- const hasLoadedTrackingState = useRef(false);
144
- const hasLoadedFilters = useRef(false);
145
- const hasLoadedSettings = useRef(false);
146
-
147
- // ============================================================================
148
- // LOAD PERSISTED STATE
149
- // ============================================================================
150
-
151
- // Tracking state is intentionally NOT persisted.
152
- // Always starts OFF to prevent infinite re-render loops on load where
153
- // the overlay highlights its own components before exclusion filters can take effect.
154
- // The user must explicitly enable tracking each session.
155
- useEffect(() => {
156
- if (!visible || hasLoadedTrackingState.current) return;
157
- hasLoadedTrackingState.current = true;
158
- }, [visible]);
159
-
160
- // Load persisted filters on mount
161
- useEffect(() => {
162
- if (!visible || hasLoadedFilters.current) return;
163
- const loadFilters = async () => {
164
- try {
165
- const storedFilters = await persistentStorage.getItem(devToolsStorageKeys.highlightUpdates.filters());
166
- if (storedFilters) {
167
- const parsedFilters = JSON.parse(storedFilters);
168
- const restoredFilters = {
169
- includeTestID: new Set(parsedFilters.includeTestID || []),
170
- includeNativeID: new Set(parsedFilters.includeNativeID || []),
171
- includeViewType: new Set(parsedFilters.includeViewType || []),
172
- includeComponent: new Set(parsedFilters.includeComponent || []),
173
- excludeTestID: new Set(parsedFilters.excludeTestID || []),
174
- excludeNativeID: new Set(parsedFilters.excludeNativeID || []),
175
- excludeViewType: new Set(parsedFilters.excludeViewType || []),
176
- excludeComponent: new Set(parsedFilters.excludeComponent || []),
177
- includePatterns: parsedFilters.includePatterns || [],
178
- excludePatterns: parsedFilters.excludePatterns || []
179
- };
180
- RenderTracker.setFilters(restoredFilters);
181
- const newFilters = RenderTracker.getFilters();
182
- filtersRef.current = newFilters;
183
- setFilters(newFilters);
184
- setActiveFilterCount(newFilters.includePatterns.length + newFilters.excludePatterns.length);
185
- }
186
- hasLoadedFilters.current = true;
187
- } catch (error) {
188
- // Failed to load filters
189
- }
190
- };
191
- loadFilters();
192
- }, [visible]);
193
-
194
- // Save filters when they change
195
- useEffect(() => {
196
- if (!hasLoadedFilters.current) return;
197
- const saveFilters = async () => {
198
- try {
199
- const filtersToSave = {
200
- includeTestID: Array.from(filters.includeTestID),
201
- includeNativeID: Array.from(filters.includeNativeID),
202
- includeViewType: Array.from(filters.includeViewType),
203
- includeComponent: Array.from(filters.includeComponent),
204
- excludeTestID: Array.from(filters.excludeTestID),
205
- excludeNativeID: Array.from(filters.excludeNativeID),
206
- excludeViewType: Array.from(filters.excludeViewType),
207
- excludeComponent: Array.from(filters.excludeComponent),
208
- includePatterns: filters.includePatterns,
209
- excludePatterns: filters.excludePatterns
210
- };
211
- await persistentStorage.setItem(devToolsStorageKeys.highlightUpdates.filters(), JSON.stringify(filtersToSave));
212
- } catch (error) {
213
- // Failed to save filters
214
- }
215
- };
216
- saveFilters();
217
- }, [filters]);
218
-
219
- // Load persisted settings on mount
220
- useEffect(() => {
221
- if (!visible || hasLoadedSettings.current) return;
222
- const loadSettings = async () => {
223
- try {
224
- const storedSettings = await persistentStorage.getItem(devToolsStorageKeys.highlightUpdates.settings());
225
- if (storedSettings) {
226
- const parsedSettings = JSON.parse(storedSettings);
227
- // Never restore excludeDevTools or performanceLogging from persistence -
228
- // excludeDevTools must always default to true to prevent infinite re-render loops
229
- const {
230
- performanceLogging: _ignored,
231
- excludeDevTools: _alwaysTrue,
232
- ...settingsToRestore
233
- } = parsedSettings;
234
- RenderTracker.setSettings(settingsToRestore);
235
- setSettings(RenderTracker.getSettings());
236
- }
237
- hasLoadedSettings.current = true;
238
- } catch (error) {
239
- hasLoadedSettings.current = true;
240
- }
241
- };
242
- loadSettings();
243
- }, [visible]);
244
-
245
- // Save settings when they change
246
- useEffect(() => {
247
- if (!hasLoadedSettings.current) return;
248
- const saveSettings = async () => {
249
- try {
250
- // Never persist excludeDevTools - it must always default to true on startup
251
- const {
252
- performanceLogging: _ignored,
253
- excludeDevTools: _neverPersist,
254
- ...settingsToSave
255
- } = settings;
256
- await persistentStorage.setItem(devToolsStorageKeys.highlightUpdates.settings(), JSON.stringify(settingsToSave));
257
- } catch (error) {
258
- // Failed to save settings
259
- }
260
- };
261
- saveSettings();
262
- }, [settings]);
263
-
264
- // ============================================================================
265
- // SUBSCRIPTIONS - only for tracking state, not renders
266
- // ============================================================================
267
-
268
- // Subscribe to tracking state changes only
269
- useEffect(() => {
270
- const unsubscribeState = RenderTracker.subscribeToState(state => {
271
- setIsTracking(state.isTracking);
272
- });
273
- return () => {
274
- unsubscribeState();
275
- };
276
- }, []);
277
-
278
- // Subscribe to freeze state changes
279
- useEffect(() => {
280
- const unsubscribeFreeze = HighlightUpdatesController.subscribeToFreeze(frozen => {
281
- setIsFrozen(frozen);
282
- });
283
- return () => {
284
- unsubscribeFreeze();
285
- };
286
- }, []);
287
-
288
- // Focus search input when activated
289
- useEffect(() => {
290
- if (isSearchActive) {
291
- requestAnimationFrame(() => {
292
- searchInputRef.current?.focus();
293
- });
294
- }
295
- }, [isSearchActive]);
296
-
297
- // Clear spotlight when modal is closed
298
- useEffect(() => {
299
- if (!visible) {
300
- HighlightUpdatesController.setSpotlight(null);
301
- }
302
- }, [visible]);
303
-
304
- // ============================================================================
305
- // DEEP LINK NAVIGATION - "Click Overlay Badge → Jump to Detail"
306
- // ============================================================================
307
-
308
- // Handle initial navigation when a badge is tapped
309
- useEffect(() => {
310
- if (visible && initialNativeTag != null) {
311
- // Look up the render by nativeTag
312
- const render = RenderTracker.getRender(String(initialNativeTag));
313
- if (render) {
314
- // Navigate to detail view for this component
315
- setSelectedRender(render);
316
- setSelectedRenderIndex(0);
317
- setShowFilterView(false);
318
- // Set spotlight to show which component is being viewed
319
- HighlightUpdatesController.setSpotlight(render.nativeTag);
320
- }
321
- // Notify parent that we've handled the navigation
322
- onInitialNativeTagHandled?.();
323
- }
324
- }, [visible, initialNativeTag, onInitialNativeTagHandled]);
325
-
326
- // ============================================================================
327
- // CALLBACKS - stable references for child components
328
- // ============================================================================
329
-
330
- const handleToggleTracking = useCallback(() => {
331
- if (!HighlightUpdatesController.isInitialized()) {
332
- HighlightUpdatesController.initialize();
333
- }
334
- HighlightUpdatesController.toggle();
335
- }, []);
336
- const handleToggleFreeze = useCallback(() => {
337
- HighlightUpdatesController.toggleFreeze();
338
- }, []);
339
- const handleClear = useCallback(() => {
340
- HighlightUpdatesController.clearRenderCounts();
341
- setHasRenders(false);
342
- }, []);
343
-
344
- // Pass a lazy getter so CopyButton calls it at click time with fresh data
345
- const copyData = hasRenders ? formatRenderDataForClipboard : "";
346
- const handleSearch = useCallback(text => {
347
- setSearchText(text);
348
- }, []);
349
- const handleRenderPress = useCallback((render, index, allRenders) => {
350
- setSelectedRender(render);
351
- setSelectedRenderIndex(index);
352
- setDetailViewActiveTab("details"); // Reset to details tab when selecting a new render
353
- setHistoryEventIndex(0); // Reset history event index when selecting a new render
354
- rendersListRef.current = allRenders;
355
- // Set spotlight to show which component is being viewed
356
- HighlightUpdatesController.setSpotlight(render.nativeTag);
357
- }, []);
358
- const handleRendersChange = useCallback(renders => {
359
- rendersListRef.current = renders;
360
- }, []);
361
- const handleBackFromDetail = useCallback(() => {
362
- setSelectedRender(null);
363
- setSelectedRenderIndex(0);
364
- // Clear the spotlight
365
- HighlightUpdatesController.setSpotlight(null);
366
- }, []);
367
- const handlePreviousRender = useCallback(() => {
368
- const renders = rendersListRef.current;
369
- if (selectedRenderIndex > 0) {
370
- const newIndex = selectedRenderIndex - 1;
371
- const newRender = renders[newIndex];
372
- if (newRender) {
373
- setSelectedRender(newRender);
374
- setSelectedRenderIndex(newIndex);
375
- HighlightUpdatesController.setSpotlight(newRender.nativeTag);
376
- }
377
- }
378
- }, [selectedRenderIndex]);
379
- const handleNextRender = useCallback(() => {
380
- const renders = rendersListRef.current;
381
- if (selectedRenderIndex < renders.length - 1) {
382
- const newIndex = selectedRenderIndex + 1;
383
- const newRender = renders[newIndex];
384
- if (newRender) {
385
- setSelectedRender(newRender);
386
- setSelectedRenderIndex(newIndex);
387
- HighlightUpdatesController.setSpotlight(newRender.nativeTag);
388
- }
389
- }
390
- }, [selectedRenderIndex]);
391
- const handleBackFromFilter = useCallback(() => {
392
- setShowFilterView(false);
393
- }, []);
394
- const handleFilterToggle = useCallback(() => {
395
- setShowFilterView(true);
396
- }, []);
397
- const handleSearchToggle = useCallback(() => {
398
- setIsSearchActive(true);
399
- }, []);
400
- const handleSearchClose = useCallback(() => {
401
- setIsSearchActive(false);
402
- }, []);
403
- const handleFilterChange = useCallback(newFilters => {
404
- RenderTracker.setFilters(newFilters);
405
- const updatedFilters = RenderTracker.getFilters();
406
- filtersRef.current = updatedFilters;
407
- setFilters(updatedFilters);
408
- setActiveFilterCount(updatedFilters.includePatterns.length + updatedFilters.excludePatterns.length);
409
- }, []);
410
-
411
- // Handler for adding a filter from the detail view (quick actions)
412
- const handleAddFilter = useCallback((pattern, mode) => {
413
- const currentFilters = RenderTracker.getFilters();
414
- const newFilters = {};
415
- if (mode === "include") {
416
- // Check if pattern already exists
417
- const exists = currentFilters.includePatterns.some(p => p.type === pattern.type && p.value === pattern.value);
418
- if (!exists) {
419
- newFilters.includePatterns = [...currentFilters.includePatterns, pattern];
420
- }
421
- } else {
422
- // Check if pattern already exists
423
- const exists = currentFilters.excludePatterns.some(p => p.type === pattern.type && p.value === pattern.value);
424
- if (!exists) {
425
- newFilters.excludePatterns = [...currentFilters.excludePatterns, pattern];
426
- }
427
- }
428
- if (Object.keys(newFilters).length > 0) {
429
- handleFilterChange(newFilters);
430
- // Go back to the list view after adding filter
431
- setSelectedRender(null);
432
- setSelectedRenderIndex(0);
433
- // Clear the spotlight
434
- HighlightUpdatesController.setSpotlight(null);
435
- }
436
- }, [handleFilterChange]);
437
- const handleSettingsChange = useCallback(newSettings => {
438
- RenderTracker.setSettings(newSettings);
439
- setSettings(RenderTracker.getSettings());
440
- }, []);
441
-
442
- // Callback for IsolatedRenderList to update hasRenders (for header clear button)
443
- const handleStatsChange = useCallback(stats => {
444
- setHasRenders(stats.totalComponents > 0);
445
- }, []);
446
-
447
- // ============================================================================
448
- // HEADER RENDERING - using memoized components
449
- // ============================================================================
450
-
451
- const renderHeaderContent = useCallback(() => {
452
- if (showFilterView) {
453
- return /*#__PURE__*/_jsx(FilterViewHeader, {
454
- onBack: handleBackFromFilter,
455
- activeTab: filterViewActiveTab,
456
- onTabChange: setFilterViewActiveTab,
457
- activeFilterCount: activeFilterCount
458
- });
459
- }
460
- if (selectedRender) {
461
- return /*#__PURE__*/_jsx(DetailViewHeader, {
462
- onBack: handleBackFromDetail,
463
- activeTab: detailViewActiveTab,
464
- onTabChange: setDetailViewActiveTab,
465
- hasHistory: (selectedRender.renderHistory?.length ?? 0) > 0
466
- });
467
- }
468
- return /*#__PURE__*/_jsx(MainListHeader, {
469
- onBack: onBack,
470
- isSearchActive: isSearchActive,
471
- searchText: searchText,
472
- onSearchChange: handleSearch,
473
- onSearchToggle: handleSearchToggle,
474
- onSearchClose: handleSearchClose,
475
- onFilterToggle: handleFilterToggle,
476
- onToggleTracking: handleToggleTracking,
477
- onToggleFreeze: handleToggleFreeze,
478
- onClear: handleClear,
479
- copyData: copyData,
480
- isTracking: isTracking,
481
- isFrozen: isFrozen,
482
- activeFilterCount: activeFilterCount,
483
- hasRenders: hasRenders,
484
- searchInputRef: searchInputRef
485
- });
486
- }, [showFilterView, selectedRender, onBack, isSearchActive, searchText, isTracking, isFrozen, activeFilterCount, hasRenders, filterViewActiveTab, detailViewActiveTab, handleBackFromFilter, handleBackFromDetail, handleSearch, handleSearchToggle, handleSearchClose, handleFilterToggle, handleToggleTracking, handleToggleFreeze, handleClear, copyData]);
487
-
488
- // ============================================================================
489
- // RENDER
490
- // ============================================================================
491
-
492
- const persistenceKey = enableSharedModalDimensions ? devToolsStorageKeys.modal.root() : devToolsStorageKeys.highlightUpdates.modal();
493
- if (!visible) return null;
494
-
495
- // Footer for navigating through the renders list or history events
496
- const totalRenders = rendersListRef.current.length;
497
- const footerNode = selectedRender ? detailViewActiveTab === "details" ? /*#__PURE__*/_jsx(EventStepperFooter, {
498
- currentIndex: selectedRenderIndex,
499
- totalItems: totalRenders,
500
- onPrevious: handlePreviousRender,
501
- onNext: handleNextRender,
502
- itemLabel: "Component",
503
- subtitle: selectedRender.componentName || selectedRender.displayName || selectedRender.viewType
504
- }) : /*#__PURE__*/_jsx(RenderHistoryFooter, {
505
- render: selectedRender,
506
- selectedEventIndex: historyEventIndex,
507
- onEventIndexChange: setHistoryEventIndex,
508
- isPro: isPro
509
- }) : null;
510
- return /*#__PURE__*/_jsx(TickProvider, {
511
- children: /*#__PURE__*/_jsx(JsModal, {
512
- visible: visible,
513
- onClose: onClose,
514
- onMinimize: onMinimize,
515
- persistenceKey: persistenceKey,
516
- header: {
517
- showToggleButton: true,
518
- customContent: renderHeaderContent()
519
- },
520
- enablePersistence: true,
521
- initialMode: "bottomSheet",
522
- enableGlitchEffects: true,
523
- styles: {},
524
- footer: footerNode,
525
- footerHeight: selectedRender ? detailViewActiveTab === "details" ? 68 : (selectedRender.renderHistory?.length ?? 0) > 1 ? 68 : 0 : 0,
526
- children: /*#__PURE__*/_jsx(View, {
527
- nativeID: "__rn_buoy__highlight-modal",
528
- style: styles.container,
529
- children: selectedRender ? detailViewActiveTab === "details" ? /*#__PURE__*/_jsx(RenderDetailView, {
530
- render: selectedRender,
531
- disableInternalFooter: true,
532
- onAddFilter: handleAddFilter,
533
- isPro: isPro
534
- }) : /*#__PURE__*/_jsx(RenderHistoryViewer, {
535
- render: selectedRender,
536
- disableInternalFooter: true,
537
- selectedEventIndex: historyEventIndex,
538
- onEventIndexChange: setHistoryEventIndex,
539
- isPro: isPro
540
- }) : showFilterView ? /*#__PURE__*/_jsx(HighlightFilterView, {
541
- filters: filters,
542
- onFilterChange: handleFilterChange,
543
- settings: settings,
544
- onSettingsChange: handleSettingsChange,
545
- availableProps: RenderTracker.getAvailableProps(),
546
- activeTab: filterViewActiveTab
547
- }) : /*#__PURE__*/_jsxs(_Fragment, {
548
- children: [!isTracking && /*#__PURE__*/_jsx(DisabledBanner, {}), /*#__PURE__*/_jsx(IsolatedRenderList, {
549
- searchText: searchText,
550
- filters: filters,
551
- onSelectRender: handleRenderPress,
552
- onStatsChange: handleStatsChange,
553
- onRendersChange: handleRendersChange,
554
- isTracking: isTracking,
555
- isPro: isPro
556
- })]
557
- })
558
- })
559
- })
560
- });
561
- }
562
- const styles = StyleSheet.create({
563
- container: {
564
- flex: 1,
565
- backgroundColor: buoyColors.base
566
- },
567
- disabledBanner: {
568
- flexDirection: "row",
569
- alignItems: "center",
570
- gap: 8,
571
- padding: 10,
572
- marginHorizontal: 12,
573
- marginTop: 8,
574
- backgroundColor: buoyColors.warning + "15",
575
- borderRadius: 8,
576
- borderWidth: 1,
577
- borderColor: buoyColors.warning + "20"
578
- },
579
- disabledText: {
580
- color: buoyColors.warning,
581
- fontSize: 11,
582
- flex: 1
583
- }
584
- });
585
- export default HighlightUpdatesModal;
1
+ "use strict";import React,{useState,useEffect,useRef,useCallback}from"react";import{View,Text,StyleSheet}from"react-native";import{Power,JsModal,devToolsStorageKeys,persistentStorage,buoyColors,TickProvider}from"@buoy-gg/shared-ui";import{useIsPro}from"@buoy-gg/license";import HighlightUpdatesController from"../utils/HighlightUpdatesController";import{RenderTracker}from"../utils/RenderTracker";import{RenderDetailView}from"./RenderDetailView";import{EventStepperFooter}from"@buoy-gg/shared-ui";import{HighlightFilterView}from"./HighlightFilterView";import{IsolatedRenderList}from"./IsolatedRenderList";import{MainListHeader,FilterViewHeader,DetailViewHeader}from"./ModalHeaderContent";import{RenderHistoryViewer,RenderHistoryFooter}from"./RenderHistoryViewer";import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";const DisabledBanner=React.memo(function(){return _jsxs(View,{style:styles.disabledBanner,children:[_jsx(Power,{size:14,color:buoyColors.warning}),_jsx(Text,{style:styles.disabledText,children:"Render tracking is disabled"})]})});function formatRenderDataForClipboard(){const e=RenderTracker.getRenders(),t={exportedAt:(new Date).toISOString(),totalComponents:e.length,totalRenders:e.reduce((e,t)=>e+t.renderCount,0),components:e.map(e=>({componentName:e.componentName||e.displayName,viewType:e.viewType,nativeTag:e.nativeTag,testID:e.testID,nativeID:e.nativeID,accessibilityLabel:e.accessibilityLabel,renderCount:e.renderCount,firstRenderTime:e.firstRenderTime,lastRenderTime:e.lastRenderTime,lastRenderCause:e.lastRenderCause?{type:e.lastRenderCause.type,componentCause:e.lastRenderCause.componentCause,changedKeys:e.lastRenderCause.changedKeys,hookChanges:e.lastRenderCause.hookChanges?.map(e=>({type:e.type,index:e.index,description:e.description}))}:null,renderHistory:e.renderHistory?.map(e=>({renderNumber:e.renderNumber,timestamp:e.timestamp,cause:{type:e.cause.type,componentCause:e.cause.componentCause,changedKeys:e.cause.changedKeys}}))}))};return JSON.stringify(t,null,2)}export function HighlightUpdatesModal({visible:e,onClose:t,onBack:r,onMinimize:n,enableSharedModalDimensions:s=!1,initialNativeTag:a,onInitialNativeTagHandled:i}){const o=useIsPro(),[l,c]=useState(!1),[d,u]=useState(()=>HighlightUpdatesController.getFrozen()),[g,h]=useState(()=>RenderTracker.getStats().totalComponents>0),[p,m]=useState(null),[C,y]=useState(0),[T,f]=useState(!1),[S,b]=useState("filters"),[x,R]=useState("details"),[k,v]=useState(0),[w,I]=useState(""),[H,D]=useState(!1),F=useRef(null),P=useRef([]),_=useRef(RenderTracker.getFilters()),[V,j]=useState(()=>{const e=RenderTracker.getFilters();return e.includePatterns.length+e.excludePatterns.length}),[N,U]=useState(()=>RenderTracker.getFilters()),[E,A]=useState(()=>RenderTracker.getSettings()),z=useRef(!1),K=useRef(!1),M=useRef(!1);useEffect(()=>{e&&!z.current&&(z.current=!0)},[e]),useEffect(()=>{e&&!K.current&&(async()=>{try{const e=await persistentStorage.getItem(devToolsStorageKeys.highlightUpdates.filters());if(e){const t=JSON.parse(e),r={includeTestID:new Set(t.includeTestID||[]),includeNativeID:new Set(t.includeNativeID||[]),includeViewType:new Set(t.includeViewType||[]),includeComponent:new Set(t.includeComponent||[]),excludeTestID:new Set(t.excludeTestID||[]),excludeNativeID:new Set(t.excludeNativeID||[]),excludeViewType:new Set(t.excludeViewType||[]),excludeComponent:new Set(t.excludeComponent||[]),includePatterns:t.includePatterns||[],excludePatterns:t.excludePatterns||[]};RenderTracker.setFilters(r);const n=RenderTracker.getFilters();_.current=n,U(n),j(n.includePatterns.length+n.excludePatterns.length)}K.current=!0}catch(e){}})()},[e]),useEffect(()=>{K.current&&(async()=>{try{const e={includeTestID:Array.from(N.includeTestID),includeNativeID:Array.from(N.includeNativeID),includeViewType:Array.from(N.includeViewType),includeComponent:Array.from(N.includeComponent),excludeTestID:Array.from(N.excludeTestID),excludeNativeID:Array.from(N.excludeNativeID),excludeViewType:Array.from(N.excludeViewType),excludeComponent:Array.from(N.excludeComponent),includePatterns:N.includePatterns,excludePatterns:N.excludePatterns};await persistentStorage.setItem(devToolsStorageKeys.highlightUpdates.filters(),JSON.stringify(e))}catch(e){}})()},[N]),useEffect(()=>{e&&!M.current&&(async()=>{try{const e=await persistentStorage.getItem(devToolsStorageKeys.highlightUpdates.settings());if(e){const t=JSON.parse(e),{performanceLogging:r,excludeDevTools:n,...s}=t;RenderTracker.setSettings(s),A(RenderTracker.getSettings())}M.current=!0}catch(e){M.current=!0}})()},[e]),useEffect(()=>{M.current&&(async()=>{try{const{performanceLogging:e,excludeDevTools:t,...r}=E;await persistentStorage.setItem(devToolsStorageKeys.highlightUpdates.settings(),JSON.stringify(r))}catch(e){}})()},[E]),useEffect(()=>{const e=RenderTracker.subscribeToState(e=>{c(e.isTracking)});return()=>{e()}},[]),useEffect(()=>{const e=HighlightUpdatesController.subscribeToFreeze(e=>{u(e)});return()=>{e()}},[]),useEffect(()=>{H&&requestAnimationFrame(()=>{F.current?.focus()})},[H]),useEffect(()=>{e||HighlightUpdatesController.setSpotlight(null)},[e]),useEffect(()=>{if(e&&null!=a){const e=RenderTracker.getRender(String(a));e&&(m(e),y(0),f(!1),HighlightUpdatesController.setSpotlight(e.nativeTag)),i?.()}},[e,a,i]);const L=useCallback(()=>{HighlightUpdatesController.isInitialized()||HighlightUpdatesController.initialize(),HighlightUpdatesController.toggle()},[]),B=useCallback(()=>{HighlightUpdatesController.toggleFreeze()},[]),J=useCallback(()=>{HighlightUpdatesController.clearRenderCounts(),h(!1)},[]),O=g?formatRenderDataForClipboard:"",q=useCallback(e=>{I(e)},[]),G=useCallback((e,t,r)=>{m(e),y(t),R("details"),v(0),P.current=r,HighlightUpdatesController.setSpotlight(e.nativeTag)},[]),W=useCallback(e=>{P.current=e},[]),Q=useCallback(()=>{m(null),y(0),HighlightUpdatesController.setSpotlight(null)},[]),X=useCallback(()=>{const e=P.current;if(C>0){const t=C-1,r=e[t];r&&(m(r),y(t),HighlightUpdatesController.setSpotlight(r.nativeTag))}},[C]),Y=useCallback(()=>{const e=P.current;if(C<e.length-1){const t=C+1,r=e[t];r&&(m(r),y(t),HighlightUpdatesController.setSpotlight(r.nativeTag))}},[C]),Z=useCallback(()=>{f(!1)},[]),$=useCallback(()=>{f(!0)},[]),ee=useCallback(()=>{D(!0)},[]),te=useCallback(()=>{D(!1)},[]),re=useCallback(e=>{RenderTracker.setFilters(e);const t=RenderTracker.getFilters();_.current=t,U(t),j(t.includePatterns.length+t.excludePatterns.length)},[]),ne=useCallback((e,t)=>{const r=RenderTracker.getFilters(),n={};"include"===t?r.includePatterns.some(t=>t.type===e.type&&t.value===e.value)||(n.includePatterns=[...r.includePatterns,e]):r.excludePatterns.some(t=>t.type===e.type&&t.value===e.value)||(n.excludePatterns=[...r.excludePatterns,e]),Object.keys(n).length>0&&(re(n),m(null),y(0),HighlightUpdatesController.setSpotlight(null))},[re]),se=useCallback(e=>{RenderTracker.setSettings(e),A(RenderTracker.getSettings())},[]),ae=useCallback(e=>{h(e.totalComponents>0)},[]),ie=useCallback(()=>T?_jsx(FilterViewHeader,{onBack:Z,activeTab:S,onTabChange:b,activeFilterCount:V}):p?_jsx(DetailViewHeader,{onBack:Q,activeTab:x,onTabChange:R,hasHistory:(p.renderHistory?.length??0)>0}):_jsx(MainListHeader,{onBack:r,isSearchActive:H,searchText:w,onSearchChange:q,onSearchToggle:ee,onSearchClose:te,onFilterToggle:$,onToggleTracking:L,onToggleFreeze:B,onClear:J,copyData:O,isTracking:l,isFrozen:d,activeFilterCount:V,hasRenders:g,searchInputRef:F}),[T,p,r,H,w,l,d,V,g,S,x,Z,Q,q,ee,te,$,L,B,J,O]),oe=s?devToolsStorageKeys.modal.root():devToolsStorageKeys.highlightUpdates.modal();if(!e)return null;const le=P.current.length,ce=p?"details"===x?_jsx(EventStepperFooter,{currentIndex:C,totalItems:le,onPrevious:X,onNext:Y,itemLabel:"Component",subtitle:p.componentName||p.displayName||p.viewType}):_jsx(RenderHistoryFooter,{render:p,selectedEventIndex:k,onEventIndexChange:v,isPro:o}):null;return _jsx(TickProvider,{children:_jsx(JsModal,{visible:e,onClose:t,onMinimize:n,persistenceKey:oe,header:{showToggleButton:!0,customContent:ie()},enablePersistence:!0,initialMode:"bottomSheet",enableGlitchEffects:!0,styles:{},footer:ce,footerHeight:p&&("details"===x||(p.renderHistory?.length??0)>1)?68:0,children:_jsx(View,{nativeID:"__rn_buoy__highlight-modal",style:styles.container,children:p?"details"===x?_jsx(RenderDetailView,{render:p,disableInternalFooter:!0,onAddFilter:ne,isPro:o}):_jsx(RenderHistoryViewer,{render:p,disableInternalFooter:!0,selectedEventIndex:k,onEventIndexChange:v,isPro:o}):T?_jsx(HighlightFilterView,{filters:N,onFilterChange:re,settings:E,onSettingsChange:se,availableProps:RenderTracker.getAvailableProps(),activeTab:S}):_jsxs(_Fragment,{children:[!l&&_jsx(DisabledBanner,{}),_jsx(IsolatedRenderList,{searchText:w,filters:N,onSelectRender:G,onStatsChange:ae,onRendersChange:W,isTracking:l,isPro:o})]})})})})}const styles=StyleSheet.create({container:{flex:1,backgroundColor:buoyColors.base},disabledBanner:{flexDirection:"row",alignItems:"center",gap:8,padding:10,marginHorizontal:12,marginTop:8,backgroundColor:buoyColors.warning+"15",borderRadius:8,borderWidth:1,borderColor:buoyColors.warning+"20"},disabledText:{color:buoyColors.warning,fontSize:11,flex:1}});export default HighlightUpdatesModal;