@buoy-gg/events 2.1.10 → 2.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/lib/commonjs/components/EventsCopySettingsView.js +3 -1
  2. package/lib/commonjs/components/ReactQueryEventDetail.js +7 -3
  3. package/lib/commonjs/components/UnifiedEventDetail.js +68 -2
  4. package/lib/commonjs/components/UnifiedEventItem.js +59 -1
  5. package/lib/commonjs/hooks/useUnifiedEvents.js +23 -1
  6. package/lib/commonjs/stores/unifiedEventStore.js +59 -1
  7. package/lib/commonjs/utils/autoDiscoverEventSources.js +146 -1
  8. package/lib/commonjs/utils/eventExportFormatter.js +2 -0
  9. package/lib/module/components/EventsCopySettingsView.js +3 -1
  10. package/lib/module/components/ReactQueryEventDetail.js +7 -3
  11. package/lib/module/components/UnifiedEventDetail.js +68 -2
  12. package/lib/module/components/UnifiedEventItem.js +59 -1
  13. package/lib/module/hooks/useUnifiedEvents.js +24 -2
  14. package/lib/module/stores/unifiedEventStore.js +54 -0
  15. package/lib/module/utils/autoDiscoverEventSources.js +146 -1
  16. package/lib/module/utils/eventExportFormatter.js +2 -0
  17. package/lib/typescript/stores/unifiedEventStore.d.ts +20 -0
  18. package/lib/typescript/types/index.d.ts +1 -1
  19. package/package.json +3 -3
  20. package/src/components/EventsCopySettingsView.tsx +3 -1
  21. package/src/components/ReactQueryEventDetail.tsx +7 -3
  22. package/src/components/UnifiedEventDetail.tsx +79 -1
  23. package/src/components/UnifiedEventItem.tsx +66 -0
  24. package/src/hooks/useUnifiedEvents.ts +28 -0
  25. package/src/stores/unifiedEventStore.ts +54 -0
  26. package/src/types/index.ts +3 -1
  27. package/src/utils/autoDiscoverEventSources.ts +165 -0
  28. package/src/utils/eventExportFormatter.ts +2 -0
@@ -266,7 +266,9 @@ function EventsCopySettingsView({
266
266
  }), settings.format === "json" ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
267
267
  data: JSON.parse(exportText),
268
268
  title: "",
269
- showTypeFilter: false
269
+ showTypeFilter: true,
270
+ rawMode: true,
271
+ initialExpanded: true
270
272
  }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
271
273
  children: [settings.format === "mermaid" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
272
274
  style: styles.mermaidHint,
@@ -184,7 +184,8 @@ const ReactQueryEventDetail = exports.ReactQueryEventDetail = /*#__PURE__*/(0, _
184
184
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
185
185
  title: "",
186
186
  data: reactQueryEvent.mutationVariables,
187
- showTypeFilter: false,
187
+ showTypeFilter: true,
188
+ rawMode: true,
188
189
  initialExpanded: true
189
190
  })
190
191
  })]
@@ -203,7 +204,8 @@ const ReactQueryEventDetail = exports.ReactQueryEventDetail = /*#__PURE__*/(0, _
203
204
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
204
205
  title: "",
205
206
  data: isQuery ? reactQueryEvent.queryData : reactQueryEvent.mutationData,
206
- showTypeFilter: false,
207
+ showTypeFilter: true,
208
+ rawMode: true,
207
209
  initialExpanded: true
208
210
  })
209
211
  })]
@@ -220,7 +222,8 @@ const ReactQueryEventDetail = exports.ReactQueryEventDetail = /*#__PURE__*/(0, _
220
222
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
221
223
  title: "",
222
224
  data: isQuery ? reactQueryEvent.queryError : reactQueryEvent.mutationError,
223
- showTypeFilter: false,
225
+ showTypeFilter: true,
226
+ rawMode: true,
224
227
  initialExpanded: true
225
228
  })
226
229
  })]
