@buoy-gg/events 2.1.2 → 2.1.3

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 (44) hide show
  1. package/lib/commonjs/components/EventsCopySettingsView.js +39 -8
  2. package/lib/commonjs/components/UnifiedEventDetail.js +32 -1
  3. package/lib/commonjs/components/UnifiedEventFilters.js +50 -21
  4. package/lib/commonjs/components/UnifiedEventItem.js +6 -1
  5. package/lib/commonjs/hooks/useUnifiedEvents.js +137 -28
  6. package/lib/commonjs/index.js +6 -0
  7. package/lib/commonjs/stores/unifiedEventStore.js +61 -16
  8. package/lib/commonjs/types/copySettings.js +29 -0
  9. package/lib/commonjs/utils/autoDiscoverEventSources.js +261 -39
  10. package/lib/commonjs/utils/badgeSelectionStorage.js +32 -0
  11. package/lib/commonjs/utils/eventExportFormatter.js +778 -1
  12. package/lib/module/components/EventsCopySettingsView.js +40 -9
  13. package/lib/module/components/UnifiedEventDetail.js +32 -1
  14. package/lib/module/components/UnifiedEventFilters.js +50 -21
  15. package/lib/module/components/UnifiedEventItem.js +6 -1
  16. package/lib/module/hooks/useUnifiedEvents.js +140 -31
  17. package/lib/module/index.js +4 -1
  18. package/lib/module/stores/unifiedEventStore.js +58 -16
  19. package/lib/module/types/copySettings.js +29 -0
  20. package/lib/module/utils/autoDiscoverEventSources.js +260 -39
  21. package/lib/module/utils/badgeSelectionStorage.js +30 -0
  22. package/lib/module/utils/eventExportFormatter.js +777 -1
  23. package/lib/typescript/components/UnifiedEventFilters.d.ts +3 -0
  24. package/lib/typescript/hooks/useUnifiedEvents.d.ts +2 -0
  25. package/lib/typescript/index.d.ts +1 -1
  26. package/lib/typescript/stores/unifiedEventStore.d.ts +18 -2
  27. package/lib/typescript/types/copySettings.d.ts +25 -1
  28. package/lib/typescript/types/index.d.ts +3 -1
  29. package/lib/typescript/utils/autoDiscoverEventSources.d.ts +17 -0
  30. package/lib/typescript/utils/badgeSelectionStorage.d.ts +9 -0
  31. package/lib/typescript/utils/eventExportFormatter.d.ts +4 -0
  32. package/package.json +3 -3
  33. package/src/components/EventsCopySettingsView.tsx +41 -5
  34. package/src/components/UnifiedEventDetail.tsx +28 -0
  35. package/src/components/UnifiedEventFilters.tsx +88 -21
  36. package/src/components/UnifiedEventItem.tsx +5 -0
  37. package/src/hooks/useUnifiedEvents.ts +153 -25
  38. package/src/index.tsx +4 -0
  39. package/src/stores/unifiedEventStore.ts +58 -12
  40. package/src/types/copySettings.ts +31 -1
  41. package/src/types/index.ts +4 -1
  42. package/src/utils/autoDiscoverEventSources.ts +268 -44
  43. package/src/utils/badgeSelectionStorage.ts +30 -0
  44. package/src/utils/eventExportFormatter.ts +797 -0
@@ -267,14 +267,19 @@ function EventsCopySettingsView({
267
267
  data: JSON.parse(exportText),
268
268
  title: "",
269
269
  showTypeFilter: false
270
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
271
- style: styles.previewScroll,
272
- nestedScrollEnabled: true,
273
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
274
- style: styles.previewText,
275
- selectable: isPro,
276
- children: exportText
277
- })
270
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
271
+ children: [settings.format === "mermaid" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
272
+ style: styles.mermaidHint,
273
+ children: "\uD83D\uDCA1 Paste into mermaid.live to visualize"
274
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
275
+ style: styles.previewScroll,
276
+ nestedScrollEnabled: true,
277
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
278
+ style: styles.previewText,
279
+ selectable: isPro,
280
+ children: exportText
281
+ })
282
+ })]
278
283
  })]
