react-native-inapp-inspector 2.2.8 → 2.3.0

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 (100) hide show
  1. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +22 -7
  2. package/dist/commonjs/components/AppHeaderLogo.js +45 -12
  3. package/dist/commonjs/components/BrandSquareIcon.js +92 -49
  4. package/dist/commonjs/components/EmptyState.d.ts +7 -2
  5. package/dist/commonjs/components/EmptyState.js +11 -5
  6. package/dist/commonjs/components/Inspector/AnalyticsTab.js +29 -18
  7. package/dist/commonjs/components/Inspector/ConsoleTab.js +6 -1
  8. package/dist/commonjs/components/Inspector/CrashDetail.js +43 -9
  9. package/dist/commonjs/components/Inspector/CrashTab.js +34 -3
  10. package/dist/commonjs/components/Inspector/DeviceInfoTab.js +80 -16
  11. package/dist/commonjs/components/Inspector/InspectorHeader.js +16 -3
  12. package/dist/commonjs/components/Inspector/NetworkDetail.js +3 -2
  13. package/dist/commonjs/components/Inspector/NetworkTab.js +103 -4
  14. package/dist/commonjs/components/Inspector/ReduxTab.js +19 -3
  15. package/dist/commonjs/components/Inspector/SettingsPanel.js +4 -4
  16. package/dist/commonjs/components/Inspector/StorageTab.js +19 -3
  17. package/dist/commonjs/components/JsonViewer.js +2 -0
  18. package/dist/commonjs/components/MetaAccordion.js +15 -3
  19. package/dist/commonjs/constants/version.d.ts +1 -1
  20. package/dist/commonjs/constants/version.js +1 -1
  21. package/dist/commonjs/customHooks/networkLogger.d.ts +3 -0
  22. package/dist/commonjs/customHooks/networkLogger.js +10 -1
  23. package/dist/commonjs/helpers/index.d.ts +6 -0
  24. package/dist/commonjs/helpers/index.js +196 -6
  25. package/dist/commonjs/helpers/searchQueryParser.js +62 -20
  26. package/dist/commonjs/i18n/index.d.ts +13 -0
  27. package/dist/commonjs/i18n/index.js +35 -2
  28. package/dist/commonjs/index.d.ts +3 -0
  29. package/dist/commonjs/index.js +80 -11
  30. package/dist/commonjs/native/NativeInspector.js +13 -0
  31. package/dist/commonjs/native/NativeNetworkInspector.d.ts +1 -1
  32. package/dist/commonjs/styles/AppColors.d.ts +4 -0
  33. package/dist/commonjs/styles/AppColors.js +11 -1
  34. package/dist/commonjs/styles/AppFonts.d.ts +11 -0
  35. package/dist/commonjs/styles/AppFonts.js +11 -1
  36. package/dist/commonjs/types/interfaces.d.ts +1 -0
  37. package/dist/esm/components/AppHeaderLogo.js +45 -12
  38. package/dist/esm/components/BrandSquareIcon.js +92 -49
  39. package/dist/esm/components/EmptyState.d.ts +7 -2
  40. package/dist/esm/components/EmptyState.js +11 -5
  41. package/dist/esm/components/Inspector/AnalyticsTab.js +30 -19
  42. package/dist/esm/components/Inspector/ConsoleTab.js +6 -1
  43. package/dist/esm/components/Inspector/CrashDetail.js +43 -9
  44. package/dist/esm/components/Inspector/CrashTab.js +34 -3
  45. package/dist/esm/components/Inspector/DeviceInfoTab.js +80 -16
  46. package/dist/esm/components/Inspector/InspectorHeader.js +17 -4
  47. package/dist/esm/components/Inspector/NetworkDetail.js +3 -2
  48. package/dist/esm/components/Inspector/NetworkTab.js +103 -4
  49. package/dist/esm/components/Inspector/ReduxTab.js +19 -3
  50. package/dist/esm/components/Inspector/SettingsPanel.js +4 -4
  51. package/dist/esm/components/Inspector/StorageTab.js +19 -3
  52. package/dist/esm/components/JsonViewer.js +2 -0
  53. package/dist/esm/components/MetaAccordion.js +15 -3
  54. package/dist/esm/constants/version.d.ts +1 -1
  55. package/dist/esm/constants/version.js +1 -1
  56. package/dist/esm/customHooks/networkLogger.d.ts +3 -0
  57. package/dist/esm/customHooks/networkLogger.js +8 -0
  58. package/dist/esm/helpers/index.d.ts +6 -0
  59. package/dist/esm/helpers/index.js +193 -5
  60. package/dist/esm/helpers/searchQueryParser.js +62 -20
  61. package/dist/esm/i18n/index.d.ts +13 -0
  62. package/dist/esm/i18n/index.js +33 -3
  63. package/dist/esm/index.d.ts +3 -0
  64. package/dist/esm/index.js +68 -12
  65. package/dist/esm/native/NativeInspector.js +13 -0
  66. package/dist/esm/native/NativeNetworkInspector.d.ts +1 -1
  67. package/dist/esm/styles/AppColors.d.ts +4 -0
  68. package/dist/esm/styles/AppColors.js +9 -0
  69. package/dist/esm/styles/AppFonts.d.ts +11 -0
  70. package/dist/esm/styles/AppFonts.js +9 -0
  71. package/dist/esm/types/interfaces.d.ts +1 -0
  72. package/ios/NetworkInspectorModule.mm +11 -1
  73. package/package.json +1 -1
  74. package/src/components/AppHeaderLogo.tsx +46 -12
  75. package/src/components/BrandSquareIcon.tsx +127 -78
  76. package/src/components/EmptyState.tsx +27 -5
  77. package/src/components/Inspector/AnalyticsTab.tsx +48 -27
  78. package/src/components/Inspector/ConsoleTab.tsx +10 -0
  79. package/src/components/Inspector/CrashDetail.tsx +65 -9
  80. package/src/components/Inspector/CrashTab.tsx +41 -3
  81. package/src/components/Inspector/DeviceInfoTab.tsx +87 -16
  82. package/src/components/Inspector/InspectorHeader.tsx +24 -4
  83. package/src/components/Inspector/NetworkDetail.tsx +4 -4
  84. package/src/components/Inspector/NetworkTab.tsx +129 -4
  85. package/src/components/Inspector/ReduxTab.tsx +26 -8
  86. package/src/components/Inspector/SettingsPanel.tsx +4 -4
  87. package/src/components/Inspector/StorageTab.tsx +21 -3
  88. package/src/components/JsonViewer.tsx +2 -0
  89. package/src/components/MetaAccordion.tsx +18 -3
  90. package/src/constants/version.ts +1 -1
  91. package/src/customHooks/networkLogger.ts +15 -0
  92. package/src/helpers/index.ts +213 -5
  93. package/src/helpers/searchQueryParser.ts +63 -20
  94. package/src/i18n/index.ts +38 -3
  95. package/src/index.tsx +89 -11
  96. package/src/native/NativeInspector.ts +13 -0
  97. package/src/native/NativeNetworkInspector.ts +1 -1
  98. package/src/styles/AppColors.ts +9 -0
  99. package/src/styles/AppFonts.ts +16 -0
  100. package/src/types/interfaces.ts +1 -0
