react-native-inapp-inspector 1.1.35 → 2.0.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 (82) hide show
  1. package/README.md +123 -101
  2. package/android/build.gradle +13 -1
  3. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +904 -0
  4. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.kt +17 -0
  5. package/dist/commonjs/components/CopyButton.js +3 -0
  6. package/dist/commonjs/components/ErrorBoundary.js +3 -0
  7. package/dist/commonjs/components/Inspector/AnalyticsTab.js +7 -3
  8. package/dist/commonjs/components/Inspector/BundleTab.js +1 -1
  9. package/dist/commonjs/components/Inspector/ConsoleTab.js +1 -1
  10. package/dist/commonjs/components/Inspector/CrashTab.js +97 -90
  11. package/dist/commonjs/components/Inspector/InspectorHeader.js +12 -8
  12. package/dist/commonjs/components/Inspector/MainScreen.js +14 -101
  13. package/dist/commonjs/components/Inspector/NetworkTab.js +47 -10
  14. package/dist/commonjs/components/Inspector/PerformanceTab.js +96 -3
  15. package/dist/commonjs/components/Inspector/ReduxTab.js +18 -10
  16. package/dist/commonjs/components/Inspector/SettingsPanel.js +342 -88
  17. package/dist/commonjs/components/Inspector/TabBar.js +6 -3
  18. package/dist/commonjs/components/NetworkIcons.d.ts +1 -0
  19. package/dist/commonjs/components/NetworkIcons.js +6 -1
  20. package/dist/commonjs/components/TouchableScale.d.ts +1 -0
  21. package/dist/commonjs/components/TouchableScale.js +30 -32
  22. package/dist/commonjs/constants/index.d.ts +1 -2
  23. package/dist/commonjs/constants/index.js +4 -0
  24. package/dist/commonjs/constants/version.d.ts +1 -1
  25. package/dist/commonjs/constants/version.js +1 -1
  26. package/dist/commonjs/customHooks/analyticsLogger.d.ts +4 -0
  27. package/dist/commonjs/customHooks/analyticsLogger.js +7 -5
  28. package/dist/commonjs/customHooks/crashHandler.js +7 -0
  29. package/dist/commonjs/helpers/index.js +10 -1
  30. package/dist/commonjs/helpers/settingsStore.d.ts +9 -0
  31. package/dist/commonjs/helpers/settingsStore.js +59 -3
  32. package/dist/commonjs/index.d.ts +1 -0
  33. package/dist/commonjs/index.js +229 -39
  34. package/dist/commonjs/native/NativeInspector.d.ts +125 -0
  35. package/dist/commonjs/native/NativeInspector.js +313 -0
  36. package/dist/commonjs/native/NativeNetworkInspector.d.ts +22 -0
  37. package/dist/commonjs/native/NativeNetworkInspector.js +4 -0
  38. package/dist/commonjs/styles/index.js +6 -6
  39. package/dist/commonjs/types/enums.d.ts +1 -1
  40. package/dist/commonjs/types/interfaces.d.ts +9 -0
  41. package/dist/esm/components/CopyButton.js +3 -0
  42. package/dist/esm/components/ErrorBoundary.js +3 -0
  43. package/dist/esm/components/Inspector/AnalyticsTab.js +7 -3
  44. package/dist/esm/components/Inspector/BundleTab.js +1 -1
  45. package/dist/esm/components/Inspector/ConsoleTab.js +1 -1
  46. package/dist/esm/components/Inspector/CrashTab.js +97 -90
  47. package/dist/esm/components/Inspector/InspectorHeader.js +13 -9
  48. package/dist/esm/components/Inspector/MainScreen.js +15 -69
  49. package/dist/esm/components/Inspector/NetworkTab.js +47 -10
  50. package/dist/esm/components/Inspector/PerformanceTab.js +96 -3
  51. package/dist/esm/components/Inspector/ReduxTab.js +18 -10
  52. package/dist/esm/components/Inspector/SettingsPanel.js +344 -90
  53. package/dist/esm/components/Inspector/TabBar.js +7 -4
  54. package/dist/esm/components/NetworkIcons.d.ts +1 -0
  55. package/dist/esm/components/NetworkIcons.js +4 -0
  56. package/dist/esm/components/TouchableScale.d.ts +1 -0
  57. package/dist/esm/components/TouchableScale.js +31 -33
  58. package/dist/esm/constants/index.d.ts +1 -2
  59. package/dist/esm/constants/index.js +4 -0
  60. package/dist/esm/constants/version.d.ts +1 -1
  61. package/dist/esm/constants/version.js +1 -1
  62. package/dist/esm/customHooks/analyticsLogger.d.ts +4 -0
  63. package/dist/esm/customHooks/analyticsLogger.js +5 -4
  64. package/dist/esm/customHooks/crashHandler.js +7 -0
  65. package/dist/esm/helpers/index.js +10 -1
  66. package/dist/esm/helpers/settingsStore.d.ts +9 -0
  67. package/dist/esm/helpers/settingsStore.js +58 -3
  68. package/dist/esm/index.d.ts +1 -0
  69. package/dist/esm/index.js +215 -39
  70. package/dist/esm/native/NativeInspector.d.ts +125 -0
  71. package/dist/esm/native/NativeInspector.js +289 -0
  72. package/dist/esm/native/NativeNetworkInspector.d.ts +22 -0
  73. package/dist/esm/native/NativeNetworkInspector.js +2 -0
  74. package/dist/esm/styles/index.js +6 -6
  75. package/dist/esm/types/enums.d.ts +1 -1
  76. package/dist/esm/types/interfaces.d.ts +9 -0
  77. package/ios/NetworkInspectorModule.h +6 -0
  78. package/ios/NetworkInspectorModule.m +720 -5
  79. package/package.json +13 -2
  80. package/react-native-inapp-inspector.podspec +7 -2
  81. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +0 -97
  82. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +0 -28
