react-native-inapp-inspector 1.1.2 → 1.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 (36) hide show
  1. package/README.md +14 -0
  2. package/dist/commonjs/components/ConsoleLogCard.js +18 -0
  3. package/dist/commonjs/components/LogCard.js +17 -0
  4. package/dist/commonjs/components/NetworkIcons.d.ts +9 -2
  5. package/dist/commonjs/components/NetworkIcons.js +59 -3
  6. package/dist/commonjs/constants/version.d.ts +1 -1
  7. package/dist/commonjs/constants/version.js +1 -1
  8. package/dist/commonjs/customHooks/reduxLogger.d.ts +21 -7
  9. package/dist/commonjs/customHooks/reduxLogger.js +147 -48
  10. package/dist/commonjs/customHooks/webViewLogger.js +13 -8
  11. package/dist/commonjs/helpers/settingsStore.d.ts +24 -0
  12. package/dist/commonjs/helpers/settingsStore.js +74 -0
  13. package/dist/commonjs/index.d.ts +1 -1
  14. package/dist/commonjs/index.js +572 -46
  15. package/dist/commonjs/styles/index.d.ts +17 -1
  16. package/dist/commonjs/styles/index.js +25 -3
  17. package/dist/commonjs/types/index.d.ts +4 -0
  18. package/dist/esm/components/ConsoleLogCard.js +18 -0
  19. package/dist/esm/components/LogCard.js +17 -0
  20. package/dist/esm/components/NetworkIcons.d.ts +9 -2
  21. package/dist/esm/components/NetworkIcons.js +51 -2
  22. package/dist/esm/constants/version.d.ts +1 -1
  23. package/dist/esm/constants/version.js +1 -1
  24. package/dist/esm/customHooks/reduxLogger.d.ts +21 -7
  25. package/dist/esm/customHooks/reduxLogger.js +145 -47
  26. package/dist/esm/customHooks/webViewLogger.js +13 -8
  27. package/dist/esm/helpers/settingsStore.d.ts +24 -0
  28. package/dist/esm/helpers/settingsStore.js +67 -0
  29. package/dist/esm/index.d.ts +1 -1
  30. package/dist/esm/index.js +570 -47
  31. package/dist/esm/styles/index.d.ts +17 -1
  32. package/dist/esm/styles/index.js +25 -3
  33. package/dist/esm/types/index.d.ts +4 -0
  34. package/example/ios/example.xcodeproj/project.pbxproj +0 -8
  35. package/example/package-lock.json +4 -3
  36. package/package.json +1 -1
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.subscribeReduxState = exports.getReduxState = exports.connectReduxStore = exports.ErrorBoundary = exports.subscribeWebView = exports.clearWebViewData = exports.getWebViewHtmlUrl = exports.getWebViewJs = exports.getWebViewCss = exports.getWebViewHtml = exports.getWebViewNavHistory = exports.getWebViewLogs = exports.WebView = exports.clearAnalyticsEvents = exports.subscribeAnalyticsEvents = exports.logAnalyticsEvent = exports.setupAnalyticsLogger = exports.subscribeConsoleLogs = exports.clearConsoleLogs = exports.setupConsoleLogger = exports.addAxiosInterceptors = exports.subscribeNetworkLogs = exports.clearNetworkLogs = exports.setupNetworkLogger = void 0;
39
+ exports.clearActionHistory = exports.getActionHistory = exports.subscribeReduxState = exports.getReduxState = exports.inspectorReduxMiddleware = exports.connectReduxStore = exports.ErrorBoundary = exports.subscribeWebView = exports.clearWebViewData = exports.getWebViewHtmlUrl = exports.getWebViewJs = exports.getWebViewCss = exports.getWebViewHtml = exports.getWebViewNavHistory = exports.getWebViewLogs = exports.WebView = exports.clearAnalyticsEvents = exports.subscribeAnalyticsEvents = exports.logAnalyticsEvent = exports.setupAnalyticsLogger = exports.subscribeConsoleLogs = exports.clearConsoleLogs = exports.setupConsoleLogger = exports.addAxiosInterceptors = exports.subscribeNetworkLogs = exports.clearNetworkLogs = exports.setupNetworkLogger = void 0;
40
40
  const react_1 = __importStar(require("react"));
41
41
  const react_native_1 = require("react-native");
42
42
  const react_native_svg_1 = __importStar(require("react-native-svg"));
@@ -62,6 +62,8 @@ const CodeSnippet_1 = __importDefault(require("./components/CodeSnippet"));
62
62
  const AnimatedEntrance_1 = __importDefault(require("./components/AnimatedEntrance"));
63
63
  // Helpers
64
64
  const helpers_1 = require("./helpers");
65
+ // #5 — settings persistence
66
+ const settingsStore_1 = require("./helpers/settingsStore");
65
67
  // Assets
66
68
  const NetworkIcons_1 = require("./components/NetworkIcons");
67
69
  const ErrorBoundary_1 = __importDefault(require("./components/ErrorBoundary"));