@@ -238,6 +241,7 @@ const ReactQueryEventDetail = exports.ReactQueryEventDetail = /*#__PURE__*/(0, _
238
241
  title: "",
239
242
  data: reactQueryEvent,
240
243
  showTypeFilter: true,
244
+ rawMode: true,
241
245
  initialExpanded: false
242
246
  })
243
247
  })]
@@ -27,7 +27,9 @@ function tryLoadOptionalDetailComponents() {
27
27
  StorageEventDetailContent: null,
28
28
  ReduxActionDetailContent: null,
29
29
  NetworkEventDetailView: null,
30
- RenderDetailView: null
30
+ RenderDetailView: null,
31
+ ZustandStateDetailContent: null,
32
+ JotaiAtomDetailContent: null
31
33
  };
32
34
 
33
35
  // Try to load storage detail component
@@ -65,6 +67,24 @@ function tryLoadOptionalDetailComponents() {
65
67
  } catch {
66
68
  // Optional dependency not installed
67
69
  }
70
+
71
+ // Try to load zustand detail component
72
+ try {
73
+ // @ts-ignore - Dynamic import that may not exist
74
+ const zustand = require("@buoy-gg/zustand");
75
+ components.ZustandStateDetailContent = zustand.ZustandStateDetailContent;
76
+ } catch {
77
+ // Optional dependency not installed
78
+ }
79
+
80
+ // Try to load jotai detail component
81
+ try {
82
+ // @ts-ignore - Dynamic import that may not exist
83
+ const jotai = require("@buoy-gg/jotai");
84
+ components.JotaiAtomDetailContent = jotai.JotaiAtomDetailContent;
85
+ } catch {
86
+ // Optional dependency not installed
87
+ }
68
88
  return components;
69
89
  }
70
90
 
@@ -111,6 +131,14 @@ const SOURCE_CONFIG = {
111
131
  label: "Route",
112
132
  color: "#06B6D4"
113
133
  },
134
+ zustand: {
135
+ label: "Zustand",
136
+ color: "#764ABC"
137
+ },
138
+ jotai: {
139
+ label: "Jotai",
140
+ color: "#14B8A6"
141
+ },
114
142
  render: {
115
143
  label: "Render",
116
144
  color: "#F472B6"
@@ -229,6 +257,42 @@ const UnifiedEventDetail = exports.UnifiedEventDetail = /*#__PURE__*/(0, _react.
229
257
  });
230
258
  }
231
259
 
260
+ // For Zustand events, use the shared ZustandStateDetailContent if available
261
+ if (event.source === "zustand" && optionalComponents.ZustandStateDetailContent) {
262
+ const {
263
+ ZustandStateDetailContent
264
+ } = optionalComponents;
265
+ const zustandChange = event.originalEvent;
266
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
267
+ style: styles.container,
268
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ZustandStateDetailContent, {
269
+ change: zustandChange,
270
+ changes: [zustandChange],
271
+ selectedIndex: 0,
272
+ onIndexChange: () => {},
273
+ disableInternalFooter: true
274
+ })
275
+ });
276
+ }
277
+
278
+ // For Jotai events, use the shared JotaiAtomDetailContent if available
279
+ if (event.source === "jotai" && optionalComponents.JotaiAtomDetailContent) {
280
+ const {
281
+ JotaiAtomDetailContent
282
+ } = optionalComponents;
283
+ const jotaiChange = event.originalEvent;
284
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
285
+ style: styles.container,
286
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(JotaiAtomDetailContent, {
287
+ change: jotaiChange,
288
+ changes: [jotaiChange],
289
+ selectedIndex: 0,
290
+ onIndexChange: () => {},
291
+ disableInternalFooter: true
292
+ })
293
+ });
294
+ }
295
+
232
296
  // For render events, use the RenderDetailView if available