@@ -176,10 +176,21 @@ const ReduxTab = React.memo(() => {
176
176
  // Filter & Sort slices: by default, the most recently updated item is at the FIRST position
177
177
  const filteredSlices = useMemo(() => {
178
178
  let list = sliceItems;
179
- if (reduxSearch.trim().length > 0) {
180
- const q = reduxSearch.toLowerCase();
181
- list = list.filter(item => item.name.toLowerCase().includes(q) ||
182
- (item.lastAction && item.lastAction.type.toLowerCase().includes(q)));
179
+ if (reduxSearch && reduxSearch.trim().length > 0) {
180
+ const queryTokens = reduxSearch.trim().toLowerCase().split(/\s+/).filter(Boolean);
181
+ list = list.filter(item => {
182
+ const sliceVal = reduxState ? reduxState[item.name] : null;
183
+ const searchTarget = [
184
+ item.name || '',
185
+ item.lastAction?.type || '',
186
+ item.lastAction?.payloadPreview || '',
187
+ item.lastAction?.originType || '',
188
+ item.status || '',
189
+ item.statusMessage || '',
190
+ sliceVal ? (typeof sliceVal === 'string' ? sliceVal : JSON.stringify(sliceVal)) : '',
191
+ ].join(' ').toLowerCase();
192
+ return queryTokens.every(tok => searchTarget.includes(tok));
193
+ });
183
194
  }
184
195
  return [...list].sort((a, b) => {
185
196
  if (sortMode === 'latest') {
@@ -189,12 +200,9 @@ const ReduxTab = React.memo(() => {
189
200
  }
190
201
  return a.name.localeCompare(b.name);
191
202
  }
192
- else {
193
- // Alphabetical A-Z
194
- return a.name.localeCompare(b.name);
195
- }
203
+ return a.name.localeCompare(b.name);
196
204
  });
197
- }, [sliceItems, reduxSearch, sortMode]);
205
+ }, [sliceItems, reduxSearch, sortMode, reduxState]);
198
206
  const lastGlobalAction = useMemo(() => {
199
207
  const actions = Object.values(reduxLastActionMap);
200
208
  if (actions.length === 0)
@@ -438,7 +446,7 @@ const ReduxTab = React.memo(() => {
438
446
  </View>
439
447
 
440
448
  {/* ─── FlatList of Slices ─── */}
441
- <FlatList data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={15} maxToRenderPerBatch={10} windowSize={7} removeClippedSubviews={true} ListEmptyComponent={<EmptyState isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
449
+ <FlatList data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<EmptyState isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
442
450
  reduxTabStyles.listContent,
443
451
  filteredSlices.length === 0 && { flexGrow: 1 },
444
452
  ]} keyboardShouldPersistTaps="handled"/>
@@ -1,5 +1,5 @@
1
1
  import React, { useMemo, useState, useEffect } from 'react';
2
- import { Alert, Platform, ScrollView, StatusBar, Text, View, } from 'react-native';
2
+ import { Alert, Platform, ScrollView, StatusBar, Text, TextInput, View, } from 'react-native';
3
3
  import Svg, { Path } from 'react-native-svg';
4
4
  import LinearGradient from 'react-native-linear-gradient';
5
5
  import { animateNextLayout, useInspector } from './InspectorContext';
@@ -8,7 +8,7 @@ import styles, { toggleGlobalTheme } from '../../styles';
8
8
  import { AppColors } from '../../styles/AppColors';
9
9
  import { AppFonts } from '../../styles/AppFonts';
10
10
  import { LIB_VERSION } from '../../constants';
11
- import { isPersistentStorageAvailable } from '../../helpers/settingsStore';
11
+ import { isPersistentStorageAvailable, calculateRamBasedLimits, } from '../../helpers/settingsStore';
12
12
  import { clearNetworkLogs } from '../../customHooks/networkLogger';
13
13
  import { clearConsoleLogs } from '../../customHooks/consoleLogger';
14
14
  import { clearAnalyticsEvents } from '../../customHooks/analyticsLogger';
@@ -21,7 +21,8 @@ import { useTranslation } from '../../i18n';
21
21
  import { SignalIcon, TerminalIcon, AnalyticsIcon, SettingsIcon, SunIcon, MoonIcon, ScreenIcon, MotionIcon, LayersIcon, EyeIcon, CheckIcon, TrashIcon, WhiteBackNavigation, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, ShieldAlertIcon, ForwardChevronIcon, } from '../NetworkIcons';
22
22
  const SettingsPanel = () => {
23
23
  const { t } = useTranslation();
24
- const { settingsPage, setSettingsPage, settingsActiveSubTab, setSettingsActiveSubTab, tabVisibility, toggleTabVisibility, defaultTab, setDefaultTab, isDark, setIsDark, modalHeightPercent, setModalHeightPercent, modalAnimationType, setModalAnimationType, showDuplicateLogs, setShowDuplicateLogs, showConsoleLevels, setShowConsoleLevels, resetToDefaults, storage, logs, consoleLogs, analyticsEvents, reduxState, maxNetworkLogs, setMaxNetworkLogs, maxConsoleLogs, setMaxConsoleLogs, reduxAutoRefresh, setReduxAutoRefreshState, reduxExpandDepth, setReduxExpandDepth, switchActiveTab, setSelected, setSelectedEvent, setReduxState, crashRecords, maxCrashLogs, setMaxCrashLogs, } = useInspector();
24
+ const { settingsPage, setSettingsPage, settingsActiveSubTab, setSettingsActiveSubTab, tabVisibility, toggleTabVisibility, defaultTab, setDefaultTab, isDark, setIsDark, modalHeightPercent, setModalHeightPercent, modalAnimationType, setModalAnimationType, showDuplicateLogs, setShowDuplicateLogs, showConsoleLevels, setShowConsoleLevels, resetToDefaults, storage, logs, consoleLogs, analyticsEvents, reduxState, maxNetworkLogs, setMaxNetworkLogs, maxConsoleLogs, setMaxConsoleLogs, maxAnalyticsEventsLimit, setMaxAnalyticsEventsLimit, isAutoRamLimitEnabled, setIsAutoRamLimitEnabled, deviceFreeRamMb, reduxAutoRefresh, setReduxAutoRefreshState, reduxExpandDepth, setReduxExpandDepth, switchActiveTab, setSelected, setSelectedEvent, setReduxState, crashRecords, maxCrashLogs, setMaxCrashLogs, } = useInspector();
25
+ const autoRamProfile = calculateRamBasedLimits(deviceFreeRamMb);
25
26
  const isPersistent = isPersistentStorageAvailable();
26
27
  // Safe Area & Status Bar Padding for All Device Form Factors
27
28
  const statusBarHeight = Platform.OS === 'android' ? StatusBar.currentHeight || 24 : 44;
@@ -664,9 +665,204 @@ const SettingsPanel = () => {
664
665
  </View>);
665
666
  })}
666
667
  </View>
667
- </View>) : (
668
- /* UI Preferences Subtab */
669
- <View style={{ gap: 14 }}>
668
+
669
+ {/* Section 3.5: RAM-Based Auto Limits */}
670
+ <View style={{
671
+ backgroundColor: AppColors.primaryLight,
672
+ borderRadius: 14,
673
+ borderWidth: 1,
674
+ borderColor: AppColors.grayBorderSecondary,
675
+ overflow: 'hidden',
676
+ padding: 14,
677
+ gap: 12,
678
+ }}>
679
+ <Text style={{
680
+ fontFamily: AppFonts.interBold,
681
+ fontSize: 11,
682
+ lineHeight: 14,
683
+ color: AppColors.grayTextWeak,
684
+ letterSpacing: 0.8,
685
+ }}>
686
+ RAM-Based Auto Limits
687
+ </Text>
688
+
689
+ {/* Auto RAM Limit Toggle */}
690
+ <View style={{
691
+ flexDirection: 'row',
692
+ alignItems: 'center',
693
+ justifyContent: 'space-between',
694
+ }}>
695
+ <View style={{
696
+ flexDirection: 'row',
697
+ alignItems: 'center',
698
+ gap: 10,
699
+ flex: 1,
700
+ }}>
701
+ <View style={{
702
+ width: 32,
703
+ height: 32,
704
+ borderRadius: 8,
705
+ backgroundColor: AppColors.purpleShade50,
706
+ alignItems: 'center',
707
+ justifyContent: 'center',
708
+ }}>
709
+ <PerformanceIcon color={AppColors.purple} size={15}/>
710
+ </View>
711
+ <View style={{ flex: 1 }}>
712
+ <Text style={{
713
+ fontFamily: AppFonts.interBold,
714
+ fontSize: 13.5,
715
+ lineHeight: 18,
716
+ color: AppColors.primaryBlack,
717
+ }}>
718
+ Auto-Calculate Limits from Device RAM
719
+ </Text>
720
+ <Text style={{
721
+ fontFamily: AppFonts.interRegular,
722
+ fontSize: 11,
723
+ lineHeight: 15,
724
+ color: AppColors.grayText,
725
+ marginTop: 1,
726
+ }}>
727
+ Automatically set API, Logs, Analytics & Crash limits based on available memory
728
+ </Text>
729
+ </View>
730
+ </View>
731
+
732
+ <TouchableScale accessible={true} accessibilityRole="switch" accessibilityLabel="Toggle auto RAM limit calculation" accessibilityState={{ checked: isAutoRamLimitEnabled }} onPress={() => setIsAutoRamLimitEnabled(prev => !prev)} style={{
733
+ width: 42,
734
+ height: 24,
735
+ borderRadius: 12,
736
+ backgroundColor: isAutoRamLimitEnabled
737
+ ? AppColors.purple
738
+ : AppColors.grayBorderSecondary,
739
+ padding: 2,
740
+ justifyContent: 'center',
741
+ alignItems: isAutoRamLimitEnabled ? 'flex-end' : 'flex-start',
742
+ }}>
743
+ <View style={{
744
+ width: 20,
745
+ height: 20,
746
+ borderRadius: 10,
747
+ backgroundColor: AppColors.white,
748
+ shadowColor: AppColors.black,
749
+ shadowOpacity: 0.18,
750
+ shadowRadius: 2,
751
+ shadowOffset: { width: 0, height: 1 },
752
+ }}/>
753
+ </TouchableScale>
754
+ </View>
755
+
756
+ <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
757
+
758
+ {/* Device Free RAM Display */}
759
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
760
+ <View style={{
761
+ width: 32,
762
+ height: 32,
763
+ borderRadius: 8,
764
+ backgroundColor: isAutoRamLimitEnabled
765
+ ? `${AppColors.purple}14`
766
+ : AppColors.grayBackground,
767
+ alignItems: 'center',
768
+ justifyContent: 'center',
769
+ }}>
770
+ <SignalIcon color={isAutoRamLimitEnabled ? AppColors.purple : AppColors.grayTextWeak} size={15}/>
771
+ </View>
772
+ <View style={{ flex: 1 }}>
773
+ <Text style={{
774
+ fontFamily: AppFonts.interBold,
775
+ fontSize: 13.5,
776
+ lineHeight: 18,
777
+ color: isAutoRamLimitEnabled
778
+ ? AppColors.primaryBlack
779
+ : AppColors.grayText,
780
+ }}>
781
+ Detected Free RAM
782
+ </Text>
783
+ <Text style={{
784
+ fontFamily: AppFonts.interRegular,
785
+ fontSize: 11,
786
+ lineHeight: 15,
787
+ color: AppColors.grayText,
788
+ marginTop: 1,
789
+ }}>
790
+ {deviceFreeRamMb} MB available
791
+ </Text>
792
+ </View>
793
+ <View style={{
794
+ paddingHorizontal: 10,
795
+ paddingVertical: 5,
796
+ borderRadius: 8,
797
+ backgroundColor: isAutoRamLimitEnabled
798
+ ? `${AppColors.purple}12`
799
+ : AppColors.grayBackground,
800
+ borderWidth: 1,
801
+ borderColor: isAutoRamLimitEnabled
802
+ ? `${AppColors.purple}26`
803
+ : AppColors.dividerColor,
804
+ }}>
805
+ <Text style={{
806
+ fontFamily: AppFonts.interBold,
807
+ fontSize: 12,
808
+ lineHeight: 15,
809
+ color: isAutoRamLimitEnabled
810
+ ? AppColors.purple
811
+ : AppColors.grayText,
812
+ }}>
813
+ {deviceFreeRamMb} MB
814
+ </Text>
815
+ </View>
816
+ </View>
817
+
818
+ {/* Current Auto-Calculated Limits Preview */}
819
+ {isAutoRamLimitEnabled && (<View style={{ marginTop: 8, paddingTop: 8, borderTopWidth: 1, borderTopColor: AppColors.dividerColor, gap: 6 }}>
820
+ <Text style={{
821
+ fontFamily: AppFonts.interBold,
822
+ fontSize: 11.5,
823
+ lineHeight: 15,
824
+ color: AppColors.purple,
825
+ }}>
826
+ Auto Profile: {autoRamProfile.profileName}
827
+ </Text>
828
+ <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>
829
+ {[
830
+ { label: 'APIs', value: autoRamProfile.maxNetworkLogs, color: AppColors.blue500 },
831
+ { label: 'Logs', value: autoRamProfile.maxConsoleLogs, color: AppColors.sky500 },
832
+ { label: 'Analytics', value: autoRamProfile.maxAnalyticsEvents, color: AppColors.purple },
833
+ { label: 'Crash', value: autoRamProfile.maxCrashRecords, color: AppColors.errorColor },
834
+ ].map(item => (<View key={item.label} style={{
835
+ flexDirection: 'row',
836
+ alignItems: 'center',
837
+ gap: 4,
838
+ paddingHorizontal: 8,
839
+ paddingVertical: 4,
840
+ borderRadius: 6,
841
+ backgroundColor: `${item.color}14`,
842
+ borderWidth: 1,
843
+ borderColor: `${item.color}33`,
844
+ }}>
845
+ <Text style={{
846
+ fontFamily: AppFonts.interBold,
847
+ fontSize: 10.5,
848
+ lineHeight: 14,
849
+ color: item.color,
850
+ }}>
851
+ {item.label}
852
+ </Text>
853
+ <Text style={{
854
+ fontFamily: AppFonts.interBold,
855
+ fontSize: 10.5,
856
+ lineHeight: 14,
857
+ color: item.color,
858
+ }}>
859
+ {item.value}
860
+ </Text>
861
+ </View>))}
862
+ </View>
863
+ </View>)}
864
+ </View>
865
+ </View>) : (<View style={{ gap: 14 }}>
670
866
  {/* Section 1: Appearance */}
671
867
  <View style={{
672
868
  backgroundColor: AppColors.primaryLight,
@@ -1421,83 +1617,128 @@ const SettingsPanel = () => {
1421
1617
  setSettingsPage('main');
1422
1618
  };
1423
1619
  // Helper: settings row with icon + label + optional description
1424
- const renderSettingRow = (opts) => (<View style={{
1425
- paddingVertical: 12,
1426
- borderBottomWidth: opts.isLast ? 0 : 1,
1427
- borderBottomColor: AppColors.dividerColor,
1428
- }}>
1429
- <TouchableScale disabled={!opts.onPress} onPress={opts.onPress} style={{
1430
- flexDirection: 'row',
1431
- alignItems: 'center',
1432
- gap: 12,
1433
- }}>
1434
- <View style={{
1435
- width: 36,
1436
- height: 36,
1437
- borderRadius: 10,
1438
- backgroundColor: AppColors.purpleShade50,
1439
- borderWidth: 1,
1440
- borderColor: `${AppColors.purple}2E`,
1441
- alignItems: 'center',
1442
- justifyContent: 'center',
1443
- }}>
1444
- {opts.icon}
1445
- </View>
1446
- <View style={{ flex: 1 }}>
1447
- <Text style={{
1448
- fontFamily: AppFonts.interBold,
1449
- fontSize: 14,
1450
- lineHeight: 18,
1451
- color: AppColors.primaryBlack,
1452
- }}>
1453
- {opts.label}
1454
- </Text>
1455
- {opts.description ? (<Text style={{
1456
- fontFamily: AppFonts.interRegular,
1457
- fontSize: 11,
1458
- lineHeight: 15,
1459
- color: AppColors.grayText,
1460
- marginTop: 1,
1620
+ const renderSettingRow = (opts) => {
1621
+ return (<View style={{
1622
+ paddingVertical: 12,
1623
+ borderBottomWidth: opts.isLast ? 0 : 1,
1624
+ borderBottomColor: AppColors.dividerColor,
1461
1625
  }}>
1462
- {opts.description}
1463
- </Text>) : null}
1464
- </View>
1465
- {opts.right || null}
1466
- </TouchableScale>
1467
- {opts.picker && (<View style={{
1626
+ <TouchableScale disabled={!opts.onPress} onPress={opts.onPress} style={{
1468
1627
  flexDirection: 'row',
1469
- backgroundColor: AppColors.grayBackground,
1470
- borderRadius: 8,
1471
- padding: 2.5,
1472
- marginTop: 10,
1628
+ alignItems: 'center',
1629
+ gap: 12,
1630
+ }}>
1631
+ <View style={{
1632
+ width: 36,
1633
+ height: 36,
1634
+ borderRadius: 10,
1635
+ backgroundColor: AppColors.purpleShade50,
1473
1636
  borderWidth: 1,
1474
- borderColor: AppColors.dividerColor,
1637
+ borderColor: `${AppColors.purple}2E`,
1638
+ alignItems: 'center',
1639
+ justifyContent: 'center',
1475
1640
  }}>
1476
- {opts.picker.options.map(opt => {
1477
- const isActive = opts.picker.selectedValue === opt;
1478
- return (<TouchableScale key={String(opt)} onPress={() => opts.picker.onSelect(opt)} style={{
1479
- flex: 1,
1480
- paddingVertical: 6,
1481
- alignItems: 'center',
1482
- borderRadius: 6,
1483
- backgroundColor: isActive
1484
- ? AppColors.purple
1485
- : 'transparent',
1486
- }}>
1487
- <Text style={{
1488
- fontFamily: AppFonts.interBold,
1489
- fontSize: 11,
1490
- lineHeight: 14,
1491
- color: isActive ? AppColors.white : AppColors.grayText,
1641
+ {opts.icon}
1642
+ </View>
1643
+ <View style={{ flex: 1 }}>
1644
+ <Text style={{
1645
+ fontFamily: AppFonts.interBold,
1646
+ fontSize: 14,
1647
+ lineHeight: 18,
1648
+ color: AppColors.primaryBlack,
1649
+ }}>
1650
+ {opts.label}
1651
+ </Text>
1652
+ {opts.description ? (<Text style={{
1653
+ fontFamily: AppFonts.interRegular,
1654
+ fontSize: 11,
1655
+ lineHeight: 15,
1656
+ color: AppColors.grayText,
1657
+ marginTop: 1,
1658
+ }}>
1659
+ {opts.description}
1660
+ </Text>) : null}
1661
+ </View>
1662
+ {opts.right || null}
1663
+ </TouchableScale>
1664
+ {opts.numericInput && (<View style={{ marginTop: 10, gap: 6 }}>
1665
+ <View style={{
1666
+ flexDirection: 'row',
1667
+ alignItems: 'center',
1668
+ backgroundColor: AppColors.grayBackground,
1669
+ borderRadius: 8,
1670
+ borderWidth: 1,
1671
+ borderColor: AppColors.dividerColor,
1672
+ paddingHorizontal: 12,
1673
+ paddingVertical: 4,
1674
+ }}>
1675
+ <TextInput style={{
1676
+ flex: 1,
1677
+ fontFamily: AppFonts.interMedium,
1678
+ fontSize: 14,
1679
+ lineHeight: 18,
1680
+ color: AppColors.primaryBlack,
1681
+ paddingVertical: 6,
1682
+ }} value={String(opts.numericInput.value || '')} onChangeText={text => {
1683
+ const num = parseInt(text.replace(/[^0-9]/g, ''), 10);
1684
+ if (!isNaN(num)) {
1685
+ const clamped = Math.max(opts.numericInput?.min ?? 1, Math.min(opts.numericInput?.max ?? 10000, num));
1686
+ opts.numericInput?.onChange(clamped);
1687
+ }
1688
+ }} keyboardType={opts.numericInput.keyboardType ?? 'number-pad'} placeholder={opts.numericInput.placeholder} placeholderTextColor={AppColors.grayTextWeak} maxLength={6} selectTextOnFocus/>
1689
+ <Text style={{
1690
+ fontFamily: AppFonts.interBold,
1691
+ fontSize: 11,
1692
+ color: AppColors.purple,
1693
+ marginLeft: 6,
1694
+ }}>
1695
+ MAX
1696
+ </Text>
1697
+ </View>
1698
+ {opts.numericInput.min !== undefined || opts.numericInput.max !== undefined ? (<Text style={{
1699
+ fontFamily: AppFonts.interRegular,
1700
+ fontSize: 10,
1701
+ lineHeight: 13,
1702
+ color: AppColors.grayTextWeak,
1492
1703
  }}>
1493
- {opts.picker.formatLabel
1494
- ? opts.picker.formatLabel(opt)
1495
- : opt}
1496
- </Text>
1497
- </TouchableScale>);
1498
- })}
1499
- </View>)}
1500
- </View>);
1704
+ Range: {opts.numericInput.min ?? 1} - {opts.numericInput.max ?? '∞'}
1705
+ </Text>) : null}
1706
+ </View>)}
1707
+ {opts.picker && (<View style={{
1708
+ flexDirection: 'row',
1709
+ backgroundColor: AppColors.grayBackground,
1710
+ borderRadius: 8,
1711
+ padding: 2.5,
1712
+ marginTop: 10,
1713
+ borderWidth: 1,
1714
+ borderColor: AppColors.dividerColor,
1715
+ }}>
1716
+ {opts.picker.options.map(opt => {
1717
+ const isActive = opts.picker.selectedValue === opt;
1718
+ return (<TouchableScale key={String(opt)} onPress={() => opts.picker.onSelect(opt)} style={{
1719
+ flex: 1,
1720
+ paddingVertical: 6,
1721
+ alignItems: 'center',
1722
+ borderRadius: 6,
1723
+ backgroundColor: isActive
1724
+ ? AppColors.purple
1725
+ : 'transparent',
1726
+ }}>
1727
+ <Text style={{
1728
+ fontFamily: AppFonts.interBold,
1729
+ fontSize: 11,
1730
+ lineHeight: 14,
1731
+ color: isActive ? AppColors.white : AppColors.grayText,
1732
+ }}>
1733
+ {opts.picker.formatLabel
1734
+ ? opts.picker.formatLabel(opt)
1735
+ : opt}
1736
+ </Text>
1737
+ </TouchableScale>);
1738
+ })}
1739
+ </View>)}
1740
+ </View>);
1741
+ };
1501
1742
  let content = null;
1502
1743
  let title = '';
1503
1744
  let icon = null;
@@ -1518,10 +1759,12 @@ const SettingsPanel = () => {
1518
1759
  icon: <SignalIcon color={AppColors.purple} size={16}/>,
1519
1760
  label: t('settings.apis.maxRequestLogs'),
1520
1761
  description: t('settings.apis.maxRequestLogsDescription'),
1521
- picker: {
1522
- options: [50, 100, 200, 500],
1523
- selectedValue: maxNetworkLogs,
1524
- onSelect: setMaxNetworkLogs,
1762
+ numericInput: {
1763
+ value: maxNetworkLogs,
1764
+ onChange: setMaxNetworkLogs,
1765
+ min: 10,
1766
+ max: 5000,
1767
+ placeholder: 'Enter max requests (10-5000)',
1525
1768
  },
1526
1769
  isLast: true,
1527
1770
  })}
@@ -1582,10 +1825,12 @@ const SettingsPanel = () => {
1582
1825
  icon: <TerminalIcon color={AppColors.purple} size={16}/>,
1583
1826
  label: t('settings.logs.maxConsoleLogs'),
1584
1827
  description: t('settings.logs.maxConsoleLogsDescription'),
1585
- picker: {
1586
- options: [100, 200, 500, 1000],
1587
- selectedValue: maxConsoleLogs,
1588
- onSelect: setMaxConsoleLogs,
1828
+ numericInput: {
1829
+ value: maxConsoleLogs,
1830
+ onChange: setMaxConsoleLogs,
1831
+ min: 10,
1832
+ max: 10000,
1833
+ placeholder: 'Enter max logs (10-10000)',
1589
1834
  },
1590
1835
  })}
1591
1836
  <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
@@ -1702,6 +1947,13 @@ const SettingsPanel = () => {
1702
1947
  icon: <AnalyticsIcon color={AppColors.purple} size={16}/>,
1703
1948
  label: t('settings.analytics.maxAnalyticsEvents'),
1704
1949
  description: t('settings.analytics.maxAnalyticsEventsDescription', { count: analyticsEvents.length }),
1950
+ numericInput: {
1951
+ value: maxAnalyticsEventsLimit,
1952
+ onChange: setMaxAnalyticsEventsLimit,
1953
+ min: 10,
1954
+ max: 5000,
1955
+ placeholder: 'Enter max events (10-5000)',
1956
+ },
1705
1957
  isLast: true,
1706
1958
  })}
1707
1959
  </View>
@@ -1848,10 +2100,12 @@ const SettingsPanel = () => {
1848
2100
  icon: <LayersIcon color={AppColors.purple} size={16}/>,
1849
2101
  label: t('settings.crash.maxCrashLogs'),
1850
2102
  description: t('settings.crash.maxCrashLogsDesc'),
1851
- picker: {
1852
- options: [25, 50, 100, 200],
1853
- selectedValue: maxCrashLogs || 100,
1854
- onSelect: val => setMaxCrashLogs(val),
2103
+ numericInput: {
2104
+ value: maxCrashLogs,
2105
+ onChange: setMaxCrashLogs,
2106
+ min: 5,
2107
+ max: 500,
2108
+ placeholder: 'Enter max crashes (5-500)',
1855
2109
  },
1856
2110
  })}
1857
2111
  <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
@@ -1,12 +1,12 @@
1
1
  import React from 'react';
2
- import { Animated, ScrollView, Text, View } from 'react-native';
2
+ import { Animated, ScrollView, Text, TouchableOpacity, View } from 'react-native';
3
3
  import { useInspector } from './InspectorContext';
4
- import TouchableScale from '../TouchableScale';
5
4
  import styles from '../../styles';
6
5
  import { AppColors } from '../../styles/AppColors';
7
6
  import { SignalIcon, TerminalIcon, AnalyticsIcon, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, } from '../NetworkIcons';
8
7
  import { isReduxConnected } from '../../customHooks/reduxLogger';
9
8
  import { isAnalyticsConnected } from '../../customHooks/analyticsLogger';
9
+ import { triggerNativeHaptic } from '../../native/NativeInspector';
10
10
  const TabBar = React.memo(() => {
11
11
  const { activeTab, switchActiveTab, tabVisibility, logs, consoleLogs, analyticsEvents, crashRecords, lastReadApisCount, lastReadLogsCount, lastReadCrashesCount, unreadPulseAnim, } = useInspector();
12
12
  const isReduxAvail = isReduxConnected();
@@ -80,7 +80,10 @@ const TabBar = React.memo(() => {
80
80
  consoleLogs.length > lastReadLogsCount;
81
81
  const hasUnreadCrashes = activeTab !== 'crash' &&
82
82
  (crashRecords?.length || 0) > (lastReadCrashesCount || 0);
83
- return (<TouchableScale key={tab.key} onPress={() => switchActiveTab(tab.key)} style={[
83
+ return (<TouchableOpacity key={tab.key} activeOpacity={0.7} onPress={() => {
84
+ switchActiveTab(tab.key);
85
+ triggerNativeHaptic('light');
86
+ }} style={[
84
87
  styles.contentTabButton,
85
88
  isActive && styles.contentTabButtonActive,
86
89
  ]}>
@@ -116,7 +119,7 @@ const TabBar = React.memo(() => {
116
119
  transform: [{ scale: unreadPulseAnim }],
117
120
  }}/>)}
118
121
  </View>
119
- </TouchableScale>);
122
+ </TouchableOpacity>);
120
123
  })}
121
124
  </ScrollView>
122
125
  </View>);
@@ -124,3 +124,4 @@ export declare const SmartphoneIcon: ({ color, size }: IconProps) => React.JSX.E
124
124
  export declare const AlertTriangleIcon: ({ color, size }: IconProps) => React.JSX.Element;
125
125
  export declare const BugIcon: ({ color, size }: IconProps) => React.JSX.Element;
126
126
  export declare const TagIcon: ({ color, size }: IconProps) => React.JSX.Element;
127
+ export declare const NpmIcon: ({ color, size }: IconProps) => React.JSX.Element;
@@ -700,3 +700,7 @@ export const TagIcon = ({ color = AppColors.grayTextWeak, size = 14 }) => (<Svg
700
700
  <Path d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8 8a2 2 0 0 0 2.828 0l7.172-7.172a2 2 0 0 0 0-2.828l-8-8z" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
701
701
  <Circle cx="7.5" cy="7.5" r="1.5" fill={color}/>
702
702
  </Svg>);
703
+ export const NpmIcon = ({ color = '#CB3837', size = 14 }) => (<Svg width={size} height={size} viewBox="0 0 256 256" fill="none">
704
+ <Rect width="256" height="256" rx="32" fill={color}/>
705
+ <Path d="M48 48h160v160h-32V96h-32v112H48V48z" fill="#FFFFFF"/>
706
+ </Svg>);
@@ -7,6 +7,7 @@ export interface TouchableScaleProps extends PressableProps {
7
7
  children?: React.ReactNode;
8
8
  hitSlop?: any;
9
9
  disabled?: boolean;
10
+ enableHaptics?: boolean;
10
11
  }
11
12
  declare const TouchableScale: React.NamedExoticComponent<TouchableScaleProps>;
12
13
  export default TouchableScale;