@@ -276,6 +278,141 @@ const NetworkInspector = ({ enabled = true, }) => {
276
278
  const [reduxExpandDepth, setReduxExpandDepth] = (0, react_1.useState)(1);
277
279
  const [slowRequestThreshold, setSlowRequestThreshold] = (0, react_1.useState)(1000);
278
280
  const [insightsShowConsoleAlerts, setInsightsShowConsoleAlerts] = (0, react_1.useState)(true);
281
+ // #6 — tab the inspector opens on. Shown with a DEFAULT badge in Settings.
282
+ const [defaultTab, setDefaultTab] = (0, react_1.useState)('apis');
283
+ // #9 — when false (default), consecutive identical entries in the API and
284
+ // Console lists are collapsed into one row with a ×N counter.
285
+ const [showDuplicateLogs, setShowDuplicateLogs] = (0, react_1.useState)(false);
286
+ // #5 — hydrate persisted settings once, then auto-save on any change.
287
+ const settingsHydratedRef = (0, react_1.useRef)(false);
288
+ (0, react_1.useEffect)(() => {
289
+ let cancelled = false;
290
+ (0, settingsStore_1.loadSettings)().then(saved => {
291
+ if (cancelled)
292
+ return;
293
+ if (saved.isDark != null) {
294
+ setIsDark(saved.isDark);
295
+ (0, styles_1.toggleGlobalTheme)(saved.isDark);
296
+ }
297
+ if (saved.modalHeightPercent != null)
298
+ setModalHeightPercent(saved.modalHeightPercent);
299
+ if (saved.tabVisibility)
300
+ setTabVisibility(prev => ({
301
+ ...prev,
302
+ ...saved.tabVisibility,
303
+ apis: true, // APIs is always required
304
+ }));
305
+ if (saved.defaultTab)
306
+ setDefaultTab(saved.defaultTab);
307
+ if (saved.maxNetworkLogs != null)
308
+ setMaxNetworkLogs(saved.maxNetworkLogs);
309
+ if (saved.maxConsoleLogs != null)
310
+ setMaxConsoleLogs(saved.maxConsoleLogs);
311
+ if (saved.showConsoleLevels)
312
+ setShowConsoleLevels(saved.showConsoleLevels);
313
+ if (saved.webViewCaptureCssJs != null)
314
+ setWebViewCaptureCssJs(saved.webViewCaptureCssJs);
315
+ if (saved.reduxAutoRefresh != null)
316
+ setReduxAutoRefreshState(saved.reduxAutoRefresh);
317
+ if (saved.reduxExpandDepth != null)
318
+ setReduxExpandDepth(saved.reduxExpandDepth);
319
+ if (saved.slowRequestThreshold != null)
320
+ setSlowRequestThreshold(saved.slowRequestThreshold);
321
+ if (saved.insightsShowConsoleAlerts != null)
322
+ setInsightsShowConsoleAlerts(saved.insightsShowConsoleAlerts);
323
+ if (saved.showDuplicateLogs != null)
324
+ setShowDuplicateLogs(saved.showDuplicateLogs);
325
+ if (saved.defaultTab) {
326
+ const dt = saved.defaultTab;
327
+ const vis = {
328
+ ...{
329
+ insights: true,
330
+ apis: true,
331
+ logs: true,
332
+ analytics: true,
333
+ webview: true,
334
+ redux: true,
335
+ },
336
+ ...(saved.tabVisibility || {}),
337
+ apis: true,
338
+ };
339
+ setActiveTab(vis[dt] ? dt : 'apis');
340
+ }
341
+ settingsHydratedRef.current = true;
342
+ });
343
+ return () => {
344
+ cancelled = true;
345
+ };
346
+ }, []);
347
+ (0, react_1.useEffect)(() => {
348
+ if (!settingsHydratedRef.current)
349
+ return;
350
+ (0, settingsStore_1.saveSettings)({
351
+ isDark,
352
+ modalHeightPercent,
353
+ tabVisibility,
354
+ defaultTab,
355
+ maxNetworkLogs,
356
+ maxConsoleLogs,
357
+ showConsoleLevels,
358
+ webViewCaptureCssJs,
359
+ reduxAutoRefresh,
360
+ reduxExpandDepth,
361
+ slowRequestThreshold,
362
+ insightsShowConsoleAlerts,
363
+ showDuplicateLogs,
364
+ });
365
+ }, [
366
+ isDark,
367
+ modalHeightPercent,
368
+ tabVisibility,
369
+ defaultTab,
370
+ maxNetworkLogs,
371
+ maxConsoleLogs,
372
+ showConsoleLevels,
373
+ webViewCaptureCssJs,
374
+ reduxAutoRefresh,
375
+ reduxExpandDepth,
376
+ slowRequestThreshold,
377
+ insightsShowConsoleAlerts,
378
+ showDuplicateLogs,
379
+ ]);
380
+ // #1 — check NPM for a newer published version; surfaces an animated dot
381
+ // in the header next to the npm chip when an update is available.
382
+ const [latestNpmVersion, setLatestNpmVersion] = (0, react_1.useState)(null);
383
+ const updateAvailable = (0, react_1.useMemo)(() => {
384
+ if (!latestNpmVersion)
385
+ return false;
386
+ const parse = (v) => v
387
+ .replace(/^v/, '')
388
+ .split('.')
389
+ .map(n => parseInt(n, 10) || 0);
390
+ const cur = parse(constants_1.LIB_VERSION);
391
+ const latest = parse(latestNpmVersion);
392
+ for (let i = 0; i < 3; i++) {
393
+ if ((latest[i] || 0) > (cur[i] || 0))
394
+ return true;
395
+ if ((latest[i] || 0) < (cur[i] || 0))
396
+ return false;
397
+ }
398
+ return false;
399
+ }, [latestNpmVersion]);
400
+ (0, react_1.useEffect)(() => {
401
+ let cancelled = false;
402
+ fetch('https://registry.npmjs.org/react-native-inapp-inspector/latest')
403
+ .then(res => (res.ok ? res.json() : null))
404
+ .then(data => {
405
+ if (!cancelled && data && typeof data.version === 'string') {
406
+ setLatestNpmVersion(data.version);
407
+ }
408
+ })
409
+ .catch(() => {
410
+ // Offline / blocked — silently skip the update check.
411
+ });
412
+ return () => {
413
+ cancelled = true;
414
+ };
415
+ }, []);
279
416
  (0, react_1.useEffect)(() => {
280
417
  (0, reduxLogger_1.setReduxAutoRefresh)(reduxAutoRefresh);
281
418
  }, [reduxAutoRefresh]);
@@ -289,6 +426,10 @@ const NetworkInspector = ({ enabled = true, }) => {
289
426
  animateNextLayout();
290
427
  setActiveTab('apis');
291
428
  }
429
+ // #6 — a hidden module can't be the default landing tab.
430
+ if (!nextVal && defaultTab === key) {
431
+ setDefaultTab('apis');
432
+ }
292
433
  return newVisibility;
293
434
  });
