react-native-inapp-inspector 1.1.34 → 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 (122) 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/ConsoleLogCard.js +57 -56
  6. package/dist/commonjs/components/CopyButton.js +10 -1
  7. package/dist/commonjs/components/ErrorBoundary.js +3 -0
  8. package/dist/commonjs/components/Inspector/AnalyticsTab.js +7 -3
  9. package/dist/commonjs/components/Inspector/BundleTab.js +20 -20
  10. package/dist/commonjs/components/Inspector/ConsoleTab.js +1 -1
  11. package/dist/commonjs/components/Inspector/CrashDetail.js +16 -12
  12. package/dist/commonjs/components/Inspector/CrashTab.js +97 -90
  13. package/dist/commonjs/components/Inspector/InspectorHeader.js +28 -24
  14. package/dist/commonjs/components/Inspector/MainScreen.js +20 -103
  15. package/dist/commonjs/components/Inspector/NetworkTab.js +143 -10
  16. package/dist/commonjs/components/Inspector/PerformanceTab.js +96 -3
  17. package/dist/commonjs/components/Inspector/ReduxDetail.js +24 -19
  18. package/dist/commonjs/components/Inspector/ReduxTab.js +30 -18
  19. package/dist/commonjs/components/Inspector/SettingsPanel.js +1578 -754
  20. package/dist/commonjs/components/Inspector/TabBar.js +6 -3
  21. package/dist/commonjs/components/LogCard.js +27 -9
  22. package/dist/commonjs/components/MetaAccordion.js +42 -0
  23. package/dist/commonjs/components/NetworkIcons.d.ts +10 -0
  24. package/dist/commonjs/components/NetworkIcons.js +53 -1
  25. package/dist/commonjs/components/Toast.js +3 -3
  26. package/dist/commonjs/components/TouchableScale.d.ts +5 -2
  27. package/dist/commonjs/components/TouchableScale.js +30 -32
  28. package/dist/commonjs/constants/index.d.ts +1 -2
  29. package/dist/commonjs/constants/index.js +4 -0
  30. package/dist/commonjs/constants/version.d.ts +1 -1
  31. package/dist/commonjs/constants/version.js +1 -1
  32. package/dist/commonjs/customHooks/analyticsLogger.d.ts +6 -0
  33. package/dist/commonjs/customHooks/analyticsLogger.js +16 -5
  34. package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +2 -0
  35. package/dist/commonjs/customHooks/bundleAnalyzer.js +26 -17
  36. package/dist/commonjs/customHooks/consoleLogger.d.ts +2 -0
  37. package/dist/commonjs/customHooks/consoleLogger.js +10 -1
  38. package/dist/commonjs/customHooks/crashHandler.d.ts +2 -0
  39. package/dist/commonjs/customHooks/crashHandler.js +17 -1
  40. package/dist/commonjs/customHooks/networkLogger.d.ts +2 -0
  41. package/dist/commonjs/customHooks/networkLogger.js +12 -1
  42. package/dist/commonjs/customHooks/performanceTracker.d.ts +3 -1
  43. package/dist/commonjs/customHooks/performanceTracker.js +18 -4
  44. package/dist/commonjs/customHooks/reduxLogger.d.ts +2 -0
  45. package/dist/commonjs/customHooks/reduxLogger.js +10 -1
  46. package/dist/commonjs/helpers/index.js +10 -1
  47. package/dist/commonjs/helpers/settingsStore.d.ts +9 -0
  48. package/dist/commonjs/helpers/settingsStore.js +59 -3
  49. package/dist/commonjs/i18n/locales/en.json +96 -28
  50. package/dist/commonjs/index.d.ts +1 -0
  51. package/dist/commonjs/index.js +307 -73
  52. package/dist/commonjs/native/NativeInspector.d.ts +125 -0
  53. package/dist/commonjs/native/NativeInspector.js +313 -0
  54. package/dist/commonjs/native/NativeNetworkInspector.d.ts +22 -0
  55. package/dist/commonjs/native/NativeNetworkInspector.js +4 -0
  56. package/dist/commonjs/styles/AppColors.d.ts +28 -0
  57. package/dist/commonjs/styles/AppColors.js +30 -0
  58. package/dist/commonjs/styles/index.js +6 -6
  59. package/dist/commonjs/types/enums.d.ts +1 -1
  60. package/dist/commonjs/types/interfaces.d.ts +9 -0
  61. package/dist/esm/components/ConsoleLogCard.js +58 -57
  62. package/dist/esm/components/CopyButton.js +10 -1
  63. package/dist/esm/components/ErrorBoundary.js +3 -0
  64. package/dist/esm/components/Inspector/AnalyticsTab.js +7 -3
  65. package/dist/esm/components/Inspector/BundleTab.js +20 -20
  66. package/dist/esm/components/Inspector/ConsoleTab.js +1 -1
  67. package/dist/esm/components/Inspector/CrashDetail.js +16 -12
  68. package/dist/esm/components/Inspector/CrashTab.js +97 -90
  69. package/dist/esm/components/Inspector/InspectorHeader.js +29 -25
  70. package/dist/esm/components/Inspector/MainScreen.js +21 -71
  71. package/dist/esm/components/Inspector/NetworkTab.js +145 -12
  72. package/dist/esm/components/Inspector/PerformanceTab.js +96 -3
  73. package/dist/esm/components/Inspector/ReduxDetail.js +24 -19
  74. package/dist/esm/components/Inspector/ReduxTab.js +30 -18
  75. package/dist/esm/components/Inspector/SettingsPanel.js +1582 -758
  76. package/dist/esm/components/Inspector/TabBar.js +7 -4
  77. package/dist/esm/components/LogCard.js +27 -9
  78. package/dist/esm/components/MetaAccordion.js +42 -0
  79. package/dist/esm/components/NetworkIcons.d.ts +10 -0
  80. package/dist/esm/components/NetworkIcons.js +42 -0
  81. package/dist/esm/components/Toast.js +3 -3
  82. package/dist/esm/components/TouchableScale.d.ts +5 -2
  83. package/dist/esm/components/TouchableScale.js +31 -33
  84. package/dist/esm/constants/index.d.ts +1 -2
  85. package/dist/esm/constants/index.js +4 -0
  86. package/dist/esm/constants/version.d.ts +1 -1
  87. package/dist/esm/constants/version.js +1 -1
  88. package/dist/esm/customHooks/analyticsLogger.d.ts +6 -0
  89. package/dist/esm/customHooks/analyticsLogger.js +12 -4
  90. package/dist/esm/customHooks/bundleAnalyzer.d.ts +2 -0
  91. package/dist/esm/customHooks/bundleAnalyzer.js +23 -16
  92. package/dist/esm/customHooks/consoleLogger.d.ts +2 -0
  93. package/dist/esm/customHooks/consoleLogger.js +7 -0
  94. package/dist/esm/customHooks/crashHandler.d.ts +2 -0
  95. package/dist/esm/customHooks/crashHandler.js +14 -0
  96. package/dist/esm/customHooks/networkLogger.d.ts +2 -0
  97. package/dist/esm/customHooks/networkLogger.js +9 -0
  98. package/dist/esm/customHooks/performanceTracker.d.ts +3 -1
  99. package/dist/esm/customHooks/performanceTracker.js +15 -3
  100. package/dist/esm/customHooks/reduxLogger.d.ts +2 -0
  101. package/dist/esm/customHooks/reduxLogger.js +7 -0
  102. package/dist/esm/helpers/index.js +10 -1
  103. package/dist/esm/helpers/settingsStore.d.ts +9 -0
  104. package/dist/esm/helpers/settingsStore.js +58 -3
  105. package/dist/esm/i18n/locales/en.json +96 -28
  106. package/dist/esm/index.d.ts +1 -0
  107. package/dist/esm/index.js +279 -59
  108. package/dist/esm/native/NativeInspector.d.ts +125 -0
  109. package/dist/esm/native/NativeInspector.js +289 -0
  110. package/dist/esm/native/NativeNetworkInspector.d.ts +22 -0
  111. package/dist/esm/native/NativeNetworkInspector.js +2 -0
  112. package/dist/esm/styles/AppColors.d.ts +28 -0
  113. package/dist/esm/styles/AppColors.js +30 -0
  114. package/dist/esm/styles/index.js +6 -6
  115. package/dist/esm/types/enums.d.ts +1 -1
  116. package/dist/esm/types/interfaces.d.ts +9 -0
  117. package/ios/NetworkInspectorModule.h +6 -0
  118. package/ios/NetworkInspectorModule.m +720 -5
  119. package/package.json +15 -3
  120. package/react-native-inapp-inspector.podspec +7 -2
  121. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +0 -97
  122. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +0 -28
@@ -1,5 +1,5 @@
1
- import React from 'react';
2
- import { Alert, Platform, ScrollView, Text, TouchableOpacity, View, } from 'react-native';
1
+ import React, { useMemo, useState, useEffect } from 'react';
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,36 +8,129 @@ 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';
15
- import { clearCrashRecords, } from '../../customHooks/crashHandler';
15
+ import { clearCrashRecords } from '../../customHooks/crashHandler';
16
+ import { clearCachedBundleAnalysis } from '../../customHooks/bundleAnalyzer';
17
+ import { clearPerformanceEvents } from '../../customHooks/performanceTracker';
16
18
  import { isReduxConnected } from '../../customHooks/reduxLogger';
17
19
  import { isAnalyticsConnected } from '../../customHooks/analyticsLogger';
18
20
  import { useTranslation } from '../../i18n';
19
- import { SignalIcon, TerminalIcon, AnalyticsIcon, SettingsIcon, SunIcon, MoonIcon, ScreenIcon, MotionIcon, LayersIcon, EyeIcon, CheckIcon, TrashIcon, WhiteBackNavigation, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, ShieldAlertIcon, } from '../NetworkIcons';
21
+ import { SignalIcon, TerminalIcon, AnalyticsIcon, SettingsIcon, SunIcon, MoonIcon, ScreenIcon, MotionIcon, LayersIcon, EyeIcon, CheckIcon, TrashIcon, WhiteBackNavigation, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, ShieldAlertIcon, ForwardChevronIcon, } from '../NetworkIcons';
20
22
  const SettingsPanel = () => {
21
23
  const { t } = useTranslation();
22
- 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);
23
26
  const isPersistent = isPersistentStorageAvailable();