233
297
  if (event.source === "render" && optionalComponents.RenderDetailView) {
234
298
  const {
@@ -309,7 +373,9 @@ const UnifiedEventDetail = exports.UnifiedEventDetail = /*#__PURE__*/(0, _react.
309
373
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
310
374
  data: getEventData(),
311
375
  title: "",
312
- showTypeFilter: false
376
+ showTypeFilter: true,
377
+ rawMode: true,
378
+ initialExpanded: true
313
379
  })
314
380
  })]
315
381
  })]
@@ -26,7 +26,9 @@ function tryLoadOptionalComponents() {
26
26
  getValueType: null,
27
27
  ReduxActionItem: null,
28
28
  NetworkEventItemCompact: null,
29
- RouteEventItemCompact: null
29
+ RouteEventItemCompact: null,
30
+ ZustandStateChangeItem: null,
31
+ JotaiAtomChangeItem: null
30
32
  };
31
33
 
32
34
  // Try to load storage components
@@ -65,6 +67,24 @@ function tryLoadOptionalComponents() {
65
67
  } catch {
66
68
  // Optional dependency not installed
67
69
  }
70
+
71
+ // Try to load zustand components
72
+ try {
73
+ // @ts-ignore - Dynamic import that may not exist
74
+ const zustand = require("@buoy-gg/zustand");
75
+ components.ZustandStateChangeItem = zustand.ZustandStateChangeItem;
76
+ } catch {
77
+ // Optional dependency not installed
78
+ }
79
+
80
+ // Try to load jotai components
81
+ try {
82
+ // @ts-ignore - Dynamic import that may not exist
83
+ const jotai = require("@buoy-gg/jotai");
84
+ components.JotaiAtomChangeItem = jotai.JotaiAtomChangeItem;
85
+ } catch {
86
+ // Optional dependency not installed
87
+ }
68
88
  return components;
69
89
  }
70
90
 
@@ -111,6 +131,14 @@ const SOURCE_CONFIG = {
111
131
  label: "Route",
112
132
  color: "#06B6D4" // Cyan
113
133
  },
134
+ zustand: {
135
+ label: "Zustand",
136
+ color: "#764ABC" // Purple
137
+ },
138
+ jotai: {
139
+ label: "Jotai",
140
+ color: "#14B8A6" // Teal
141
+ },
114
142
  render: {
115
143
  label: "Render",
116
144
  color: "#F472B6" // Pink
@@ -182,6 +210,8 @@ const SOURCE_BADGE_LABELS = {
182
210
  "react-query-query": "QUERY",
183
211
  "react-query-mutation": "MUTATION",
184
212
  route: "ROUTE",
213
+ zustand: "ZUSTAND",
214
+ jotai: "JOTAI",
185
215
  render: "RENDER"
186
216
  };
187
217
 
@@ -293,6 +323,34 @@ const UnifiedEventItem = exports.UnifiedEventItem = /*#__PURE__*/(0, _react.memo
293
323
  });
294
324
  }
295
325
 
326
+ // For Zustand events, try to use the shared ZustandStateChangeItem component
327
+ if (event.source === "zustand" && optionalComponents.ZustandStateChangeItem) {
328
+ const {
329
+ ZustandStateChangeItem
330
+ } = optionalComponents;
331
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(SourceBadgeWrapper, {
332
+ source: event.source,
333
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ZustandStateChangeItem, {
334
+ change: event.originalEvent,
335
+ onPress: () => onPress()
336
+ })
337
+ });
338
+ }
339
+
340
+ // For Jotai events, try to use the shared JotaiAtomChangeItem component
341
+ if (event.source === "jotai" && optionalComponents.JotaiAtomChangeItem) {
342
+ const {
343
+ JotaiAtomChangeItem
344
+ } = optionalComponents;
345
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(SourceBadgeWrapper, {
346
+ source: event.source,
347
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(JotaiAtomChangeItem, {
348
+ change: event.originalEvent,
349
+ onPress: () => onPress()
350
+ })
351
+ });
352
+ }
353
+
296
354
  // Fallback: Use CompactRow for all events when specific components aren't available