279
284
  });
280
285
  }, [isPreviewExpanded, previewEvents, settings, estimatedSize, sizeWarningLevel, hasLiveData, generateCopyText, isPro]);
@@ -341,6 +346,14 @@ function EventsCopySettingsView({
341
346
  value: "minimal",
342
347
  isActive: activePreset === "minimal",
343
348
  description: _copySettings.PRESET_METADATA.minimal.description
349
+ }, {
350
+ id: "preset::mermaid",
351
+ label: _copySettings.PRESET_METADATA.mermaid.label,
352
+ icon: _sharedUi.GitBranch,
353
+ color: _copySettings.PRESET_METADATA.mermaid.color,
354
+ value: "mermaid",
355
+ isActive: activePreset === "mermaid",
356
+ description: _copySettings.PRESET_METADATA.mermaid.description
344
357
  }, {
345
358
  id: "preset::custom",
346
359
  label: "Custom",
@@ -424,6 +437,14 @@ function EventsCopySettingsView({
424
437
  color: _sharedUi.macOSColors.text.secondary,
425
438
  value: "plaintext",
426
439
  isActive: settings.format === "plaintext"
440
+ }, {
441
+ id: "format::mermaid",
442
+ label: "Diagram",
443
+ icon: _sharedUi.GitBranch,
444
+ color: "#A855F7",
445
+ value: "mermaid",
446
+ isActive: settings.format === "mermaid",
447
+ description: "Mermaid sequence diagram"
427
448
  }]
428
449
  },
429
450
  // Timestamp Section
@@ -625,6 +646,16 @@ const styles = _reactNative.StyleSheet.create({
625
646
  color: _sharedUi.macOSColors.text.primary,
626
647
  lineHeight: 18
627
648
  },
649
+ mermaidHint: {
650
+ fontSize: 11,
651
+ color: _sharedUi.macOSColors.text.secondary,
652
+ marginBottom: 8,
653
+ paddingHorizontal: 8,
654
+ paddingVertical: 6,
655
+ backgroundColor: "#A855F7" + "15",
656
+ borderRadius: 6,
657
+ overflow: "hidden"
658
+ },
628
659
  statusLabel: {
629
660
  fontSize: 11,
630
661
  fontWeight: "600",
@@ -26,7 +26,8 @@ function tryLoadOptionalDetailComponents() {
26
26
  const components = {
27
27
  StorageEventDetailContent: null,
28
28
  ReduxActionDetailContent: null,
29
- NetworkEventDetailView: null
29
+ NetworkEventDetailView: null,
30
+ RenderDetailView: null
30
31
  };
31
32
 
32
33
  // Try to load storage detail component
@@ -55,6 +56,15 @@ function tryLoadOptionalDetailComponents() {
55
56
  } catch {
56
57
  // Optional dependency not installed
57
58
  }
59
+
60
+ // Try to load render detail component
61
+ try {
62
+ // @ts-ignore - Dynamic import that may not exist
63
+ const highlightUpdates = require("@buoy-gg/highlight-updates");
64
+ components.RenderDetailView = highlightUpdates.RenderDetailView;
65
+ } catch {
66
+ // Optional dependency not installed
67
+ }
58
68
  return components;
59
69
  }
60
70
 
@@ -100,6 +110,10 @@ const SOURCE_CONFIG = {
100
110
  route: {
101
111
  label: "Route",
102
112
  color: "#06B6D4"
113
+ },
114
+ render: {
115
+ label: "Render",
116
+ color: "#F472B6"
103
117
  }
104
118
  };
105
119
 
@@ -215,6 +229,23 @@ const UnifiedEventDetail = exports.UnifiedEventDetail = /*#__PURE__*/(0, _react.
215
229
  });
216
230
  }
217
231
 
232
+ // For render events, use the RenderDetailView if available
233
+ if (event.source === "render" && optionalComponents.RenderDetailView) {
234
+ const {
235
+ RenderDetailView
236
+ } = optionalComponents;
237
+ const renderData = event.originalEvent;
238
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
239
+ style: styles.container,
240
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
241
+ style: styles.content,
242
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(RenderDetailView, {
243
+ render: renderData.render
244
+ })
245
+ })
246
+ });
247
+ }
248
+
218
249
  // Route events expand in-place in the list, so they shouldn't reach this detail view
