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