294
435
  };
@@ -370,6 +511,8 @@ const NetworkInspector = ({ enabled = true, }) => {
370
511
  const badgeAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(1)).current;
371
512
  const activePulseAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0.4)).current;
372
513
  const unreadPulseAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(1)).current;
514
+ // #4 — diagonal light streak sweeping across the floating launcher
515
+ const fabShineAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
373
516
  // #11 — header "clear all" icon spin/scale animation
374
517
  const clearAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
375
518
  // #4 — draggable floating launcher (drag anywhere on screen)
@@ -412,9 +555,9 @@ const NetworkInspector = ({ enabled = true, }) => {
412
555
  fabDraggedRef.current = false;
413
556
  },
414
557
  })).current;
415
- // #10 — scroll-to-top affordance for the main APIs list
558
+ // #2 — scroll-to-top button for the main APIs list, always visible at the
559
+ // bottom right of the list.
416
560
  const apisListRef = (0, react_1.useRef)(null);
417
- const [showScrollTop, setShowScrollTop] = (0, react_1.useState)(false);
418
561
  const runClearAllWithAnimation = (0, react_1.useCallback)(() => {
419
562
  react_native_1.Animated.sequence([
420
563
  react_native_1.Animated.timing(clearAnim, {
@@ -451,6 +594,24 @@ const NetworkInspector = ({ enabled = true, }) => {
451
594
  loop.start();
452
595
  return () => loop.stop();
453
596
  }, [pulseAnim]);
597
+ // #4 — sweep the shine streak across the launcher, pause, repeat.
598
+ (0, react_1.useEffect)(() => {
599
+ const loop = react_native_1.Animated.loop(react_native_1.Animated.sequence([
600
+ react_native_1.Animated.timing(fabShineAnim, {
601
+ toValue: 1,
602
+ duration: 1100,
603
+ useNativeDriver: true,
604
+ }),
605
+ react_native_1.Animated.delay(1600),
606
+ react_native_1.Animated.timing(fabShineAnim, {
607
+ toValue: 0,
608
+ duration: 0,
609
+ useNativeDriver: true,
610
+ }),
611
+ ]));
612
+ loop.start();
613
+ return () => loop.stop();
614
+ }, [fabShineAnim]);
454
615
  (0, react_1.useEffect)(() => {
455
616
  const loop = react_native_1.Animated.loop(react_native_1.Animated.sequence([
456
617
  react_native_1.Animated.timing(activePulseAnim, {
@@ -494,6 +655,16 @@ const NetworkInspector = ({ enabled = true, }) => {
494
655
  }).start();
495
656
  }
496
657
  }, [newLogIds]);
658
+ // #6 — every time the inspector is opened, land on the chosen default tab.
659
+ (0, react_1.useEffect)(() => {
660
+ if (visible) {
661
+ const target = defaultTab === 'apis' || tabVisibility[defaultTab]
662
+ ? defaultTab
663
+ : 'apis';
664
+ setActiveTab(target);
665
+ }
666
+ // eslint-disable-next-line react-hooks/exhaustive-deps
667
+ }, [visible]);
497
668
  (0, react_1.useEffect)(() => {
498
669
  if (visible) {
499
670
  const task = react_native_1.InteractionManager.runAfterInteractions(() => {
@@ -647,8 +818,38 @@ const NetworkInspector = ({ enabled = true, }) => {
647
818
  if (sortOrder === 'oldest') {
648
819
  result = [...result].reverse();
649
820
  }
821
+ // #9 — collapse consecutive identical requests (same method + url +
822
+ // status) into one row carrying a ×N counter, unless the user opted in
823
+ // to seeing every duplicate via Settings → "Show Duplicate Logs".
824
+ if (!showDuplicateLogs) {
825
+ const collapsed = [];
826
+ for (const log of result) {
827
+ const last = collapsed[collapsed.length - 1];
828
+ if (last &&
829
+ last.method === log.method &&
830
+ last.url === log.url &&
831
+ last.status === log.status) {
832
+ collapsed[collapsed.length - 1] = {
833
+ ...last,
834
+ duplicateCount: (last.duplicateCount || 1) + 1,
835
+ };
836
+ }
837
+ else {
838
+ collapsed.push({ ...log, duplicateCount: 1 });
839
+ }
840
+ }
841
+ result = collapsed;
842
+ }
650
843
  return result.slice(0, maxNetworkLogs);
651
- }, [logs, search, statusFilters, methodFilters, sortOrder, maxNetworkLogs]);
844
+ }, [
845
+ logs,
846
+ search,
847
+ statusFilters,
848
+ methodFilters,
849
+ sortOrder,
850
+ maxNetworkLogs,
851
+ showDuplicateLogs,
852
+ ]);
652
853
  const availableMethods = (0, react_1.useMemo)(() => {
653
854
  const methods = new Set();
654
855
  logs.forEach(log => {
@@ -858,8 +1059,35 @@ const NetworkInspector = ({ enabled = true, }) => {
858
1059
  result = [...result].sort((a, b) => logSortOrder === 'newest'
859
1060
  ? b.timestamp - a.timestamp
860
1061
  : a.timestamp - b.timestamp);
1062
+ // #9 — collapse consecutive identical messages into one row with a ×N
1063
+ // counter unless duplicates are explicitly enabled in Settings.
1064
+ if (!showDuplicateLogs) {
1065
+ const collapsed = [];
1066
+ for (const log of result) {
1067
+ const last = collapsed[collapsed.length - 1];
1068
+ if (last &&
1069
+ last.type === log.type &&
1070
+ last.sourceMethod === log.sourceMethod &&
1071
+ last.message === log.message) {
1072
+ collapsed[collapsed.length - 1] = {
1073
+ ...last,
1074
+ duplicateCount: (last.duplicateCount || 1) + 1,
1075
+ };
1076
+ }
1077
+ else {
1078
+ collapsed.push({ ...log, duplicateCount: 1 });
1079
+ }
1080
+ }
1081
+ result = collapsed;
1082
+ }
861
1083
  return result;
862
- }, [visibleConsoleLogs, logFilters, logSearch, logSortOrder]);
1084
+ }, [
1085
+ visibleConsoleLogs,
1086
+ logFilters,
1087
+ logSearch,
1088
+ logSortOrder,
1089
+ showDuplicateLogs,
1090
+ ]);
863
1091
  const filteredWebViewLogs = (0, react_1.useMemo)(() => {
864
1092
  let result = webViewLogs;
865
1093
  if (webViewSearch) {
@@ -1008,7 +1236,11 @@ const NetworkInspector = ({ enabled = true, }) => {
1008
1236
  else {
1009
1237
  react_native_1.Alert.alert('Clear Logs', 'Are you sure you want to clear all network logs?', [
1010
1238
  { text: 'Cancel', style: 'cancel' },
1011
- { text: 'Clear All', onPress: clearNetworkOnly, style: 'destructive' },
1239
+ {
1240
+ text: 'Clear All',
1241
+ onPress: clearNetworkOnly,
1242
+ style: 'destructive',
1243
+ },
1012
1244
  ]);
1013
1245
  }
1014
1246
  }
@@ -1252,7 +1484,8 @@ const NetworkInspector = ({ enabled = true, }) => {
1252
1484
  }}>
1253
1485
  {tab.label}
1254
1486
  </react_native_1.Text>
1255
- {isLocked && (<react_native_1.View style={{
1487
+ {/* #6 badge marks the configured default tab */}
1488
+ {tab.key === defaultTab && (<react_native_1.View style={{
1256
1489
  flexDirection: 'row',
1257
1490
  alignItems: 'center',
1258
1491
  backgroundColor: 'rgba(104,75,155,0.08)',
@@ -1520,6 +1753,169 @@ const NetworkInspector = ({ enabled = true, }) => {
1520
1753
  })}
1521
1754
  </react_native_1.View>
1522
1755
  </react_native_1.View>
1756
+
1757
+ {/* Divider */}
1758
+ <react_native_1.View style={{
1759
+ height: 1,
1760
+ backgroundColor: AppColors_1.AppColors.dividerColor,
1761
+ }}/>
1762
+
1763
+ {/* #6 — Default Tab */}
1764
+ <react_native_1.View style={{
1765
+ paddingVertical: 12,
1766
+ paddingHorizontal: 14,
1767
+ }}>
1768
+ <react_native_1.View style={{
1769
+ flexDirection: 'row',
1770
+ alignItems: 'center',
1771
+ gap: 8,
1772
+ }}>
1773
+ <react_native_1.View style={{
1774
+ width: 20,
1775
+ height: 20,
1776
+ borderRadius: 6,
1777
+ backgroundColor: AppColors_1.AppColors.purpleShade50,
1778
+ borderWidth: 1,
1779
+ borderColor: 'rgba(104,75,155,0.2)',
1780
+ alignItems: 'center',
1781
+ justifyContent: 'center',
1782
+ }}>
1783
+ <NetworkIcons_1.LayersIcon color={AppColors_1.AppColors.purple} size={11}/>
1784
+ </react_native_1.View>
1785
+ <react_native_1.View style={{ flex: 1 }}>
1786
+ <react_native_1.Text style={{
1787
+ fontFamily: AppFonts_1.AppFonts.interBold,
1788
+ fontSize: 13,
1789
+ color: AppColors_1.AppColors.primaryBlack,
1790
+ }}>
1791
+ Default Tab
1792
+ </react_native_1.Text>
1793
+ <react_native_1.Text style={{
1794
+ fontFamily: AppFonts_1.AppFonts.interRegular,
1795
+ fontSize: 11,
1796
+ color: AppColors_1.AppColors.grayText,
1797
+ marginTop: 1,
1798
+ }}>
1799
+ Tab shown when the inspector opens
1800
+ </react_native_1.Text>
1801
+ </react_native_1.View>
1802
+ </react_native_1.View>
1803
+
1804
+ {/* Segmented picker — only visible tabs are offered */}
1805
+ <react_native_1.View style={{
1806
+ flexDirection: 'row',
1807
+ flexWrap: 'wrap',
1808
+ backgroundColor: AppColors_1.AppColors.grayBackground,
1809
+ borderRadius: 8,
1810
+ padding: 2.5,
1811
+ marginTop: 10,
1812
+ borderWidth: 1,
1813
+ borderColor: AppColors_1.AppColors.dividerColor,
1814
+ gap: 2,
1815
+ }}>
1816
+ {settingsTabs
1817
+ .filter(tab => tab.key === 'apis' || tabVisibility[tab.key])
1818
+ .map(tab => {
1819
+ const isActive = defaultTab === tab.key;
1820
+ return (<TouchableScale_1.default key={tab.key} onPress={() => setDefaultTab(tab.key)} style={{
1821
+ paddingVertical: 6,
1822
+ paddingHorizontal: 10,
1823
+ alignItems: 'center',
1824
+ borderRadius: 6,
1825
+ backgroundColor: isActive
1826
+ ? AppColors_1.AppColors.purple
1827
+ : 'transparent',
1828
+ }}>
1829
+ <react_native_1.Text style={{
1830
+ fontFamily: AppFonts_1.AppFonts.interBold,
1831
+ fontSize: 11,
1832
+ color: isActive
1833
+ ? '#FFFFFF'
1834
+ : AppColors_1.AppColors.grayText,
1835
+ }}>
1836
+ {tab.label}
1837
+ </react_native_1.Text>
1838
+ </TouchableScale_1.default>);
1839
+ })}
1840
+ </react_native_1.View>
1841
+ </react_native_1.View>
1842
+
1843
+ {/* Divider */}
1844
+ <react_native_1.View style={{
1845
+ height: 1,
1846
+ backgroundColor: AppColors_1.AppColors.dividerColor,
1847
+ }}/>
1848
+
1849
+ {/* #9 — Show Duplicate Logs */}
1850
+ <react_native_1.View style={{
1851
+ flexDirection: 'row',
1852
+ alignItems: 'center',
1853
+ paddingVertical: 12,
1854
+ paddingHorizontal: 14,
1855
+ gap: 12,
1856
+ }}>
1857
+ <react_native_1.View style={{
1858
+ flex: 1,
1859
+ flexDirection: 'row',
1860
+ alignItems: 'center',
1861
+ gap: 8,
1862
+ }}>
1863
+ <react_native_1.View style={{
1864
+ width: 20,
1865
+ height: 20,
1866
+ borderRadius: 6,
1867
+ backgroundColor: AppColors_1.AppColors.purpleShade50,
1868
+ borderWidth: 1,
1869
+ borderColor: 'rgba(104,75,155,0.2)',
1870
+ alignItems: 'center',
1871
+ justifyContent: 'center',
1872
+ }}>
1873
+ <NetworkIcons_1.EyeIcon color={AppColors_1.AppColors.purple} size={11}/>
1874
+ </react_native_1.View>
1875
+ <react_native_1.View style={{ flex: 1 }}>
1876
+ <react_native_1.Text style={{
1877
+ fontFamily: AppFonts_1.AppFonts.interBold,
1878
+ fontSize: 13,
1879
+ color: AppColors_1.AppColors.primaryBlack,
1880
+ }}>
1881
+ Show Duplicate Logs
1882
+ </react_native_1.Text>
1883
+ <react_native_1.Text style={{
1884
+ fontFamily: AppFonts_1.AppFonts.interRegular,
1885
+ fontSize: 11,
1886
+ color: AppColors_1.AppColors.grayText,
1887
+ marginTop: 1,
1888
+ }}>
1889
+ Off: repeated identical entries collapse into one row
1890
+ with a ×N count
1891
+ </react_native_1.Text>
1892
+ </react_native_1.View>
1893
+ </react_native_1.View>
1894
+
1895
+ {/* Toggle Switch */}
1896
+ <TouchableScale_1.default onPress={() => setShowDuplicateLogs(prev => !prev)} style={{
1897
+ width: 38,
1898
+ height: 22,
1899
+ borderRadius: 11,
1900
+ backgroundColor: showDuplicateLogs
1901
+ ? AppColors_1.AppColors.purple
1902
+ : AppColors_1.AppColors.grayBorderSecondary,
1903
+ padding: 2,
1904
+ justifyContent: 'center',
1905
+ alignItems: showDuplicateLogs ? 'flex-end' : 'flex-start',
1906
+ }}>
1907
+ <react_native_1.View style={{
1908
+ width: 18,
1909
+ height: 18,
1910
+ borderRadius: 9,
1911
+ backgroundColor: '#FFFFFF',
1912
+ shadowColor: '#000',
1913
+ shadowOpacity: 0.15,
1914
+ shadowRadius: 1.5,
1915
+ shadowOffset: { width: 0, height: 1 },
1916
+ }}/>
1917
+ </TouchableScale_1.default>
1918
+ </react_native_1.View>
1523
1919
  </react_native_1.View>
1524
1920
  </react_native_1.View>
1525
1921
  </react_native_1.ScrollView>
@@ -2790,6 +3186,29 @@ const NetworkInspector = ({ enabled = true, }) => {
2790
3186
  }} hitSlop={10}>
2791
3187
  <react_native_1.Animated.View style={[styles_1.default.fabPulseRing, { transform: [{ scale: pulseAnim }] }]}/>
2792
3188
  <NetworkIcons_1.BrandCircleIcon size={62}/>
3189
+ {/* #4 — shining sweep, clipped inside the circular launcher */}
3190
+ <react_native_1.View pointerEvents="none" style={styles_1.default.fabShineClip}>
3191
+ <react_native_1.Animated.View style={[
3192
+ styles_1.default.fabShineStreak,
3193
+ {
3194
+ transform: [
3195
+ {
3196
+ translateX: fabShineAnim.interpolate({
3197
+ inputRange: [0, 1],
3198
+ outputRange: [-48, 96],
3199
+ }),
3200
+ },
3201
+ { rotate: '25deg' },
3202
+ ],
3203
+ },
3204
+ ]}>
3205
+ <react_native_linear_gradient_1.default colors={[
3206
+ 'rgba(255,255,255,0)',
3207
+ 'rgba(255,255,255,0.55)',
3208
+ 'rgba(255,255,255,0)',
3209
+ ]} start={{ x: 0, y: 0.5 }} end={{ x: 1, y: 0.5 }} style={{ flex: 1 }}/>
3210
+ </react_native_1.Animated.View>
3211
+ </react_native_1.View>
2793
3212
  {(logs.length > 0 || analyticsEvents.length > 0) && (<react_native_1.Animated.View style={[
2794
3213
  styles_1.default.fabBadge,
2795
3214
  hasErrors ? styles_1.default.fabBadgeError : styles_1.default.fabBadgeNormal,
@@ -2962,6 +3381,29 @@ const NetworkInspector = ({ enabled = true, }) => {
2962
3381
  </react_native_1.Text>
2963
3382
  </react_native_1.View>
2964
3383
  </react_native_1.View>
3384
+
3385
+ {/* #1 — pulsing dot when a newer version is on NPM */}
3386
+ {updateAvailable && (<react_native_1.Pressable hitSlop={10} onPress={() => react_native_1.Alert.alert('Update Available', `react-native-inapp-inspector v${latestNpmVersion} is available on NPM (installed: v${constants_1.LIB_VERSION}).`, [
3387
+ { text: 'Later', style: 'cancel' },
3388
+ {
3389
+ text: 'View on NPM',
3390
+ onPress: () => react_native_1.Linking.openURL('https://www.npmjs.com/package/react-native-inapp-inspector').catch(() => { }),
3391
+ },
3392
+ ])} style={{
3393
+ alignItems: 'center',
3394
+ justifyContent: 'center',
3395
+ }}>
3396
+ <react_native_1.Animated.View style={{
3397
+ width: 8,
3398
+ height: 8,
3399
+ borderRadius: 4,
3400
+ backgroundColor: '#4ADE80',
3401
+ borderWidth: 1,
3402
+ borderColor: 'rgba(255,255,255,0.9)',
3403
+ opacity: activePulseAnim,
3404
+ transform: [{ scale: unreadPulseAnim }],
3405
+ }}/>
3406
+ </react_native_1.Pressable>)}
2965
3407
  </react_native_1.View>
2966
3408
  </react_native_1.View>
2967
3409
  </react_native_1.View>) : null}
@@ -3110,7 +3552,7 @@ const NetworkInspector = ({ enabled = true, }) => {
3110
3552
  },
3111
3553
  ],
3112
3554
  }}>
3113
- <NetworkIcons_1.TrashIcon color="#FFFFFF" size={15}/>
3555
+ <NetworkIcons_1.WipeIcon color="#FFFFFF" size={16}/>
3114
3556
  </react_native_1.Animated.View>
3115
3557
  </TouchableScale_1.default>)}
3116
3558
 
@@ -3290,7 +3732,16 @@ const NetworkInspector = ({ enabled = true, }) => {
3290
3732
  animateNextLayout();
3291
3733
  setAnalyticsSubTab('ga_events');
3292
3734
  }}>
3293
- <react_native_1.Text style={[
3735
+ <react_native_1.View style={{
3736
+ flexDirection: 'row',
3737
+ alignItems: 'center',
3738
+ gap: 6,
3739
+ }}>
3740
+ {/* #7 */}
3741
+ <NetworkIcons_1.AnalyticsIcon size={13} color={analyticsSubTab === 'ga_events'
3742
+ ? AppColors_1.AppColors.purple
3743
+ : AppColors_1.AppColors.grayTextStrong}/>
3744
+ <react_native_1.Text style={[
3294
3745
  {
3295
3746
  fontFamily: AppFonts_1.AppFonts.interMedium,
3296
3747
  fontSize: 13,
@@ -3301,12 +3752,13 @@ const NetworkInspector = ({ enabled = true, }) => {
3301
3752
  color: AppColors_1.AppColors.purple,
3302
3753
  },
3303
3754
  ]}>
3304
- GA Events (
3305
- {analyticsSearch
3755
+ GA Events (
3756
+ {analyticsSearch
3306
3757
  ? filteredAnalyticsEvents.length
3307
3758
  : analyticsEvents.length}
3308
- )
3309
- </react_native_1.Text>
3759
+ )
3760
+ </react_native_1.Text>
3761
+ </react_native_1.View>
3310
3762
  </react_native_1.Pressable>
3311
3763
  <react_native_1.Pressable style={[
3312
3764
  {
@@ -3327,7 +3779,16 @@ const NetworkInspector = ({ enabled = true, }) => {
3327
3779
  animateNextLayout();
3328
3780
  setAnalyticsSubTab('top_events');
3329
3781
  }}>
3330
- <react_native_1.Text style={[
3782
+ <react_native_1.View style={{
3783
+ flexDirection: 'row',
3784
+ alignItems: 'center',
3785
+ gap: 6,
3786
+ }}>
3787
+ {/* #7 */}
3788
+ <NetworkIcons_1.TrendingUpIcon size={13} color={analyticsSubTab === 'top_events'
3789
+ ? AppColors_1.AppColors.purple
3790
+ : AppColors_1.AppColors.grayTextStrong}/>
3791
+ <react_native_1.Text style={[
3331
3792
  {
3332
3793
  fontFamily: AppFonts_1.AppFonts.interMedium,
3333
3794
  fontSize: 13,
@@ -3338,8 +3799,9 @@ const NetworkInspector = ({ enabled = true, }) => {
3338
3799
  color: AppColors_1.AppColors.purple,
3339
3800
  },
3340
3801
  ]}>
3341
- Top Events ({topEventsArray.length})
3342
- </react_native_1.Text>
3802
+ Top Events ({topEventsArray.length})
3803
+ </react_native_1.Text>
3804
+ </react_native_1.View>
3343
3805
  </react_native_1.Pressable>
3344
3806
  </react_native_1.View>
3345
3807
  </react_native_1.View>)}
@@ -3449,7 +3911,7 @@ const NetworkInspector = ({ enabled = true, }) => {
3449
3911
  flexGrow: 1,
3450
3912
  },
3451
3913
  ]} keyboardShouldPersistTaps="handled"/>)) : activeTab === 'apis' && selected == null ? (<react_native_1.View style={{ flex: 1 }}>
3452
- <react_native_1.FlatList ref={apisListRef} onScroll={e => setShowScrollTop(e.nativeEvent.contentOffset.y > 320)} scrollEventThrottle={16} data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} removeClippedSubviews={true} ListHeaderComponent={<react_native_1.View style={{ marginTop: 8 }}>
3914
+ <react_native_1.FlatList ref={apisListRef} data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} removeClippedSubviews={true} ListHeaderComponent={<react_native_1.View style={{ marginTop: 8 }}>
3453
3915
  <react_native_1.View style={styles_1.default.toolbarRow}>
3454
3916
  <react_native_1.View style={styles_1.default.searchContainer}>
3455
3917
  <NetworkIcons_1.SearchIcon color={AppColors_1.AppColors.grayTextWeak} size={16}/>
@@ -3600,17 +4062,17 @@ const NetworkInspector = ({ enabled = true, }) => {
3600
4062
  styles_1.default.listContent,
3601
4063
  filteredLogs.length === 0 && { flexGrow: 1 },
3602
4064
  ]} keyboardShouldPersistTaps="handled"/>
3603
- {showScrollTop && (<TouchableScale_1.default onPress={() => {
3604
- apisListRef.current?.scrollToOffset({
3605
- offset: 0,
3606
- animated: true,
3607
- });
3608
- setShowScrollTop(false);
3609
- }} hitSlop={10} style={styles_1.default.scrollTopBtn}>
3610
- <react_native_1.View style={{ transform: [{ rotate: '180deg' }] }}>
3611
- <NetworkIcons_1.ChevronIcon color="#FFFFFF" size={18}/>
3612
- </react_native_1.View>
3613
- </TouchableScale_1.default>)}
4065
+ {/* #2 always-visible scroll-to-top, bottom right */}
4066
+ <TouchableScale_1.default onPress={() => {
4067
+ apisListRef.current?.scrollToOffset({
4068
+ offset: 0,
4069
+ animated: true,
4070
+ });
4071
+ }} hitSlop={10} style={styles_1.default.scrollTopBtn}>
4072
+ <react_native_1.View style={{ transform: [{ rotate: '180deg' }] }}>
4073
+ <NetworkIcons_1.ChevronIcon color="#FFFFFF" size={18}/>
4074
+ </react_native_1.View>
4075
+ </TouchableScale_1.default>
3614
4076
  </react_native_1.View>) : activeTab === 'logs' ? (<react_native_1.View style={{ flex: 1 }}>
3615
4077
  <react_native_1.View style={{
3616
4078
  backgroundColor: '#FFFFFF',
@@ -3659,15 +4121,25 @@ const NetworkInspector = ({ enabled = true, }) => {
3659
4121
  backgroundColor: '#F4EBFF',
3660
4122
  },
3661
4123
  ]}>
3662
- <react_native_1.Text numberOfLines={1} style={[
4124
+ {/* #7 */}
4125
+ <react_native_1.View style={{
4126
+ flexDirection: 'row',
4127
+ alignItems: 'center',
4128
+ gap: 5,
4129
+ }}>
4130
+ <NetworkIcons_1.LayersIcon size={12} color={active
4131
+ ? AppColors_1.AppColors.purpleShade700
4132
+ : AppColors_1.AppColors.grayTextStrong}/>
4133
+ <react_native_1.Text numberOfLines={1} style={[
3663
4134
  styles_1.default.statusFilterText,
3664
4135
  active && {
3665
4136
  color: AppColors_1.AppColors.purpleShade700,
3666
4137
  fontFamily: AppFonts_1.AppFonts.interBold,
3667
4138
  },
3668
4139
  ]}>
3669
- All ({logCounts.all})
3670
- </react_native_1.Text>
4140
+ All ({logCounts.all})
4141
+ </react_native_1.Text>
4142
+ </react_native_1.View>
3671
4143
  </react_native_1.View>
3672
4144
  </TouchableScale_1.default>);
3673
4145
  })()}
@@ -3693,15 +4165,25 @@ const NetworkInspector = ({ enabled = true, }) => {
3693
4165
  backgroundColor: '#F1F5F9',
3694
4166
  },
3695
4167
  ]}>
3696
- <react_native_1.Text numberOfLines={1} style={[
4168
+ {/* #7 */}
4169
+ <react_native_1.View style={{
4170
+ flexDirection: 'row',
4171
+ alignItems: 'center',
4172
+ gap: 5,
4173
+ }}>
4174
+ <NetworkIcons_1.UserIcon size={12} color={active
4175
+ ? '#334155'
4176
+ : AppColors_1.AppColors.grayTextStrong}/>
4177
+ <react_native_1.Text numberOfLines={1} style={[
3697
4178
  styles_1.default.statusFilterText,
3698
4179
  active && {
3699
4180
  color: '#334155',
3700
4181
  fontFamily: AppFonts_1.AppFonts.interBold,
3701
4182
  },
3702
4183
  ]}>
3703
- User Log ({logCounts['user-log']})
3704
- </react_native_1.Text>
4184
+ User Log ({logCounts['user-log']})
4185
+ </react_native_1.Text>
4186
+ </react_native_1.View>
3705
4187
  </react_native_1.View>
3706
4188
  </TouchableScale_1.default>);
3707
4189
  })()}
@@ -3727,15 +4209,25 @@ const NetworkInspector = ({ enabled = true, }) => {
3727
4209
  backgroundColor: AppColors_1.AppColors.purpleShade50,
3728
4210
  },
3729
4211
  ]}>
3730
- <react_native_1.Text numberOfLines={1} style={[
4212
+ {/* #7 */}
4213
+ <react_native_1.View style={{
4214
+ flexDirection: 'row',
4215
+ alignItems: 'center',
4216
+ gap: 5,
4217
+ }}>
4218
+ <NetworkIcons_1.InfoCircleIcon size={12} color={active
4219
+ ? AppColors_1.AppColors.purple
4220
+ : AppColors_1.AppColors.grayTextStrong}/>
4221
+ <react_native_1.Text numberOfLines={1} style={[
3731
4222
  styles_1.default.statusFilterText,
3732
4223
  active && {
3733
4224
  color: AppColors_1.AppColors.purple,
3734
4225
  fontFamily: AppFonts_1.AppFonts.interBold,
3735
4226
  },
3736
4227
  ]}>
3737
- Info ({logCounts.info})
3738
- </react_native_1.Text>
4228
+ Info ({logCounts.info})
4229
+ </react_native_1.Text>
4230
+ </react_native_1.View>
3739
4231
  </react_native_1.View>
3740
4232
  </TouchableScale_1.default>);
3741
4233
  })()}
@@ -3761,7 +4253,17 @@ const NetworkInspector = ({ enabled = true, }) => {
3761
4253
  backgroundColor: '#FFFDF6',
3762
4254
  },
3763
4255
  ]}>
3764
- <react_native_1.Text numberOfLines={1} style={[
4256
+ {/* #7 */}
4257
+ <react_native_1.View style={{
4258
+ flexDirection: 'row',
4259
+ alignItems: 'center',
4260
+ gap: 5,
4261
+ }}>
4262
+ <NetworkIcons_1.WarningTriangleIcon size={12} color={active
4263
+ ? AppColors_1.AppColors.darkOrange ||
4264
+ AppColors_1.AppColors.lightOrange
4265
+ : AppColors_1.AppColors.grayTextStrong}/>
4266
+ <react_native_1.Text numberOfLines={1} style={[
3765
4267
  styles_1.default.statusFilterText,
3766
4268
  active && {
3767
4269
  color: AppColors_1.AppColors.darkOrange ||
@@ -3769,8 +4271,9 @@ const NetworkInspector = ({ enabled = true, }) => {
3769
4271
  fontFamily: AppFonts_1.AppFonts.interBold,
3770
4272
  },
3771
4273
  ]}>
3772
- Warning ({logCounts.warn})
3773
- </react_native_1.Text>
4274
+ Warning ({logCounts.warn})
4275
+ </react_native_1.Text>
4276
+ </react_native_1.View>
3774
4277
  </react_native_1.View>
3775
4278
  </TouchableScale_1.default>);
3776
4279
  })()}
@@ -3796,15 +4299,25 @@ const NetworkInspector = ({ enabled = true, }) => {
3796
4299
  backgroundColor: '#FFF5F6',
3797
4300
  },
3798
4301
  ]}>
3799
- <react_native_1.Text numberOfLines={1} style={[
4302
+ {/* #7 */}
4303
+ <react_native_1.View style={{
4304
+ flexDirection: 'row',
4305
+ alignItems: 'center',
4306
+ gap: 5,
4307
+ }}>
4308
+ <NetworkIcons_1.ErrorCircleIcon size={12} color={active
4309
+ ? AppColors_1.AppColors.errorColor
4310
+ : AppColors_1.AppColors.grayTextStrong}/>
4311
+ <react_native_1.Text numberOfLines={1} style={[
3800
4312
  styles_1.default.statusFilterText,
3801
4313
  active && {
3802
4314
  color: AppColors_1.AppColors.errorColor,
3803
4315
  fontFamily: AppFonts_1.AppFonts.interBold,
3804
4316
  },
3805
4317
  ]}>
3806
- Error ({logCounts.error})
3807
- </react_native_1.Text>
4318
+ Error ({logCounts.error})
4319
+ </react_native_1.Text>
4320
+ </react_native_1.View>
3808
4321
  </react_native_1.View>
3809
4322
  </TouchableScale_1.default>);
3810
4323
  })()}
@@ -3830,15 +4343,25 @@ const NetworkInspector = ({ enabled = true, }) => {
3830
4343
  backgroundColor: `${AppColors_1.AppColors.skyBlue}15`,
3831
4344
  },
3832
4345
  ]}>