219
250
  // If they do somehow, fall through to the generic view
220
251
 
@@ -14,6 +14,9 @@ var _jsxRuntime = require("react/jsx-runtime");
14
14
  * Filter bar showing toggleable badges for each available event source.
15
15
  * Badges are sorted: enabled first, disabled last.
16
16
  * Toggling a badge enables/disables event listening for that source.
17
+ *
18
+ * Badge layout (inspired by React Query DevTools):
19
+ * [subscriber_count] • Label [event_count]
17
20
  */
18
21
 
19
22
  const UnifiedEventFilters = exports.UnifiedEventFilters = /*#__PURE__*/(0, _react.memo)(function UnifiedEventFilters({
@@ -33,27 +36,45 @@ const UnifiedEventFilters = exports.UnifiedEventFilters = /*#__PURE__*/(0, _reac
33
36
  contentContainerStyle: styles.badgesRow,
34
37
  children: availableSources.map(source => {
35
38
  const isEnabled = source.enabled !== false;
39
+ const subscriberCount = source.subscriberCount;
40
+ const hasSubscriberTracking = subscriberCount !== undefined;
41
+ const eventCount = source.count || 0;
36
42
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
37
43
  style: [styles.badge, isEnabled ? {
38
- backgroundColor: source.color + "20",
39
- borderColor: source.color + "60"
44
+ backgroundColor: _sharedUi.buoyColors.card,
45
+ borderColor: source.color + "50"
40
46
  } : styles.badgeDisabled],
41
47
  onPress: () => onToggleSource(source.source),
42
48
  activeOpacity: 0.7,
43
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
49
+ children: [hasSubscriberTracking && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
50
+ style: [styles.countBadge, {
51
+ backgroundColor: isEnabled ? subscriberCount > 0 ? source.color + "25" : _sharedUi.buoyColors.textMuted + "20" : _sharedUi.buoyColors.textMuted + "15"
52
+ }],
53
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
54
+ style: [styles.countText, {
55
+ color: isEnabled ? subscriberCount > 0 ? source.color : _sharedUi.buoyColors.textMuted : _sharedUi.buoyColors.textMuted + "60"
56
+ }],
57
+ children: subscriberCount
58
+ })
59
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
44
60
  style: [styles.dot, {
45
- backgroundColor: isEnabled ? source.color : _sharedUi.buoyColors.textMuted + "60"
61
+ backgroundColor: isEnabled ? source.color : _sharedUi.buoyColors.textMuted + "40"
46
62
  }]
47
63
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
48
64
  style: [styles.badgeLabel, {
49
- color: isEnabled ? source.color : _sharedUi.buoyColors.textMuted + "80"
65
+ color: isEnabled ? _sharedUi.buoyColors.text : _sharedUi.buoyColors.textMuted + "70"
50
66
  }],
51
67
  children: source.label
52
- }), isEnabled && source.count > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
53
- style: [styles.badgeCount, {
54
- color: source.color
68
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
69
+ style: [styles.countBadge, {
70
+ backgroundColor: isEnabled ? eventCount > 0 ? source.color + "20" : _sharedUi.buoyColors.textMuted + "15" : _sharedUi.buoyColors.textMuted + "10"
55
71
  }],
56
- children: source.count
72
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
73
+ style: [styles.countText, {
74
+ color: isEnabled ? eventCount > 0 ? source.color : _sharedUi.buoyColors.textMuted + "80" : _sharedUi.buoyColors.textMuted + "50"
75
+ }],
76
+ children: eventCount
77
+ })
57
78
  })]
58
79
  }, source.source);
59
80
  })