297
355
  const customBadge = correlationLabel && correlationColor ? /*#__PURE__*/(0, _jsxRuntime.jsx)(CorrelationBadge, {
298
356
  label: correlationLabel,
@@ -54,7 +54,7 @@ function isBuoyInternalEvent(event) {
54
54
  /**
55
55
  * All possible sources for display
56
56
  */
57
- const ALL_DISPLAY_SOURCES = ["storage-async", "redux", "network", "react-query-query", "react-query-mutation", "route", "render"];
57
+ const ALL_DISPLAY_SOURCES = ["storage-async", "redux", "network", "react-query-query", "react-query-mutation", "route", "zustand", "jotai", "render"];
58
58
 
59
59
  /**
60
60
  * Sources enabled by default on first load.
@@ -75,6 +75,8 @@ const SOURCE_TO_EVENT_SOURCES = {
75
75
  "react-query-query": ["react-query", "react-query-query"],
76
76
  "react-query-mutation": ["react-query-mutation"],
77
77
  route: ["route"],
78
+ zustand: ["zustand"],
79
+ jotai: ["jotai"],
78
80
  render: ["render"]
79
81
  };
80
82
 
@@ -90,6 +92,8 @@ const EVENT_SOURCE_TO_DISCOVERY_ID = {
90
92
  "react-query-query": "react-query",
91
93
  "react-query-mutation": "react-query",
92
94
  route: "route-events",
95
+ zustand: "zustand",
96
+ jotai: "jotai",
93
97
  render: "render"
94
98
  };
95
99
  function useUnifiedEvents() {
@@ -169,6 +173,8 @@ function useUnifiedEvents() {
169
173
  (0, _unifiedEventStore.subscribeToReactQuery)();
170
174
  }
171
175
  if (sourcesToEnable.has("route")) (0, _unifiedEventStore.subscribeToRoutes)();
176
+ if (sourcesToEnable.has("zustand")) (0, _unifiedEventStore.subscribeToZustand)();
177
+ if (sourcesToEnable.has("jotai")) (0, _unifiedEventStore.subscribeToJotai)();
172
178
  if (sourcesToEnable.has("render")) (0, _unifiedEventStore.subscribeToRender)();
173
179
  }
174
180
  isStateRestoredRef.current = true;
@@ -249,6 +255,8 @@ function useUnifiedEvents() {
249
255
  "react-query-query": "react-query",
250
256
  "react-query-mutation": "react-query",
251
257
  route: "route-events",
258
+ zustand: "zustand",
259
+ jotai: "jotai",
252
260
  render: "render"
253
261
  };
254
262
  const sources = availableDisplaySources.map(source => {
@@ -299,6 +307,12 @@ function useUnifiedEvents() {
299
307
  case "route":
300
308
  (0, _unifiedEventStore.unsubscribeFromRoutes)();
301
309
  break;
310
+ case "zustand":
311
+ (0, _unifiedEventStore.unsubscribeFromZustand)();
312
+ break;
313
+ case "jotai":
314
+ (0, _unifiedEventStore.unsubscribeFromJotai)();
315
+ break;
302
316
  case "render":
303
317
  (0, _unifiedEventStore.unsubscribeFromRender)();
304
318
  break;
@@ -324,6 +338,12 @@ function useUnifiedEvents() {
324
338
  case "route":
325
339
  (0, _unifiedEventStore.subscribeToRoutes)();
326
340
  break;
341
+ case "zustand":
342
+ (0, _unifiedEventStore.subscribeToZustand)();
343
+ break;
344
+ case "jotai":
345
+ (0, _unifiedEventStore.subscribeToJotai)();
346
+ break;
327
347
  case "render":
328
348
  (0, _unifiedEventStore.subscribeToRender)();
329
349
  break;
@@ -348,6 +368,8 @@ function useUnifiedEvents() {
348
368
  (0, _unifiedEventStore.subscribeToReactQuery)();
349
369
  }
350
370
  if (enabledSources.has("route")) (0, _unifiedEventStore.subscribeToRoutes)();
371
+ if (enabledSources.has("zustand")) (0, _unifiedEventStore.subscribeToZustand)();
372
+ if (enabledSources.has("jotai")) (0, _unifiedEventStore.subscribeToJotai)();
351
373
  if (enabledSources.has("render")) (0, _unifiedEventStore.subscribeToRender)();
352
374
  setIsCapturing(true);
353
375
  }, [enabledSources]);
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.unsubscribeFromStorage = exports.unsubscribeFromRoutes = exports.unsubscribeFromRender = exports.unsubscribeFromRedux = exports.unsubscribeFromReactQuery = exports.unsubscribeFromNetwork = exports.unsubscribeAll = exports.unifiedEventStore = exports.subscribeToStorage = exports.subscribeToRoutes = exports.subscribeToRender = exports.subscribeToRedux = exports.subscribeToReactQuery = exports.subscribeToNetwork = exports.subscribeToAll = exports.subscribe = exports.isSourceSubscribed = exports.getSubscriptionStatus = exports.getSubscriberCounts = exports.getSourceCounts = exports.getEvents = exports.getEventCount = exports.getDiscoveredSources = exports.getAvailableEventSources = exports.getActiveSources = exports.clearEvents = void 0;
6
+ exports.unsubscribeFromZustand = exports.unsubscribeFromStorage = exports.unsubscribeFromRoutes = exports.unsubscribeFromRender = exports.unsubscribeFromRedux = exports.unsubscribeFromReactQuery = exports.unsubscribeFromNetwork = exports.unsubscribeFromJotai = exports.unsubscribeAll = exports.unifiedEventStore = exports.subscribeToZustand = exports.subscribeToStorage = exports.subscribeToRoutes = exports.subscribeToRender = exports.subscribeToRedux = exports.subscribeToReactQuery = exports.subscribeToNetwork = exports.subscribeToJotai = exports.subscribeToAll = exports.subscribe = exports.isSourceSubscribed = exports.getSubscriptionStatus = exports.getSubscriberCounts = exports.getSourceCounts = exports.getEvents = exports.getEventCount = exports.getDiscoveredSources = exports.getAvailableEventSources = exports.getActiveSources = exports.clearEvents = void 0;
7
7
  var _autoDiscoverEventSources = require("../utils/autoDiscoverEventSources");
8
8
  /**
9
9
  * Unified Event Store
@@ -198,6 +198,48 @@ class UnifiedEventStore {
198
198
  this.activeSources.delete("route");
199
199
  }
200
200
 
201
+ /**
202
+ * Subscribe to Zustand state changes (if @buoy-gg/zustand is installed)
203
+ */
204
+ subscribeToZustand() {
205
+ const {
206
+ sources
207
+ } = (0, _autoDiscoverEventSources.getCachedDiscovery)();
208
+ const zustandSource = sources.find(s => s.id === "zustand");
209
+ if (zustandSource) {
210
+ this.subscribeToSource(zustandSource);
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Unsubscribe from Zustand state changes
216
+ */
217
+ unsubscribeFromZustand() {
218
+ this.unsubscribeFromSource("zustand");
219
+ this.activeSources.delete("zustand");
220
+ }
221
+
222
+ /**
223
+ * Subscribe to Jotai atom changes (if @buoy-gg/jotai is installed)
224
+ */
225
+ subscribeToJotai() {
226
+ const {
227
+ sources
228
+ } = (0, _autoDiscoverEventSources.getCachedDiscovery)();
229
+ const jotaiSource = sources.find(s => s.id === "jotai");
230
+ if (jotaiSource) {
231
+ this.subscribeToSource(jotaiSource);
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Unsubscribe from Jotai atom changes
237
+ */
238
+ unsubscribeFromJotai() {
239
+ this.unsubscribeFromSource("jotai");
240
+ this.activeSources.delete("jotai");
241
+ }
242
+
201
243
  /**
202
244
  * Subscribe to render events (if @buoy-gg/highlight-updates is installed)
203
245
  */
@@ -283,6 +325,8 @@ class UnifiedEventStore {
283
325
  "react-query-query": 0,
284
326
  "react-query-mutation": 0,
285
327
  route: 0,
328
+ zustand: 0,
329
+ jotai: 0,
286
330
  render: 0
287
331
  };
288
332
  for (const event of this.events) {
@@ -364,6 +408,10 @@ class UnifiedEventStore {
364
408
  return this.sourceUnsubscribers.has("react-query");
365
409
  case "route":
366
410
  return this.sourceUnsubscribers.has("route-events");
411
+ case "zustand":
412
+ return this.sourceUnsubscribers.has("zustand");
413
+ case "jotai":
414
+ return this.sourceUnsubscribers.has("jotai");
367
415
  case "render":
368
416
  return this.sourceUnsubscribers.has("render");
369
417
  default:
@@ -384,6 +432,8 @@ class UnifiedEventStore {
384
432
  "react-query-query": this.sourceUnsubscribers.has("react-query"),
385
433
  "react-query-mutation": this.sourceUnsubscribers.has("react-query"),
386
434
  route: this.sourceUnsubscribers.has("route-events"),
435
+ zustand: this.sourceUnsubscribers.has("zustand"),
436
+ jotai: this.sourceUnsubscribers.has("jotai"),
387
437
  render: this.sourceUnsubscribers.has("render")
388
438
  };
389
439
  }
@@ -421,6 +471,14 @@ const subscribeToRoutes = () => unifiedEventStore.subscribeToRoutes();
421
471
  exports.subscribeToRoutes = subscribeToRoutes;
422
472
  const unsubscribeFromRoutes = () => unifiedEventStore.unsubscribeFromRoutes();
423
473
  exports.unsubscribeFromRoutes = unsubscribeFromRoutes;
474
+ const subscribeToZustand = () => unifiedEventStore.subscribeToZustand();
475
+ exports.subscribeToZustand = subscribeToZustand;
476
+ const unsubscribeFromZustand = () => unifiedEventStore.unsubscribeFromZustand();
477
+ exports.unsubscribeFromZustand = unsubscribeFromZustand;
478
+ const subscribeToJotai = () => unifiedEventStore.subscribeToJotai();
479
+ exports.subscribeToJotai = subscribeToJotai;
480
+ const unsubscribeFromJotai = () => unifiedEventStore.unsubscribeFromJotai();
481
+ exports.unsubscribeFromJotai = unsubscribeFromJotai;
424
482
  const subscribeToRender = () => unifiedEventStore.subscribeToRender();
425
483
  exports.subscribeToRender = subscribeToRender;
426
484
  const unsubscribeFromRender = () => unifiedEventStore.unsubscribeFromRender();
@@ -525,6 +525,141 @@ function transformRouteEvent(event) {
525
525
  };
526
526
  }
527
527
 
528
+ // ============================================================================
529
+ // Zustand Event Source Discovery
530
+ // ============================================================================
531
+
532
+ function tryLoadZustandSource() {
533
+ try {
534
+ // @ts-ignore - Dynamic import that may not exist
535
+ const {
536
+ zustandStateStore
537
+ } = require("@buoy-gg/zustand");
538
+ let lastChangeId = null;
539
+ return {
540
+ id: "zustand",
541
+ name: "Zustand",
542
+ eventSources: ["zustand"],
543
+ available: true,
544
+ subscribe: onEvent => {
545
+ return zustandStateStore.subscribe(changes => {
546
+ if (changes.length > 0) {
547
+ const latestChange = changes[0];
548
+ if (latestChange.id !== lastChangeId) {
549
+ lastChangeId = latestChange.id;
550
+ const unifiedEvent = transformZustandChange(latestChange);
551
+ onEvent(unifiedEvent);
552
+ }
553
+ }
554
+ });
555
+ }
556
+ };
557
+ } catch {
558
+ return null;
559
+ }
560
+ }
561
+ function transformZustandChange(change) {
562
+ const c = change;
563
+
564
+ // Get status
565
+ let status = "neutral";
566
+ if (c.isSlowUpdate) {
567
+ status = "pending"; // Yellow for slow updates
568
+ } else if (c.hasStateChange) {
569
+ status = "success";
570
+ }
571
+
572
+ // Get subtitle
573
+ const parts = [];
574
+ if (c.changedKeys && c.changedKeys.length > 0) {
575
+ const keys = c.changedKeys.slice(0, 3).join(", ");
576
+ const more = c.changedKeys.length > 3 ? ` +${c.changedKeys.length - 3}` : "";
577
+ parts.push(`${keys}${more}`);
578
+ } else if (c.diffSummary) {
579
+ parts.push(c.diffSummary);
580
+ }
581
+ if (c.duration !== undefined) {
582
+ parts.push(`${c.duration.toFixed(1)}ms`);
583
+ }
584
+ if (c.category === "replace") {
585
+ parts.push("replace");
586
+ }
587
+ return {
588
+ id: generateEventId("zustand"),
589
+ source: "zustand",
590
+ timestamp: c.timestamp,
591
+ title: c.storeName,
592
+ subtitle: parts.join(" · ") || (c.hasStateChange ? "state changed" : "no change"),
593
+ status,
594
+ originalEvent: change
595
+ };
596
+ }
597
+
598
+ // ============================================================================
599
+ // Jotai Event Source Discovery
600
+ // ============================================================================
601
+
602
+ function tryLoadJotaiSource() {
603
+ try {
604
+ // @ts-ignore - Dynamic import that may not exist
605
+ const {
606
+ jotaiStateStore
607
+ } = require("@buoy-gg/jotai");
608
+ let lastChangeId = null;
609
+ return {
610
+ id: "jotai",
611
+ name: "Jotai",
612
+ eventSources: ["jotai"],
613
+ available: true,
614
+ subscribe: onEvent => {
615
+ return jotaiStateStore.subscribe(changes => {
616
+ if (changes.length > 0) {
617
+ const latestChange = changes[0];
618
+ if (latestChange.id !== lastChangeId) {
619
+ lastChangeId = latestChange.id;
620
+ const unifiedEvent = transformJotaiChange(latestChange);
621
+ onEvent(unifiedEvent);
622
+ }
623
+ }
624
+ });
625
+ }
626
+ };
627
+ } catch {
628
+ return null;
629
+ }
630
+ }
631
+ function transformJotaiChange(change) {
632
+ const c = change;
633
+
634
+ // Get status
635
+ let status = "neutral";
636
+ if (c.category === "write" && c.hasValueChange) {
637
+ status = "success";
638
+ } else if (c.category === "read") {
639
+ status = "neutral";
640
+ }
641
+
642
+ // Get subtitle
643
+ const parts = [];
644
+ if (c.valuePreview) {
645
+ parts.push(c.valuePreview);
646
+ } else if (c.diffSummary) {
647
+ parts.push(c.diffSummary);
648
+ }
649
+ if (c.category) {
650
+ parts.push(c.category);
651
+ }
652
+ return {
653
+ id: generateEventId("jotai"),
654
+ source: "jotai",
655
+ timestamp: c.timestamp,
656
+ title: c.atomLabel,
657
+ subtitle: parts.join(" · ") || (c.hasValueChange ? "value changed" : "no change"),
658
+ status,
659
+ originalEvent: change
660
+ };
661
+ }
662
+
528
663
  // ============================================================================
529
664
  // Render Events Source Discovery (Highlight Updates)
530
665
  // ============================================================================
@@ -679,7 +814,7 @@ function autoDiscoverEventSources() {
679
814
  const availableEventSources = new Set();
680
815
 
681
816
  // Try to load each source
682
- const loaders = [tryLoadStorageSource, tryLoadReduxSource, tryLoadNetworkSource, tryLoadReactQuerySource, tryLoadRouteEventsSource, tryLoadHighlightUpdatesSource];
817
+ const loaders = [tryLoadStorageSource, tryLoadReduxSource, tryLoadNetworkSource, tryLoadReactQuerySource, tryLoadRouteEventsSource, tryLoadZustandSource, tryLoadJotaiSource, tryLoadHighlightUpdatesSource];
683
818
  for (const loader of loaders) {
684
819
  const source = loader();
685
820
  if (source && source.available) {
@@ -739,6 +874,16 @@ function getSourceDisplayConfig(source) {
739
874
  color: "#06B6D4",
740
875
  icon: "navigate-outline"
741
876
  },
877
+ zustand: {
878
+ label: "Zustand",
879
+ color: "#764ABC",
880
+ icon: "cube-outline"
881
+ },
882
+ jotai: {
883
+ label: "Jotai",
884
+ color: "#14B8A6",
885
+ icon: "ellipse-outline"
886
+ },
742
887
  render: {
743
888
  label: "Render",
744
889
  color: "#F472B6",
@@ -57,6 +57,8 @@ const SOURCE_LABELS = {
57
57
  "react-query-query": "Query",
58
58
  "react-query-mutation": "Mutation",
59
59
  route: "Route",
60
+ zustand: "Zustand",
61
+ jotai: "Jotai",
60
62
  render: "Render"
61
63
  };
62
64
 
@@ -262,7 +262,9 @@ export function EventsCopySettingsView({
262
262
  }), settings.format === "json" ? /*#__PURE__*/_jsx(DataViewer, {
263
263
  data: JSON.parse(exportText),
264
264
  title: "",
265
- showTypeFilter: false
265
+ showTypeFilter: true,
266
+ rawMode: true,
267
+ initialExpanded: true
266
268
  }) : /*#__PURE__*/_jsxs(View, {
267
269
  children: [settings.format === "mermaid" && /*#__PURE__*/_jsx(Text, {
268
270
  style: styles.mermaidHint,
@@ -180,7 +180,8 @@ export const ReactQueryEventDetail = /*#__PURE__*/memo(function ReactQueryEventD
180
180
  children: /*#__PURE__*/_jsx(DataViewer, {
181
181
  title: "",
182
182
  data: reactQueryEvent.mutationVariables,
183
- showTypeFilter: false,
183
+ showTypeFilter: true,
184
+ rawMode: true,
184
185
  initialExpanded: true
185
186
  })
186
187
  })]
@@ -199,7 +200,8 @@ export const ReactQueryEventDetail = /*#__PURE__*/memo(function ReactQueryEventD
199
200
  children: /*#__PURE__*/_jsx(DataViewer, {
200
201
  title: "",
201
202
  data: isQuery ? reactQueryEvent.queryData : reactQueryEvent.mutationData,
202
- showTypeFilter: false,
203
+ showTypeFilter: true,
204
+ rawMode: true,
203
205
  initialExpanded: true
204
206
  })
205
207
  })]
@@ -216,7 +218,8 @@ export const ReactQueryEventDetail = /*#__PURE__*/memo(function ReactQueryEventD
216
218
  children: /*#__PURE__*/_jsx(DataViewer, {
217
219
  title: "",
218
220
  data: isQuery ? reactQueryEvent.queryError : reactQueryEvent.mutationError,
219
- showTypeFilter: false,
221
+ showTypeFilter: true,
222
+ rawMode: true,
220
223
  initialExpanded: true
221
224
  })
222
225
  })]
@@ -234,6 +237,7 @@ export const ReactQueryEventDetail = /*#__PURE__*/memo(function ReactQueryEventD
234
237
  title: "",
235
238
  data: reactQueryEvent,
236
239
  showTypeFilter: true,
240
+ rawMode: true,
237
241
  initialExpanded: false
238
242
  })
239
243
  })]