3833
- <react_native_1.Text numberOfLines={1} style={[
4346
+ {/* #7 */}
4347
+ <react_native_1.View style={{
4348
+ flexDirection: 'row',
4349
+ alignItems: 'center',
4350
+ gap: 5,
4351
+ }}>
4352
+ <NetworkIcons_1.AnalyticsIcon size={12} color={active
4353
+ ? AppColors_1.AppColors.skyBlue
4354
+ : AppColors_1.AppColors.grayTextStrong}/>
4355
+ <react_native_1.Text numberOfLines={1} style={[
3834
4356
  styles_1.default.statusFilterText,
3835
4357
  active && {
3836
4358
  color: AppColors_1.AppColors.skyBlue,
3837
4359
  fontFamily: AppFonts_1.AppFonts.interBold,
3838
4360
  },
3839
4361
  ]}>
3840
- Analytics ({logCounts.analytics})
3841
- </react_native_1.Text>
4362
+ Analytics ({logCounts.analytics})
4363
+ </react_native_1.Text>
4364
+ </react_native_1.View>
3842
4365
  </react_native_1.View>
3843
4366
  </TouchableScale_1.default>);
3844
4367
  })()}
@@ -4934,5 +5457,8 @@ var ErrorBoundary_2 = require("./components/ErrorBoundary");
4934
5457
  Object.defineProperty(exports, "ErrorBoundary", { enumerable: true, get: function () { return __importDefault(ErrorBoundary_2).default; } });
4935
5458
  var reduxLogger_2 = require("./customHooks/reduxLogger");
4936
5459
  Object.defineProperty(exports, "connectReduxStore", { enumerable: true, get: function () { return reduxLogger_2.connectReduxStore; } });
5460
+ Object.defineProperty(exports, "inspectorReduxMiddleware", { enumerable: true, get: function () { return reduxLogger_2.inspectorReduxMiddleware; } });
4937
5461
  Object.defineProperty(exports, "getReduxState", { enumerable: true, get: function () { return reduxLogger_2.getReduxState; } });
4938
5462
  Object.defineProperty(exports, "subscribeReduxState", { enumerable: true, get: function () { return reduxLogger_2.subscribeReduxState; } });
5463
+ Object.defineProperty(exports, "getActionHistory", { enumerable: true, get: function () { return reduxLogger_2.getActionHistory; } });
5464
+ Object.defineProperty(exports, "clearActionHistory", { enumerable: true, get: function () { return reduxLogger_2.clearActionHistory; } });