@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
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* Filter bar showing toggleable badges for each available event source.
|
|
5
5
|
* Badges are sorted: enabled first, disabled last.
|
|
6
6
|
* Toggling a badge enables/disables event listening for that source.
|
|
7
|
+
*
|
|
8
|
+
* Badge layout (inspired by React Query DevTools):
|
|
9
|
+
* [subscriber_count] • Label [event_count]
|
|
7
10
|
*/
|
|
8
11
|
import type { EventSource, SourceInfo } from "../types";
|
|
9
12
|
interface UnifiedEventFiltersProps {
|
|
@@ -25,6 +25,8 @@ export interface UseUnifiedEventsResult {
|
|
|
25
25
|
hiddenEventsCount: number;
|
|
26
26
|
/** Whether user is on Pro plan */
|
|
27
27
|
isPro: boolean;
|
|
28
|
+
/** Total number of subscribers across all event stores (for debugging) */
|
|
29
|
+
totalSubscriberCount: number;
|
|
28
30
|
}
|
|
29
31
|
export declare function useUnifiedEvents(): UseUnifiedEventsResult;
|
|
30
32
|
//# sourceMappingURL=useUnifiedEvents.d.ts.map
|
|
@@ -22,7 +22,7 @@ export { UnifiedEventDetail } from "./components/UnifiedEventDetail";
|
|
|
22
22
|
export { ReactQueryEventDetail } from "./components/ReactQueryEventDetail";
|
|
23
23
|
export { EventsCopySettingsView } from "./components/EventsCopySettingsView";
|
|
24
24
|
export { findRelatedEvents, hasRelatedEvents, getRelatedEventsCount, buildCorrelationCountMap, getCorrelationGroup, getCorrelationLabel, getCorrelationColor, } from "./utils/correlationUtils";
|
|
25
|
-
export { generateExport, generateMarkdownExport, generateJsonExport, generatePlaintextExport, estimateExportSize, getExportSummary, filterEvents, } from "./utils/eventExportFormatter";
|
|
25
|
+
export { generateExport, generateMarkdownExport, generateJsonExport, generatePlaintextExport, generateMermaidExport, estimateExportSize, getExportSummary, filterEvents, } from "./utils/eventExportFormatter";
|
|
26
26
|
export type { ExportSummary } from "./utils/eventExportFormatter";
|
|
27
27
|
export { loadCopySettings, saveCopySettings, clearCopySettings, } from "./utils/copySettingsStorage";
|
|
28
28
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Uses auto-discovery to find installed tool packages - no hard dependencies required!
|
|
8
8
|
*/
|
|
9
9
|
import type { UnifiedEvent, UnifiedEventListener, EventSource } from "../types";
|
|
10
|
-
import { type DiscoveredEventSource } from "../utils/autoDiscoverEventSources";
|
|
10
|
+
import { type DiscoveredEventSource, type AggregatedSubscriberCounts } from "../utils/autoDiscoverEventSources";
|
|
11
11
|
declare class UnifiedEventStore {
|
|
12
12
|
private events;
|
|
13
13
|
private listeners;
|
|
@@ -74,6 +74,14 @@ declare class UnifiedEventStore {
|
|
|
74
74
|
* Unsubscribe from route events
|
|
75
75
|
*/
|
|
76
76
|
unsubscribeFromRoutes(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Subscribe to render events (if @buoy-gg/highlight-updates is installed)
|
|
79
|
+
*/
|
|
80
|
+
subscribeToRender(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Unsubscribe from render events
|
|
83
|
+
*/
|
|
84
|
+
unsubscribeFromRender(): void;
|
|
77
85
|
/**
|
|
78
86
|
* Add an event to the store
|
|
79
87
|
*/
|
|
@@ -118,6 +126,11 @@ declare class UnifiedEventStore {
|
|
|
118
126
|
* Get subscription status for all sources
|
|
119
127
|
*/
|
|
120
128
|
getSubscriptionStatus(): Record<EventSource, boolean>;
|
|
129
|
+
/**
|
|
130
|
+
* Get subscriber counts from all underlying event stores.
|
|
131
|
+
* Useful for debugging the event system.
|
|
132
|
+
*/
|
|
133
|
+
getSubscriberCounts(): AggregatedSubscriberCounts;
|
|
121
134
|
}
|
|
122
135
|
export declare const unifiedEventStore: UnifiedEventStore;
|
|
123
136
|
export declare const subscribeToStorage: () => Promise<void>;
|
|
@@ -130,6 +143,8 @@ export declare const subscribeToReactQuery: () => void;
|
|
|
130
143
|
export declare const unsubscribeFromReactQuery: () => void;
|
|
131
144
|
export declare const subscribeToRoutes: () => void;
|
|
132
145
|
export declare const unsubscribeFromRoutes: () => void;
|
|
146
|
+
export declare const subscribeToRender: () => void;
|
|
147
|
+
export declare const unsubscribeFromRender: () => void;
|
|
133
148
|
export declare const getEvents: (enabledSources?: Set<EventSource>) => UnifiedEvent[];
|
|
134
149
|
export declare const getActiveSources: () => EventSource[];
|
|
135
150
|
export declare const getSourceCounts: () => Record<EventSource, number>;
|
|
@@ -142,5 +157,6 @@ export declare const unsubscribeAll: () => void;
|
|
|
142
157
|
export declare const getDiscoveredSources: () => DiscoveredEventSource[];
|
|
143
158
|
export declare const getAvailableEventSources: () => Set<EventSource>;
|
|
144
159
|
export declare const subscribeToAll: () => Promise<void>;
|
|
145
|
-
export
|
|
160
|
+
export declare const getSubscriberCounts: () => AggregatedSubscriberCounts;
|
|
161
|
+
export type { AggregatedSubscriberCounts } from "../utils/autoDiscoverEventSources";
|
|
146
162
|
//# sourceMappingURL=unifiedEventStore.d.ts.map
|
|
@@ -19,7 +19,7 @@ export interface EventsCopySettings {
|
|
|
19
19
|
includeTotalDuration: boolean;
|
|
20
20
|
includeEventData: boolean;
|
|
21
21
|
dataSizeThreshold: 1 | 5 | 10 | 50 | -1;
|
|
22
|
-
format: "markdown" | "json" | "plaintext";
|
|
22
|
+
format: "markdown" | "json" | "plaintext" | "mermaid";
|
|
23
23
|
filterMode: "all" | "errors" | "success" | "pending";
|
|
24
24
|
filterSources: EventSource[];
|
|
25
25
|
/** Compact mode - single line per event, no JSON blocks */
|
|
@@ -162,6 +162,30 @@ export declare const COPY_PRESETS: {
|
|
|
162
162
|
readonly showStorageDiff: false;
|
|
163
163
|
readonly stripVerboseFields: true;
|
|
164
164
|
};
|
|
165
|
+
/**
|
|
166
|
+
* Mermaid Diagram - Visual sequence diagram for flow visualization
|
|
167
|
+
*/
|
|
168
|
+
readonly mermaid: {
|
|
169
|
+
readonly timestampFormat: "relative";
|
|
170
|
+
readonly includeSource: true;
|
|
171
|
+
readonly includeStatus: true;
|
|
172
|
+
readonly includeTitle: true;
|
|
173
|
+
readonly includeSubtitle: false;
|
|
174
|
+
readonly includeCorrelation: true;
|
|
175
|
+
readonly includeDuration: true;
|
|
176
|
+
readonly includeSummaryHeader: false;
|
|
177
|
+
readonly includeTotalDuration: false;
|
|
178
|
+
readonly includeEventData: false;
|
|
179
|
+
readonly dataSizeThreshold: 1;
|
|
180
|
+
readonly format: "mermaid";
|
|
181
|
+
readonly filterMode: "all";
|
|
182
|
+
readonly filterSources: readonly [];
|
|
183
|
+
readonly compactMode: false;
|
|
184
|
+
readonly smartJsonParsing: true;
|
|
185
|
+
readonly reduxChangedOnly: true;
|
|
186
|
+
readonly showStorageDiff: true;
|
|
187
|
+
readonly stripVerboseFields: true;
|
|
188
|
+
};
|
|
165
189
|
};
|
|
166
190
|
export type CopyPresetName = keyof typeof COPY_PRESETS;
|
|
167
191
|
/**
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Event source identifiers
|
|
8
8
|
*/
|
|
9
|
-
export type EventSource = "storage-async" | "storage-mmkv" | "redux" | "network" | "react-query" | "react-query-query" | "react-query-mutation" | "route";
|
|
9
|
+
export type EventSource = "storage-async" | "storage-mmkv" | "redux" | "network" | "react-query" | "react-query-query" | "react-query-mutation" | "route" | "render";
|
|
10
10
|
/**
|
|
11
11
|
* Event status for visual indicators
|
|
12
12
|
*/
|
|
@@ -61,6 +61,8 @@ export interface SourceInfo {
|
|
|
61
61
|
count: number;
|
|
62
62
|
/** Whether this source is currently enabled (listening and showing events) */
|
|
63
63
|
enabled?: boolean;
|
|
64
|
+
/** Number of subscribers to this source's event store (for debugging) */
|
|
65
|
+
subscriberCount?: number;
|
|
64
66
|
}
|
|
65
67
|
/**
|
|
66
68
|
* Event listener callback
|
|
@@ -71,4 +71,21 @@ export declare function getCachedDiscovery(): DiscoveryResult;
|
|
|
71
71
|
* Clear the discovery cache (useful for testing or hot reload).
|
|
72
72
|
*/
|
|
73
73
|
export declare function clearDiscoveryCache(): void;
|
|
74
|
+
export interface SourceSubscriberCounts {
|
|
75
|
+
sourceId: string;
|
|
76
|
+
counts: {
|
|
77
|
+
eventCallbacks: number;
|
|
78
|
+
arrayListeners: number;
|
|
79
|
+
total: number;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export interface AggregatedSubscriberCounts {
|
|
83
|
+
sources: SourceSubscriberCounts[];
|
|
84
|
+
totalSubscribers: number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get subscriber counts from all discovered event stores.
|
|
88
|
+
* Useful for debugging and monitoring the event system.
|
|
89
|
+
*/
|
|
90
|
+
export declare function getAggregatedSubscriberCounts(): AggregatedSubscriberCounts;
|
|
74
91
|
//# sourceMappingURL=autoDiscoverEventSources.d.ts.map
|
|
@@ -18,4 +18,13 @@ export declare function loadEnabledSources(): Promise<EventSource[] | null>;
|
|
|
18
18
|
* Clear the saved enabled sources
|
|
19
19
|
*/
|
|
20
20
|
export declare function clearEnabledSources(): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Save the capturing state to persistent storage
|
|
23
|
+
*/
|
|
24
|
+
export declare function saveCapturingState(isCapturing: boolean): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Load the capturing state from persistent storage
|
|
27
|
+
* Returns null if no saved state exists (defaults to true on first load)
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadCapturingState(): Promise<boolean | null>;
|
|
21
30
|
//# sourceMappingURL=badgeSelectionStorage.d.ts.map
|
|
@@ -42,6 +42,10 @@ export declare function generateJsonExport(events: UnifiedEvent[], settings: Eve
|
|
|
42
42
|
* Generate plaintext export
|
|
43
43
|
*/
|
|
44
44
|
export declare function generatePlaintextExport(events: UnifiedEvent[], settings: EventsCopySettings): string;
|
|
45
|
+
/**
|
|
46
|
+
* Generate Mermaid sequence diagram export
|
|
47
|
+
*/
|
|
48
|
+
export declare function generateMermaidExport(events: UnifiedEvent[], settings: EventsCopySettings): string;
|
|
45
49
|
/**
|
|
46
50
|
* Generate export based on settings format
|
|
47
51
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/events",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "events package",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
],
|
|
28
28
|
"sideEffects": false,
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@buoy-gg/floating-tools-core": "2.1.
|
|
31
|
-
"@buoy-gg/shared-ui": "2.1.
|
|
30
|
+
"@buoy-gg/floating-tools-core": "2.1.3",
|
|
31
|
+
"@buoy-gg/shared-ui": "2.1.3"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"react": "*",
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
ChevronDown,
|
|
22
22
|
ChevronUp,
|
|
23
23
|
XCircle,
|
|
24
|
+
GitBranch,
|
|
24
25
|
ToolbarCopyButton,
|
|
25
26
|
useFeatureGate,
|
|
26
27
|
ProFeatureBanner,
|
|
@@ -310,11 +311,18 @@ export function EventsCopySettingsView({
|
|
|
310
311
|
showTypeFilter={false}
|
|
311
312
|
/>
|
|
312
313
|
) : (
|
|
313
|
-
<
|
|
314
|
-
|
|
315
|
-
{
|
|
316
|
-
|
|
317
|
-
|
|
314
|
+
<View>
|
|
315
|
+
{settings.format === "mermaid" && (
|
|
316
|
+
<Text style={styles.mermaidHint}>
|
|
317
|
+
💡 Paste into mermaid.live to visualize
|
|
318
|
+
</Text>
|
|
319
|
+
)}
|
|
320
|
+
<ScrollView style={styles.previewScroll} nestedScrollEnabled>
|
|
321
|
+
<Text style={styles.previewText} selectable={isPro}>
|
|
322
|
+
{exportText}
|
|
323
|
+
</Text>
|
|
324
|
+
</ScrollView>
|
|
325
|
+
</View>
|
|
318
326
|
)}
|
|
319
327
|
</View>
|
|
320
328
|
);
|
|
@@ -399,6 +407,15 @@ export function EventsCopySettingsView({
|
|
|
399
407
|
isActive: activePreset === "minimal",
|
|
400
408
|
description: PRESET_METADATA.minimal.description,
|
|
401
409
|
},
|
|
410
|
+
{
|
|
411
|
+
id: "preset::mermaid",
|
|
412
|
+
label: PRESET_METADATA.mermaid.label,
|
|
413
|
+
icon: GitBranch,
|
|
414
|
+
color: PRESET_METADATA.mermaid.color,
|
|
415
|
+
value: "mermaid",
|
|
416
|
+
isActive: activePreset === "mermaid",
|
|
417
|
+
description: PRESET_METADATA.mermaid.description,
|
|
418
|
+
},
|
|
402
419
|
{
|
|
403
420
|
id: "preset::custom",
|
|
404
421
|
label: "Custom",
|
|
@@ -498,6 +515,15 @@ export function EventsCopySettingsView({
|
|
|
498
515
|
value: "plaintext",
|
|
499
516
|
isActive: settings.format === "plaintext",
|
|
500
517
|
},
|
|
518
|
+
{
|
|
519
|
+
id: "format::mermaid",
|
|
520
|
+
label: "Diagram",
|
|
521
|
+
icon: GitBranch,
|
|
522
|
+
color: "#A855F7",
|
|
523
|
+
value: "mermaid",
|
|
524
|
+
isActive: settings.format === "mermaid",
|
|
525
|
+
description: "Mermaid sequence diagram",
|
|
526
|
+
},
|
|
501
527
|
],
|
|
502
528
|
},
|
|
503
529
|
|
|
@@ -723,6 +749,16 @@ const styles = StyleSheet.create({
|
|
|
723
749
|
color: macOSColors.text.primary,
|
|
724
750
|
lineHeight: 18,
|
|
725
751
|
},
|
|
752
|
+
mermaidHint: {
|
|
753
|
+
fontSize: 11,
|
|
754
|
+
color: macOSColors.text.secondary,
|
|
755
|
+
marginBottom: 8,
|
|
756
|
+
paddingHorizontal: 8,
|
|
757
|
+
paddingVertical: 6,
|
|
758
|
+
backgroundColor: "#A855F7" + "15",
|
|
759
|
+
borderRadius: 6,
|
|
760
|
+
overflow: "hidden",
|
|
761
|
+
},
|
|
726
762
|
statusLabel: {
|
|
727
763
|
fontSize: 11,
|
|
728
764
|
fontWeight: "600",
|
|
@@ -13,11 +13,11 @@ import {
|
|
|
13
13
|
ModalHeader,
|
|
14
14
|
buoyColors,
|
|
15
15
|
Trash2,
|
|
16
|
-
Power,
|
|
17
16
|
Copy,
|
|
18
17
|
devToolsStorageKeys,
|
|
19
18
|
ToolbarCopyButton,
|
|
20
19
|
UpgradeModal,
|
|
20
|
+
PowerToggleButton,
|
|
21
21
|
} from "@buoy-gg/shared-ui";
|
|
22
22
|
import type { ModalMode } from "@buoy-gg/shared-ui";
|
|
23
23
|
import { useUnifiedEvents } from "../hooks/useUnifiedEvents";
|
|
@@ -180,22 +180,11 @@ export function EventsModal({
|
|
|
180
180
|
/>
|
|
181
181
|
</TouchableOpacity>
|
|
182
182
|
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
]}
|
|
189
|
-
>
|
|
190
|
-
<Power
|
|
191
|
-
size={14}
|
|
192
|
-
color={
|
|
193
|
-
isCapturing
|
|
194
|
-
? buoyColors.success
|
|
195
|
-
: buoyColors.error
|
|
196
|
-
}
|
|
197
|
-
/>
|
|
198
|
-
</TouchableOpacity>
|
|
183
|
+
<PowerToggleButton
|
|
184
|
+
isEnabled={isCapturing}
|
|
185
|
+
onToggle={toggleCapturing}
|
|
186
|
+
accessibilityLabel="Toggle event capture"
|
|
187
|
+
/>
|
|
199
188
|
|
|
200
189
|
<TouchableOpacity
|
|
201
190
|
onPress={clearEvents}
|
|
@@ -45,6 +45,9 @@ interface OptionalDetailComponents {
|
|
|
45
45
|
NetworkEventDetailView: ComponentType<{
|
|
46
46
|
event: unknown;
|
|
47
47
|
}> | null;
|
|
48
|
+
RenderDetailView: ComponentType<{
|
|
49
|
+
render: unknown;
|
|
50
|
+
}> | null;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
function tryLoadOptionalDetailComponents(): OptionalDetailComponents {
|
|
@@ -52,6 +55,7 @@ function tryLoadOptionalDetailComponents(): OptionalDetailComponents {
|
|
|
52
55
|
StorageEventDetailContent: null,
|
|
53
56
|
ReduxActionDetailContent: null,
|
|
54
57
|
NetworkEventDetailView: null,
|
|
58
|
+
RenderDetailView: null,
|
|
55
59
|
};
|
|
56
60
|
|
|
57
61
|
// Try to load storage detail component
|
|
@@ -81,6 +85,15 @@ function tryLoadOptionalDetailComponents(): OptionalDetailComponents {
|
|
|
81
85
|
// Optional dependency not installed
|
|
82
86
|
}
|
|
83
87
|
|
|
88
|
+
// Try to load render detail component
|
|
89
|
+
try {
|
|
90
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
91
|
+
const highlightUpdates = require("@buoy-gg/highlight-updates");
|
|
92
|
+
components.RenderDetailView = highlightUpdates.RenderDetailView;
|
|
93
|
+
} catch {
|
|
94
|
+
// Optional dependency not installed
|
|
95
|
+
}
|
|
96
|
+
|
|
84
97
|
return components;
|
|
85
98
|
}
|
|
86
99
|
|
|
@@ -103,6 +116,7 @@ const SOURCE_CONFIG: Record<EventSource, { label: string; color: string }> = {
|
|
|
103
116
|
"react-query-query": { label: "Query", color: "#EC4899" },
|
|
104
117
|
"react-query-mutation": { label: "Mutation", color: "#F97316" },
|
|
105
118
|
route: { label: "Route", color: "#06B6D4" },
|
|
119
|
+
render: { label: "Render", color: "#F472B6" },
|
|
106
120
|
};
|
|
107
121
|
|
|
108
122
|
/**
|
|
@@ -222,6 +236,20 @@ export const UnifiedEventDetail = memo(function UnifiedEventDetail({
|
|
|
222
236
|
);
|
|
223
237
|
}
|
|
224
238
|
|
|
239
|
+
// For render events, use the RenderDetailView if available
|
|
240
|
+
if (event.source === "render" && optionalComponents.RenderDetailView) {
|
|
241
|
+
const { RenderDetailView } = optionalComponents;
|
|
242
|
+
const renderData = event.originalEvent as { render: unknown };
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<View style={styles.container}>
|
|
246
|
+
<ScrollView style={styles.content}>
|
|
247
|
+
<RenderDetailView render={renderData.render} />
|
|
248
|
+
</ScrollView>
|
|
249
|
+
</View>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
225
253
|
// Route events expand in-place in the list, so they shouldn't reach this detail view
|
|
226
254
|
// If they do somehow, fall through to the generic view
|
|
227
255
|
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* Filter bar showing toggleable badges for each available event source.
|
|
5
5
|
* Badges are sorted: enabled first, disabled last.
|
|
6
6
|
* Toggling a badge enables/disables event listening for that source.
|
|
7
|
+
*
|
|
8
|
+
* Badge layout (inspired by React Query DevTools):
|
|
9
|
+
* [subscriber_count] • Label [event_count]
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
12
|
import { View, Text, TouchableOpacity, StyleSheet, ScrollView } from "react-native";
|
|
@@ -44,6 +47,9 @@ export const UnifiedEventFilters = memo(function UnifiedEventFilters({
|
|
|
44
47
|
>
|
|
45
48
|
{availableSources.map((source) => {
|
|
46
49
|
const isEnabled = source.enabled !== false;
|
|
50
|
+
const subscriberCount = source.subscriberCount;
|
|
51
|
+
const hasSubscriberTracking = subscriberCount !== undefined;
|
|
52
|
+
const eventCount = source.count || 0;
|
|
47
53
|
|
|
48
54
|
return (
|
|
49
55
|
<TouchableOpacity
|
|
@@ -52,46 +58,99 @@ export const UnifiedEventFilters = memo(function UnifiedEventFilters({
|
|
|
52
58
|
styles.badge,
|
|
53
59
|
isEnabled
|
|
54
60
|
? {
|
|
55
|
-
backgroundColor:
|
|
56
|
-
borderColor: source.color + "
|
|
61
|
+
backgroundColor: buoyColors.card,
|
|
62
|
+
borderColor: source.color + "50",
|
|
57
63
|
}
|
|
58
64
|
: styles.badgeDisabled,
|
|
59
65
|
]}
|
|
60
66
|
onPress={() => onToggleSource(source.source)}
|
|
61
67
|
activeOpacity={0.7}
|
|
62
68
|
>
|
|
69
|
+
{/* Subscriber count on left - only show if tracking is available */}
|
|
70
|
+
{hasSubscriberTracking && (
|
|
71
|
+
<View
|
|
72
|
+
style={[
|
|
73
|
+
styles.countBadge,
|
|
74
|
+
{
|
|
75
|
+
backgroundColor: isEnabled
|
|
76
|
+
? subscriberCount > 0
|
|
77
|
+
? source.color + "25"
|
|
78
|
+
: buoyColors.textMuted + "20"
|
|
79
|
+
: buoyColors.textMuted + "15",
|
|
80
|
+
},
|
|
81
|
+
]}
|
|
82
|
+
>
|
|
83
|
+
<Text
|
|
84
|
+
style={[
|
|
85
|
+
styles.countText,
|
|
86
|
+
{
|
|
87
|
+
color: isEnabled
|
|
88
|
+
? subscriberCount > 0
|
|
89
|
+
? source.color
|
|
90
|
+
: buoyColors.textMuted
|
|
91
|
+
: buoyColors.textMuted + "60",
|
|
92
|
+
},
|
|
93
|
+
]}
|
|
94
|
+
>
|
|
95
|
+
{subscriberCount}
|
|
96
|
+
</Text>
|
|
97
|
+
</View>
|
|
98
|
+
)}
|
|
99
|
+
|
|
100
|
+
{/* Dot indicator */}
|
|
63
101
|
<View
|
|
64
102
|
style={[
|
|
65
103
|
styles.dot,
|
|
66
104
|
{
|
|
67
105
|
backgroundColor: isEnabled
|
|
68
106
|
? source.color
|
|
69
|
-
: buoyColors.textMuted + "
|
|
107
|
+
: buoyColors.textMuted + "40",
|
|
70
108
|
},
|
|
71
109
|
]}
|
|
72
110
|
/>
|
|
111
|
+
|
|
112
|
+
{/* Label */}
|
|
73
113
|
<Text
|
|
74
114
|
style={[
|
|
75
115
|
styles.badgeLabel,
|
|
76
116
|
{
|
|
77
|
-
color: isEnabled
|
|
117
|
+
color: isEnabled
|
|
118
|
+
? buoyColors.text
|
|
119
|
+
: buoyColors.textMuted + "70",
|
|
78
120
|
},
|
|
79
121
|
]}
|
|
80
122
|
>
|
|
81
123
|
{source.label}
|
|
82
124
|
</Text>
|
|
83
|
-
|
|
125
|
+
|
|
126
|
+
{/* Event count on right */}
|
|
127
|
+
<View
|
|
128
|
+
style={[
|
|
129
|
+
styles.countBadge,
|
|
130
|
+
{
|
|
131
|
+
backgroundColor: isEnabled
|
|
132
|
+
? eventCount > 0
|
|
133
|
+
? source.color + "20"
|
|
134
|
+
: buoyColors.textMuted + "15"
|
|
135
|
+
: buoyColors.textMuted + "10",
|
|
136
|
+
},
|
|
137
|
+
]}
|
|
138
|
+
>
|
|
84
139
|
<Text
|
|
85
140
|
style={[
|
|
86
|
-
styles.
|
|
141
|
+
styles.countText,
|
|
87
142
|
{
|
|
88
|
-
color:
|
|
143
|
+
color: isEnabled
|
|
144
|
+
? eventCount > 0
|
|
145
|
+
? source.color
|
|
146
|
+
: buoyColors.textMuted + "80"
|
|
147
|
+
: buoyColors.textMuted + "50",
|
|
89
148
|
},
|
|
90
149
|
]}
|
|
91
150
|
>
|
|
92
|
-
{
|
|
151
|
+
{eventCount}
|
|
93
152
|
</Text>
|
|
94
|
-
|
|
153
|
+
</View>
|
|
95
154
|
</TouchableOpacity>
|
|
96
155
|
);
|
|
97
156
|
})}
|
|
@@ -122,30 +181,38 @@ const styles = StyleSheet.create({
|
|
|
122
181
|
badge: {
|
|
123
182
|
flexDirection: "row",
|
|
124
183
|
alignItems: "center",
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
borderRadius:
|
|
184
|
+
paddingVertical: 5,
|
|
185
|
+
paddingHorizontal: 6,
|
|
186
|
+
borderRadius: 20,
|
|
128
187
|
borderWidth: 1,
|
|
129
188
|
gap: 6,
|
|
130
189
|
},
|
|
131
190
|
badgeDisabled: {
|
|
132
191
|
backgroundColor: buoyColors.card,
|
|
133
192
|
borderColor: buoyColors.border,
|
|
134
|
-
opacity: 0.
|
|
193
|
+
opacity: 0.5,
|
|
135
194
|
},
|
|
136
195
|
dot: {
|
|
137
|
-
width:
|
|
138
|
-
height:
|
|
139
|
-
borderRadius:
|
|
196
|
+
width: 7,
|
|
197
|
+
height: 7,
|
|
198
|
+
borderRadius: 4,
|
|
140
199
|
},
|
|
141
200
|
badgeLabel: {
|
|
142
201
|
fontSize: 12,
|
|
143
|
-
fontWeight: "600",
|
|
144
|
-
},
|
|
145
|
-
badgeCount: {
|
|
146
|
-
fontSize: 11,
|
|
147
202
|
fontWeight: "500",
|
|
148
|
-
|
|
203
|
+
},
|
|
204
|
+
countBadge: {
|
|
205
|
+
minWidth: 18,
|
|
206
|
+
height: 18,
|
|
207
|
+
borderRadius: 9,
|
|
208
|
+
alignItems: "center",
|
|
209
|
+
justifyContent: "center",
|
|
210
|
+
paddingHorizontal: 5,
|
|
211
|
+
},
|
|
212
|
+
countText: {
|
|
213
|
+
fontSize: 10,
|
|
214
|
+
fontWeight: "600",
|
|
215
|
+
fontVariant: ["tabular-nums"],
|
|
149
216
|
},
|
|
150
217
|
filterInfo: {
|
|
151
218
|
marginTop: 6,
|
|
@@ -140,6 +140,10 @@ const SOURCE_CONFIG: Record<
|
|
|
140
140
|
label: "Route",
|
|
141
141
|
color: "#06B6D4", // Cyan
|
|
142
142
|
},
|
|
143
|
+
render: {
|
|
144
|
+
label: "Render",
|
|
145
|
+
color: "#F472B6", // Pink
|
|
146
|
+
},
|
|
143
147
|
};
|
|
144
148
|
|
|
145
149
|
/**
|
|
@@ -216,6 +220,7 @@ const SOURCE_BADGE_LABELS: Record<EventSource, string> = {
|
|
|
216
220
|
"react-query-query": "QUERY",
|
|
217
221
|
"react-query-mutation": "MUTATION",
|
|
218
222
|
route: "ROUTE",
|
|
223
|
+
render: "RENDER",
|
|
219
224
|
};
|
|
220
225
|
|
|
221
226
|
/**
|