24
- const headerTopPadding = Platform.OS === 'ios' && modalHeightPercent >= 95 ? 44 : 0;
27
+ // Safe Area & Status Bar Padding for All Device Form Factors
28
+ const statusBarHeight = Platform.OS === 'android' ? StatusBar.currentHeight || 24 : 44;
29
+ const headerTopPadding = modalHeightPercent >= 95 ? statusBarHeight : 0;
30
+ const allModules = [
31
+ {
32
+ key: 'apis',
33
+ label: 'APIs (Network)',
34
+ category: 'core',
35
+ icon: 'apis',
36
+ desc: 'HTTP/HTTPS requests, GraphQL, Axios & WebSocket inspector',
37
+ },
38
+ {
39
+ key: 'logs',
40
+ label: 'Console Logs',
41
+ category: 'core',
42
+ icon: 'logs',
43
+ desc: 'Terminal console logs, warnings, errors & stack traces',
44
+ },
45
+ {
46
+ key: 'performance',
47
+ label: 'Performance Tracker',
48
+ category: 'diagnostic',
49
+ icon: 'performance',
50
+ desc: '60 FPS monitor, Hermes memory telemetry & re-render profiler',
51
+ },
52
+ {
53
+ key: 'bundle',
54
+ label: 'Bundle Analyzer',
55
+ category: 'diagnostic',
56
+ icon: 'bundle',
57
+ desc: 'Metro packager dependencies, source maps & asset breakdown',
58
+ },
59
+ {
60
+ key: 'crash',
61
+ label: 'Crash Protection',
62
+ category: 'diagnostic',
63
+ icon: 'crash',
64
+ desc: 'Runtime exception guard, breadcrumbs & memory snapshot',
65
+ },
66
+ {
67
+ key: 'analytics',
68
+ label: 'Analytics Logger',
69
+ category: 'telemetry',
70
+ icon: 'analytics',
71
+ desc: 'Firebase & custom analytics events, user properties & params',
72
+ },
73
+ {
74
+ key: 'redux',
75
+ label: 'Redux Inspector',
76
+ category: 'telemetry',
77
+ icon: 'redux',
78
+ desc: 'Store state diffing, action history & reducer timeline',
79
+ },
80
+ ];
81
+ // Staged selection state for checkboxes before clicking "Save Changes"
82
+ const [stagedTabVisibility, setStagedTabVisibility] = useState(() => ({
83
+ apis: true,
84
+ logs: Boolean(tabVisibility?.logs),
85
+ analytics: Boolean(tabVisibility?.analytics),
86
+ redux: Boolean(tabVisibility?.redux),
87
+ bundle: Boolean(tabVisibility?.bundle),
88
+ performance: Boolean(tabVisibility?.performance),
89
+ crash: Boolean(tabVisibility?.crash),
90
+ }));
91
+ // Synchronize staged state with tabVisibility when tabVisibility updates
92
+ useEffect(() => {
93
+ setStagedTabVisibility({
94
+ apis: true,
95
+ logs: Boolean(tabVisibility?.logs),
96
+ analytics: Boolean(tabVisibility?.analytics),
97
+ redux: Boolean(tabVisibility?.redux),
98
+ bundle: Boolean(tabVisibility?.bundle),
99
+ performance: Boolean(tabVisibility?.performance),
100
+ crash: Boolean(tabVisibility?.crash),
101
+ });
102
+ }, [tabVisibility]);
103
+ const hasUnsavedChanges = useMemo(() => {
104
+ return allModules.some(m => Boolean(stagedTabVisibility[m.key]) !==
105
+ Boolean(tabVisibility?.[m.key]));
106
+ }, [stagedTabVisibility, tabVisibility, allModules]);
107
+ const stagedActiveCount = allModules.filter(m => m.key === 'apis' || Boolean(stagedTabVisibility[m.key])).length;
108
+ const handleSaveChanges = () => {
109
+ animateNextLayout();
110
+ allModules.forEach(m => {
111
+ if (m.key !== 'apis') {
112
+ const isStagedOn = Boolean(stagedTabVisibility[m.key]);
113
+ const isCurrentOn = Boolean(tabVisibility?.[m.key]);
114
+ if (isStagedOn !== isCurrentOn) {
115
+ toggleTabVisibility(m.key);
116
+ }
117
+ }
118
+ });
119
+ Alert.alert(t('settings.settingsSaved'), t('settings.settingsSavedDesc'));
120
+ };
25
121
  if (settingsPage === 'main') {
26
- const settingsTabs = [
27
- { key: 'apis', label: 'APIs', icon: 'apis' },
28
- { key: 'logs', label: 'Logs', icon: 'logs' },
29
- { key: 'analytics', label: 'Analytics', icon: 'analytics' },
30
- { key: 'redux', label: 'Redux', icon: 'redux' },
31
- { key: 'bundle', label: 'Bundle', icon: 'bundle' },
32
- { key: 'performance', label: 'Performance', icon: 'performance' },
33
- { key: 'crash', label: 'Crash', icon: 'crash' },
34
- ];
35
- return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
36
- {/* Settings Header with back button */}
122
+ return (<View style={{
123
+ flex: 1,
124
+ backgroundColor: AppColors.grayBackground,
125
+ }}>
126
+ {/* Modern Floating Gradient Header */}
37
127
  <LinearGradient colors={[AppColors.purple, AppColors.brandPurple]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.headerGradient}>
38
128
  <View style={{ paddingTop: headerTopPadding, width: '100%' }}>
39
- <View style={[styles.header, { paddingHorizontal: 16, gap: 12 }]}>
40
- <TouchableScale onPress={() => {
129
+ <View style={[
130
+ styles.header,
131
+ { paddingHorizontal: 16, paddingVertical: 12, gap: 12 },
132
+ ]}>
133
+ <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={t('settings.back')} onPress={() => {
41
134
  animateNextLayout();
42
135
  setSettingsPage(null);
43
136
  switchActiveTab('apis');
@@ -46,223 +139,360 @@ const SettingsPanel = () => {
46
139
  borderRadius: 10,
47
140
  backgroundColor: `${AppColors.white}26`,
48
141
  borderWidth: 1,
49
- borderColor: `${AppColors.white}14`,
142
+ borderColor: `${AppColors.white}18`,
50
143
  }}>
51
- <WhiteBackNavigation color={AppColors.white} size={16}/>
52
- </TouchableScale>
53
- <View style={{ flex: 1 }}>
54
- <Text style={{
144
+ <WhiteBackNavigation color={AppColors.white} size={16}/>
145
+ </TouchableScale>
146
+
147
+ <View style={{ flex: 1 }}>
148
+ <View style={{
149
+ flexDirection: 'row',
150
+ alignItems: 'center',
151
+ gap: 8,
152
+ }}>
153
+ <Text style={{
55
154
  fontFamily: AppFonts.interBold,
56
155
  fontSize: 17,
57
156
  color: AppColors.white,
157
+ letterSpacing: -0.3,
158
+ lineHeight: 22,
58
159
  }}>
59
- Settings
60
- </Text>
61
- <Text style={{
160
+ {t('settings.mainTitle')}
161
+ </Text>
162
+ <View style={{
163
+ backgroundColor: `${AppColors.white}26`,
164
+ paddingHorizontal: 7,
165
+ paddingVertical: 2,
166
+ borderRadius: 12,
167
+ }}>
168
+ <Text style={{
169
+ fontFamily: AppFonts.interBold,
170
+ fontSize: 10,
171
+ color: AppColors.white,
172
+ lineHeight: 13,
173
+ }}>
174
+ {stagedActiveCount}/{allModules.length} Active
175
+ </Text>
176
+ </View>
177
+ </View>
178
+ <Text style={{
62
179
  fontFamily: AppFonts.interRegular,
63
180
  fontSize: 11,
64
- color: `${AppColors.white}BF`,
65
- marginTop: 1,
181
+ color: `${AppColors.white}CC`,
182
+ marginTop: 2,
183
+ lineHeight: 15,
66
184
  }}>
67
- Manage modules and preferences
68
- </Text>
69
- </View>
70
- <View style={{
71
- backgroundColor: `${AppColors.white}26`,
185
+ {t('settings.mainSubtitle')}
186
+ </Text>
187
+ </View>
188
+
189
+ <View style={{
190
+ backgroundColor: `${AppColors.white}22`,
72
191
  paddingHorizontal: 8,
73
192
  paddingVertical: 4,
74
193
  borderRadius: 8,
75
194
  borderWidth: 1,
76
195
  borderColor: `${AppColors.white}1A`,
77
196
  }}>
78
- <Text style={{
197
+ <Text style={{
79
198
  fontFamily: AppFonts.interBold,
80
199
  fontSize: 10.5,
81
200
  color: AppColors.white,
201
+ lineHeight: 14,
82
202
  }}>
83
- v{LIB_VERSION}
84
- </Text>
203
+ v{LIB_VERSION}
204
+ </Text>
205
+ </View>
85
206
  </View>
86
207
  </View>
87
- </View>
88
- </LinearGradient>
208
+ </LinearGradient>
89
209
 
90
- <ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
91
- {/* Sub Tabs */}
210
+ <ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 14 }} showsVerticalScrollIndicator={false}>
211
+ {/* Segmented Top Navigation Sub-Tabs */}
92
212
  <View style={{
93
213
  flexDirection: 'row',
94
214
  backgroundColor: AppColors.primaryLight,
95
- borderRadius: 10,
96
- padding: 3,
215
+ borderRadius: 12,
216
+ padding: 4,
97
217
  borderWidth: 1,
98
218
  borderColor: AppColors.grayBorderSecondary,
99
- marginBottom: 4,
219
+ shadowColor: AppColors.black,
220
+ shadowOpacity: 0.04,
221
+ shadowRadius: 4,
222
+ shadowOffset: { width: 0, height: 2 },
100
223
  }}>
101
- <TouchableOpacity onPress={() => {
224
+ <TouchableScale accessible={true} accessibilityRole="tab" accessibilityLabel={t('settings.modulesAndTools')} accessibilityState={{ selected: settingsActiveSubTab === 'module' }} onPress={() => {
102
225
  animateNextLayout();
103
226
  setSettingsActiveSubTab('module');
104
227
  }} style={{
105
228
  flex: 1,
106
- paddingVertical: 8,
229
+ paddingVertical: 9,
230
+ flexDirection: 'row',
107
231
  alignItems: 'center',
108
232
  justifyContent: 'center',
109
- borderRadius: 8,
233
+ gap: 6,
234
+ borderRadius: 9,
110
235
  backgroundColor: settingsActiveSubTab === 'module'
111
236
  ? AppColors.purple
112
237
  : 'transparent',
113
238
  }}>
239
+ <LayersIcon color={settingsActiveSubTab === 'module'
240
+ ? AppColors.white
241
+ : AppColors.grayText} size={13}/>
114
242
  <Text style={{
115
243
  fontFamily: AppFonts.interBold,
116
- fontSize: 12,
244
+ fontSize: 12.5,
245
+ lineHeight: 16,
117
246
  color: settingsActiveSubTab === 'module'
118
247
  ? AppColors.white
119
248
  : AppColors.grayText,
120
249
  }}>
121
- Module
250
+ {t('settings.modulesAndTools')}
122
251
  </Text>
123
- </TouchableOpacity>
124
- <TouchableOpacity onPress={() => {
252
+ </TouchableScale>
253
+
254
+ <TouchableScale accessible={true} accessibilityRole="tab" accessibilityLabel={t('settings.uiPreferences')} accessibilityState={{ selected: settingsActiveSubTab === 'ui' }} onPress={() => {
125
255
  animateNextLayout();
126
256
  setSettingsActiveSubTab('ui');
127
257
  }} style={{
128
258
  flex: 1,
129
- paddingVertical: 8,
259
+ paddingVertical: 9,
260
+ flexDirection: 'row',
130
261
  alignItems: 'center',
131
262
  justifyContent: 'center',
132
- borderRadius: 8,
263
+ gap: 6,
264
+ borderRadius: 9,
133
265
  backgroundColor: settingsActiveSubTab === 'ui'
134
266
  ? AppColors.purple
135
267
  : 'transparent',
136
268
  }}>
269
+ <ScreenIcon color={settingsActiveSubTab === 'ui'
270
+ ? AppColors.white
271
+ : AppColors.grayText} size={13}/>
137
272
  <Text style={{
138
273
  fontFamily: AppFonts.interBold,
139
- fontSize: 12,
274
+ fontSize: 12.5,
275
+ lineHeight: 16,
140
276
  color: settingsActiveSubTab === 'ui'
141
277
  ? AppColors.white
142
278
  : AppColors.grayText,
143
279
  }}>
144
- UI Preferences
280
+ {t('settings.uiPreferences')}
145
281
  </Text>
146
- </TouchableOpacity>
282
+ </TouchableScale>
147
283
  </View>
148
284
 
149
- {settingsActiveSubTab === 'module' ? (
150
- /* Tab list */
151
- <View style={{
152
- backgroundColor: AppColors.primaryLight,
285
+ {settingsActiveSubTab === 'module' ? (<View style={{ gap: 12 }}>
286
+ {/* Performance Advisory Banner */}
287
+ <View style={{
288
+ backgroundColor: `${AppColors.purple}0A`,
153
289
  borderRadius: 12,
290
+ padding: 12,
154
291
  borderWidth: 1,
155
- borderColor: AppColors.grayBorderSecondary,
156
- overflow: 'hidden',
157
- }}>
158
- {/* Table Header */}
159
- <View style={{
292
+ borderColor: `${AppColors.purple}20`,
160
293
  flexDirection: 'row',
161
294
  alignItems: 'center',
162
- paddingVertical: 8,
163
- paddingHorizontal: 14,
164
- backgroundColor: AppColors.grayBackground,
165
- borderBottomWidth: 1,
166
- borderBottomColor: AppColors.dividerColor,
167
- gap: 12,
295
+ gap: 10,
168
296
  }}>
169
- <Text style={{
170
- fontFamily: AppFonts.interBold,
171
- fontSize: 10,
172
- color: AppColors.grayTextWeak,
173
- letterSpacing: 0.6,
174
- flex: 1,
297
+ <View style={{
298
+ width: 32,
299
+ height: 32,
300
+ borderRadius: 8,
301
+ backgroundColor: `${AppColors.purple}14`,
302
+ alignItems: 'center',
303
+ justifyContent: 'center',
175
304
  }}>
176
- MODULE
177
- </Text>
178
- <Text style={{
305
+ <SignalIcon color={AppColors.purple} size={15}/>
306
+ </View>
307
+ <View style={{ flex: 1 }}>
308
+ <Text style={{
179
309
  fontFamily: AppFonts.interBold,
180
- fontSize: 10,
181
- color: AppColors.grayTextWeak,
182
- letterSpacing: 0.6,
183
- width: 90,
184
- textAlign: 'right',
185
- paddingRight: 4,
310
+ fontSize: 12,
311
+ color: AppColors.primaryBlack,
312
+ lineHeight: 16,
186
313
  }}>
187
- VISIBILITY
188
- </Text>
314
+ {t('settings.zeroOverheadTitle')}
315
+ </Text>
316
+ <Text style={{
317
+ fontFamily: AppFonts.interRegular,
318
+ fontSize: 10.5,
319
+ color: AppColors.grayText,
320
+ marginTop: 2,
321
+ lineHeight: 14,
322
+ }}>
323
+ {t('settings.zeroOverheadDesc')}
324
+ </Text>
325
+ </View>
189
326
  </View>
190
327
 
191
- {settingsTabs.map((tab, idx) => {
328
+ {/* Enhanced Individual Module Cards with Decorative Checkboxes */}
329
+ <View style={{ gap: 10 }}>
330
+ {allModules.map(moduleItem => {
192
331
  const isReduxAvail = isReduxConnected();
193
332
  const isAnalyticsAvail = isAnalyticsConnected();
194
- const isUnavailable = (tab.key === 'redux' && !isReduxAvail) ||
195
- (tab.key === 'analytics' && !isAnalyticsAvail);
196
- const isLocked = tab.key === 'apis' || isUnavailable;
197
- const isVisible = tab.key === 'apis' ||
198
- (Boolean(tabVisibility?.[tab.key]) && !isUnavailable);
199
- const isLast = idx === settingsTabs.length - 1;
200
- return (<View key={tab.key} style={{
333
+ const isUnavailable = (moduleItem.key === 'redux' && !isReduxAvail) ||
334
+ (moduleItem.key === 'analytics' && !isAnalyticsAvail);
335
+ const isLocked = moduleItem.key === 'apis' || isUnavailable;
336
+ const isChecked = moduleItem.key === 'apis' ||
337
+ (Boolean(stagedTabVisibility?.[moduleItem.key]) &&
338
+ !isUnavailable);
339
+ const liveStats = moduleItem.key === 'apis'
340
+ ? `${logs.length} requests • Limit: ${maxNetworkLogs}`
341
+ : moduleItem.key === 'logs'
342
+ ? `${consoleLogs.length} logs • Limit: ${maxConsoleLogs}`
343
+ : moduleItem.key === 'performance'
344
+ ? '60 FPS Target • Memory Telemetry'
345
+ : moduleItem.key === 'bundle'
346
+ ? 'Metro Packager • Dependency Map'
347
+ : moduleItem.key === 'crash'
348
+ ? `${crashRecords?.length || 0} crashes recorded • Crash Guard`
349
+ : moduleItem.key === 'analytics'
350
+ ? `${analyticsEvents.length} events logged`
351
+ : moduleItem.key === 'redux'
352
+ ? `${Object.keys(reduxState || {}).length} slices • Depth: ${reduxExpandDepth}`
353
+ : '';
354
+ return (<View key={moduleItem.key} style={{
355
+ backgroundColor: AppColors.primaryLight,
356
+ borderRadius: 14,
357
+ borderWidth: 1.5,
358
+ borderColor: isChecked
359
+ ? `${AppColors.purple}38`
360
+ : AppColors.grayBorderSecondary,
361
+ padding: 14,
362
+ gap: 10,
363
+ shadowColor: AppColors.black,
364
+ shadowOpacity: isChecked ? 0.04 : 0.02,
365
+ shadowRadius: 5,
366
+ shadowOffset: { width: 0, height: 2 },
367
+ }}>
368
+ {/* Top Row: Icon + Title & Badges + Checkbox */}
369
+ <View style={{
201
370
  flexDirection: 'row',
202
- alignItems: 'center',
203
- paddingVertical: 10,
204
- paddingHorizontal: 14,
205
- borderBottomWidth: isLast ? 0 : 1,
206
- borderBottomColor: AppColors.dividerColor,
207
- gap: 12,
371
+ alignItems: 'flex-start',
372
+ justifyContent: 'space-between',
373
+ gap: 10,
208
374
  }}>
209
- {/* Icon + Label fills remaining space */}
210
- <View style={{
375
+ {/* Tap Zone for Details/Settings */}
376
+ <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={`${moduleItem.label} settings`} disabled={isUnavailable} onPress={() => {
377
+ animateNextLayout();
378
+ setSettingsPage(moduleItem.key);
379
+ }} style={{
211
380
  flex: 1,
212
381
  flexDirection: 'row',
213
- alignItems: 'center',
214
- gap: 8,
215
- flexWrap: 'wrap',
382
+ alignItems: 'flex-start',
383
+ gap: 10,
216
384
  }}>
217
- {/* Small icon tile */}
218
- <View style={{
219
- width: 20,
220
- height: 20,
221
- borderRadius: 6,
385
+ {/* Icon Tile */}
386
+ <View style={{
387
+ width: 36,
388
+ height: 36,
389
+ borderRadius: 10,
222
390
  backgroundColor: isLocked
223
- ? AppColors.grayBorderSecondary
224
- : AppColors.purpleShade50,
391
+ ? `${AppColors.grayBorderSecondary}4D`
392
+ : isChecked
393
+ ? `${AppColors.purple}14`
394
+ : AppColors.grayBackground,
225
395
  borderWidth: 1,
226
396
  borderColor: isLocked
227
397
  ? AppColors.dividerColor
228
- : `${AppColors.purple}33`,
398
+ : isChecked
399
+ ? `${AppColors.purple}33`
400
+ : AppColors.grayBorderSecondary,
229
401
  alignItems: 'center',
230
402
  justifyContent: 'center',
403
+ marginTop: 1,
231
404
  }}>
232
- {tab.icon === 'apis' && (<SignalIcon color={isLocked
233
- ? AppColors.grayTextWeak
234
- : AppColors.purple} size={11}/>)}
235
- {tab.icon === 'logs' && (<TerminalIcon color={isLocked
236
- ? AppColors.grayTextWeak
237
- : AppColors.purple} size={11}/>)}
238
- {tab.icon === 'analytics' && (<AnalyticsIcon color={isLocked
239
- ? AppColors.grayTextWeak
240
- : AppColors.purple} size={11}/>)}
241
- {tab.icon === 'redux' && (<ReduxIcon color={isLocked
242
- ? AppColors.grayTextWeak
243
- : AppColors.purple} size={11}/>)}
244
- {tab.icon === 'bundle' && (<PackageIcon color={isLocked
245
- ? AppColors.grayTextWeak
246
- : AppColors.purple} size={11}/>)}
247
- {tab.icon === 'performance' && (<PerformanceIcon color={isLocked
248
- ? AppColors.grayTextWeak
249
- : AppColors.purple} size={11}/>)}
250
- {tab.icon === 'crash' && (<CrashIcon color={isLocked
251
- ? AppColors.grayTextWeak
252
- : AppColors.purple} size={11}/>)}
253
- </View>
254
- {/* Label + Required badge */}
255
- <Text style={{
405
+ {moduleItem.icon === 'apis' && (<SignalIcon color={isChecked
406
+ ? AppColors.purple
407
+ : AppColors.grayTextWeak} size={16}/>)}
408
+ {moduleItem.icon === 'logs' && (<TerminalIcon color={isChecked
409
+ ? AppColors.purple
410
+ : AppColors.grayTextWeak} size={16}/>)}
411
+ {moduleItem.icon === 'performance' && (<PerformanceIcon color={isChecked
412
+ ? AppColors.purple
413
+ : AppColors.grayTextWeak} size={16}/>)}
414
+ {moduleItem.icon === 'bundle' && (<PackageIcon color={isChecked
415
+ ? AppColors.purple
416
+ : AppColors.grayTextWeak} size={16}/>)}
417
+ {moduleItem.icon === 'crash' && (<CrashIcon color={isChecked
418
+ ? AppColors.purple
419
+ : AppColors.grayTextWeak} size={16}/>)}
420
+ {moduleItem.icon === 'analytics' && (<AnalyticsIcon color={isChecked
421
+ ? AppColors.purple
422
+ : AppColors.grayTextWeak} size={16}/>)}
423
+ {moduleItem.icon === 'redux' && (<ReduxIcon color={isChecked
424
+ ? AppColors.purple
425
+ : AppColors.grayTextWeak} size={16}/>)}
426
+ </View>
427
+
428
+ {/* Titles & Status Pill */}
429
+ <View style={{ flex: 1, gap: 2 }}>
430
+ <View style={{
431
+ flexDirection: 'row',
432
+ alignItems: 'center',
433
+ gap: 6,
434
+ flexWrap: 'wrap',
435
+ }}>
436
+ <Text style={{
256
437
  fontFamily: AppFonts.interBold,
257
- fontSize: 13,
438
+ fontSize: 14,
439
+ lineHeight: 18,
258
440
  color: isLocked
259
441
  ? AppColors.grayText
260
442
  : AppColors.primaryBlack,
261
443
  }}>
262
- {tab.label}
263
- </Text>
264
- {/* Unavailable badge */}
265
- {isUnavailable && (<View style={{
444
+ {moduleItem.label}
445
+ </Text>
446
+
447
+ {/* Default Startup Tab Badge */}
448
+ {moduleItem.key === defaultTab &&
449
+ !isUnavailable && (<View style={{
450
+ flexDirection: 'row',
451
+ alignItems: 'center',
452
+ backgroundColor: `${AppColors.purple}14`,
453
+ borderRadius: 12,
454
+ paddingHorizontal: 5,
455
+ paddingVertical: 1.5,
456
+ borderWidth: 1,
457
+ borderColor: `${AppColors.purple}2E`,
458
+ gap: 3,
459
+ }}>
460
+ <View style={{
461
+ width: 4,
462
+ height: 4,
463
+ borderRadius: 2,
464
+ backgroundColor: AppColors.purple,
465
+ }}/>
466
+ <Text style={{
467
+ fontFamily: AppFonts.interBold,
468
+ fontSize: 8,
469
+ lineHeight: 11,
470
+ color: AppColors.purple,
471
+ letterSpacing: 0.4,
472
+ }}>
473
+ {t('settings.defaultBadge')}
474
+ </Text>
475
+ </View>)}
476
+
477
+ {/* Status Pill */}
478
+ {moduleItem.key === 'apis' ? (<View style={{
479
+ backgroundColor: `${AppColors.blue500}14`,
480
+ borderRadius: 4,
481
+ paddingHorizontal: 5,
482
+ paddingVertical: 1.5,
483
+ borderWidth: 1,
484
+ borderColor: `${AppColors.blue500}33`,
485
+ }}>
486
+ <Text style={{
487
+ fontFamily: AppFonts.interBold,
488
+ fontSize: 8,
489
+ lineHeight: 11,
490
+ color: AppColors.blue500,
491
+ letterSpacing: 0.3,
492
+ }}>
493
+ {t('settings.coreBadge')}
494
+ </Text>
495
+ </View>) : isUnavailable ? (<View style={{
266
496
  backgroundColor: `${AppColors.amber500}18`,
267
497
  borderRadius: 4,
268
498
  paddingHorizontal: 5,
@@ -270,181 +500,439 @@ const SettingsPanel = () => {
270
500
  borderWidth: 1,
271
501
  borderColor: `${AppColors.amber500}35`,
272
502
  }}>
273
- <Text style={{
503
+ <Text style={{
274
504
  fontFamily: AppFonts.interBold,
275
505
  fontSize: 8,
506
+ lineHeight: 11,
276
507
  color: AppColors.amber700,
277
508
  letterSpacing: 0.3,
278
509
  }}>
279
- {tab.key === 'redux'
510
+ {moduleItem.key === 'redux'
280
511
  ? t('settings.notConnectedBadge')
281
512
  : t('settings.notDetectedBadge')}
282
- </Text>
283
- </View>)}
284
- {/* #6 — badge marks the configured default tab */}
285
- {tab.key === defaultTab && !isUnavailable && (<View style={{
286
- flexDirection: 'row',
287
- alignItems: 'center',
288
- backgroundColor: `${AppColors.purple}14`,
289
- borderRadius: 20,
290
- paddingHorizontal: 6,
291
- paddingVertical: 2,
513
+ </Text>
514
+ </View>) : isChecked ? (<View style={{
515
+ backgroundColor: `${AppColors.liveGreen}18`,
516
+ borderRadius: 4,
517
+ paddingHorizontal: 5,
518
+ paddingVertical: 1.5,
292
519
  borderWidth: 1,
293
- borderColor: `${AppColors.purple}2E`,
294
- gap: 3,
520
+ borderColor: `${AppColors.liveGreen}38`,
295
521
  }}>
296
- <View style={{
297
- width: 4,
298
- height: 4,
299
- borderRadius: 2,
300
- backgroundColor: AppColors.purple,
301
- opacity: 0.7,
302
- }}/>
303
- <Text style={{
522
+ <Text style={{
304
523
  fontFamily: AppFonts.interBold,
305
- fontSize: 8.5,
306
- color: AppColors.purple,
307
- letterSpacing: 0.4,
524
+ fontSize: 8,
525
+ lineHeight: 11,
526
+ color: AppColors.green700,
527
+ letterSpacing: 0.3,
308
528
  }}>
309
- DEFAULT
310
- </Text>
311
- </View>)}
312
-
313
- {/* Settings gear icon next to label */}
314
- {!isUnavailable && (<TouchableScale onPress={() => {
315
- animateNextLayout();
316
- setSettingsPage(tab.key);
317
- }} hitSlop={8} style={{
318
- marginLeft: 4,
319
- padding: 4,
320
- borderRadius: 6,
321
- backgroundColor: AppColors.purpleShade50,
529
+ {t('settings.activeBadge')}
530
+ </Text>
531
+ </View>) : (<View style={{
532
+ backgroundColor: `${AppColors.grayTextWeak}18`,
533
+ borderRadius: 4,
534
+ paddingHorizontal: 5,
535
+ paddingVertical: 1.5,
322
536
  borderWidth: 1,
323
- borderColor: `${AppColors.purple}26`,
324
- alignItems: 'center',
325
- justifyContent: 'center',
537
+ borderColor: `${AppColors.grayTextWeak}2E`,
326
538
  }}>
327
- <SettingsIcon color={AppColors.purple} size={10}/>
328
- </TouchableScale>)}
329
- </View>
539
+ <Text style={{
540
+ fontFamily: AppFonts.interBold,
541
+ fontSize: 8,
542
+ lineHeight: 11,
543
+ color: AppColors.grayTextWeak,
544
+ letterSpacing: 0.3,
545
+ }}>
546
+ {t('settings.dormantBadge')}
547
+ </Text>
548
+ </View>)}
549
+ </View>
330
550
 
331
- {/* Visibility Switch in VISIBILITY column */}
332
- <View style={{
333
- width: 90,
334
- alignItems: 'flex-end',
335
- justifyContent: 'center',
551
+ <Text style={{
552
+ fontFamily: AppFonts.interRegular,
553
+ fontSize: 11,
554
+ color: AppColors.grayText,
555
+ lineHeight: 15,
556
+ marginTop: 1,
336
557
  }}>
337
- <TouchableScale disabled={isLocked} onPress={() => toggleTabVisibility(tab.key)} style={{
338
- width: 38,
339
- height: 22,
340
- borderRadius: 11,
341
- backgroundColor: isLocked
342
- ? AppColors.grayBackground
343
- : isVisible
344
- ? AppColors.purple
345
- : AppColors.grayBorderSecondary,
346
- borderWidth: isLocked ? 1.5 : 0,
558
+ {moduleItem.desc}
559
+ </Text>
560
+ </View>
561
+ </TouchableScale>
562
+
563
+ {/* Decorative Custom Checkbox */}
564
+ <TouchableScale accessible={true} accessibilityRole="checkbox" accessibilityLabel={`Select ${moduleItem.label}`} accessibilityState={{
565
+ checked: isChecked,
566
+ disabled: isLocked,
567
+ }} disabled={isLocked} onPress={() => {
568
+ if (isLocked)
569
+ return;
570
+ setStagedTabVisibility(prev => ({
571
+ ...prev,
572
+ [moduleItem.key]: !prev[moduleItem.key],
573
+ }));
574
+ }} style={{
575
+ width: 26,
576
+ height: 26,
577
+ borderRadius: 8,
578
+ borderWidth: isChecked ? 0 : 2,
347
579
  borderColor: isLocked
348
- ? AppColors.grayBorderSecondary
349
- : 'transparent',
350
- borderStyle: isLocked ? 'dashed' : 'solid',
351
- padding: 2,
352
- justifyContent: 'center',
353
- alignItems: isVisible ? 'flex-end' : 'flex-start',
354
- opacity: isLocked ? 0.9 : 1,
355
- }}>
356
- <View style={{
357
- width: 18,
358
- height: 18,
359
- borderRadius: 9,
360
- backgroundColor: isLocked
361
- ? AppColors.grayBorderSecondary
362
- : AppColors.white,
580
+ ? AppColors.dividerColor
581
+ : AppColors.grayBorderSecondary,
582
+ backgroundColor: moduleItem.key === 'apis'
583
+ ? `${AppColors.blue500}22`
584
+ : isChecked
585
+ ? AppColors.purple
586
+ : AppColors.grayBackground,
363
587
  alignItems: 'center',
364
588
  justifyContent: 'center',
365
- shadowColor: AppColors.black,
366
- shadowOpacity: isLocked ? 0 : 0.15,
367
- shadowRadius: 1.5,
589
+ shadowColor: isChecked
590
+ ? AppColors.purple
591
+ : 'transparent',
592
+ shadowOpacity: isChecked ? 0.3 : 0,
593
+ shadowRadius: 3,
368
594
  shadowOffset: { width: 0, height: 1 },
595
+ elevation: isChecked ? 2 : 0,
369
596
  }}>
370
- {isLocked && (<Svg width={10} height={10} viewBox="0 0 24 24" fill="none">
597
+ {moduleItem.key === 'apis' ? (<CheckIcon size={14} color={AppColors.blue500}/>) : isChecked ? (<CheckIcon size={14} color={AppColors.white}/>) : isLocked ? (<Svg width={10} height={10} viewBox="0 0 24 24" fill="none">
371
598
  <Path d="M7 10V7a5 5 0 0 1 10 0v3" stroke={AppColors.grayText} strokeWidth="2.2" strokeLinecap="round"/>
372
599
  <Path d="M5 10h14v9a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1z" fill={AppColors.grayText}/>
373
- </Svg>)}
374
- </View>
375
- </TouchableScale>
376
- {isLocked && (<Text style={{
377
- fontFamily: AppFonts.interBold,
378
- fontSize: 7.5,
379
- color: isUnavailable
380
- ? AppColors.amber700
600
+ </Svg>) : null}
601
+ </TouchableScale>
602
+ </View>
603
+
604
+ {/* Card Footer: Live Stats + Configure Button */}
605
+ {!isUnavailable && (<View style={{
606
+ flexDirection: 'row',
607
+ alignItems: 'center',
608
+ justifyContent: 'space-between',
609
+ paddingTop: 8,
610
+ borderTopWidth: 1,
611
+ borderTopColor: AppColors.dividerColor,
612
+ }}>
613
+ {/* Live Info Pill */}
614
+ <View style={{
615
+ flexDirection: 'row',
616
+ alignItems: 'center',
617
+ gap: 6,
618
+ flex: 1,
619
+ }}>
620
+ <View style={{
621
+ width: 6,
622
+ height: 6,
623
+ borderRadius: 3,
624
+ backgroundColor: isChecked
625
+ ? AppColors.liveGreen
381
626
  : AppColors.grayTextWeak,
382
- letterSpacing: 0.3,
383
- marginTop: 3,
384
- textAlign: 'right',
627
+ }}/>
628
+ <Text style={{
629
+ fontFamily: AppFonts.interMedium,
630
+ fontSize: 11,
631
+ color: AppColors.grayText,
632
+ lineHeight: 14,
385
633
  }}>
386
- {tab.key === 'apis'
387
- ? t('settings.requiredBadge')
388
- : t('settings.readOnlyBadge')}
389
- </Text>)}
390
- </View>
391
- </View>);
634
+ {liveStats}
635
+ </Text>
636
+ </View>
637
+
638
+ {/* Sleek Configure Button */}
639
+ <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={`Configure ${moduleItem.label} settings`} onPress={() => {
640
+ animateNextLayout();
641
+ setSettingsPage(moduleItem.key);
642
+ }} style={{
643
+ flexDirection: 'row',
644
+ alignItems: 'center',
645
+ gap: 5,
646
+ backgroundColor: `${AppColors.purple}12`,
647
+ paddingHorizontal: 10,
648
+ paddingVertical: 5,
649
+ borderRadius: 8,
650
+ borderWidth: 1,
651
+ borderColor: `${AppColors.purple}26`,
652
+ }}>
653
+ <SettingsIcon color={AppColors.purple} size={11}/>
654
+ <Text style={{
655
+ fontFamily: AppFonts.interBold,
656
+ fontSize: 11,
657
+ lineHeight: 14,
658
+ color: AppColors.purple,
659
+ }}>
660
+ {t('settings.configure')}
661
+ </Text>
662
+ <ForwardChevronIcon color={AppColors.purple} size={9}/>
663
+ </TouchableScale>
664
+ </View>)}
665
+ </View>);
392
666
  })}
393
- </View>) : (
394
- /* UI Preferences Section */
395
- <View style={{
667
+ </View>
668
+
669
+ {/* Section 3.5: RAM-Based Auto Limits */}
670
+ <View style={{
396
671
  backgroundColor: AppColors.primaryLight,
397
- borderRadius: 12,
672
+ borderRadius: 14,
398
673
  borderWidth: 1,
399
674
  borderColor: AppColors.grayBorderSecondary,
400
675
  overflow: 'hidden',
401
- }}>
402
- <View style={{
403
- flexDirection: 'row',
404
- alignItems: 'center',
405
- paddingVertical: 12,
406
- paddingHorizontal: 14,
676
+ padding: 14,
407
677
  gap: 12,
408
678
  }}>
409
- {/* Icon + Label */}
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 */}
410
690
  <View style={{
411
- flex: 1,
412
691
  flexDirection: 'row',
413
692
  alignItems: 'center',
414
- gap: 8,
693
+ justifyContent: 'space-between',
415
694
  }}>
416
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={{
417
744
  width: 20,
418
745
  height: 20,
419
- borderRadius: 6,
420
- backgroundColor: AppColors.purpleShade50,
421
- borderWidth: 1,
422
- borderColor: `${AppColors.purple}33`,
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,
423
767
  alignItems: 'center',
424
768
  justifyContent: 'center',
425
769
  }}>
426
- {isDark ? (<SunIcon color={AppColors.purple} size={11}/>) : (<MoonIcon color={AppColors.purple} size={11}/>)}
770
+ <SignalIcon color={isAutoRamLimitEnabled ? AppColors.purple : AppColors.grayTextWeak} size={15}/>
427
771
  </View>
428
772
  <View style={{ flex: 1 }}>
429
773
  <Text style={{
430
774
  fontFamily: AppFonts.interBold,
431
- fontSize: 13,
432
- color: AppColors.primaryBlack,
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,
433
789
  }}>
434
- Dark Theme
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
435
814
  </Text>
436
815
  </View>
437
816
  </View>
438
817
 
439
- {/* Toggle Switch */}
440
- <TouchableScale onPress={() => {
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 }}>
866
+ {/* Section 1: Appearance */}
867
+ <View style={{
868
+ backgroundColor: AppColors.primaryLight,
869
+ borderRadius: 14,
870
+ borderWidth: 1,
871
+ borderColor: AppColors.grayBorderSecondary,
872
+ overflow: 'hidden',
873
+ padding: 14,
874
+ gap: 12,
875
+ }}>
876
+ <Text style={{
877
+ fontFamily: AppFonts.interBold,
878
+ fontSize: 11,
879
+ lineHeight: 14,
880
+ color: AppColors.grayTextWeak,
881
+ letterSpacing: 0.8,
882
+ }}>
883
+ {t('settings.appearanceTheme')}
884
+ </Text>
885
+
886
+ <View style={{
887
+ flexDirection: 'row',
888
+ alignItems: 'center',
889
+ justifyContent: 'space-between',
890
+ }}>
891
+ <View style={{
892
+ flexDirection: 'row',
893
+ alignItems: 'center',
894
+ gap: 10,
895
+ flex: 1,
896
+ }}>
897
+ <View style={{
898
+ width: 32,
899
+ height: 32,
900
+ borderRadius: 8,
901
+ backgroundColor: AppColors.purpleShade50,
902
+ alignItems: 'center',
903
+ justifyContent: 'center',
904
+ }}>
905
+ {isDark ? (<SunIcon color={AppColors.purple} size={15}/>) : (<MoonIcon color={AppColors.purple} size={15}/>)}
906
+ </View>
907
+ <View style={{ flex: 1 }}>
908
+ <Text style={{
909
+ fontFamily: AppFonts.interBold,
910
+ fontSize: 13.5,
911
+ lineHeight: 18,
912
+ color: AppColors.primaryBlack,
913
+ }}>
914
+ {t('settings.general.darkMode')}
915
+ </Text>
916
+ <Text style={{
917
+ fontFamily: AppFonts.interRegular,
918
+ fontSize: 11,
919
+ lineHeight: 15,
920
+ color: AppColors.grayText,
921
+ marginTop: 1,
922
+ }}>
923
+ {t('settings.general.darkModeDescription')}
924
+ </Text>
925
+ </View>
926
+ </View>
927
+
928
+ <TouchableScale accessible={true} accessibilityRole="switch" accessibilityLabel="Toggle Dark Theme" accessibilityState={{ checked: isDark }} onPress={() => {
441
929
  const newTheme = !isDark;
442
930
  setIsDark(newTheme);
443
931
  toggleGlobalTheme(newTheme);
444
932
  }} style={{
445
- width: 38,
446
- height: 22,
447
- borderRadius: 11,
933
+ width: 42,
934
+ height: 24,
935
+ borderRadius: 12,
448
936
  backgroundColor: isDark
449
937
  ? AppColors.purple
450
938
  : AppColors.grayBorderSecondary,
@@ -452,226 +940,243 @@ const SettingsPanel = () => {
452
940
  justifyContent: 'center',
453
941
  alignItems: isDark ? 'flex-end' : 'flex-start',
454
942
  }}>
455
- <View style={{
456
- width: 18,
457
- height: 18,
458
- borderRadius: 9,
943
+ <View style={{
944
+ width: 20,
945
+ height: 20,
946
+ borderRadius: 10,
459
947
  backgroundColor: AppColors.white,
460
948
  shadowColor: AppColors.black,
461
- shadowOpacity: 0.15,
462
- shadowRadius: 1.5,
949
+ shadowOpacity: 0.18,
950
+ shadowRadius: 2,
463
951
  shadowOffset: { width: 0, height: 1 },
464
952
  }}/>
465
- </TouchableScale>
953
+ </TouchableScale>
954
+ </View>
466
955
  </View>
467
956
 
468
- {/* Divider */}
469
- <View style={{
470
- height: 1,
471
- backgroundColor: AppColors.dividerColor,
472
- }}/>
473
-
474
- {/* Modal Height */}
957
+ {/* Section 2: Window & Layout */}
475
958
  <View style={{
476
- paddingVertical: 12,
477
- paddingHorizontal: 14,
959
+ backgroundColor: AppColors.primaryLight,
960
+ borderRadius: 14,
961
+ borderWidth: 1,
962
+ borderColor: AppColors.grayBorderSecondary,
963
+ overflow: 'hidden',
964
+ padding: 14,
965
+ gap: 14,
478
966
  }}>
479
- <View style={{
967
+ <Text style={{
968
+ fontFamily: AppFonts.interBold,
969
+ fontSize: 11,
970
+ lineHeight: 14,
971
+ color: AppColors.grayTextWeak,
972
+ letterSpacing: 0.8,
973
+ }}>
974
+ {t('settings.windowLayout')}
975
+ </Text>
976
+
977
+ {/* Modal Height */}
978
+ <View style={{ gap: 8 }}>
979
+ <View style={{
480
980
  flexDirection: 'row',
481
981
  alignItems: 'center',
482
- gap: 8,
982
+ gap: 10,
483
983
  }}>
484
- <View style={{
485
- width: 20,
486
- height: 20,
487
- borderRadius: 6,
984
+ <View style={{
985
+ width: 32,
986
+ height: 32,
987
+ borderRadius: 8,
488
988
  backgroundColor: AppColors.purpleShade50,
489
- borderWidth: 1,
490
- borderColor: `${AppColors.purple}33`,
491
989
  alignItems: 'center',
492
990
  justifyContent: 'center',
493
991
  }}>
494
- <ScreenIcon color={AppColors.purple} size={11}/>
495
- </View>
496
- <View style={{ flex: 1 }}>
497
- <Text style={{
992
+ <ScreenIcon color={AppColors.purple} size={15}/>
993
+ </View>
994
+ <View style={{ flex: 1 }}>
995
+ <Text style={{
498
996
  fontFamily: AppFonts.interBold,
499
- fontSize: 13,
997
+ fontSize: 13.5,
998
+ lineHeight: 18,
500
999
  color: AppColors.primaryBlack,
501
1000
  }}>
502
- Modal Height
503
- </Text>
504
- <Text style={{
1001
+ {t('settings.general.modalHeight')}
1002
+ </Text>
1003
+ <Text style={{
505
1004
  fontFamily: AppFonts.interRegular,
506
1005
  fontSize: 11,
1006
+ lineHeight: 15,
507
1007
  color: AppColors.grayText,
508
1008
  marginTop: 1,
509
1009
  }}>
510
- Height of the inspector panel relative to the screen
511
- </Text>
1010
+ {t('settings.general.modalHeightDescription')}
1011
+ </Text>
1012
+ </View>
512
1013
  </View>
513
- </View>
514
1014
 
515
- {/* Segmented picker */}
516
- <View style={{
1015
+ <View style={{
517
1016
  flexDirection: 'row',
518
1017
  backgroundColor: AppColors.grayBackground,
519
- borderRadius: 8,
520
- padding: 2.5,
521
- marginTop: 10,
1018
+ borderRadius: 10,
1019
+ padding: 3,
522
1020
  borderWidth: 1,
523
1021
  borderColor: AppColors.dividerColor,
524
1022
  }}>
525
- {[50, 70, 90, 100].map(opt => {
1023
+ {[50, 70, 90, 100].map(opt => {
526
1024
  const isActive = modalHeightPercent === opt;
527
- return (<TouchableScale key={opt} onPress={() => setModalHeightPercent(opt)} style={{
1025
+ return (<TouchableScale key={opt} accessible={true} accessibilityRole="button" accessibilityLabel={`Set window height to ${opt} percent`} accessibilityState={{ selected: isActive }} onPress={() => setModalHeightPercent(opt)} style={{
528
1026
  flex: 1,
529
- paddingVertical: 6,
1027
+ paddingVertical: 7,
530
1028
  alignItems: 'center',
531
- borderRadius: 6,
1029
+ borderRadius: 8,
532
1030
  backgroundColor: isActive
533
1031
  ? AppColors.purple
534
1032
  : 'transparent',
535
1033
  }}>
536
- <Text style={{
1034
+ <Text style={{
537
1035
  fontFamily: AppFonts.interBold,
538
- fontSize: 11,
539
- color: isActive ? AppColors.white : AppColors.grayText,
1036
+ fontSize: 11.5,
1037
+ lineHeight: 15,
1038
+ color: isActive
1039
+ ? AppColors.white
1040
+ : AppColors.grayText,
540
1041
  }}>
541
- {opt}%
542
- </Text>
543
- </TouchableScale>);
1042
+ {opt}%
1043
+ </Text>
1044
+ </TouchableScale>);
544
1045
  })}
1046
+ </View>
545
1047
  </View>
546
- </View>
547
1048
 
548
- {/* Divider */}
549
- <View style={{
550
- height: 1,
551
- backgroundColor: AppColors.dividerColor,
552
- }}/>
1049
+ <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
553
1050
 
554
- {/* Modal Animation */}
555
- <View style={{
556
- paddingVertical: 12,
557
- paddingHorizontal: 14,
558
- }}>
559
- <View style={{
1051
+ {/* Modal Animation */}
1052
+ <View style={{ gap: 8 }}>
1053
+ <View style={{
560
1054
  flexDirection: 'row',
561
1055
  alignItems: 'center',
562
- gap: 8,
1056
+ gap: 10,
563
1057
  }}>
564
- <View style={{
565
- width: 20,
566
- height: 20,
567
- borderRadius: 6,
1058
+ <View style={{
1059
+ width: 32,
1060
+ height: 32,
1061
+ borderRadius: 8,
568
1062
  backgroundColor: AppColors.purpleShade50,
569
- borderWidth: 1,
570
- borderColor: `${AppColors.purple}33`,
571
1063
  alignItems: 'center',
572
1064
  justifyContent: 'center',
573
1065
  }}>
574
- <MotionIcon color={AppColors.purple} size={11}/>
575
- </View>
576
- <View style={{ flex: 1 }}>
577
- <Text style={{
1066
+ <MotionIcon color={AppColors.purple} size={15}/>
1067
+ </View>
1068
+ <View style={{ flex: 1 }}>
1069
+ <Text style={{
578
1070
  fontFamily: AppFonts.interBold,
579
- fontSize: 13,
1071
+ fontSize: 13.5,
1072
+ lineHeight: 18,
580
1073
  color: AppColors.primaryBlack,
581
1074
  }}>
582
- Modal Animation
583
- </Text>
584
- <Text style={{
1075
+ {t('settings.general.modalAnimation')}
1076
+ </Text>
1077
+ <Text style={{
585
1078
  fontFamily: AppFonts.interRegular,
586
1079
  fontSize: 11,
1080
+ lineHeight: 15,
587
1081
  color: AppColors.grayText,
588
1082
  marginTop: 1,
589
1083
  }}>
590
- How the inspector panel enters and exits the screen
591
- </Text>
1084
+ {t('settings.general.modalAnimationDescription')}
1085
+ </Text>
1086
+ </View>
592
1087
  </View>
593
- </View>
594
1088
 
595
- {/* Segmented picker */}
596
- <View style={{
1089
+ <View style={{
597
1090
  flexDirection: 'row',
598
1091
  backgroundColor: AppColors.grayBackground,
599
- borderRadius: 8,
600
- padding: 2.5,
601
- marginTop: 10,
1092
+ borderRadius: 10,
1093
+ padding: 3,
602
1094
  borderWidth: 1,
603
1095
  borderColor: AppColors.dividerColor,
604
1096
  }}>
605
- {([
1097
+ {[
606
1098
  { key: 'slide', label: 'Slide Up' },
607
1099
  { key: 'fade', label: 'Fade' },
608
1100
  { key: 'none', label: 'None' },
609
- ]).map(opt => {
1101
+ ].map(opt => {
610
1102
  const isActive = modalAnimationType === opt.key;
611
- return (<TouchableScale key={opt.key} onPress={() => setModalAnimationType(opt.key)} style={{
1103
+ return (<TouchableScale key={opt.key} accessible={true} accessibilityRole="button" accessibilityLabel={`Set transition animation to ${opt.label}`} accessibilityState={{ selected: isActive }} onPress={() => setModalAnimationType(opt.key)} style={{
612
1104
  flex: 1,
613
- paddingVertical: 6,
1105
+ paddingVertical: 7,
614
1106
  alignItems: 'center',
615
- borderRadius: 6,
1107
+ borderRadius: 8,
616
1108
  backgroundColor: isActive
617
1109
  ? AppColors.purple
618
1110
  : 'transparent',
619
1111
  }}>
620
- <Text style={{
1112
+ <Text style={{
621
1113
  fontFamily: AppFonts.interBold,
622
- fontSize: 11,
623
- color: isActive ? AppColors.white : AppColors.grayText,
1114
+ fontSize: 11.5,
1115
+ lineHeight: 15,
1116
+ color: isActive
1117
+ ? AppColors.white
1118
+ : AppColors.grayText,
624
1119
  }}>
625
- {opt.label}
626
- </Text>
627
- </TouchableScale>);
1120
+ {opt.label}
1121
+ </Text>
1122
+ </TouchableScale>);
628
1123
  })}
1124
+ </View>
629
1125
  </View>
630
1126
  </View>
631
1127
 
632
- {/* Divider */}
633
- <View style={{
634
- height: 1,
635
- backgroundColor: AppColors.dividerColor,
636
- }}/>
637
-
638
- {/* #6 — Default Tab */}
1128
+ {/* Section 3: Default Startup Tab */}
639
1129
  <View style={{
640
- paddingVertical: 12,
641
- paddingHorizontal: 14,
1130
+ backgroundColor: AppColors.primaryLight,
1131
+ borderRadius: 14,
1132
+ borderWidth: 1,
1133
+ borderColor: AppColors.grayBorderSecondary,
1134
+ overflow: 'hidden',
1135
+ padding: 14,
1136
+ gap: 12,
1137
+ }}>
1138
+ <Text style={{
1139
+ fontFamily: AppFonts.interBold,
1140
+ fontSize: 11,
1141
+ lineHeight: 14,
1142
+ color: AppColors.grayTextWeak,
1143
+ letterSpacing: 0.8,
642
1144
  }}>
1145
+ {t('settings.startupDefault')}
1146
+ </Text>
1147
+
643
1148
  <View style={{
644
1149
  flexDirection: 'row',
645
1150
  alignItems: 'center',
646
- gap: 8,
1151
+ gap: 10,
647
1152
  }}>
648
1153
  <View style={{
649
- width: 20,
650
- height: 20,
651
- borderRadius: 6,
1154
+ width: 32,
1155
+ height: 32,
1156
+ borderRadius: 8,
652
1157
  backgroundColor: AppColors.purpleShade50,
653
- borderWidth: 1,
654
- borderColor: `${AppColors.purple}33`,
655
1158
  alignItems: 'center',
656
1159
  justifyContent: 'center',
657
1160
  }}>
658
- <LayersIcon color={AppColors.purple} size={11}/>
1161
+ <LayersIcon color={AppColors.purple} size={15}/>
659
1162
  </View>
660
1163
  <View style={{ flex: 1 }}>
661
1164
  <Text style={{
662
1165
  fontFamily: AppFonts.interBold,
663
- fontSize: 13,
1166
+ fontSize: 13.5,
1167
+ lineHeight: 18,
664
1168
  color: AppColors.primaryBlack,
665
1169
  }}>
666
- Default Tab
1170
+ {t('settings.general.defaultOpeningTab')}
667
1171
  </Text>
668
1172
  <Text style={{
669
1173
  fontFamily: AppFonts.interRegular,
670
1174
  fontSize: 11,
1175
+ lineHeight: 15,
671
1176
  color: AppColors.grayText,
672
1177
  marginTop: 1,
673
1178
  }}>
674
- Tab shown when the inspector opens
1179
+ {t('settings.general.defaultOpeningTabDesc')}
675
1180
  </Text>
676
1181
  </View>
677
1182
  </View>
@@ -681,13 +1186,15 @@ const SettingsPanel = () => {
681
1186
  flexDirection: 'row',
682
1187
  flexWrap: 'wrap',
683
1188
  gap: 8,
684
- marginTop: 12,
1189
+ marginTop: 4,
685
1190
  }}>
686
- {settingsTabs
687
- .filter(tab => tab.key === 'apis' || tabVisibility?.[tab.key])
1191
+ {allModules
1192
+ .filter(tab => tab.key === 'apis' ||
1193
+ stagedTabVisibility?.[tab.key] ||
1194
+ tabVisibility?.[tab.key])
688
1195
  .map(tab => {
689
1196
  const isActive = defaultTab === tab.key;
690
- return (<TouchableScale key={tab.key} onPress={() => setDefaultTab(tab.key)} style={{
1197
+ return (<TouchableScale key={tab.key} accessible={true} accessibilityRole="button" accessibilityLabel={`Set ${tab.label} as default tab`} accessibilityState={{ selected: isActive }} onPress={() => setDefaultTab(tab.key)} style={{
691
1198
  flexDirection: 'row',
692
1199
  alignItems: 'center',
693
1200
  gap: 8,
@@ -695,186 +1202,190 @@ const SettingsPanel = () => {
695
1202
  paddingHorizontal: 12,
696
1203
  borderRadius: 10,
697
1204
  borderWidth: 1.5,
698
- borderColor: isActive ? AppColors.purple : AppColors.grayBorderSecondary,
699
- backgroundColor: isActive ? `${AppColors.purple}0F` : AppColors.primaryLight,
1205
+ borderColor: isActive
1206
+ ? AppColors.purple
1207
+ : AppColors.grayBorderSecondary,
1208
+ backgroundColor: isActive
1209
+ ? `${AppColors.purple}0F`
1210
+ : AppColors.primaryLight,
700
1211
  minWidth: '47%',
701
1212
  flex: 1,
702
1213
  }}>
703
- <View style={{
704
- width: 22,
705
- height: 22,
706
- borderRadius: 6,
707
- backgroundColor: isActive ? AppColors.purple : AppColors.purpleShade50,
708
- alignItems: 'center',
709
- justifyContent: 'center',
710
- }}>
711
- {tab.icon === 'apis' && (<SignalIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
712
- {tab.icon === 'logs' && (<TerminalIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
713
- {tab.icon === 'analytics' && (<AnalyticsIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
714
- {tab.icon === 'redux' && (<ReduxIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
715
- {tab.icon === 'bundle' && (<PackageIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
716
- {tab.icon === 'performance' && (<PerformanceIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
717
- {tab.icon === 'crash' && (<CrashIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
718
- </View>
719
1214
  <Text style={{
720
1215
  fontFamily: AppFonts.interBold,
721
- fontSize: 13,
722
- color: isActive ? AppColors.purple : AppColors.primaryBlack,
1216
+ fontSize: 12.5,
1217
+ lineHeight: 16,
1218
+ color: isActive
1219
+ ? AppColors.purple
1220
+ : AppColors.primaryBlack,
723
1221
  flex: 1,
724
1222
  }}>
725
- {tab.label}
1223
+ {tab.label}
726
1224
  </Text>
727
1225
  {isActive && (<View style={{
728
- width: 14,
729
- height: 14,
730
- borderRadius: 7,
1226
+ width: 16,
1227
+ height: 16,
1228
+ borderRadius: 8,
731
1229
  backgroundColor: AppColors.purple,
732
1230
  alignItems: 'center',
733
1231
  justifyContent: 'center',
734
1232
  }}>
735
- <CheckIcon size={8} color={AppColors.white}/>
1233
+ <CheckIcon size={9} color={AppColors.white}/>
736
1234
  </View>)}
737
1235
  </TouchableScale>);
738
1236
  })}
739
1237
  </View>
740
1238
  </View>
741
1239
 
742
- {/* Divider */}
1240
+ {/* Section 4: Log Deduplication & Levels */}
743
1241
  <View style={{
744
- height: 1,
745
- backgroundColor: AppColors.dividerColor,
746
- }}/>
1242
+ backgroundColor: AppColors.primaryLight,
1243
+ borderRadius: 14,
1244
+ borderWidth: 1,
1245
+ borderColor: AppColors.grayBorderSecondary,
1246
+ overflow: 'hidden',
1247
+ padding: 14,
1248
+ gap: 14,
1249
+ }}>
1250
+ <Text style={{
1251
+ fontFamily: AppFonts.interBold,
1252
+ fontSize: 11,
1253
+ lineHeight: 14,
1254
+ color: AppColors.grayTextWeak,
1255
+ letterSpacing: 0.8,
1256
+ }}>
1257
+ {t('settings.logFiltersDeduplication')}
1258
+ </Text>
747
1259
 
748
- {/* #9 — Show Duplicate Logs */}
749
- <View style={{
1260
+ {/* Show Duplicate Logs */}
1261
+ <View style={{
750
1262
  flexDirection: 'row',
751
1263
  alignItems: 'center',
752
- paddingVertical: 12,
753
- paddingHorizontal: 14,
754
- gap: 12,
1264
+ justifyContent: 'space-between',
755
1265
  }}>
756
- <View style={{
757
- flex: 1,
1266
+ <View style={{
758
1267
  flexDirection: 'row',
759
1268
  alignItems: 'center',
760
- gap: 8,
1269
+ gap: 10,
1270
+ flex: 1,
761
1271
  }}>
762
- <View style={{
763
- width: 20,
764
- height: 20,
765
- borderRadius: 6,
1272
+ <View style={{
1273
+ width: 32,
1274
+ height: 32,
1275
+ borderRadius: 8,
766
1276
  backgroundColor: AppColors.purpleShade50,
767
- borderWidth: 1,
768
- borderColor: `${AppColors.purple}33`,
769
1277
  alignItems: 'center',
770
1278
  justifyContent: 'center',
771
1279
  }}>
772
- <EyeIcon color={AppColors.purple} size={11}/>
773
- </View>
774
- <View style={{ flex: 1 }}>
775
- <Text style={{
1280
+ <EyeIcon color={AppColors.purple} size={15}/>
1281
+ </View>
1282
+ <View style={{ flex: 1 }}>
1283
+ <Text style={{
776
1284
  fontFamily: AppFonts.interBold,
777
- fontSize: 13,
1285
+ fontSize: 13.5,
1286
+ lineHeight: 18,
778
1287
  color: AppColors.primaryBlack,
779
1288
  }}>
780
- Show Duplicate Logs
781
- </Text>
782
- <Text style={{
1289
+ {t('settings.general.duplicateLogs')}
1290
+ </Text>
1291
+ <Text style={{
783
1292
  fontFamily: AppFonts.interRegular,
784
1293
  fontSize: 11,
1294
+ lineHeight: 15,
785
1295
  color: AppColors.grayText,
786
1296
  marginTop: 1,
787
1297
  }}>
788
- Off: repeated identical entries collapse into one row
789
- with a ×N count
790
- </Text>
1298
+ {t('settings.general.duplicateLogsDescription')}
1299
+ </Text>
1300
+ </View>
791
1301
  </View>
792
- </View>
793
1302
 
794
- {/* Toggle Switch */}
795
- <TouchableScale onPress={() => setShowDuplicateLogs(prev => !prev)} style={{
796
- width: 38,
797
- height: 22,
798
- borderRadius: 11,
1303
+ <TouchableScale accessible={true} accessibilityRole="switch" accessibilityLabel="Toggle show duplicate logs" accessibilityState={{ checked: showDuplicateLogs }} onPress={() => setShowDuplicateLogs(prev => !prev)} style={{
1304
+ width: 42,
1305
+ height: 24,
1306
+ borderRadius: 12,
799
1307
  backgroundColor: showDuplicateLogs
800
1308
  ? AppColors.purple
801
1309
  : AppColors.grayBorderSecondary,
802
1310
  padding: 2,
803
1311
  justifyContent: 'center',
804
- alignItems: showDuplicateLogs ? 'flex-end' : 'flex-start',
1312
+ alignItems: showDuplicateLogs
1313
+ ? 'flex-end'
1314
+ : 'flex-start',
805
1315
  }}>
806
- <View style={{
807
- width: 18,
808
- height: 18,
809
- borderRadius: 9,
1316
+ <View style={{
1317
+ width: 20,
1318
+ height: 20,
1319
+ borderRadius: 10,
810
1320
  backgroundColor: AppColors.white,
811
1321
  shadowColor: AppColors.black,
812
- shadowOpacity: 0.15,
813
- shadowRadius: 1.5,
1322
+ shadowOpacity: 0.18,
1323
+ shadowRadius: 2,
814
1324
  shadowOffset: { width: 0, height: 1 },
815
1325
  }}/>
816
- </TouchableScale>
817
- </View>
1326
+ </TouchableScale>
1327
+ </View>
818
1328
 
819
- {/* Divider */}
820
- <View style={{
821
- height: 1,
822
- backgroundColor: AppColors.dividerColor,
823
- }}/>
1329
+ <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
824
1330
 
825
- {/* Logs Console Levels */}
826
- <View style={{
827
- paddingVertical: 12,
828
- paddingHorizontal: 14,
829
- }}>
830
- <View style={{
1331
+ {/* Console Log Levels */}
1332
+ <View style={{ gap: 8 }}>
1333
+ <View style={{
831
1334
  flexDirection: 'row',
832
1335
  alignItems: 'center',
833
- gap: 8,
1336
+ gap: 10,
834
1337
  }}>
835
- <View style={{
836
- width: 20,
837
- height: 20,
838
- borderRadius: 6,
1338
+ <View style={{
1339
+ width: 32,
1340
+ height: 32,
1341
+ borderRadius: 8,
839
1342
  backgroundColor: AppColors.purpleShade50,
840
- borderWidth: 1,
841
- borderColor: `${AppColors.purple}33`,
842
1343
  alignItems: 'center',
843
1344
  justifyContent: 'center',
844
1345
  }}>
845
- <TerminalIcon color={AppColors.purple} size={11}/>
846
- </View>
847
- <View style={{ flex: 1 }}>
848
- <Text style={{
1346
+ <TerminalIcon color={AppColors.purple} size={15}/>
1347
+ </View>
1348
+ <View style={{ flex: 1 }}>
1349
+ <Text style={{
849
1350
  fontFamily: AppFonts.interBold,
850
- fontSize: 13,
1351
+ fontSize: 13.5,
1352
+ lineHeight: 18,
851
1353
  color: AppColors.primaryBlack,
852
1354
  }}>
853
- Logs Console Levels
854
- </Text>
855
- <Text style={{
1355
+ {t('settings.general.activeConsoleLogLevels')}
1356
+ </Text>
1357
+ <Text style={{
856
1358
  fontFamily: AppFonts.interRegular,
857
1359
  fontSize: 11,
1360
+ lineHeight: 15,
858
1361
  color: AppColors.grayText,
859
1362
  marginTop: 1,
860
1363
  }}>
861
- Toggle which log levels are visible in the Logs tab
862
- </Text>
1364
+ {t('settings.general.activeConsoleLogLevelsDesc')}
1365
+ </Text>
1366
+ </View>
863
1367
  </View>
864
- </View>
865
1368
 
866
- <View style={{
867
- flexDirection: 'row',
868
- gap: 8,
869
- marginTop: 10,
870
- }}>
871
- {([
872
- { key: 'info', label: 'Info', color: AppColors.blue500 },
873
- { key: 'warn', label: 'Warning', color: AppColors.amber500 },
874
- { key: 'error', label: 'Error', color: AppColors.errorColor },
875
- ]).map(level => {
1369
+ <View style={{ flexDirection: 'row', gap: 8, marginTop: 4 }}>
1370
+ {[
1371
+ {
1372
+ key: 'info',
1373
+ label: 'Info',
1374
+ color: AppColors.blue500,
1375
+ },
1376
+ {
1377
+ key: 'warn',
1378
+ label: 'Warning',
1379
+ color: AppColors.amber500,
1380
+ },
1381
+ {
1382
+ key: 'error',
1383
+ label: 'Error',
1384
+ color: AppColors.errorColor,
1385
+ },
1386
+ ].map(level => {
876
1387
  const isActive = showConsoleLevels[level.key];
877
- return (<TouchableScale key={level.key} onPress={() => setShowConsoleLevels(prev => ({
1388
+ return (<TouchableScale key={level.key} accessible={true} accessibilityRole="checkbox" accessibilityLabel={`Toggle ${level.label} logs`} accessibilityState={{ checked: isActive }} onPress={() => setShowConsoleLevels(prev => ({
878
1389
  ...prev,
879
1390
  [level.key]: !prev[level.key],
880
1391
  }))} style={{
@@ -884,112 +1395,113 @@ const SettingsPanel = () => {
884
1395
  justifyContent: 'center',
885
1396
  gap: 6,
886
1397
  paddingVertical: 8,
887
- borderRadius: 8,
1398
+ borderRadius: 9,
888
1399
  borderWidth: 1.5,
889
- borderColor: isActive ? level.color : AppColors.grayBorderSecondary,
890
- backgroundColor: isActive ? `${level.color}0D` : AppColors.primaryLight,
1400
+ borderColor: isActive
1401
+ ? level.color
1402
+ : AppColors.grayBorderSecondary,
1403
+ backgroundColor: isActive
1404
+ ? `${level.color}0D`
1405
+ : AppColors.primaryLight,
891
1406
  }}>
892
- <View style={{
1407
+ <View style={{
893
1408
  width: 8,
894
1409
  height: 8,
895
1410
  borderRadius: 4,
896
- backgroundColor: isActive ? level.color : AppColors.grayBorderSecondary,
1411
+ backgroundColor: isActive
1412
+ ? level.color
1413
+ : AppColors.grayBorderSecondary,
897
1414
  }}/>
898
- <Text style={{
1415
+ <Text style={{
899
1416
  fontFamily: AppFonts.interBold,
900
- fontSize: 11,
901
- color: isActive ? level.color : AppColors.grayText,
1417
+ fontSize: 11.5,
1418
+ lineHeight: 15,
1419
+ color: isActive
1420
+ ? level.color
1421
+ : AppColors.grayText,
902
1422
  }}>
903
- {level.label}
904
- </Text>
905
- </TouchableScale>);
1423
+ {level.label}
1424
+ </Text>
1425
+ </TouchableScale>);
906
1426
  })}
1427
+ </View>
907
1428
  </View>
908
1429
  </View>
909
1430
 
910
- {/* Divider */}
911
- <View style={{
912
- height: 1,
913
- backgroundColor: AppColors.dividerColor,
914
- }}/>
915
-
916
- {/* Reset Settings */}
1431
+ {/* Section 5: Reset Settings */}
917
1432
  <View style={{
1433
+ backgroundColor: AppColors.primaryLight,
1434
+ borderRadius: 14,
1435
+ borderWidth: 1,
1436
+ borderColor: `${AppColors.errorColor}33`,
1437
+ overflow: 'hidden',
1438
+ padding: 14,
918
1439
  flexDirection: 'row',
919
1440
  alignItems: 'center',
920
- paddingVertical: 12,
921
- paddingHorizontal: 14,
922
1441
  gap: 12,
923
1442
  }}>
924
1443
  <View style={{
925
- flex: 1,
926
- flexDirection: 'row',
927
- alignItems: 'center',
928
- gap: 8,
929
- }}>
930
- <View style={{
931
- width: 20,
932
- height: 20,
933
- borderRadius: 6,
1444
+ width: 34,
1445
+ height: 34,
1446
+ borderRadius: 9,
934
1447
  backgroundColor: `${AppColors.errorColor}14`,
935
- borderWidth: 1,
936
- borderColor: `${AppColors.errorColor}33`,
937
1448
  alignItems: 'center',
938
1449
  justifyContent: 'center',
939
1450
  }}>
940
- <TrashIcon color={AppColors.errorColor} size={11}/>
941
- </View>
942
- <View style={{ flex: 1 }}>
943
- <Text style={{
1451
+ <TrashIcon color={AppColors.errorColor} size={15}/>
1452
+ </View>
1453
+ <View style={{ flex: 1 }}>
1454
+ <Text style={{
944
1455
  fontFamily: AppFonts.interBold,
945
- fontSize: 13,
1456
+ fontSize: 13.5,
1457
+ lineHeight: 18,
946
1458
  color: AppColors.primaryBlack,
947
1459
  }}>
948
- Reset Settings
949
- </Text>
950
- <Text style={{
1460
+ {t('settings.resetSettings')}
1461
+ </Text>
1462
+ <Text style={{
951
1463
  fontFamily: AppFonts.interRegular,
952
1464
  fontSize: 11,
1465
+ lineHeight: 15,
953
1466
  color: AppColors.grayText,
954
1467
  marginTop: 1,
955
1468
  }}>
956
- Wipe custom configurations and load package defaults
957
- </Text>
958
- </View>
1469
+ {t('settings.resetSettingsDesc')}
1470
+ </Text>
959
1471
  </View>
960
1472
 
961
- <TouchableScale onPress={resetToDefaults} style={{
962
- paddingHorizontal: 10,
963
- paddingVertical: 5,
964
- borderRadius: 7,
1473
+ <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel="Reset all settings to default" onPress={resetToDefaults} style={{
1474
+ paddingHorizontal: 12,
1475
+ paddingVertical: 7,
1476
+ borderRadius: 8,
965
1477
  backgroundColor: `${AppColors.errorColor}14`,
966
1478
  borderWidth: 1,
967
- borderColor: `${AppColors.errorColor}33`,
1479
+ borderColor: `${AppColors.errorColor}38`,
968
1480
  }}>
969
1481
  <Text style={{
970
1482
  fontFamily: AppFonts.interBold,
971
- fontSize: 11,
1483
+ fontSize: 11.5,
1484
+ lineHeight: 15,
972
1485
  color: AppColors.errorColor,
973
1486
  }}>
974
- Reset
1487
+ {t('settings.reset')}
975
1488
  </Text>
976
1489
  </TouchableScale>
977
1490
  </View>
978
1491
  </View>)}
979
1492
 
980
-
981
- {/* Storage Status */}
1493
+ {/* Storage & Engine Status Card */}
982
1494
  <View style={{
983
- backgroundColor: isPersistentStorageAvailable()
984
- ? `${AppColors.liveGreen}14`
985
- : `${AppColors.amber600}14`,
1495
+ backgroundColor: isPersistent
1496
+ ? `${AppColors.liveGreen}12`
1497
+ : `${AppColors.amber600}12`,
986
1498
  borderRadius: 12,
987
1499
  borderWidth: 1,
988
- borderColor: isPersistentStorageAvailable()
1500
+ borderColor: isPersistent
989
1501
  ? `${AppColors.liveGreen}33`
990
1502
  : `${AppColors.amber600}33`,
991
1503
  padding: 12,
992
- marginTop: 16,
1504
+ marginBottom: 10,
993
1505
  flexDirection: 'row',
994
1506
  alignItems: 'center',
995
1507
  gap: 10,
@@ -998,32 +1510,106 @@ const SettingsPanel = () => {
998
1510
  width: 8,
999
1511
  height: 8,
1000
1512
  borderRadius: 4,
1001
- backgroundColor: isPersistentStorageAvailable() ? AppColors.green500 : AppColors.amber600,
1513
+ backgroundColor: isPersistent
1514
+ ? AppColors.green500
1515
+ : AppColors.amber600,
1002
1516
  }}/>
1003
1517
  <View style={{ flex: 1 }}>
1004
1518
  <Text style={{
1005
1519
  fontFamily: AppFonts.interBold,
1006
1520
  fontSize: 12,
1007
- color: isPersistentStorageAvailable() ? AppColors.green700 : AppColors.amber800,
1521
+ lineHeight: 16,
1522
+ color: isPersistent
1523
+ ? AppColors.green700
1524
+ : AppColors.amber800,
1008
1525
  }}>
1009
- {isPersistentStorageAvailable()
1010
- ? `Storage: Persistent (${storage ? 'Custom' : 'iOS Settings'})`
1011
- : 'Storage: Temporary (In-Memory)'}
1526
+ {isPersistent
1527
+ ? t('settings.general.storageStatusEnabled', {
1528
+ type: storage ? 'Custom Storage' : 'iOS NSUserDefaults',
1529
+ })
1530
+ : t('settings.general.storageStatusDisabled')}
1012
1531
  </Text>
1013
1532
  <Text style={{
1014
1533
  fontFamily: AppFonts.interRegular,
1015
1534
  fontSize: 10.5,
1016
- color: isPersistentStorageAvailable() ? AppColors.green800 : AppColors.amber800,
1535
+ lineHeight: 14,
1536
+ color: isPersistent
1537
+ ? AppColors.green800
1538
+ : AppColors.amber800,
1017
1539
  marginTop: 2,
1018
- opacity: 0.8,
1540
+ opacity: 0.85,
1019
1541
  }}>
1020
- {isPersistentStorageAvailable()
1021
- ? 'Your settings are saved across app restarts.'
1022
- : 'Settings reset when closed. To persist settings, pass a storage object to <NetworkInspector storage={...} />.'}
1542
+ {isPersistent
1543
+ ? t('settings.general.storageStatusEnabledDesc')
1544
+ : t('settings.general.storageStatusDisabledDesc')}
1545
+ </Text>
1546
+ </View>
1547
+ </View>
1548
+ </ScrollView>
1549
+
1550
+ {/* Bottom Sticky Action Bar for Save Changes */}
1551
+ {settingsActiveSubTab === 'module' && (<View style={{
1552
+ paddingHorizontal: 16,
1553
+ paddingTop: 12,
1554
+ paddingBottom: Platform.OS === 'ios' ? 26 : 14,
1555
+ backgroundColor: AppColors.primaryLight,
1556
+ borderTopWidth: 1,
1557
+ borderTopColor: AppColors.dividerColor,
1558
+ flexDirection: 'row',
1559
+ alignItems: 'center',
1560
+ gap: 12,
1561
+ shadowColor: AppColors.black,
1562
+ shadowOpacity: 0.08,
1563
+ shadowRadius: 8,
1564
+ shadowOffset: { width: 0, height: -3 },
1565
+ elevation: 8,
1566
+ }}>
1567
+ <View style={{ flex: 1 }}>
1568
+ <Text style={{
1569
+ fontFamily: AppFonts.interBold,
1570
+ fontSize: 13,
1571
+ lineHeight: 17,
1572
+ color: AppColors.primaryBlack,
1573
+ }}>
1574
+ {stagedActiveCount} of {allModules.length} Modules Active
1575
+ </Text>
1576
+ <Text style={{
1577
+ fontFamily: AppFonts.interRegular,
1578
+ fontSize: 11,
1579
+ lineHeight: 15,
1580
+ color: hasUnsavedChanges
1581
+ ? AppColors.purple
1582
+ : AppColors.grayText,
1583
+ marginTop: 1,
1584
+ }}>
1585
+ {hasUnsavedChanges
1586
+ ? t('settings.unsavedPending')
1587
+ : t('settings.allSynchronized')}
1588
+ </Text>
1589
+ </View>
1590
+
1591
+ <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel="Save Changes" onPress={handleSaveChanges} style={{
1592
+ flexDirection: 'row',
1593
+ alignItems: 'center',
1594
+ gap: 6,
1595
+ backgroundColor: hasUnsavedChanges
1596
+ ? AppColors.purple
1597
+ : `${AppColors.purple}22`,
1598
+ paddingHorizontal: 18,
1599
+ paddingVertical: 10,
1600
+ borderRadius: 10,
1601
+ }}>
1602
+ <CheckIcon size={14} color={hasUnsavedChanges ? AppColors.white : AppColors.purple}/>
1603
+ <Text style={{
1604
+ fontFamily: AppFonts.interBold,
1605
+ fontSize: 13,
1606
+ lineHeight: 17,
1607
+ color: hasUnsavedChanges ? AppColors.white : AppColors.purple,
1608
+ }}>
1609
+ {t('settings.saveChanges')}
1023
1610
  </Text>
1024
- </View>
1025
- </View>
1026
- </ScrollView>
1611
+ </TouchableScale>
1612
+ </View>)}
1027
1613
  </View>);
1028
1614
  }
1029
1615
  const goBackToMain = () => {
@@ -1031,105 +1617,154 @@ const SettingsPanel = () => {
1031
1617
  setSettingsPage('main');
1032
1618
  };
1033
1619
  // Helper: settings row with icon + label + optional description
1034
- const renderSettingRow = (opts) => (<View style={{
1035
- paddingVertical: 12,
1036
- borderBottomWidth: opts.isLast ? 0 : 1,
1037
- borderBottomColor: AppColors.dividerColor,
1038
- }}>
1039
- <TouchableScale disabled={!opts.onPress} onPress={opts.onPress} style={{
1040
- flexDirection: 'row',
1041
- alignItems: 'center',
1042
- gap: 12,
1043
- }}>
1044
- <View style={{
1045
- width: 36,
1046
- height: 36,
1047
- borderRadius: 10,
1048
- backgroundColor: AppColors.purpleShade50,
1049
- borderWidth: 1,
1050
- borderColor: `${AppColors.purple}2E`,
1051
- alignItems: 'center',
1052
- justifyContent: 'center',
1053
- }}>
1054
- {opts.icon}
1055
- </View>
1056
- <View style={{ flex: 1 }}>
1057
- <Text style={{
1058
- fontFamily: AppFonts.interBold,
1059
- fontSize: 14,
1060
- color: AppColors.primaryBlack,
1061
- }}>
1062
- {opts.label}
1063
- </Text>
1064
- {opts.description ? (<Text style={{
1065
- fontFamily: AppFonts.interRegular,
1066
- fontSize: 11,
1067
- color: AppColors.grayText,
1068
- marginTop: 1,
1620
+ const renderSettingRow = (opts) => {
1621
+ return (<View style={{
1622
+ paddingVertical: 12,
1623
+ borderBottomWidth: opts.isLast ? 0 : 1,
1624
+ borderBottomColor: AppColors.dividerColor,
1069
1625
  }}>
1070
- {opts.description}
1071
- </Text>) : null}
1072
- </View>
1073
- {opts.right || null}
1074
- </TouchableScale>
1075
- {opts.picker && (<View style={{
1626
+ <TouchableScale disabled={!opts.onPress} onPress={opts.onPress} style={{
1076
1627
  flexDirection: 'row',
1077
- backgroundColor: AppColors.grayBackground,
1078
- borderRadius: 8,
1079
- padding: 2.5,
1080
- 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,
1081
1636
  borderWidth: 1,
1082
- borderColor: AppColors.dividerColor,
1637
+ borderColor: `${AppColors.purple}2E`,
1638
+ alignItems: 'center',
1639
+ justifyContent: 'center',
1083
1640
  }}>
1084
- {opts.picker.options.map(opt => {
1085
- const isActive = opts.picker.selectedValue === opt;
1086
- return (<TouchableScale key={String(opt)} onPress={() => opts.picker.onSelect(opt)} style={{
1087
- flex: 1,
1088
- paddingVertical: 6,
1089
- alignItems: 'center',
1090
- borderRadius: 6,
1091
- backgroundColor: isActive
1092
- ? AppColors.purple
1093
- : 'transparent',
1094
- }}>
1095
- <Text style={{
1096
- fontFamily: AppFonts.interBold,
1097
- fontSize: 11,
1098
- 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,
1099
1703
  }}>
1100
- {opts.picker.formatLabel
1101
- ? opts.picker.formatLabel(opt)
1102
- : opt}
1103
- </Text>
1104
- </TouchableScale>);
1105
- })}
1106
- </View>)}
1107
- </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
+ };
1108
1742
  let content = null;
1109
1743
  let title = '';
1110
1744
  let icon = null;
1111
1745
  let rightInfo = '';
1112
1746
  if (settingsPage === 'apis') {
1113
- title = 'APIs Settings';
1747
+ title = t('settings.apis.title');
1114
1748
  icon = <SignalIcon color={AppColors.white} size={16}/>;
1115
- rightInfo = `Total: ${logs.length}`;
1116
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
1749
+ rightInfo = t('settings.apis.total', { count: logs.length });
1750
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1117
1751
  <View style={{
1118
1752
  backgroundColor: AppColors.primaryLight,
1119
1753
  padding: 16,
1120
- borderRadius: 12,
1754
+ borderRadius: 14,
1121
1755
  borderWidth: 1,
1122
1756
  borderColor: AppColors.grayBorderSecondary,
1123
- gap: 4,
1124
1757
  }}>
1125
1758
  {renderSettingRow({
1126
1759
  icon: <SignalIcon color={AppColors.purple} size={16}/>,
1127
- label: 'Max Request Logs',
1128
- description: 'How many network requests to keep in memory',
1129
- picker: {
1130
- options: [50, 100, 200, 500],
1131
- selectedValue: maxNetworkLogs,
1132
- onSelect: setMaxNetworkLogs,
1760
+ label: t('settings.apis.maxRequestLogs'),
1761
+ description: t('settings.apis.maxRequestLogsDescription'),
1762
+ numericInput: {
1763
+ value: maxNetworkLogs,
1764
+ onChange: setMaxNetworkLogs,
1765
+ min: 10,
1766
+ max: 5000,
1767
+ placeholder: 'Enter max requests (10-5000)',
1133
1768
  },
1134
1769
  isLast: true,
1135
1770
  })}
@@ -1137,26 +1772,25 @@ const SettingsPanel = () => {
1137
1772
 
1138
1773
  <View style={{
1139
1774
  backgroundColor: AppColors.primaryLight,
1140
- borderRadius: 12,
1775
+ borderRadius: 14,
1141
1776
  borderWidth: 1,
1142
1777
  borderColor: AppColors.grayBorderSecondary,
1143
1778
  padding: 16,
1144
- marginTop: 12,
1145
1779
  }}>
1146
1780
  {renderSettingRow({
1147
1781
  icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1148
- label: 'Clear Network Logs',
1149
- description: `${logs.length} requests stored`,
1782
+ label: t('settings.apis.clearNetworkLogs'),
1783
+ description: t('settings.apis.clearNetworkLogsDescription', { count: logs.length }),
1150
1784
  isLast: true,
1151
1785
  onPress: () => {
1152
1786
  clearNetworkLogs();
1153
1787
  setSelected(null);
1154
- Alert.alert('Success', 'Network logs cleared.');
1788
+ Alert.alert(t('common.success'), t('settings.apis.networkLogsCleared'));
1155
1789
  },
1156
1790
  right: (<View style={{
1157
- paddingHorizontal: 10,
1158
- paddingVertical: 5,
1159
- borderRadius: 7,
1791
+ paddingHorizontal: 12,
1792
+ paddingVertical: 6,
1793
+ borderRadius: 8,
1160
1794
  backgroundColor: `${AppColors.errorColor}14`,
1161
1795
  borderWidth: 1,
1162
1796
  borderColor: `${AppColors.errorColor}33`,
@@ -1164,9 +1798,10 @@ const SettingsPanel = () => {
1164
1798
  <Text style={{
1165
1799
  fontFamily: AppFonts.interBold,
1166
1800
  fontSize: 11,
1801
+ lineHeight: 14,
1167
1802
  color: AppColors.errorColor,
1168
1803
  }}>
1169
- Clear
1804
+ {t('common.clear')}
1170
1805
  </Text>
1171
1806
  </View>),
1172
1807
  })}
@@ -1174,36 +1809,39 @@ const SettingsPanel = () => {
1174
1809
  </ScrollView>);
1175
1810
  }
1176
1811
  else if (settingsPage === 'logs') {
1177
- title = 'Logs Settings';
1812
+ title = t('settings.logs.title');
1178
1813
  icon = <TerminalIcon color={AppColors.white} size={16}/>;
1179
- rightInfo = `Total: ${consoleLogs.length}`;
1180
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
1814
+ rightInfo = t('settings.logs.total', { count: consoleLogs.length });
1815
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1181
1816
  <View style={{
1182
1817
  backgroundColor: AppColors.primaryLight,
1183
1818
  padding: 16,
1184
- borderRadius: 12,
1819
+ borderRadius: 14,
1185
1820
  borderWidth: 1,
1186
1821
  borderColor: AppColors.grayBorderSecondary,
1187
1822
  gap: 4,
1188
1823
  }}>
1189
1824
  {renderSettingRow({
1190
1825
  icon: <TerminalIcon color={AppColors.purple} size={16}/>,
1191
- label: 'Max Console Logs',
1192
- description: 'How many console messages to retain',
1193
- picker: {
1194
- options: [100, 200, 500, 1000],
1195
- selectedValue: maxConsoleLogs,
1196
- onSelect: setMaxConsoleLogs,
1826
+ label: t('settings.logs.maxConsoleLogs'),
1827
+ description: t('settings.logs.maxConsoleLogsDescription'),
1828
+ numericInput: {
1829
+ value: maxConsoleLogs,
1830
+ onChange: setMaxConsoleLogs,
1831
+ min: 10,
1832
+ max: 10000,
1833
+ placeholder: 'Enter max logs (10-10000)',
1197
1834
  },
1198
1835
  })}
1199
1836
  <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
1200
1837
  <Text style={{
1201
1838
  fontFamily: AppFonts.interBold,
1202
1839
  fontSize: 13,
1840
+ lineHeight: 17,
1203
1841
  color: AppColors.primaryBlack,
1204
- paddingTop: 4,
1842
+ paddingTop: 6,
1205
1843
  }}>
1206
- Log Levels
1844
+ {t('settings.logs.logLevels')}
1207
1845
  </Text>
1208
1846
  {['info', 'warn', 'error'].map(level => {
1209
1847
  const isLvlActive = showConsoleLevels?.[level];
@@ -1212,19 +1850,25 @@ const SettingsPanel = () => {
1212
1850
  : level === 'warn'
1213
1851
  ? AppColors.warningIconGold
1214
1852
  : AppColors.skyBlue;
1853
+ const label = level === 'info'
1854
+ ? t('settings.logs.showInfo')
1855
+ : level === 'warn'
1856
+ ? t('settings.logs.showWarn')
1857
+ : t('settings.logs.showError');
1858
+ const desc = level === 'info'
1859
+ ? t('settings.logs.showInfoDesc')
1860
+ : level === 'warn'
1861
+ ? t('settings.logs.showWarnDesc')
1862
+ : t('settings.logs.showErrorDesc');
1215
1863
  return renderSettingRow({
1216
1864
  icon: (<View style={{
1217
- width: 10,
1218
- height: 10,
1219
- borderRadius: 5,
1865
+ width: 12,
1866
+ height: 12,
1867
+ borderRadius: 6,
1220
1868
  backgroundColor: levelColor,
1221
1869
  }}/>),
1222
- label: `Show ${level.charAt(0).toUpperCase() + level.slice(1)} logs`,
1223
- description: level === 'info'
1224
- ? 'Informational messages'
1225
- : level === 'warn'
1226
- ? 'Warning messages'
1227
- : 'Error messages',
1870
+ label,
1871
+ description: desc,
1228
1872
  isLast: level === 'error',
1229
1873
  onPress: () => setShowConsoleLevels(prev => ({
1230
1874
  ...prev,
@@ -1252,25 +1896,24 @@ const SettingsPanel = () => {
1252
1896
 
1253
1897
  <View style={{
1254
1898
  backgroundColor: AppColors.primaryLight,
1255
- borderRadius: 12,
1899
+ borderRadius: 14,
1256
1900
  borderWidth: 1,
1257
1901
  borderColor: AppColors.grayBorderSecondary,
1258
1902
  padding: 16,
1259
- marginTop: 12,
1260
1903
  }}>
1261
1904
  {renderSettingRow({
1262
1905
  icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1263
- label: 'Clear Console Logs',
1264
- description: `${consoleLogs.length} logs stored`,
1906
+ label: t('settings.logs.clearConsoleLogs'),
1907
+ description: t('settings.logs.clearConsoleLogsDescription', { count: consoleLogs.length }),
1265
1908
  isLast: true,
1266
1909
  onPress: () => {
1267
1910
  clearConsoleLogs();
1268
- Alert.alert('Success', 'Console logs cleared.');
1911
+ Alert.alert(t('common.success'), t('settings.logs.consoleLogsCleared'));
1269
1912
  },
1270
1913
  right: (<View style={{
1271
- paddingHorizontal: 10,
1272
- paddingVertical: 5,
1273
- borderRadius: 7,
1914
+ paddingHorizontal: 12,
1915
+ paddingVertical: 6,
1916
+ borderRadius: 8,
1274
1917
  backgroundColor: `${AppColors.errorColor}14`,
1275
1918
  borderWidth: 1,
1276
1919
  borderColor: `${AppColors.errorColor}33`,
@@ -1278,9 +1921,10 @@ const SettingsPanel = () => {
1278
1921
  <Text style={{
1279
1922
  fontFamily: AppFonts.interBold,
1280
1923
  fontSize: 11,
1924
+ lineHeight: 14,
1281
1925
  color: AppColors.errorColor,
1282
1926
  }}>
1283
- Clear
1927
+ {t('common.clear')}
1284
1928
  </Text>
1285
1929
  </View>),
1286
1930
  })}
@@ -1288,47 +1932,52 @@ const SettingsPanel = () => {
1288
1932
  </ScrollView>);
1289
1933
  }
1290
1934
  else if (settingsPage === 'analytics') {
1291
- title = 'Analytics Settings';
1935
+ title = t('settings.analytics.title');
1292
1936
  icon = <AnalyticsIcon color={AppColors.white} size={16}/>;
1293
- rightInfo = `Events: ${analyticsEvents.length}`;
1294
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
1937
+ rightInfo = t('settings.analytics.total', { count: analyticsEvents.length });
1938
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1295
1939
  <View style={{
1296
1940
  backgroundColor: AppColors.primaryLight,
1297
1941
  padding: 16,
1298
- borderRadius: 12,
1942
+ borderRadius: 14,
1299
1943
  borderWidth: 1,
1300
1944
  borderColor: AppColors.grayBorderSecondary,
1301
- gap: 4,
1302
1945
  }}>
1303
1946
  {renderSettingRow({
1304
1947
  icon: <AnalyticsIcon color={AppColors.purple} size={16}/>,
1305
- label: 'Events Captured',
1306
- description: `${analyticsEvents.length} analytics events stored`,
1948
+ label: t('settings.analytics.maxAnalyticsEvents'),
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
+ },
1307
1957
  isLast: true,
1308
1958
  })}
1309
1959
  </View>
1310
1960
  <View style={{
1311
1961
  backgroundColor: AppColors.primaryLight,
1312
- borderRadius: 12,
1962
+ borderRadius: 14,
1313
1963
  borderWidth: 1,
1314
1964
  borderColor: AppColors.grayBorderSecondary,
1315
1965
  padding: 16,
1316
- marginTop: 12,
1317
1966
  }}>
1318
1967
  {renderSettingRow({
1319
1968
  icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1320
- label: 'Clear Analytics History',
1321
- description: 'Remove all captured events',
1969
+ label: t('settings.analytics.clearAnalyticsEvents'),
1970
+ description: t('settings.analytics.clearAnalyticsEventsDescription'),
1322
1971
  isLast: true,
1323
1972
  onPress: () => {
1324
1973
  clearAnalyticsEvents();
1325
1974
  setSelectedEvent(null);
1326
- Alert.alert('Success', 'Analytics events cleared.');
1975
+ Alert.alert(t('common.success'), t('settings.analytics.analyticsEventsCleared'));
1327
1976
  },
1328
1977
  right: (<View style={{
1329
- paddingHorizontal: 10,
1330
- paddingVertical: 5,
1331
- borderRadius: 7,
1978
+ paddingHorizontal: 12,
1979
+ paddingVertical: 6,
1980
+ borderRadius: 8,
1332
1981
  backgroundColor: `${AppColors.errorColor}14`,
1333
1982
  borderWidth: 1,
1334
1983
  borderColor: `${AppColors.errorColor}33`,
@@ -1336,9 +1985,10 @@ const SettingsPanel = () => {
1336
1985
  <Text style={{
1337
1986
  fontFamily: AppFonts.interBold,
1338
1987
  fontSize: 11,
1988
+ lineHeight: 14,
1339
1989
  color: AppColors.errorColor,
1340
1990
  }}>
1341
- Clear
1991
+ {t('common.clear')}
1342
1992
  </Text>
1343
1993
  </View>),
1344
1994
  })}
@@ -1346,22 +1996,22 @@ const SettingsPanel = () => {
1346
1996
  </ScrollView>);
1347
1997
  }
1348
1998
  else if (settingsPage === 'redux') {
1349
- title = 'Redux Settings';
1350
- icon = <TerminalIcon color={AppColors.white} size={16}/>;
1351
- rightInfo = `Reducers: ${Object.keys(reduxState || {}).length}`;
1352
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
1999
+ title = t('settings.redux.title');
2000
+ icon = <ReduxIcon color={AppColors.white} size={16}/>;
2001
+ rightInfo = t('settings.redux.reducers', { count: Object.keys(reduxState || {}).length });
2002
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1353
2003
  <View style={{
1354
2004
  backgroundColor: AppColors.primaryLight,
1355
2005
  padding: 16,
1356
- borderRadius: 12,
2006
+ borderRadius: 14,
1357
2007
  borderWidth: 1,
1358
2008
  borderColor: AppColors.grayBorderSecondary,
1359
2009
  gap: 4,
1360
2010
  }}>
1361
2011
  {renderSettingRow({
1362
- icon: <TerminalIcon color={AppColors.purple} size={16}/>,
1363
- label: 'Auto-refresh Store',
1364
- description: 'Automatically capture Redux store state updates',
2012
+ icon: <ReduxIcon color={AppColors.purple} size={16}/>,
2013
+ label: t('settings.redux.autoRefresh'),
2014
+ description: t('settings.redux.autoRefreshDescription'),
1365
2015
  onPress: () => setReduxAutoRefreshState(prev => !prev),
1366
2016
  right: (<View style={{
1367
2017
  width: 22,
@@ -1383,8 +2033,8 @@ const SettingsPanel = () => {
1383
2033
  <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
1384
2034
  {renderSettingRow({
1385
2035
  icon: <LayersIcon color={AppColors.purple} size={16}/>,
1386
- label: 'Default JSON Expand Depth',
1387
- description: 'Initial depth of Redux state tree to auto-expand',
2036
+ label: t('settings.redux.defaultJsonExpandDepth'),
2037
+ description: t('settings.redux.defaultJsonExpandDepthDescription'),
1388
2038
  picker: {
1389
2039
  options: [1, 2, 3, 5],
1390
2040
  selectedValue: reduxExpandDepth,
@@ -1396,27 +2046,26 @@ const SettingsPanel = () => {
1396
2046
 
1397
2047
  <View style={{
1398
2048
  backgroundColor: AppColors.primaryLight,
1399
- borderRadius: 12,
2049
+ borderRadius: 14,
1400
2050
  borderWidth: 1,
1401
2051
  borderColor: AppColors.grayBorderSecondary,
1402
2052
  padding: 16,
1403
- marginTop: 12,
1404
2053
  }}>
1405
2054
  {renderSettingRow({
1406
2055
  icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1407
- label: 'Clear Redux State',
2056
+ label: t('settings.redux.clearReduxState'),
1408
2057
  description: reduxState
1409
- ? 'Reset state snapshot in inspector'
1410
- : 'No store snapshot stored',
2058
+ ? t('settings.redux.clearReduxStateDescription')
2059
+ : t('settings.redux.clearReduxStateEmpty'),
1411
2060
  isLast: true,
1412
2061
  onPress: () => {
1413
2062
  setReduxState(null);
1414
- Alert.alert('Success', 'Redux state snapshot cleared.');
2063
+ Alert.alert(t('common.success'), t('settings.redux.reduxStateCleared'));
1415
2064
  },
1416
2065
  right: (<View style={{
1417
- paddingHorizontal: 10,
1418
- paddingVertical: 5,
1419
- borderRadius: 7,
2066
+ paddingHorizontal: 12,
2067
+ paddingVertical: 6,
2068
+ borderRadius: 8,
1420
2069
  backgroundColor: `${AppColors.errorColor}14`,
1421
2070
  borderWidth: 1,
1422
2071
  borderColor: `${AppColors.errorColor}33`,
@@ -1424,9 +2073,10 @@ const SettingsPanel = () => {
1424
2073
  <Text style={{
1425
2074
  fontFamily: AppFonts.interBold,
1426
2075
  fontSize: 11,
2076
+ lineHeight: 14,
1427
2077
  color: AppColors.errorColor,
1428
2078
  }}>
1429
- Clear
2079
+ {t('common.clear')}
1430
2080
  </Text>
1431
2081
  </View>),
1432
2082
  })}
@@ -1434,33 +2084,35 @@ const SettingsPanel = () => {
1434
2084
  </ScrollView>);
1435
2085
  }
1436
2086
  else if (settingsPage === 'crash') {
1437
- title = 'Crash Settings';
2087
+ title = t('settings.crash.title');
1438
2088
  icon = <CrashIcon color={AppColors.white} size={16}/>;
1439
2089
  rightInfo = `Total: ${crashRecords?.length || 0}`;
1440
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
2090
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1441
2091
  <View style={{
1442
2092
  backgroundColor: AppColors.primaryLight,
1443
2093
  padding: 16,
1444
- borderRadius: 12,
2094
+ borderRadius: 14,
1445
2095
  borderWidth: 1,
1446
2096
  borderColor: AppColors.grayBorderSecondary,
1447
2097
  gap: 4,
1448
2098
  }}>
1449
2099
  {renderSettingRow({
1450
2100
  icon: <LayersIcon color={AppColors.purple} size={16}/>,
1451
- label: 'Max Crash Logs',
1452
- description: 'How many crash records to preserve in history',
1453
- picker: {
1454
- options: [25, 50, 100, 200],
1455
- selectedValue: maxCrashLogs || 100,
1456
- onSelect: val => setMaxCrashLogs(val),
2101
+ label: t('settings.crash.maxCrashLogs'),
2102
+ description: t('settings.crash.maxCrashLogsDesc'),
2103
+ numericInput: {
2104
+ value: maxCrashLogs,
2105
+ onChange: setMaxCrashLogs,
2106
+ min: 5,
2107
+ max: 500,
2108
+ placeholder: 'Enter max crashes (5-500)',
1457
2109
  },
1458
2110
  })}
1459
2111
  <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
1460
2112
  {renderSettingRow({
1461
2113
  icon: <ShieldAlertIcon color={AppColors.greenColor} size={16}/>,
1462
- label: 'Global Crash Guard',
1463
- description: 'Intercepts native, JS, and render errors directly',
2114
+ label: t('settings.crash.globalGuard'),
2115
+ description: t('settings.crash.globalGuardDesc'),
1464
2116
  isLast: true,
1465
2117
  right: (<View style={{
1466
2118
  paddingHorizontal: 8,
@@ -1471,9 +2123,10 @@ const SettingsPanel = () => {
1471
2123
  <Text style={{
1472
2124
  fontFamily: AppFonts.interBold,
1473
2125
  fontSize: 10,
2126
+ lineHeight: 13,
1474
2127
  color: AppColors.greenColor,
1475
2128
  }}>
1476
- PROTECTED
2129
+ {t('settings.protectedBadge')}
1477
2130
  </Text>
1478
2131
  </View>),
1479
2132
  })}
@@ -1481,25 +2134,190 @@ const SettingsPanel = () => {
1481
2134
 
1482
2135
  <View style={{
1483
2136
  backgroundColor: AppColors.primaryLight,
1484
- borderRadius: 12,
2137
+ borderRadius: 14,
1485
2138
  borderWidth: 1,
1486
2139
  borderColor: AppColors.grayBorderSecondary,
1487
2140
  padding: 16,
1488
- marginTop: 12,
1489
2141
  }}>
1490
2142
  {renderSettingRow({
1491
2143
  icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1492
- label: 'Clear Crash History',
1493
- description: `${crashRecords?.length || 0} crash logs stored`,
2144
+ label: t('settings.crash.clearHistory'),
2145
+ description: t('settings.crash.clearHistoryDesc', { count: crashRecords?.length || 0 }),
1494
2146
  isLast: true,
1495
2147
  onPress: () => {
1496
2148
  clearCrashRecords();
1497
- Alert.alert('Success', 'Crash logs cleared.');
2149
+ Alert.alert(t('common.success'), t('settings.crash.historyCleared'));
2150
+ },
2151
+ right: (<View style={{
2152
+ paddingHorizontal: 12,
2153
+ paddingVertical: 6,
2154
+ borderRadius: 8,
2155
+ backgroundColor: `${AppColors.errorColor}14`,
2156
+ borderWidth: 1,
2157
+ borderColor: `${AppColors.errorColor}33`,
2158
+ }}>
2159
+ <Text style={{
2160
+ fontFamily: AppFonts.interBold,
2161
+ fontSize: 11,
2162
+ lineHeight: 14,
2163
+ color: AppColors.errorColor,
2164
+ }}>
2165
+ {t('common.clear')}
2166
+ </Text>
2167
+ </View>),
2168
+ })}
2169
+ </View>
2170
+ </ScrollView>);
2171
+ }
2172
+ else if (settingsPage === 'bundle') {
2173
+ title = t('settings.bundle.title');
2174
+ icon = <PackageIcon color={AppColors.white} size={16}/>;
2175
+ rightInfo = 'Metro';
2176
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
2177
+ <View style={{
2178
+ backgroundColor: AppColors.primaryLight,
2179
+ padding: 16,
2180
+ borderRadius: 14,
2181
+ borderWidth: 1,
2182
+ borderColor: AppColors.grayBorderSecondary,
2183
+ gap: 8,
2184
+ }}>
2185
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
2186
+ <View style={{
2187
+ width: 36,
2188
+ height: 36,
2189
+ borderRadius: 10,
2190
+ backgroundColor: AppColors.purpleShade50,
2191
+ alignItems: 'center',
2192
+ justifyContent: 'center',
2193
+ }}>
2194
+ <PackageIcon color={AppColors.purple} size={16}/>
2195
+ </View>
2196
+ <View style={{ flex: 1 }}>
2197
+ <Text style={{
2198
+ fontFamily: AppFonts.interBold,
2199
+ fontSize: 14,
2200
+ lineHeight: 18,
2201
+ color: AppColors.primaryBlack,
2202
+ }}>
2203
+ {t('settings.bundle.sourceBundler')}
2204
+ </Text>
2205
+ <Text style={{
2206
+ fontFamily: AppFonts.interRegular,
2207
+ fontSize: 11,
2208
+ lineHeight: 15,
2209
+ color: AppColors.grayText,
2210
+ marginTop: 1,
2211
+ }}>
2212
+ {t('settings.bundle.sourceBundlerDesc')}
2213
+ </Text>
2214
+ </View>
2215
+ </View>
2216
+ </View>
2217
+
2218
+ <View style={{
2219
+ backgroundColor: AppColors.primaryLight,
2220
+ borderRadius: 14,
2221
+ borderWidth: 1,
2222
+ borderColor: AppColors.grayBorderSecondary,
2223
+ padding: 16,
2224
+ }}>
2225
+ {renderSettingRow({
2226
+ icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
2227
+ label: t('settings.bundle.clearCache'),
2228
+ description: t('settings.bundle.clearCacheDesc'),
2229
+ isLast: true,
2230
+ onPress: () => {
2231
+ clearCachedBundleAnalysis();
2232
+ Alert.alert(t('common.success'), t('settings.bundle.cacheCleared'));
2233
+ },
2234
+ right: (<View style={{
2235
+ paddingHorizontal: 12,
2236
+ paddingVertical: 6,
2237
+ borderRadius: 8,
2238
+ backgroundColor: `${AppColors.errorColor}14`,
2239
+ borderWidth: 1,
2240
+ borderColor: `${AppColors.errorColor}33`,
2241
+ }}>
2242
+ <Text style={{
2243
+ fontFamily: AppFonts.interBold,
2244
+ fontSize: 11,
2245
+ lineHeight: 14,
2246
+ color: AppColors.errorColor,
2247
+ }}>
2248
+ {t('common.clear')}
2249
+ </Text>
2250
+ </View>),
2251
+ })}
2252
+ </View>
2253
+ </ScrollView>);
2254
+ }
2255
+ else if (settingsPage === 'performance') {
2256
+ title = t('settings.performance.title');
2257
+ icon = <PerformanceIcon color={AppColors.white} size={16}/>;
2258
+ rightInfo = '60 FPS Target';
2259
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
2260
+ <View style={{
2261
+ backgroundColor: AppColors.primaryLight,
2262
+ padding: 16,
2263
+ borderRadius: 14,
2264
+ borderWidth: 1,
2265
+ borderColor: AppColors.grayBorderSecondary,
2266
+ gap: 8,
2267
+ }}>
2268
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
2269
+ <View style={{
2270
+ width: 36,
2271
+ height: 36,
2272
+ borderRadius: 10,
2273
+ backgroundColor: AppColors.purpleShade50,
2274
+ alignItems: 'center',
2275
+ justifyContent: 'center',
2276
+ }}>
2277
+ <PerformanceIcon color={AppColors.purple} size={16}/>
2278
+ </View>
2279
+ <View style={{ flex: 1 }}>
2280
+ <Text style={{
2281
+ fontFamily: AppFonts.interBold,
2282
+ fontSize: 14,
2283
+ lineHeight: 18,
2284
+ color: AppColors.primaryBlack,
2285
+ }}>
2286
+ {t('settings.performance.frameMeasurement')}
2287
+ </Text>
2288
+ <Text style={{
2289
+ fontFamily: AppFonts.interRegular,
2290
+ fontSize: 11,
2291
+ lineHeight: 15,
2292
+ color: AppColors.grayText,
2293
+ marginTop: 1,
2294
+ }}>
2295
+ {t('settings.performance.frameMeasurementDesc')}
2296
+ </Text>
2297
+ </View>
2298
+ </View>
2299
+ </View>
2300
+
2301
+ <View style={{
2302
+ backgroundColor: AppColors.primaryLight,
2303
+ borderRadius: 14,
2304
+ borderWidth: 1,
2305
+ borderColor: AppColors.grayBorderSecondary,
2306
+ padding: 16,
2307
+ }}>
2308
+ {renderSettingRow({
2309
+ icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
2310
+ label: t('settings.performance.clearEvents'),
2311
+ description: t('settings.performance.clearEventsDesc'),
2312
+ isLast: true,
2313
+ onPress: () => {
2314
+ clearPerformanceEvents();
2315
+ Alert.alert(t('common.success'), t('settings.performance.eventsCleared'));
1498
2316
  },
1499
2317
  right: (<View style={{
1500
- paddingHorizontal: 10,
1501
- paddingVertical: 5,
1502
- borderRadius: 7,
2318
+ paddingHorizontal: 12,
2319
+ paddingVertical: 6,
2320
+ borderRadius: 8,
1503
2321
  backgroundColor: `${AppColors.errorColor}14`,
1504
2322
  borderWidth: 1,
1505
2323
  borderColor: `${AppColors.errorColor}33`,
@@ -1507,29 +2325,33 @@ const SettingsPanel = () => {
1507
2325
  <Text style={{
1508
2326
  fontFamily: AppFonts.interBold,
1509
2327
  fontSize: 11,
2328
+ lineHeight: 14,
1510
2329
  color: AppColors.errorColor,
1511
2330
  }}>
1512
- Clear
2331
+ {t('common.clear')}
1513
2332
  </Text>
1514
2333
  </View>),
1515
2334
  })}
1516
2335
  </View>
1517
2336
  </ScrollView>);
1518
2337
  }
1519
- return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
2338
+ return (<View style={{
2339
+ flex: 1,
2340
+ backgroundColor: AppColors.grayBackground,
2341
+ }}>
1520
2342
  <LinearGradient colors={[AppColors.purple, AppColors.brandPurple]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.headerGradient}>
1521
2343
  <View style={{ paddingTop: headerTopPadding, width: '100%' }}>
1522
2344
  <View style={[styles.header, { paddingHorizontal: 16, gap: 12 }]}>
1523
- <TouchableScale onPress={goBackToMain} hitSlop={12} style={{
2345
+ <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={t('settings.back')} onPress={goBackToMain} hitSlop={12} style={{
1524
2346
  padding: 8,
1525
2347
  borderRadius: 10,
1526
2348
  backgroundColor: `${AppColors.white}26`,
1527
2349
  borderWidth: 1,
1528
2350
  borderColor: `${AppColors.white}14`,
1529
2351
  }}>
1530
- <WhiteBackNavigation color={AppColors.white} size={16}/>
1531
- </TouchableScale>
1532
- {icon && (<View style={{
2352
+ <WhiteBackNavigation color={AppColors.white} size={16}/>
2353
+ </TouchableScale>
2354
+ {icon && (<View style={{
1533
2355
  width: 30,
1534
2356
  height: 30,
1535
2357
  borderRadius: 8,
@@ -1537,18 +2359,19 @@ const SettingsPanel = () => {
1537
2359
  alignItems: 'center',
1538
2360
  justifyContent: 'center',
1539
2361
  }}>
1540
- {icon}
1541
- </View>)}
1542
- <View style={{ flex: 1 }}>
1543
- <Text style={{
2362
+ {icon}
2363
+ </View>)}
2364
+ <View style={{ flex: 1 }}>
2365
+ <Text style={{
1544
2366
  fontFamily: AppFonts.interBold,
1545
2367
  fontSize: 17,
2368
+ lineHeight: 22,
1546
2369
  color: AppColors.white,
1547
2370
  }}>
1548
- {title}
1549
- </Text>
1550
- </View>
1551
- {rightInfo ? (<View style={{
2371
+ {title}
2372
+ </Text>
2373
+ </View>
2374
+ {rightInfo ? (<View style={{
1552
2375
  backgroundColor: `${AppColors.white}26`,
1553
2376
  paddingHorizontal: 8,
1554
2377
  paddingVertical: 4,
@@ -1556,17 +2379,18 @@ const SettingsPanel = () => {
1556
2379
  borderWidth: 1,
1557
2380
  borderColor: `${AppColors.white}1A`,
1558
2381
  }}>
1559
- <Text style={{
2382
+ <Text style={{
1560
2383
  fontFamily: AppFonts.interBold,
1561
2384
  fontSize: 11,
2385
+ lineHeight: 14,
1562
2386
  color: AppColors.white,
1563
2387
  }}>
1564
- {rightInfo}
1565
- </Text>
1566
- </View>) : null}
2388
+ {rightInfo}
2389
+ </Text>
2390
+ </View>) : null}
2391
+ </View>
1567
2392
  </View>
1568
- </View>
1569
- </LinearGradient>
2393
+ </LinearGradient>
1570
2394
  {content}
1571
2395
  </View>);
1572
2396
  };