@@ -78,30 +99,38 @@ const styles = _reactNative.StyleSheet.create({
78
99
  badge: {
79
100
  flexDirection: "row",
80
101
  alignItems: "center",
81
- paddingHorizontal: 10,
82
- paddingVertical: 6,
83
- borderRadius: 16,
102
+ paddingVertical: 5,
103
+ paddingHorizontal: 6,
104
+ borderRadius: 20,
84
105
  borderWidth: 1,
85
106
  gap: 6
86
107
  },
87
108
  badgeDisabled: {
88
109
  backgroundColor: _sharedUi.buoyColors.card,
89
110
  borderColor: _sharedUi.buoyColors.border,
90
- opacity: 0.6
111
+ opacity: 0.5
91
112
  },
92
113
  dot: {
93
- width: 6,
94
- height: 6,
95
- borderRadius: 3
114
+ width: 7,
115
+ height: 7,
116
+ borderRadius: 4
96
117
  },
97
118
  badgeLabel: {
98
119
  fontSize: 12,
99
- fontWeight: "600"
120
+ fontWeight: "500"
100
121
  },
101
- badgeCount: {
102
- fontSize: 11,
103
- fontWeight: "500",
104
- opacity: 0.8
122
+ countBadge: {
123
+ minWidth: 18,
124
+ height: 18,
125
+ borderRadius: 9,
126
+ alignItems: "center",
127
+ justifyContent: "center",
128
+ paddingHorizontal: 5
129
+ },
130
+ countText: {
131
+ fontSize: 10,
132
+ fontWeight: "600",
133
+ fontVariant: ["tabular-nums"]
105
134
  },
106
135
  filterInfo: {
107
136
  marginTop: 6,
@@ -110,6 +110,10 @@ const SOURCE_CONFIG = {
110
110
  route: {
111
111
  label: "Route",
112
112
  color: "#06B6D4" // Cyan
113
+ },
114
+ render: {
115
+ label: "Render",
116
+ color: "#F472B6" // Pink
113
117
  }
114
118
  };
115
119
 
@@ -177,7 +181,8 @@ const SOURCE_BADGE_LABELS = {
177
181
  "react-query": "QUERY",
178
182
  "react-query-query": "QUERY",
179
183
  "react-query-mutation": "MUTATION",
180
- route: "ROUTE"
184
+ route: "ROUTE",
185
+ render: "RENDER"
181
186
  };
182
187
 
183
188
  /**
@@ -22,7 +22,7 @@ const FREE_TIER_MAX_EVENTS = 25;
22
22
  /**
23
23
  * All possible sources for display
24
24
  */
25
- const ALL_DISPLAY_SOURCES = ["storage-async", "redux", "network", "react-query-query", "react-query-mutation", "route"];
25
+ const ALL_DISPLAY_SOURCES = ["storage-async", "redux", "network", "react-query-query", "react-query-mutation", "route", "render"];
26
26
 
27
27
  /**
28
28
  * Map display sources to actual event sources they should match
@@ -35,7 +35,8 @@ const SOURCE_TO_EVENT_SOURCES = {
35
35
  "react-query": ["react-query", "react-query-query"],
36
36
  "react-query-query": ["react-query", "react-query-query"],
37
37
  "react-query-mutation": ["react-query-mutation"],
38
- route: ["route"]
38
+ route: ["route"],
39
+ render: ["render"]
39
40
  };
40
41
 
41
42
  /**
@@ -49,7 +50,8 @@ const EVENT_SOURCE_TO_DISCOVERY_ID = {
49
50
  "react-query": "react-query",
50
51
  "react-query-query": "react-query",
51
52
  "react-query-mutation": "react-query",
52
- route: "route-events"
53
+ route: "route-events",
54
+ render: "render"
53
55
  };
54
56
  function useUnifiedEvents() {
55
57
  const {
@@ -65,6 +67,14 @@ function useUnifiedEvents() {
65
67
  return (0, _unifiedEventStore.getAvailableEventSources)();
66
68
  }, []);
67
69
 
70
+ // Subscribe to subscriber count changes for instant UI updates (TanStack Query pattern)
71
+ const [subscriberCountVersion, setSubscriberCountVersion] = (0, _react.useState)(0);
72
+ (0, _react.useEffect)(() => {
73
+ return (0, _sharedUi.subscribeToSubscriberCountChanges)(() => {
74
+ setSubscriberCountVersion(v => v + 1);
75
+ });
76
+ }, []);
77
+
68
78
  // Get available display sources (only show sources for installed packages)
69
79
  const availableDisplaySources = (0, _react.useMemo)(() => {
70
80
  return ALL_DISPLAY_SOURCES.filter(displaySource => {
@@ -74,39 +84,50 @@ function useUnifiedEvents() {
74
84
  });
75
85
  }, [discoveredSources]);
76
86
 
77
- // Subscribe to store changes and start capturing on mount
87
+ // Subscribe to store changes (always subscribe to get events when they come)
78
88
  (0, _react.useEffect)(() => {
79
- // Subscribe to store updates
80
89
  const unsubscribe = (0, _unifiedEventStore.subscribe)(newEvents => {
81
90
  setEvents(newEvents);
82
91
  });
83
-
84
- // Start capturing from all available sources
85
- // These functions are now safe - they check if the source is available
86
- (0, _unifiedEventStore.subscribeToStorage)();
87
- (0, _unifiedEventStore.subscribeToRedux)();
88
- (0, _unifiedEventStore.subscribeToNetwork)();
89
- (0, _unifiedEventStore.subscribeToReactQuery)();
90
- (0, _unifiedEventStore.subscribeToRoutes)();
91
92
  return unsubscribe;
92
93
  }, []);
93
94
 
94
- // Restore saved badge selection state on mount
95
+ // Restore saved state on mount (badge selection + capturing state)
95
96
  (0, _react.useEffect)(() => {
96
97
  const restoreState = async () => {
98
+ // Load badge selection
97
99
  const savedSources = await (0, _badgeSelectionStorage.loadEnabledSources)();
100
+ let sourcesToEnable;
98
101
  if (savedSources && savedSources.length > 0) {
99
102
  // Filter to only include valid sources that still exist and are available
100
103
  const validSources = savedSources.filter(s => availableDisplaySources.includes(s));
101
104
  if (validSources.length > 0) {
102
- setEnabledSources(new Set(validSources));
105
+ sourcesToEnable = new Set(validSources);
103
106
  } else {
104
107
  // If no saved sources are valid, enable all available sources
105
- setEnabledSources(new Set(availableDisplaySources));
108
+ sourcesToEnable = new Set(availableDisplaySources);
106
109
  }
107
110
  } else {
108
111
  // No saved state - enable all available sources
109
- setEnabledSources(new Set(availableDisplaySources));
112
+ sourcesToEnable = new Set(availableDisplaySources);
113
+ }
114
+ setEnabledSources(sourcesToEnable);
115
+
116
+ // Load capturing state (default to true if not saved)
117
+ const savedCapturing = await (0, _badgeSelectionStorage.loadCapturingState)();
118
+ const shouldCapture = savedCapturing !== null ? savedCapturing : true;
119
+ setIsCapturing(shouldCapture);
120
+
121
+ // Start capturing if enabled - only subscribe to enabled sources
122
+ if (shouldCapture) {
123
+ if (sourcesToEnable.has("storage-async")) (0, _unifiedEventStore.subscribeToStorage)();
124
+ if (sourcesToEnable.has("redux")) (0, _unifiedEventStore.subscribeToRedux)();
125
+ if (sourcesToEnable.has("network")) (0, _unifiedEventStore.subscribeToNetwork)();
126
+ if (sourcesToEnable.has("react-query-query") || sourcesToEnable.has("react-query-mutation")) {
127
+ (0, _unifiedEventStore.subscribeToReactQuery)();
128
+ }
129
+ if (sourcesToEnable.has("route")) (0, _unifiedEventStore.subscribeToRoutes)();
130
+ if (sourcesToEnable.has("render")) (0, _unifiedEventStore.subscribeToRender)();
110
131
  }
111
132
  isStateRestoredRef.current = true;
112
133
  };
@@ -121,6 +142,14 @@ function useUnifiedEvents() {
121
142
  (0, _badgeSelectionStorage.saveEnabledSources)(Array.from(enabledSources));
122
143
  }, [enabledSources]);
123
144
 
145
+ // Persist capturing state whenever it changes (after initial restoration)
146
+ (0, _react.useEffect)(() => {
147
+ if (!isStateRestoredRef.current) {
148
+ return;
149
+ }
150
+ (0, _badgeSelectionStorage.saveCapturingState)(isCapturing);
151
+ }, [isCapturing]);
152
+
124
153
  // Build set of all event sources that should be shown
125
154
  const allowedEventSources = (0, _react.useMemo)(() => {
126
155
  const allowed = new Set();
@@ -160,15 +189,40 @@ function useUnifiedEvents() {
160
189
  // Get all available sources with counts, sorted: enabled first, disabled last
161
190
  const availableSources = (0, _react.useMemo)(() => {
162
191
  const counts = (0, _unifiedEventStore.getSourceCounts)();
192
+ const subscriberCounts = (0, _unifiedEventStore.getSubscriberCounts)();
193
+
194
+ // Build a map from source ID to subscriber count
195
+ const subscriberCountBySourceId = {};
196
+ for (const source of subscriberCounts.sources) {
197
+ subscriberCountBySourceId[source.sourceId] = source.counts.total;
198
+ }
199
+
200
+ // Map display source to store source ID
201
+ const displaySourceToStoreId = {
202
+ "storage-async": "storage",
203
+ "storage-mmkv": "storage",
204
+ redux: "redux",
205
+ network: "network",
206
+ "react-query": "react-query",
207
+ "react-query-query": "react-query",
208
+ "react-query-mutation": "react-query",
209
+ route: "route-events",
210
+ render: "render"
211
+ };
163
212
  const sources = availableDisplaySources.map(source => {
164
213
  const eventSources = SOURCE_TO_EVENT_SOURCES[source] || [source];
165
214
  const totalCount = eventSources.reduce((sum, s) => sum + (counts[s] || 0), 0);
166
215
  const displayConfig = (0, _autoDiscoverEventSources.getSourceDisplayConfig)(source);
216
+ const storeId = displaySourceToStoreId[source];
217
+ // Only set subscriberCount if the source has subscriber tracking
218
+ // (undefined means no tracking, vs 0 which means tracked but no subscribers)
219
+ const subscriberCount = storeId && storeId in subscriberCountBySourceId ? subscriberCountBySourceId[storeId] : undefined;
167
220
  return {
168
221
  source,
169
222
  ...displayConfig,
170
223
  count: totalCount,
171
- enabled: enabledSources.has(source)
224
+ enabled: enabledSources.has(source),
225
+ subscriberCount
172
226
  };
173
227
  });
174
228
 
@@ -178,18 +232,65 @@ function useUnifiedEvents() {
178
232
  if (!a.enabled && b.enabled) return 1;
179
233
  return 0;
180
234
  });
181
- }, [events, enabledSources, availableDisplaySources]);
235
+ }, [events, enabledSources, availableDisplaySources, subscriberCountVersion]);
182
236
  const toggleSource = (0, _react.useCallback)(source => {
183
237
  setEnabledSources(prev => {
184
238
  const next = new Set(prev);
185
- if (next.has(source)) {
239
+ const wasEnabled = next.has(source);
240
+ if (wasEnabled) {
186
241
  next.delete(source);
242
+ // Unsubscribe from the source
243
+ switch (source) {
244
+ case "storage-async":
245
+ (0, _unifiedEventStore.unsubscribeFromStorage)();
246
+ break;
247
+ case "redux":
248
+ (0, _unifiedEventStore.unsubscribeFromRedux)();
249
+ break;
250
+ case "network":
251
+ (0, _unifiedEventStore.unsubscribeFromNetwork)();
252
+ break;
253
+ case "react-query-query":
254
+ case "react-query-mutation":
255
+ (0, _unifiedEventStore.unsubscribeFromReactQuery)();
256
+ break;
257
+ case "route":
258
+ (0, _unifiedEventStore.unsubscribeFromRoutes)();
259
+ break;
260
+ case "render":
261
+ (0, _unifiedEventStore.unsubscribeFromRender)();
262
+ break;
263
+ }
187
264
  } else {
188
265
  next.add(source);
266
+ // Subscribe to the source (only if capturing is active)
267
+ if (isCapturing) {
268
+ switch (source) {
269
+ case "storage-async":
270
+ (0, _unifiedEventStore.subscribeToStorage)();
271
+ break;
272
+ case "redux":
273
+ (0, _unifiedEventStore.subscribeToRedux)();
274
+ break;
275
+ case "network":
276
+ (0, _unifiedEventStore.subscribeToNetwork)();
277
+ break;
278
+ case "react-query-query":
279
+ case "react-query-mutation":
280
+ (0, _unifiedEventStore.subscribeToReactQuery)();
281
+ break;
282
+ case "route":
283
+ (0, _unifiedEventStore.subscribeToRoutes)();
284
+ break;
285
+ case "render":
286
+ (0, _unifiedEventStore.subscribeToRender)();
287
+ break;
288
+ }
289
+ }
189
290
  }
190
291
  return next;
191
292
  });
192
- }, []);
293
+ }, [isCapturing]);
193
294
  const enableAllSources = (0, _react.useCallback)(() => {
194
295
  setEnabledSources(new Set(availableDisplaySources));
195
296
  }, [availableDisplaySources]);
@@ -197,13 +298,17 @@ function useUnifiedEvents() {
197
298
  (0, _unifiedEventStore.clearEvents)();
198
299
  }, []);
199
300
  const startCapturing = (0, _react.useCallback)(() => {
200
- (0, _unifiedEventStore.subscribeToStorage)();
201
- (0, _unifiedEventStore.subscribeToRedux)();
202
- (0, _unifiedEventStore.subscribeToNetwork)();
203
- (0, _unifiedEventStore.subscribeToReactQuery)();
204
- (0, _unifiedEventStore.subscribeToRoutes)();
301
+ // Only subscribe to sources that are enabled
302
+ if (enabledSources.has("storage-async")) (0, _unifiedEventStore.subscribeToStorage)();
303
+ if (enabledSources.has("redux")) (0, _unifiedEventStore.subscribeToRedux)();
304
+ if (enabledSources.has("network")) (0, _unifiedEventStore.subscribeToNetwork)();
305
+ if (enabledSources.has("react-query-query") || enabledSources.has("react-query-mutation")) {
306
+ (0, _unifiedEventStore.subscribeToReactQuery)();
307
+ }
308
+ if (enabledSources.has("route")) (0, _unifiedEventStore.subscribeToRoutes)();
309
+ if (enabledSources.has("render")) (0, _unifiedEventStore.subscribeToRender)();
205
310
  setIsCapturing(true);
206
- }, []);
311
+ }, [enabledSources]);
207
312
  const stopCapturing = (0, _react.useCallback)(() => {
208
313
  (0, _unifiedEventStore.unsubscribeAll)();
209
314
  setIsCapturing(false);
@@ -215,6 +320,9 @@ function useUnifiedEvents() {
215
320
  startCapturing();
216
321
  }
217
322
  }, [isCapturing, startCapturing, stopCapturing]);
323
+
324
+ // Get subscriber counts (recalculates on every render, but it's a cheap operation)
325
+ const totalSubscriberCount = (0, _unifiedEventStore.getSubscriberCounts)().totalSubscribers;
218
326
  return {
219
327
  events,
220
328
  filteredEvents,
@@ -231,7 +339,8 @@ function useUnifiedEvents() {
231
339
  toggleCapturing,
232
340
  discoveredSources,
233
341
  hiddenEventsCount,
234
- isPro
342
+ isPro,
343
+ totalSubscriberCount
235
344
  };
236
345
  }
237
346
  //# sourceMappingURL=useUnifiedEvents.js.map
@@ -129,6 +129,12 @@ Object.defineProperty(exports, "generateMarkdownExport", {
129
129
  return _eventExportFormatter.generateMarkdownExport;
130
130
  }
131
131
  });
132
+ Object.defineProperty(exports, "generateMermaidExport", {
133
+ enumerable: true,
134
+ get: function () {
135
+ return _eventExportFormatter.generateMermaidExport;
136
+ }
137
+ });
132
138
  Object.defineProperty(exports, "generatePlaintextExport", {
133
139
  enumerable: true,
134
140
  get: function () {