@@ -60,9 +60,9 @@ const InfoRow = react_1.default.memo(({ label, value, copyable = true, badge, su
60
60
  styles.infoRow,
61
61
  isLast && { borderBottomWidth: 0 },
62
62
  ]}>
63
- <react_native_1.View style={{ flex: 1, paddingRight: 8 }}>
64
- <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
65
- <react_native_1.Text style={styles.infoLabel}>{label}</react_native_1.Text>
63
+ <react_native_1.View style={{ flex: 1, paddingRight: 8, minWidth: 0 }}>
64
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', gap: 6 }}>
65
+ <react_native_1.Text style={styles.infoLabel} numberOfLines={2}>{label}</react_native_1.Text>
66
66
  {badge && (<react_native_1.View style={[
67
67
  styles.infoBadge,
68
68
  { backgroundColor: badge.bg, borderColor: `${badge.color}33` },
@@ -72,15 +72,22 @@ const InfoRow = react_1.default.memo(({ label, value, copyable = true, badge, su
72
72
  </react_native_1.Text>
73
73
  </react_native_1.View>)}
74
74
  </react_native_1.View>
75
- {subtext && <react_native_1.Text style={styles.infoSubtext}>{subtext}</react_native_1.Text>}
75
+ {subtext && <react_native_1.Text style={styles.infoSubtext} numberOfLines={2}>{subtext}</react_native_1.Text>}
76
76
  </react_native_1.View>
77
- <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
77
+ <react_native_1.View style={{
78
+ flexDirection: 'row',
79
+ alignItems: 'center',
80
+ gap: 6,
81
+ flexShrink: 0,
82
+ maxWidth: '58%',
83
+ justifyContent: 'flex-end',
84
+ }}>
78
85
  <react_native_1.Text style={[
79
86
  styles.infoValue,
80
87
  typeof value === 'boolean' && {
81
88
  color: value ? AppColors_1.AppColors.emerald500 : AppColors_1.AppColors.errorColor,
82
89
  },
83
- ]} numberOfLines={2} selectable>
90
+ ]} numberOfLines={3} selectable>
84
91
  {displayValue}
85
92
  </react_native_1.Text>
86
93
  {copyable && value !== null && value !== undefined && (<TouchableScale_1.default hitSlop={8} onPress={() => {
@@ -387,6 +394,14 @@ exports.DeviceInfoTab = react_1.default.memo(() => {
387
394
  return true;
388
395
  return text.toLowerCase().includes(search.toLowerCase().trim());
389
396
  };
397
+ const hasAnyMatch = (0, react_1.useMemo)(() => {
398
+ if (!search.trim())
399
+ return true;
400
+ const query = search.toLowerCase().trim();
401
+ return (JSON.stringify(fullDeviceData).toLowerCase().includes(query) ||
402
+ ipAddress.toLowerCase().includes(query) ||
403
+ pseudoUDID.toLowerCase().includes(query));
404
+ }, [search, fullDeviceData, ipAddress, pseudoUDID]);
390
405
  return (<react_native_1.View style={styles.container}>
391
406
  {/* ─── Top Sub-Tabs Navigation Bar ─── */}
392
407
  <react_native_1.View style={styles.subTabsWrapper}>
@@ -436,11 +451,11 @@ exports.DeviceInfoTab = react_1.default.memo(() => {
436
451
  <react_native_1.View style={styles.heroIconWrap}>
437
452
  {react_native_1.Platform.OS === 'ios' ? (<NetworkIcons_1.AppleIcon size={20} color={AppColors_1.AppColors.white}/>) : (<NetworkIcons_1.AndroidIcon size={20} color={AppColors_1.AppColors.white}/>)}
438
453
  </react_native_1.View>
439
- <react_native_1.View style={{ flex: 1 }}>
440
- <react_native_1.Text style={styles.heroTitle}>
454
+ <react_native_1.View style={{ flex: 1, minWidth: 0 }}>
455
+ <react_native_1.Text style={styles.heroTitle} numberOfLines={1} ellipsizeMode="tail">
441
456
  {deviceMetrics?.deviceModel || react_native_1.Platform.constants?.Model || (react_native_1.Platform.OS === 'ios' ? 'Apple iPhone' : 'Android Device')}
442
457
  </react_native_1.Text>
443
- <react_native_1.Text style={styles.heroSubtitle}>
458
+ <react_native_1.Text style={styles.heroSubtitle} numberOfLines={1} ellipsizeMode="tail">
444
459
  {react_native_1.Platform.OS === 'ios' ? `iOS ${react_native_1.Platform.Version}` : `Android ${react_native_1.Platform.Version} (API ${deviceMetrics?.apiLevel || react_native_1.Platform.Version})`}
445
460
  </react_native_1.Text>
446
461
  </react_native_1.View>
@@ -453,22 +468,22 @@ exports.DeviceInfoTab = react_1.default.memo(() => {
453
468
  {/* Quick Metrics Strip */}
454
469
  <react_native_1.View style={styles.heroMetricsStrip}>
455
470
  <react_native_1.View style={styles.heroMetricItem}>
456
- <react_native_1.Text style={styles.heroMetricLabel}>IP ADDRESS</react_native_1.Text>
457
- <react_native_1.Text style={styles.heroMetricValue} numberOfLines={1}>
471
+ <react_native_1.Text style={styles.heroMetricLabel} numberOfLines={1} ellipsizeMode="tail">IP ADDRESS</react_native_1.Text>
472
+ <react_native_1.Text style={styles.heroMetricValue} numberOfLines={1} ellipsizeMode="tail">
458
473
  {ipAddress}
459
474
  </react_native_1.Text>
460
475
  </react_native_1.View>
461
476
  <react_native_1.View style={styles.heroMetricDivider}/>
462
477
  <react_native_1.View style={styles.heroMetricItem}>
463
- <react_native_1.Text style={styles.heroMetricLabel}>RAM (USED/TOTAL)</react_native_1.Text>
464
- <react_native_1.Text style={styles.heroMetricValue}>
478
+ <react_native_1.Text style={styles.heroMetricLabel} numberOfLines={1} ellipsizeMode="tail">RAM (USED/TOTAL)</react_native_1.Text>
479
+ <react_native_1.Text style={styles.heroMetricValue} numberOfLines={1} ellipsizeMode="tail">
465
480
  {usedRamMb}/{totalRamMb} MB
466
481
  </react_native_1.Text>
467
482
  </react_native_1.View>
468
483
  <react_native_1.View style={styles.heroMetricDivider}/>
469
484
  <react_native_1.View style={styles.heroMetricItem}>
470
- <react_native_1.Text style={styles.heroMetricLabel}>UPTIME</react_native_1.Text>
471
- <react_native_1.Text style={styles.heroMetricValue}>{deviceUptime}</react_native_1.Text>
485
+ <react_native_1.Text style={styles.heroMetricLabel} numberOfLines={1} ellipsizeMode="tail">UPTIME</react_native_1.Text>
486
+ <react_native_1.Text style={styles.heroMetricValue} numberOfLines={1} ellipsizeMode="tail">{deviceUptime}</react_native_1.Text>
472
487
  </react_native_1.View>
473
488
  </react_native_1.View>
474
489
  </react_native_1.View>
@@ -581,6 +596,18 @@ exports.DeviceInfoTab = react_1.default.memo(() => {
581
596
  {isMatch('Sandbox Integrity') && (<InfoRow label="App Sandbox File Isolation" value="Enforced by OS Kernel" isLast/>)}
582
597
  </react_native_1.View>)}
583
598
 
599
+ {/* Empty search results state */}
600
+ {search.trim().length > 0 && !hasAnyMatch && (<react_native_1.View style={styles.emptySearchCard}>
601
+ <NetworkIcons_1.SearchIcon size={26} color={AppColors_1.AppColors.grayTextWeak}/>
602
+ <react_native_1.Text style={styles.emptySearchTitle}>No matching device specifications</react_native_1.Text>
603
+ <react_native_1.Text style={styles.emptySearchSubtitle}>
604
+ No properties matched &ldquo;{search}&rdquo;
605
+ </react_native_1.Text>
606
+ <TouchableScale_1.default onPress={() => setSearch('')} style={styles.clearSearchBtn}>
607
+ <react_native_1.Text style={styles.clearSearchBtnText}>Clear Search</react_native_1.Text>
608
+ </TouchableScale_1.default>
609
+ </react_native_1.View>)}
610
+
584
611
  <react_native_1.View style={{ height: 48 }}/>
585
612
  </react_native_1.ScrollView>
586
613
  </react_native_1.View>);
@@ -752,6 +779,8 @@ const styles = react_native_1.StyleSheet.create({
752
779
  heroMetricItem: {
753
780
  flex: 1,
754
781
  alignItems: 'center',
782
+ paddingHorizontal: 2,
783
+ minWidth: 0,
755
784
  },
756
785
  heroMetricLabel: {
757
786
  fontFamily: AppFonts_1.AppFonts.interBold,
@@ -823,7 +852,7 @@ const styles = react_native_1.StyleSheet.create({
823
852
  fontFamily: AppFonts_1.AppFonts.interBold,
824
853
  fontSize: 12,
825
854
  color: AppColors_1.AppColors.primaryBlack,
826
- maxWidth: 180,
855
+ flexShrink: 1,
827
856
  textAlign: 'right',
828
857
  },
829
858
  rowCopyBtn: {
@@ -831,5 +860,40 @@ const styles = react_native_1.StyleSheet.create({
831
860
  borderRadius: 4,
832
861
  backgroundColor: AppColors_1.AppColors.grayBackground,
833
862
  },
863
+ emptySearchCard: {
864
+ backgroundColor: AppColors_1.AppColors.primaryLight,
865
+ borderRadius: 14,
866
+ borderWidth: 1,
867
+ borderColor: AppColors_1.AppColors.grayBorderSecondary,
868
+ padding: 24,
869
+ alignItems: 'center',
870
+ justifyContent: 'center',
871
+ gap: 8,
872
+ marginTop: 8,
873
+ },
874
+ emptySearchTitle: {
875
+ fontFamily: AppFonts_1.AppFonts.interBold,
876
+ fontSize: 13.5,
877
+ color: AppColors_1.AppColors.primaryBlack,
878
+ textAlign: 'center',
879
+ },
880
+ emptySearchSubtitle: {
881
+ fontFamily: AppFonts_1.AppFonts.interRegular,
882
+ fontSize: 11.5,
883
+ color: AppColors_1.AppColors.grayTextWeak,
884
+ textAlign: 'center',
885
+ },
886
+ clearSearchBtn: {
887
+ marginTop: 6,
888
+ paddingHorizontal: 14,
889
+ paddingVertical: 6.5,
890
+ borderRadius: 8,
891
+ backgroundColor: AppColors_1.AppColors.purple,
892
+ },
893
+ clearSearchBtnText: {
894
+ fontFamily: AppFonts_1.AppFonts.interBold,
895
+ fontSize: 11,
896
+ color: AppColors_1.AppColors.white,
897
+ },
834
898
  });
835
899
  exports.default = exports.DeviceInfoTab;
@@ -48,11 +48,26 @@ const AppFonts_1 = require("../../styles/AppFonts");
48
48
  const constants_1 = require("../../constants");
49
49
  const constants_2 = require("../../constants");
50
50
  const helpers_1 = require("../../helpers");
51
+ const NativeInspector_1 = require("../../native/NativeInspector");
51
52
  const UpdateAvailableModal_1 = require("./UpdateAvailableModal");
52
53
  const NetworkIcons_1 = require("../NetworkIcons");
53
54
  const InspectorHeader = react_1.default.memo(() => {
54
55
  const { modalHeightPercent, appIcon, selected, setSelected, selectedEvent, setSelectedEvent, selectedLog, setSelectedLog, selectedReduxSlice, setSelectedReduxSlice, selectedReduxAction, setSelectedReduxAction, reduxState, reduxLastActionMap, showHeaderInfo, setShowHeaderInfo, updateAvailable, latestNpmVersion, clearAnim, activePulseAnim, unreadPulseAnim, runClearAllWithAnimation, settingsPage, setSettingsPage, resetToDefaults, closeModal, detailTitle, activeTab, environment, visible, selectedCrash, setSelectedCrash, } = (0, InspectorContext_1.useInspector)();
55
56
  const [showUpdateModal, setShowUpdateModal] = react_1.default.useState(false);
57
+ const [appVersionString, setAppVersionString] = react_1.default.useState(() => {
58
+ return (0, helpers_1.getAppVersionAndBuild)().formatted;
59
+ });
60
+ react_1.default.useEffect(() => {
61
+ (0, NativeInspector_1.getNativeDeviceMetrics)()
62
+ .then(metrics => {
63
+ if (metrics?.appVersion) {
64
+ const v = metrics.appVersion;
65
+ const b = metrics.appBuild || '1';
66
+ setAppVersionString(`${v} (${b})`);
67
+ }
68
+ })
69
+ .catch(() => { });
70
+ }, []);
56
71
  const envConfig = (0, react_1.useMemo)(() => {
57
72
  const rawEnv = (environment || (__DEV__ ? 'DEV' : 'PROD')).trim();
58
73
  const clean = rawEnv.toUpperCase();
@@ -303,9 +318,7 @@ const InspectorHeader = react_1.default.memo(() => {
303
318
  color: `${AppColors_1.AppColors.white}EB`,
304
319
  letterSpacing: 0.1,
305
320
  }} numberOfLines={1}>
306
- {react_native_1.Platform.OS === 'ios'
307
- ? `iOS ${react_native_1.Platform.Version}`
308
- : `Android ${react_native_1.Platform.Version}`}
321
+ {appVersionString}
309
322
  </react_native_1.Text>
310
323
  </react_native_1.View>
311
324
 
@@ -401,8 +401,9 @@ const NetworkDetail = react_1.default.memo(() => {
401
401
  selected.responseHeaders?.['Content-Type']} url={selected.url}/>
402
402
 
403
403
  {(() => {
404
- const routeInfo = logRouteMapRef.current.get(selected.id);
405
- if (!routeInfo || routeInfo.path === 'Navigators')
404
+ const routeInfo = logRouteMapRef.current.get(selected.id) ||
405
+ selected?.routeInfo;
406
+ if (!routeInfo || !routeInfo.path || routeInfo.path === 'Navigators')
406
407
  return null;
407
408
  return <SourcePageCard_1.default routeInfo={routeInfo}/>;
408
409
  })()}
@@ -507,7 +507,7 @@ const NetworkTab = react_1.default.memo(() => {
507
507
  <react_native_1.View style={{
508
508
  backgroundColor: `${AppColors_1.AppColors.purple}20`,
509
509
  borderRadius: 10,
510
- paddingHorizontal: 5,
510
+ paddingHorizontal: 6,
511
511
  paddingVertical: 1.5,
512
512
  }}>
513
513
  <react_native_1.Text style={{
@@ -560,14 +560,104 @@ const NetworkTab = react_1.default.memo(() => {
560
560
  styles_1.default.toolbarBtn,
561
561
  filtersAccordion.isOpen &&
562
562
  styles_1.default.toolbarBtnActive,
563
- ]} onPress={filtersAccordion.toggleOpen} hitSlop={10}>
563
+ ]} onPress={filtersAccordion.toggleOpen} hitSlop={6}>
564
564
  <NetworkIcons_1.FilterIcon color={filtersAccordion.isOpen
565
565
  ? AppColors_1.AppColors.purple
566
- : AppColors_1.AppColors.grayTextStrong} size={18}/>
566
+ : AppColors_1.AppColors.grayTextStrong} size={16}/>
567
+ {(statusFilters.size > 0 ||
568
+ methodFilters.size > 0) && (<react_native_1.View style={styles_1.default.activeFilterDot}/>)}
567
569
  </TouchableScale_1.default>
568
570
  </react_native_1.View>
569
571
  </react_native_1.View>
570
572
 
573
+ {/* Quick Search Scope & Filter Tags Row */}
574
+ <react_native_1.View style={{ paddingBottom: 4 }}>
575
+ <react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
576
+ paddingHorizontal: 12,
577
+ flexDirection: 'row',
578
+ alignItems: 'center',
579
+ gap: 5,
580
+ paddingVertical: 2,
581
+ }}>
582
+ {/* Search Scopes */}
583
+ {SEARCH_SCOPES.map(s => {
584
+ const isActive = searchScope === s.id;
585
+ return (<TouchableScale_1.default key={`scope_${s.id}`} onPress={() => setSearchScope(s.id)} hitSlop={6} style={{
586
+ flexDirection: 'row',
587
+ alignItems: 'center',
588
+ gap: 3.5,
589
+ paddingHorizontal: 7.5,
590
+ paddingVertical: 3.5,
591
+ borderRadius: 6,
592
+ backgroundColor: isActive
593
+ ? AppColors_1.AppColors.purple
594
+ : AppColors_1.AppColors.grayBackground,
595
+ borderWidth: 1,
596
+ borderColor: isActive
597
+ ? AppColors_1.AppColors.purple
598
+ : AppColors_1.AppColors.grayBorderSecondary,
599
+ }}>
600
+ {s.icon(isActive ? AppColors_1.AppColors.white : AppColors_1.AppColors.grayTextWeak)}
601
+ <react_native_1.Text style={{
602
+ fontFamily: AppFonts_1.AppFonts.interBold,
603
+ fontSize: 10,
604
+ color: isActive ? AppColors_1.AppColors.white : AppColors_1.AppColors.grayText,
605
+ }}>
606
+ {s.label}
607
+ </react_native_1.Text>
608
+ </TouchableScale_1.default>);
609
+ })}
610
+
611
+ <react_native_1.View style={{
612
+ width: 1,
613
+ height: 14,
614
+ backgroundColor: AppColors_1.AppColors.dividerColor,
615
+ marginHorizontal: 3,
616
+ }}/>
617
+
618
+ {/* Quick Query Suggestions */}
619
+ {QUICK_API_QUERY_TAGS.map(q => {
620
+ const isActive = search.includes(q.query);
621
+ return (<TouchableScale_1.default key={`quick_${q.query}`} onPress={() => {
622
+ setSearch(prev => {
623
+ if (prev.includes(q.query)) {
624
+ return prev
625
+ .replace(q.query, '')
626
+ .replace(/\s+/g, ' ')
627
+ .trim();
628
+ }
629
+ return `${prev} ${q.query}`.trim();
630
+ });
631
+ }} hitSlop={6} style={{
632
+ flexDirection: 'row',
633
+ alignItems: 'center',
634
+ gap: 3.5,
635
+ paddingHorizontal: 7.5,
636
+ paddingVertical: 3.5,
637
+ borderRadius: 6,
638
+ backgroundColor: isActive
639
+ ? `${AppColors_1.AppColors.purple}20`
640
+ : AppColors_1.AppColors.grayBackground,
641
+ borderWidth: 1,
642
+ borderColor: isActive
643
+ ? AppColors_1.AppColors.purple
644
+ : AppColors_1.AppColors.grayBorderSecondary,
645
+ }}>
646
+ {q.icon(isActive ? AppColors_1.AppColors.purple : AppColors_1.AppColors.grayTextWeak)}
647
+ <react_native_1.Text style={{
648
+ fontFamily: AppFonts_1.AppFonts.interMedium,
649
+ fontSize: 10,
650
+ color: isActive
651
+ ? AppColors_1.AppColors.purple
652
+ : AppColors_1.AppColors.grayText,
653
+ }}>
654
+ {q.label}
655
+ </react_native_1.Text>
656
+ </TouchableScale_1.default>);
657
+ })}
658
+ </react_native_1.ScrollView>
659
+ </react_native_1.View>
660
+
571
661
  {/* Quick Filter Horizontal Chips Bar with Live Counts */}
572
662
  <react_native_1.View style={{ marginBottom: 8 }}>
573
663
  <react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
@@ -853,7 +943,16 @@ const NetworkTab = react_1.default.memo(() => {
853
943
  ? `${logs.length} requests`
854
944
  : `${filteredLogs.length} of ${logs.length} filtered requests`}
855
945
  </react_native_1.Text>)}
856
- </react_native_1.View>} ListEmptyComponent={<EmptyState_1.default isSearch={search.length > 0 || statusFilters.size > 0}/>} ListFooterComponent={groupedData.length > 0 ? (<EndOfListFooter_1.default count={filteredLogs.length} label="requests"/>) : null} contentContainerStyle={[
946
+ </react_native_1.View>} ListEmptyComponent={<EmptyState_1.default isSearch={search.length > 0 ||
947
+ statusFilters.size > 0 ||
948
+ methodFilters.size > 0} searchQuery={search} customTitle={search.length > 0
949
+ ? 'No matching API requests'
950
+ : 'No network activity'} onClearSearch={() => {
951
+ setSearch('');
952
+ setStatusFilters(new Set());
953
+ setMethodFilters(new Set());
954
+ setSearchScope('all');
955
+ }}/>} ListFooterComponent={groupedData.length > 0 ? (<EndOfListFooter_1.default count={filteredLogs.length} label="requests"/>) : null} contentContainerStyle={[
857
956
  styles_1.default.listContent,
858
957
  filteredLogs.length === 0 && { flexGrow: 1 },
859
958
  ]} keyboardShouldPersistTaps="handled"/>
@@ -434,9 +434,25 @@ const ReduxTab = react_1.default.memo(() => {
434
434
  <react_native_1.View style={styles_1.default.searchContainer}>
435
435
  <NetworkIcons_1.SearchIcon color={AppColors_1.AppColors.grayTextWeak} size={15}/>
436
436
  <react_native_1.TextInput accessible={true} accessibilityRole="search" accessibilityLabel="Search Redux slices or actions" accessibilityHint="Type to filter state slices in real time" placeholder="Search Redux slices or actions..." placeholderTextColor={AppColors_1.AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={styles_1.default.searchInput} autoCorrect={false} autoCapitalize="none"/>
437
- {reduxSearch.length > 0 && (<react_native_1.Pressable accessible={true} accessibilityRole="button" accessibilityLabel="Clear search input" onPress={() => setReduxSearch('')} hitSlop={10}>
438
- <NetworkIcons_1.ClearIcon color={AppColors_1.AppColors.grayTextWeak} size={14}/>
439
- </react_native_1.Pressable>)}
437
+ {reduxSearch.length > 0 && (<react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
438
+ <react_native_1.View style={{
439
+ backgroundColor: `${AppColors_1.AppColors.purple}20`,
440
+ borderRadius: 10,
441
+ paddingHorizontal: 6,
442
+ paddingVertical: 1.5,
443
+ }}>
444
+ <react_native_1.Text style={{
445
+ color: AppColors_1.AppColors.purple,
446
+ fontSize: 9.5,
447
+ fontFamily: AppFonts_1.AppFonts.interBold,
448
+ }}>
449
+ {filteredSlices.length}
450
+ </react_native_1.Text>
451
+ </react_native_1.View>
452
+ <react_native_1.Pressable accessible={true} accessibilityRole="button" accessibilityLabel="Clear search input" onPress={() => setReduxSearch('')} hitSlop={10}>
453
+ <NetworkIcons_1.ClearIcon color={AppColors_1.AppColors.grayTextWeak} size={14}/>
454
+ </react_native_1.Pressable>
455
+ </react_native_1.View>)}
440
456
  </react_native_1.View>
441
457
 
442
458
  {/* Pause / Resume Live Updates Button */}
@@ -146,8 +146,8 @@ const SettingsPanel = () => {
146
146
  bundle: Boolean(tabVisibility?.bundle),
147
147
  performance: Boolean(tabVisibility?.performance),
148
148
  crash: Boolean(tabVisibility?.crash),
149
- device: Boolean(tabVisibility?.device ?? true),
150
- storage: Boolean(tabVisibility?.storage ?? true),
149
+ device: Boolean(tabVisibility?.device),
150
+ storage: Boolean(tabVisibility?.storage),
151
151
  }));
152
152
  // Synchronize staged state with tabVisibility when tabVisibility updates
153
153
  (0, react_1.useEffect)(() => {
@@ -159,8 +159,8 @@ const SettingsPanel = () => {
159
159
  bundle: Boolean(tabVisibility?.bundle),
160
160
  performance: Boolean(tabVisibility?.performance),
161
161
  crash: Boolean(tabVisibility?.crash),
162
- device: Boolean(tabVisibility?.device ?? true),
163
- storage: Boolean(tabVisibility?.storage ?? true),
162
+ device: Boolean(tabVisibility?.device),
163
+ storage: Boolean(tabVisibility?.storage),
164
164
  });
165
165
  }, [tabVisibility]);
166
166
  const hasUnsavedChanges = (0, react_1.useMemo)(() => {
@@ -284,9 +284,25 @@ exports.StorageTab = react_1.default.memo(() => {
284
284
  <react_native_1.View style={styles.searchBar}>
285
285
  <NetworkIcons_1.SearchIcon size={14} color={AppColors_1.AppColors.grayTextWeak}/>
286
286
  <react_native_1.TextInput value={search} onChangeText={setSearch} placeholder={`Search ${entries.length} keys & values...`} placeholderTextColor={AppColors_1.AppColors.grayTextWeak} style={styles.searchInput} clearButtonMode="while-editing"/>
287
- {search.length > 0 && (<TouchableScale_1.default onPress={() => setSearch('')} hitSlop={8}>
288
- <NetworkIcons_1.ClearIcon size={13} color={AppColors_1.AppColors.grayTextWeak}/>
289
- </TouchableScale_1.default>)}
287
+ {search.length > 0 && (<react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
288
+ <react_native_1.View style={{
289
+ backgroundColor: `${AppColors_1.AppColors.purple}20`,
290
+ borderRadius: 10,
291
+ paddingHorizontal: 6,
292
+ paddingVertical: 1.5,
293
+ }}>
294
+ <react_native_1.Text style={{
295
+ color: AppColors_1.AppColors.purple,
296
+ fontSize: 9.5,
297
+ fontFamily: AppFonts_1.AppFonts.interBold,
298
+ }}>
299
+ {filteredEntries.length}
300
+ </react_native_1.Text>
301
+ </react_native_1.View>
302
+ <TouchableScale_1.default onPress={() => setSearch('')} hitSlop={8}>
303
+ <NetworkIcons_1.ClearIcon size={13} color={AppColors_1.AppColors.grayTextWeak}/>
304
+ </TouchableScale_1.default>
305
+ </react_native_1.View>)}
290
306
  </react_native_1.View>
291
307
 
292
308
  {/* Action Buttons: Add Key & Refresh */}
@@ -43,6 +43,7 @@ const i18n_1 = require("../i18n");
43
43
  const TreeNode_1 = __importDefault(require("./TreeNode"));
44
44
  const SegmentedTabs_1 = __importDefault(require("./SegmentedTabs"));
45
45
  const HighlightText_1 = __importDefault(require("./HighlightText"));
46
+ const CopyButton_1 = __importDefault(require("./CopyButton"));
46
47
  const NetworkIcons_1 = require("./NetworkIcons");
47
48
  // Styles
48
49
  const AppColors_1 = require("../styles/AppColors");
@@ -189,6 +190,7 @@ const JsonViewer = react_1.default.memo(({ data, search, forceOpen, defaultExpan
189
190
  icon: (isActive) => (<NetworkIcons_1.TableIcon color={isActive ? AppColors_1.AppColors.white : AppColors_1.AppColors.slate400} size={12}/>),
190
191
  },
191
192
  ]} activeKey={mode} onChange={key => setMode(key)}/>
193
+ <CopyButton_1.default value={data} label="JSON"/>
192
194
  </react_native_1.View>)}
193
195
 
194
196
  {/* ── Main View Content Area (Royal Monospace Theme) ── */}
@@ -49,6 +49,7 @@ const helpers_1 = require("../helpers");
49
49
  const NetworkIcons_1 = require("./NetworkIcons");
50
50
  // Components
51
51
  const TouchableScale_1 = __importDefault(require("./TouchableScale"));
52
+ const CopyButton_1 = __importDefault(require("./CopyButton"));
52
53
  // Stylesheet
53
54
  const AppColors_1 = require("../styles/AppColors");
54
55
  const AppFonts_1 = require("../styles/AppFonts");
@@ -98,9 +99,20 @@ const MetaAccordion = ({ status, statusColor, duration, size, triggeredAt, metho
98
99
  <react_native_1.Pressable onPress={toggleOpen} hitSlop={12}>
99
100
  <react_native_1.View style={styles_1.default.metaHeader}>
100
101
  <react_native_1.Text style={styles_1.default.metaTitle}>Metadata</react_native_1.Text>
101
- <react_native_1.Animated.View style={chevronStyle}>
102
- <NetworkIcons_1.ChevronIcon color={AppColors_1.AppColors.grayTextWeak} size={14}/>
103
- </react_native_1.Animated.View>
102
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
103
+ <CopyButton_1.default value={{
104
+ status: status ?? 0,
105
+ duration: duration != null ? `${duration} ms` : '—',
106
+ size: size || '—',
107
+ triggeredAt: triggeredAt || '',
108
+ method: method || 'GET',
109
+ contentType: contentType || 'application/json',
110
+ url: url || '',
111
+ }} label="Metadata"/>
112
+ <react_native_1.Animated.View style={chevronStyle}>
113
+ <NetworkIcons_1.ChevronIcon color={AppColors_1.AppColors.grayTextWeak} size={14}/>
114
+ </react_native_1.Animated.View>
115
+ </react_native_1.View>
104
116
  </react_native_1.View>
105
117
  </react_native_1.Pressable>
106
118
 
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "2.2.8";
1
+ export declare const LIB_VERSION = "2.3.0";
@@ -3,4 +3,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LIB_VERSION = void 0;
4
4
  // AUTO-GENERATED FILE — do not edit by hand.
5
5
  // Regenerated from package.json on every build by scripts/gen-version.js.
6
- exports.LIB_VERSION = '2.2.8';
6
+ exports.LIB_VERSION = '2.3.0';
@@ -1,3 +1,4 @@
1
+ import { RouteInfo } from "../types";
1
2
  type NetworkLog = {
2
3
  id: number;
3
4
  url: string;
@@ -11,7 +12,9 @@ type NetworkLog = {
11
12
  client?: string;
12
13
  requestHeaders?: Record<string, string>;
13
14
  responseHeaders?: Record<string, string>;
15
+ routeInfo?: RouteInfo;
14
16
  };
17
+ export declare const setRouteInfoProvider: (provider: (() => RouteInfo | null) | null) => void;
15
18
  export declare const setNetworkModuleEnabled: (enabled: boolean) => void;
16
19
  export declare const getNetworkModuleEnabled: () => boolean;
17
20
  export declare const subscribeNetworkLogs: (callback: (logs: NetworkLog[]) => void) => () => void;
@@ -3,13 +3,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.addAxiosInterceptors = exports.setupNetworkLogger = exports.pruneNetworkLogs = exports.getMaxNetworkLogsLimit = exports.setMaxNetworkLogsLimit = exports.getNetworkLogs = exports.clearNetworkLogs = exports.subscribeNetworkLogs = exports.getNetworkModuleEnabled = exports.setNetworkModuleEnabled = void 0;
6
+ exports.addAxiosInterceptors = exports.setupNetworkLogger = exports.pruneNetworkLogs = exports.getMaxNetworkLogsLimit = exports.setMaxNetworkLogsLimit = exports.getNetworkLogs = exports.clearNetworkLogs = exports.subscribeNetworkLogs = exports.getNetworkModuleEnabled = exports.setNetworkModuleEnabled = exports.setRouteInfoProvider = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const crashHandler_1 = require("./crashHandler");
9
9
  let logs = [];
10
10
  let listeners = [];
11
11
  let counter = 0;
12
12
  let isNetworkModuleEnabled = true;
13
+ let currentRouteProvider = null;
14
+ const setRouteInfoProvider = (provider) => {
15
+ currentRouteProvider = provider;
16
+ };
17
+ exports.setRouteInfoProvider = setRouteInfoProvider;
13
18
  const setNetworkModuleEnabled = (enabled) => {
14
19
  isNetworkModuleEnabled = enabled;
15
20
  };
@@ -199,6 +204,7 @@ const setupNetworkLogger = () => {
199
204
  else if (caller?.toLowerCase().includes('axios')) {
200
205
  client = 'axios';
201
206
  }
207
+ const currentRoute = currentRouteProvider ? currentRouteProvider() : null;
202
208
  addOrUpdateLog({
203
209
  id,
204
210
  url: finalUrl,
@@ -206,6 +212,7 @@ const setupNetworkLogger = () => {
206
212
  startTime: start,
207
213
  caller,
208
214
  client,
215
+ routeInfo: currentRoute || undefined,
209
216
  request: method === "GET" ? undefined : parseRequestData(options?.body),
210
217
  requestHeaders,
211
218
  });
@@ -313,6 +320,7 @@ const addAxiosInterceptors = (axiosInstance) => {
313
320
  config.__logId = id;
314
321
  config.__logStart = start;
315
322
  config.__logCaller = caller;
323
+ const currentRoute = currentRouteProvider ? currentRouteProvider() : null;
316
324
  addOrUpdateLog({
317
325
  id,
318
326
  url,
@@ -320,6 +328,7 @@ const addAxiosInterceptors = (axiosInstance) => {
320
328
  startTime: start,
321
329
  caller,
322
330
  client: 'axios',
331
+ routeInfo: currentRoute || undefined,
323
332
  request: method === "GET" ? undefined : parseRequestData(config.data),
324
333
  requestHeaders: normaliseHeaders(config.headers),
325
334
  });
@@ -15,6 +15,7 @@ export declare const getFetchCommand: (log: NetworkLog) => string;
15
15
  export declare const deduplicateLogs: (raw: NetworkLog[]) => NetworkLog[];
16
16
  export declare const escapeRegex: (str: string) => string;
17
17
  export declare const getNavigationInfo: (state: any, path?: string[]) => RouteInfo;
18
+ export declare const getLogPageName: (log: NetworkLog, routeInfo?: RouteInfo | null) => string;
18
19
  export declare const flattenObject: (obj: any, prefix?: string) => Record<string, any>;
19
20
  export declare const getDiff: (oldObj: any, newObj: any) => DiffResult[];
20
21
  export declare const formatDisplayUrl: (url: string) => string;
@@ -24,6 +25,11 @@ export declare const isAllValuesEmpty: (obj: Record<string, any>) => boolean;
24
25
  export declare const formatDateTimeToAnalytics: (ts: number) => string;
25
26
  export declare const getBundleIdentifier: () => string;
26
27
  export declare const getAppName: () => string;
28
+ export declare const getAppVersionAndBuild: () => {
29
+ version: string;
30
+ build: string;
31
+ formatted: string;
32
+ };
27
33
  export declare const handleOpenExternalLink: (url: string) => void;
28
34
  /** Formats a timestamp as HH:MM:SS (no milliseconds). */
29
35
  export declare const formatTimeShort: (ts: number) => string;