@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.
Files changed (47) hide show
  1. package/lib/commonjs/components/EventsCopySettingsView.js +39 -8
  2. package/lib/commonjs/components/EventsModal.js +4 -7
  3. package/lib/commonjs/components/UnifiedEventDetail.js +32 -1
  4. package/lib/commonjs/components/UnifiedEventFilters.js +50 -21
  5. package/lib/commonjs/components/UnifiedEventItem.js +6 -1
  6. package/lib/commonjs/hooks/useUnifiedEvents.js +137 -28
  7. package/lib/commonjs/index.js +6 -0
  8. package/lib/commonjs/stores/unifiedEventStore.js +61 -16
  9. package/lib/commonjs/types/copySettings.js +29 -0
  10. package/lib/commonjs/utils/autoDiscoverEventSources.js +261 -39
  11. package/lib/commonjs/utils/badgeSelectionStorage.js +32 -0
  12. package/lib/commonjs/utils/eventExportFormatter.js +778 -1
  13. package/lib/module/components/EventsCopySettingsView.js +40 -9
  14. package/lib/module/components/EventsModal.js +5 -8
  15. package/lib/module/components/UnifiedEventDetail.js +32 -1
  16. package/lib/module/components/UnifiedEventFilters.js +50 -21
  17. package/lib/module/components/UnifiedEventItem.js +6 -1
  18. package/lib/module/hooks/useUnifiedEvents.js +140 -31
  19. package/lib/module/index.js +4 -1
  20. package/lib/module/stores/unifiedEventStore.js +58 -16
  21. package/lib/module/types/copySettings.js +29 -0
  22. package/lib/module/utils/autoDiscoverEventSources.js +260 -39
  23. package/lib/module/utils/badgeSelectionStorage.js +30 -0
  24. package/lib/module/utils/eventExportFormatter.js +777 -1
  25. package/lib/typescript/components/UnifiedEventFilters.d.ts +3 -0
  26. package/lib/typescript/hooks/useUnifiedEvents.d.ts +2 -0
  27. package/lib/typescript/index.d.ts +1 -1
  28. package/lib/typescript/stores/unifiedEventStore.d.ts +18 -2
  29. package/lib/typescript/types/copySettings.d.ts +25 -1
  30. package/lib/typescript/types/index.d.ts +3 -1
  31. package/lib/typescript/utils/autoDiscoverEventSources.d.ts +17 -0
  32. package/lib/typescript/utils/badgeSelectionStorage.d.ts +9 -0
  33. package/lib/typescript/utils/eventExportFormatter.d.ts +4 -0
  34. package/package.json +3 -3
  35. package/src/components/EventsCopySettingsView.tsx +41 -5
  36. package/src/components/EventsModal.tsx +6 -17
  37. package/src/components/UnifiedEventDetail.tsx +28 -0
  38. package/src/components/UnifiedEventFilters.tsx +88 -21
  39. package/src/components/UnifiedEventItem.tsx +5 -0
  40. package/src/hooks/useUnifiedEvents.ts +153 -25
  41. package/src/index.tsx +4 -0
  42. package/src/stores/unifiedEventStore.ts +58 -12
  43. package/src/types/copySettings.ts +31 -1
  44. package/src/types/index.ts +4 -1
  45. package/src/utils/autoDiscoverEventSources.ts +268 -44
  46. package/src/utils/badgeSelectionStorage.ts +30 -0
  47. 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.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.1",
31
- "@buoy-gg/shared-ui": "2.1.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
- <ScrollView style={styles.previewScroll} nestedScrollEnabled>
314
- <Text style={styles.previewText} selectable={isPro}>
315
- {exportText}
316
- </Text>
317
- </ScrollView>
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
- <TouchableOpacity
184
- onPress={toggleCapturing}
185
- style={[
186
- styles.headerActionButton,
187
- isCapturing ? styles.startButton : styles.stopButton,
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: source.color + "20",
56
- borderColor: source.color + "60",
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 + "60",
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 ? source.color : buoyColors.textMuted + "80",
117
+ color: isEnabled
118
+ ? buoyColors.text
119
+ : buoyColors.textMuted + "70",
78
120
  },
79
121
  ]}
80
122
  >
81
123
  {source.label}
82
124
  </Text>
83
- {isEnabled && source.count > 0 && (
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.badgeCount,
141
+ styles.countText,
87
142
  {
88
- color: source.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
- {source.count}
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
- paddingHorizontal: 10,
126
- paddingVertical: 6,
127
- borderRadius: 16,
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.6,
193
+ opacity: 0.5,
135
194
  },
136
195
  dot: {
137
- width: 6,
138
- height: 6,
139
- borderRadius: 3,
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
- opacity: 0.8,
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
  /**