react-native-inapp-inspector 2.0.0 → 2.0.2

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 (95) hide show
  1. package/README.md +10 -0
  2. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  3. package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
  4. package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
  5. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  6. package/android/.gradle/8.9/executionHistory/executionHistory.lock +0 -0
  7. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  8. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  9. package/android/.gradle/8.9/gc.properties +0 -0
  10. package/android/.gradle/9.2.0/checksums/checksums.lock +0 -0
  11. package/android/.gradle/9.2.0/fileChanges/last-build.bin +0 -0
  12. package/android/.gradle/9.2.0/fileHashes/fileHashes.bin +0 -0
  13. package/android/.gradle/9.2.0/fileHashes/fileHashes.lock +0 -0
  14. package/android/.gradle/9.2.0/gc.properties +0 -0
  15. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  16. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  17. package/android/.gradle/vcs-1/gc.properties +0 -0
  18. package/android/build/reports/problems/problems-report.html +659 -0
  19. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +33 -24
  20. package/dist/commonjs/components/ConsoleLogCard.js +5 -5
  21. package/dist/commonjs/components/EndOfListFooter.js +1 -1
  22. package/dist/commonjs/components/Inspector/AnalyticsTab.js +19 -71
  23. package/dist/commonjs/components/Inspector/BundleTab.js +670 -188
  24. package/dist/commonjs/components/Inspector/ConsoleTab.js +109 -98
  25. package/dist/commonjs/components/Inspector/CrashTab.js +2 -1
  26. package/dist/commonjs/components/Inspector/InspectorHeader.js +102 -19
  27. package/dist/commonjs/components/Inspector/MainScreen.js +151 -17
  28. package/dist/commonjs/components/Inspector/NetworkTab.js +104 -20
  29. package/dist/commonjs/components/Inspector/SettingsPanel.js +1111 -1385
  30. package/dist/commonjs/components/Inspector/TabBar.js +3 -4
  31. package/dist/commonjs/components/Inspector/TelemetryConsentModal.d.ts +3 -0
  32. package/dist/commonjs/components/Inspector/TelemetryConsentModal.js +212 -0
  33. package/dist/commonjs/components/JsonViewer.js +9 -4
  34. package/dist/commonjs/components/LogCard.js +31 -1
  35. package/dist/commonjs/components/LogSyntaxHighlighter.d.ts +16 -0
  36. package/dist/commonjs/components/LogSyntaxHighlighter.js +130 -0
  37. package/dist/commonjs/components/NetworkIcons.d.ts +2 -0
  38. package/dist/commonjs/components/NetworkIcons.js +10 -1
  39. package/dist/commonjs/components/Slider.d.ts +13 -0
  40. package/dist/commonjs/components/Slider.js +261 -0
  41. package/dist/commonjs/constants/version.d.ts +1 -1
  42. package/dist/commonjs/constants/version.js +1 -1
  43. package/dist/commonjs/customHooks/analyticsLogger.js +1 -1
  44. package/dist/commonjs/customHooks/crashHandler.js +1 -1
  45. package/dist/commonjs/helpers/index.d.ts +2 -0
  46. package/dist/commonjs/helpers/index.js +16 -0
  47. package/dist/commonjs/helpers/searchQueryParser.d.ts +6 -0
  48. package/dist/commonjs/helpers/searchQueryParser.js +236 -0
  49. package/dist/commonjs/helpers/settingsStore.js +13 -13
  50. package/dist/commonjs/helpers/telemetry.d.ts +99 -0
  51. package/dist/commonjs/helpers/telemetry.js +398 -0
  52. package/dist/commonjs/index.d.ts +2 -0
  53. package/dist/commonjs/index.js +30 -21
  54. package/dist/commonjs/native/NativeInspector.d.ts +5 -0
  55. package/dist/commonjs/native/NativeInspector.js +3 -2
  56. package/dist/commonjs/styles/index.js +35 -35
  57. package/dist/esm/components/ConsoleLogCard.js +5 -5
  58. package/dist/esm/components/EndOfListFooter.js +2 -2
  59. package/dist/esm/components/Inspector/AnalyticsTab.js +21 -73
  60. package/dist/esm/components/Inspector/BundleTab.js +670 -188
  61. package/dist/esm/components/Inspector/ConsoleTab.js +110 -99
  62. package/dist/esm/components/Inspector/CrashTab.js +2 -1
  63. package/dist/esm/components/Inspector/InspectorHeader.js +103 -20
  64. package/dist/esm/components/Inspector/MainScreen.js +119 -18
  65. package/dist/esm/components/Inspector/NetworkTab.js +105 -21
  66. package/dist/esm/components/Inspector/SettingsPanel.js +1114 -1388
  67. package/dist/esm/components/Inspector/TabBar.js +4 -5
  68. package/dist/esm/components/Inspector/TelemetryConsentModal.d.ts +3 -0
  69. package/dist/esm/components/Inspector/TelemetryConsentModal.js +175 -0
  70. package/dist/esm/components/JsonViewer.js +9 -4
  71. package/dist/esm/components/LogCard.js +32 -2
  72. package/dist/esm/components/LogSyntaxHighlighter.d.ts +16 -0
  73. package/dist/esm/components/LogSyntaxHighlighter.js +123 -0
  74. package/dist/esm/components/NetworkIcons.d.ts +2 -0
  75. package/dist/esm/components/NetworkIcons.js +7 -0
  76. package/dist/esm/components/Slider.d.ts +13 -0
  77. package/dist/esm/components/Slider.js +222 -0
  78. package/dist/esm/constants/version.d.ts +1 -1
  79. package/dist/esm/constants/version.js +1 -1
  80. package/dist/esm/customHooks/analyticsLogger.js +1 -1
  81. package/dist/esm/customHooks/crashHandler.js +1 -1
  82. package/dist/esm/helpers/index.d.ts +2 -0
  83. package/dist/esm/helpers/index.js +2 -0
  84. package/dist/esm/helpers/searchQueryParser.d.ts +6 -0
  85. package/dist/esm/helpers/searchQueryParser.js +233 -0
  86. package/dist/esm/helpers/settingsStore.js +13 -13
  87. package/dist/esm/helpers/telemetry.d.ts +99 -0
  88. package/dist/esm/helpers/telemetry.js +380 -0
  89. package/dist/esm/index.d.ts +2 -0
  90. package/dist/esm/index.js +20 -21
  91. package/dist/esm/native/NativeInspector.d.ts +5 -0
  92. package/dist/esm/native/NativeInspector.js +3 -2
  93. package/dist/esm/styles/index.js +35 -35
  94. package/ios/{NetworkInspectorModule.m → NetworkInspectorModule.mm} +193 -97
  95. package/package.json +2 -2
@@ -1,13 +1,12 @@
1
- import React, { useMemo, useState, useEffect } from 'react';
2
- import { Alert, Platform, ScrollView, StatusBar, Text, TextInput, View, } from 'react-native';
1
+ import React, { useMemo, useState, useEffect, useRef } from 'react';
2
+ import { Alert, Animated, Platform, ScrollView, StatusBar, StyleSheet, Text, TextInput, View, } from 'react-native';
3
3
  import Svg, { Path } from 'react-native-svg';
4
- import LinearGradient from 'react-native-linear-gradient';
5
4
  import { animateNextLayout, useInspector } from './InspectorContext';
6
5
  import TouchableScale from '../TouchableScale';
7
- import styles, { toggleGlobalTheme } from '../../styles';
6
+ import Slider from '../Slider';
7
+ import { toggleGlobalTheme } from '../../styles';
8
8
  import { AppColors } from '../../styles/AppColors';
9
9
  import { AppFonts } from '../../styles/AppFonts';
10
- import { LIB_VERSION } from '../../constants';
11
10
  import { isPersistentStorageAvailable, calculateRamBasedLimits, } from '../../helpers/settingsStore';
12
11
  import { clearNetworkLogs } from '../../customHooks/networkLogger';
13
12
  import { clearConsoleLogs } from '../../customHooks/consoleLogger';
@@ -18,10 +17,14 @@ import { clearPerformanceEvents } from '../../customHooks/performanceTracker';
18
17
  import { isReduxConnected } from '../../customHooks/reduxLogger';
19
18
  import { isAnalyticsConnected } from '../../customHooks/analyticsLogger';
20
19
  import { useTranslation } from '../../i18n';
21
- import { SignalIcon, TerminalIcon, AnalyticsIcon, SettingsIcon, SunIcon, MoonIcon, ScreenIcon, MotionIcon, LayersIcon, EyeIcon, CheckIcon, TrashIcon, WhiteBackNavigation, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, ShieldAlertIcon, ForwardChevronIcon, } from '../NetworkIcons';
20
+ import { SignalIcon, TerminalIcon, AnalyticsIcon, SettingsIcon, SunIcon, MoonIcon, ScreenIcon, MotionIcon, LayersIcon, EyeIcon, CheckIcon, TrashIcon, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, ShieldAlertIcon, ForwardChevronIcon, ChevronDownIcon, } from '../NetworkIcons';
22
21
  const SettingsPanel = () => {
23
22
  const { t } = useTranslation();
24
23
  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();
24
+ const [stagedHeight, setStagedHeight] = useState(modalHeightPercent);
25
+ useEffect(() => {
26
+ setStagedHeight(modalHeightPercent);
27
+ }, [modalHeightPercent]);
25
28
  const autoRamProfile = calculateRamBasedLimits(deviceFreeRamMb);
26
29
  const isPersistent = isPersistentStorageAvailable();
27
30
  // Safe Area & Status Bar Padding for All Device Form Factors
@@ -118,638 +121,638 @@ const SettingsPanel = () => {
118
121
  });
119
122
  Alert.alert(t('settings.settingsSaved'), t('settings.settingsSavedDesc'));
120
123
  };
121
- if (settingsPage === 'main') {
124
+ const [isDefaultTabDropdownOpen, setIsDefaultTabDropdownOpen] = useState(false);
125
+ const goBackToMain = () => {
126
+ animateNextLayout();
127
+ setSettingsPage('main');
128
+ };
129
+ // Helper: settings row with icon + label + optional description
130
+ const renderSettingRow = (opts) => {
122
131
  return (<View style={{
123
- flex: 1,
124
- backgroundColor: AppColors.grayBackground,
125
- }}>
126
- {/* Modern Floating Gradient Header */}
127
- <LinearGradient colors={[AppColors.purple, AppColors.brandPurple]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.headerGradient}>
128
- <View style={{ paddingTop: headerTopPadding, width: '100%' }}>
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={() => {
134
- animateNextLayout();
135
- setSettingsPage(null);
136
- switchActiveTab('apis');
137
- }} hitSlop={12} style={{
138
- padding: 8,
139
- borderRadius: 10,
140
- backgroundColor: `${AppColors.white}26`,
141
- borderWidth: 1,
142
- borderColor: `${AppColors.white}18`,
132
+ paddingVertical: 12,
133
+ borderBottomWidth: opts.isLast ? 0 : 1,
134
+ borderBottomColor: AppColors.dividerColor,
143
135
  }}>
144
- <WhiteBackNavigation color={AppColors.white} size={16}/>
145
- </TouchableScale>
146
-
147
- <View style={{ flex: 1 }}>
148
- <View style={{
136
+ <TouchableScale disabled={!opts.onPress} onPress={opts.onPress} style={{
149
137
  flexDirection: 'row',
150
138
  alignItems: 'center',
151
- gap: 8,
152
- }}>
153
- <Text style={{
154
- fontFamily: AppFonts.interBold,
155
- fontSize: 17,
156
- color: AppColors.white,
157
- letterSpacing: -0.3,
158
- lineHeight: 22,
159
- }}>
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={{
179
- fontFamily: AppFonts.interRegular,
180
- fontSize: 11,
181
- color: `${AppColors.white}CC`,
182
- marginTop: 2,
183
- lineHeight: 15,
139
+ gap: 12,
184
140
  }}>
185
- {t('settings.mainSubtitle')}
186
- </Text>
187
- </View>
188
-
189
- <View style={{
190
- backgroundColor: `${AppColors.white}22`,
191
- paddingHorizontal: 8,
192
- paddingVertical: 4,
193
- borderRadius: 8,
141
+ <View style={{
142
+ width: 36,
143
+ height: 36,
144
+ borderRadius: 10,
145
+ backgroundColor: AppColors.purpleShade50,
194
146
  borderWidth: 1,
195
- borderColor: `${AppColors.white}1A`,
147
+ borderColor: `${AppColors.purple}2E`,
148
+ alignItems: 'center',
149
+ justifyContent: 'center',
196
150
  }}>
197
- <Text style={{
151
+ {opts.icon}
152
+ </View>
153
+ <View style={{ flex: 1 }}>
154
+ <Text style={{
198
155
  fontFamily: AppFonts.interBold,
199
- fontSize: 10.5,
200
- color: AppColors.white,
201
- lineHeight: 14,
156
+ fontSize: 14,
157
+ lineHeight: 18,
158
+ color: AppColors.primaryBlack,
202
159
  }}>
203
- v{LIB_VERSION}
204
- </Text>
205
- </View>
206
- </View>
160
+ {opts.label}
161
+ </Text>
162
+ {opts.description ? (<Text style={{
163
+ fontFamily: AppFonts.interRegular,
164
+ fontSize: 11,
165
+ lineHeight: 15,
166
+ color: AppColors.grayText,
167
+ marginTop: 1,
168
+ }}>
169
+ {opts.description}
170
+ </Text>) : null}
207
171
  </View>
208
- </LinearGradient>
209
-
210
- <ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 14 }} showsVerticalScrollIndicator={false}>
172
+ {opts.right || null}
173
+ </TouchableScale>
174
+ {opts.numericInput && (<View style={{ marginTop: 10, gap: 6 }}>
175
+ <View style={{
176
+ flexDirection: 'row',
177
+ alignItems: 'center',
178
+ backgroundColor: AppColors.grayBackground,
179
+ borderRadius: 8,
180
+ borderWidth: 1,
181
+ borderColor: AppColors.dividerColor,
182
+ paddingHorizontal: 12,
183
+ paddingVertical: 4,
184
+ }}>
185
+ <TextInput style={{
186
+ flex: 1,
187
+ fontFamily: AppFonts.interMedium,
188
+ fontSize: 14,
189
+ lineHeight: 18,
190
+ color: AppColors.primaryBlack,
191
+ paddingVertical: 6,
192
+ }} value={String(opts.numericInput.value || '')} onChangeText={text => {
193
+ const num = parseInt(text.replace(/[^0-9]/g, ''), 10);
194
+ if (!isNaN(num)) {
195
+ const clamped = Math.max(opts.numericInput?.min ?? 1, Math.min(opts.numericInput?.max ?? 10000, num));
196
+ opts.numericInput?.onChange(clamped);
197
+ }
198
+ }} keyboardType={opts.numericInput.keyboardType ?? 'number-pad'} placeholder={opts.numericInput.placeholder} placeholderTextColor={AppColors.grayTextWeak} maxLength={6} selectTextOnFocus/>
199
+ <Text style={{
200
+ fontFamily: AppFonts.interBold,
201
+ fontSize: 11,
202
+ color: AppColors.purple,
203
+ marginLeft: 6,
204
+ }}>
205
+ MAX
206
+ </Text>
207
+ </View>
208
+ {opts.numericInput.min !== undefined || opts.numericInput.max !== undefined ? (<Text style={{
209
+ fontFamily: AppFonts.interRegular,
210
+ fontSize: 10,
211
+ lineHeight: 13,
212
+ color: AppColors.grayTextWeak,
213
+ }}>
214
+ Range: {opts.numericInput.min ?? 1} - {opts.numericInput.max ?? '∞'}
215
+ </Text>) : null}
216
+ </View>)}
217
+ {opts.picker && (<View style={{
218
+ flexDirection: 'row',
219
+ backgroundColor: AppColors.grayBackground,
220
+ borderRadius: 8,
221
+ padding: 2.5,
222
+ marginTop: 10,
223
+ borderWidth: 1,
224
+ borderColor: AppColors.dividerColor,
225
+ }}>
226
+ {opts.picker.options.map(opt => {
227
+ const isActive = opts.picker.selectedValue === opt;
228
+ return (<TouchableScale key={String(opt)} onPress={() => opts.picker.onSelect(opt)} style={{
229
+ flex: 1,
230
+ paddingVertical: 6,
231
+ alignItems: 'center',
232
+ borderRadius: 6,
233
+ backgroundColor: isActive
234
+ ? AppColors.purple
235
+ : 'transparent',
236
+ }}>
237
+ <Text style={{
238
+ fontFamily: AppFonts.interBold,
239
+ fontSize: 11,
240
+ lineHeight: 14,
241
+ color: isActive ? AppColors.white : AppColors.grayText,
242
+ }}>
243
+ {opts.picker.formatLabel
244
+ ? opts.picker.formatLabel(opt)
245
+ : opt}
246
+ </Text>
247
+ </TouchableScale>);
248
+ })}
249
+ </View>)}
250
+ </View>);
251
+ };
252
+ const renderMainSettingsContent = () => (<View style={{
253
+ flex: 1,
254
+ backgroundColor: AppColors.grayBackground,
255
+ }}>
256
+ <ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, paddingBottom: 100, gap: 14 }} showsVerticalScrollIndicator={false}>
211
257
  {/* Segmented Top Navigation Sub-Tabs */}
212
258
  <View style={{
213
- flexDirection: 'row',
214
- backgroundColor: AppColors.primaryLight,
215
- borderRadius: 12,
216
- padding: 4,
217
- borderWidth: 1,
218
- borderColor: AppColors.grayBorderSecondary,
219
- shadowColor: AppColors.black,
220
- shadowOpacity: 0.04,
221
- shadowRadius: 4,
222
- shadowOffset: { width: 0, height: 2 },
223
- }}>
259
+ flexDirection: 'row',
260
+ backgroundColor: AppColors.primaryLight,
261
+ borderRadius: 12,
262
+ padding: 4,
263
+ borderWidth: 1,
264
+ borderColor: AppColors.grayBorderSecondary,
265
+ shadowColor: AppColors.black,
266
+ shadowOpacity: 0.04,
267
+ shadowRadius: 4,
268
+ shadowOffset: { width: 0, height: 2 },
269
+ }}>
224
270
  <TouchableScale accessible={true} accessibilityRole="tab" accessibilityLabel={t('settings.modulesAndTools')} accessibilityState={{ selected: settingsActiveSubTab === 'module' }} onPress={() => {
225
- animateNextLayout();
226
- setSettingsActiveSubTab('module');
227
- }} style={{
228
- flex: 1,
229
- paddingVertical: 9,
230
- flexDirection: 'row',
231
- alignItems: 'center',
232
- justifyContent: 'center',
233
- gap: 6,
234
- borderRadius: 9,
235
- backgroundColor: settingsActiveSubTab === 'module'
236
- ? AppColors.purple
237
- : 'transparent',
238
- }}>
271
+ animateNextLayout();
272
+ setSettingsActiveSubTab('module');
273
+ }} style={{
274
+ flex: 1,
275
+ paddingVertical: 9,
276
+ flexDirection: 'row',
277
+ alignItems: 'center',
278
+ justifyContent: 'center',
279
+ gap: 6,
280
+ borderRadius: 9,
281
+ backgroundColor: settingsActiveSubTab === 'module'
282
+ ? AppColors.purple
283
+ : 'transparent',
284
+ }}>
239
285
  <LayersIcon color={settingsActiveSubTab === 'module'
240
- ? AppColors.white
241
- : AppColors.grayText} size={13}/>
286
+ ? AppColors.white
287
+ : AppColors.grayText} size={13}/>
242
288
  <Text style={{
243
- fontFamily: AppFonts.interBold,
244
- fontSize: 12.5,
245
- lineHeight: 16,
246
- color: settingsActiveSubTab === 'module'
247
- ? AppColors.white
248
- : AppColors.grayText,
249
- }}>
289
+ fontFamily: AppFonts.interBold,
290
+ fontSize: 12.5,
291
+ lineHeight: 16,
292
+ color: settingsActiveSubTab === 'module'
293
+ ? AppColors.white
294
+ : AppColors.grayText,
295
+ }}>
250
296
  {t('settings.modulesAndTools')}
251
297
  </Text>
252
298
  </TouchableScale>
253
299
 
254
300
  <TouchableScale accessible={true} accessibilityRole="tab" accessibilityLabel={t('settings.uiPreferences')} accessibilityState={{ selected: settingsActiveSubTab === 'ui' }} onPress={() => {
255
- animateNextLayout();
256
- setSettingsActiveSubTab('ui');
257
- }} style={{
258
- flex: 1,
259
- paddingVertical: 9,
260
- flexDirection: 'row',
261
- alignItems: 'center',
262
- justifyContent: 'center',
263
- gap: 6,
264
- borderRadius: 9,
265
- backgroundColor: settingsActiveSubTab === 'ui'
266
- ? AppColors.purple
267
- : 'transparent',
268
- }}>
301
+ animateNextLayout();
302
+ setSettingsActiveSubTab('ui');
303
+ }} style={{
304
+ flex: 1,
305
+ paddingVertical: 9,
306
+ flexDirection: 'row',
307
+ alignItems: 'center',
308
+ justifyContent: 'center',
309
+ gap: 6,
310
+ borderRadius: 9,
311
+ backgroundColor: settingsActiveSubTab === 'ui'
312
+ ? AppColors.purple
313
+ : 'transparent',
314
+ }}>
269
315
  <ScreenIcon color={settingsActiveSubTab === 'ui'
270
- ? AppColors.white
271
- : AppColors.grayText} size={13}/>
316
+ ? AppColors.white
317
+ : AppColors.grayText} size={13}/>
272
318
  <Text style={{
273
- fontFamily: AppFonts.interBold,
274
- fontSize: 12.5,
275
- lineHeight: 16,
276
- color: settingsActiveSubTab === 'ui'
277
- ? AppColors.white
278
- : AppColors.grayText,
279
- }}>
319
+ fontFamily: AppFonts.interBold,
320
+ fontSize: 12.5,
321
+ lineHeight: 16,
322
+ color: settingsActiveSubTab === 'ui'
323
+ ? AppColors.white
324
+ : AppColors.grayText,
325
+ }}>
280
326
  {t('settings.uiPreferences')}
281
327
  </Text>
282
328
  </TouchableScale>
283
329
  </View>
284
330
 
285
331
  {settingsActiveSubTab === 'module' ? (<View style={{ gap: 12 }}>
286
- {/* Performance Advisory Banner */}
287
- <View style={{
288
- backgroundColor: `${AppColors.purple}0A`,
289
- borderRadius: 12,
290
- padding: 12,
291
- borderWidth: 1,
292
- borderColor: `${AppColors.purple}20`,
293
- flexDirection: 'row',
294
- alignItems: 'center',
295
- gap: 10,
296
- }}>
297
- <View style={{
298
- width: 32,
299
- height: 32,
300
- borderRadius: 8,
301
- backgroundColor: `${AppColors.purple}14`,
302
- alignItems: 'center',
303
- justifyContent: 'center',
304
- }}>
305
- <SignalIcon color={AppColors.purple} size={15}/>
306
- </View>
307
- <View style={{ flex: 1 }}>
308
- <Text style={{
309
- fontFamily: AppFonts.interBold,
310
- fontSize: 12,
311
- color: AppColors.primaryBlack,
312
- lineHeight: 16,
313
- }}>
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>
326
- </View>
327
-
328
- {/* Enhanced Individual Module Cards with Decorative Checkboxes */}
332
+ {/* Individual Module Cards with Left Checkboxes */}
329
333
  <View style={{ gap: 10 }}>
330
334
  {allModules.map(moduleItem => {
331
- const isReduxAvail = isReduxConnected();
332
- const isAnalyticsAvail = isAnalyticsConnected();
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 */}
335
+ const isReduxAvail = isReduxConnected();
336
+ const isAnalyticsAvail = isAnalyticsConnected();
337
+ const isUnavailable = (moduleItem.key === 'redux' && !isReduxAvail) ||
338
+ (moduleItem.key === 'analytics' && !isAnalyticsAvail);
339
+ const isLocked = moduleItem.key === 'apis' || isUnavailable;
340
+ const isChecked = moduleItem.key === 'apis' ||
341
+ (Boolean(stagedTabVisibility?.[moduleItem.key]) &&
342
+ !isUnavailable);
343
+ const liveStats = moduleItem.key === 'apis'
344
+ ? `${logs.length} requests • Limit: ${maxNetworkLogs}`
345
+ : moduleItem.key === 'logs'
346
+ ? `${consoleLogs.length} logs • Limit: ${maxConsoleLogs}`
347
+ : moduleItem.key === 'performance'
348
+ ? '60 FPS Target • Memory Telemetry'
349
+ : moduleItem.key === 'bundle'
350
+ ? 'Metro Packager • Dependency Map'
351
+ : moduleItem.key === 'crash'
352
+ ? `${crashRecords?.length || 0} crashes recorded • Crash Guard`
353
+ : moduleItem.key === 'analytics'
354
+ ? `${analyticsEvents.length} events logged`
355
+ : moduleItem.key === 'redux'
356
+ ? `${Object.keys(reduxState || {}).length} slices • Depth: ${reduxExpandDepth}`
357
+ : '';
358
+ return (<View key={moduleItem.key} style={{
359
+ backgroundColor: AppColors.primaryLight,
360
+ borderRadius: 14,
361
+ borderWidth: 1.5,
362
+ borderColor: isChecked
363
+ ? `${AppColors.purple}38`
364
+ : AppColors.grayBorderSecondary,
365
+ padding: 14,
366
+ gap: 10,
367
+ shadowColor: AppColors.black,
368
+ shadowOpacity: isChecked ? 0.04 : 0.02,
369
+ shadowRadius: 5,
370
+ shadowOffset: { width: 0, height: 2 },
371
+ }}>
372
+ {/* Top Row: [Checkbox] -> [Icon Tile] -> [Title & Subtitle] */}
369
373
  <View style={{
370
- flexDirection: 'row',
371
- alignItems: 'flex-start',
372
- justifyContent: 'space-between',
373
- gap: 10,
374
- }}>
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={{
380
- flex: 1,
381
- flexDirection: 'row',
382
- alignItems: 'flex-start',
383
- gap: 10,
384
- }}>
385
- {/* Icon Tile */}
386
- <View style={{
387
- width: 36,
388
- height: 36,
389
- borderRadius: 10,
390
- backgroundColor: isLocked
391
- ? `${AppColors.grayBorderSecondary}4D`
392
- : isChecked
393
- ? `${AppColors.purple}14`
394
- : AppColors.grayBackground,
395
- borderWidth: 1,
396
- borderColor: isLocked
397
- ? AppColors.dividerColor
398
- : isChecked
399
- ? `${AppColors.purple}33`
400
- : AppColors.grayBorderSecondary,
401
- alignItems: 'center',
402
- justifyContent: 'center',
403
- marginTop: 1,
404
- }}>
405
- {moduleItem.icon === 'apis' && (<SignalIcon color={isChecked
374
+ flexDirection: 'row',
375
+ alignItems: 'flex-start',
376
+ gap: 10,
377
+ }}>
378
+ {/* 1. Checkbox on the Left */}
379
+ <TouchableScale accessible={true} accessibilityRole="checkbox" accessibilityLabel={`Select ${moduleItem.label}`} accessibilityState={{
380
+ checked: isChecked,
381
+ disabled: isLocked,
382
+ }} disabled={isLocked} onPress={() => {
383
+ if (isLocked)
384
+ return;
385
+ setStagedTabVisibility(prev => ({
386
+ ...prev,
387
+ [moduleItem.key]: !prev[moduleItem.key],
388
+ }));
389
+ }} hitSlop={8} style={{
390
+ width: 22,
391
+ height: 22,
392
+ borderRadius: 6,
393
+ borderWidth: isChecked ? 0 : 1.8,
394
+ borderColor: isLocked
395
+ ? AppColors.dividerColor
396
+ : AppColors.grayBorderSecondary,
397
+ backgroundColor: moduleItem.key === 'apis'
398
+ ? `${AppColors.blue500}22`
399
+ : isChecked
406
400
  ? AppColors.purple
407
- : AppColors.grayTextWeak} size={16}/>)}
408
- {moduleItem.icon === 'logs' && (<TerminalIcon color={isChecked
409
- ? AppColors.purple
410
- : AppColors.grayTextWeak} size={16}/>)}
401
+ : AppColors.grayBackground,
402
+ alignItems: 'center',
403
+ justifyContent: 'center',
404
+ marginTop: 7,
405
+ shadowColor: isChecked
406
+ ? AppColors.purple
407
+ : 'transparent',
408
+ shadowOpacity: isChecked ? 0.25 : 0,
409
+ shadowRadius: 2,
410
+ elevation: isChecked ? 2 : 0,
411
+ }}>
412
+ {moduleItem.key === 'apis' ? (<CheckIcon size={12} color={AppColors.blue500}/>) : isChecked ? (<CheckIcon size={12} color={AppColors.white}/>) : isLocked ? (<Svg width={10} height={10} viewBox="0 0 24 24" fill="none">
413
+ <Path d="M7 10V7a5 5 0 0 1 10 0v3" stroke={AppColors.grayText} strokeWidth="2.2" strokeLinecap="round"/>
414
+ <Path d="M5 10h14v9a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1z" fill={AppColors.grayText}/>
415
+ </Svg>) : null}
416
+ </TouchableScale>
417
+
418
+ {/* 2. Middle Tap Zone: Icon + Title & Desc */}
419
+ <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={`${moduleItem.label} settings`} disabled={isUnavailable} onPress={() => {
420
+ animateNextLayout();
421
+ setSettingsPage(moduleItem.key);
422
+ }} style={{
423
+ flex: 1,
424
+ flexDirection: 'row',
425
+ alignItems: 'flex-start',
426
+ gap: 10,
427
+ }}>
428
+ {/* Icon Tile */}
429
+ <View style={{
430
+ width: 36,
431
+ height: 36,
432
+ borderRadius: 10,
433
+ backgroundColor: isLocked
434
+ ? `${AppColors.grayBorderSecondary}4D`
435
+ : isChecked
436
+ ? `${AppColors.purple}14`
437
+ : AppColors.grayBackground,
438
+ borderWidth: 1,
439
+ borderColor: isLocked
440
+ ? AppColors.dividerColor
441
+ : isChecked
442
+ ? `${AppColors.purple}33`
443
+ : AppColors.grayBorderSecondary,
444
+ alignItems: 'center',
445
+ justifyContent: 'center',
446
+ marginTop: 1,
447
+ }}>
448
+ {moduleItem.icon === 'apis' && (<SignalIcon color={isChecked
449
+ ? AppColors.purple
450
+ : AppColors.grayTextWeak} size={16}/>)}
451
+ {moduleItem.icon === 'logs' && (<TerminalIcon color={isChecked
452
+ ? AppColors.purple
453
+ : AppColors.grayTextWeak} size={16}/>)}
411
454
  {moduleItem.icon === 'performance' && (<PerformanceIcon color={isChecked
412
- ? AppColors.purple
413
- : AppColors.grayTextWeak} size={16}/>)}
455
+ ? AppColors.purple
456
+ : AppColors.grayTextWeak} size={16}/>)}
414
457
  {moduleItem.icon === 'bundle' && (<PackageIcon color={isChecked
415
- ? AppColors.purple
416
- : AppColors.grayTextWeak} size={16}/>)}
458
+ ? AppColors.purple
459
+ : AppColors.grayTextWeak} size={16}/>)}
417
460
  {moduleItem.icon === 'crash' && (<CrashIcon color={isChecked
418
- ? AppColors.purple
419
- : AppColors.grayTextWeak} size={16}/>)}
461
+ ? AppColors.purple
462
+ : AppColors.grayTextWeak} size={16}/>)}
420
463
  {moduleItem.icon === 'analytics' && (<AnalyticsIcon color={isChecked
421
- ? AppColors.purple
422
- : AppColors.grayTextWeak} size={16}/>)}
464
+ ? AppColors.purple
465
+ : AppColors.grayTextWeak} size={16}/>)}
423
466
  {moduleItem.icon === 'redux' && (<ReduxIcon color={isChecked
424
- ? AppColors.purple
425
- : AppColors.grayTextWeak} size={16}/>)}
467
+ ? AppColors.purple
468
+ : AppColors.grayTextWeak} size={16}/>)}
426
469
  </View>
427
470
 
428
471
  {/* Titles & Status Pill */}
429
472
  <View style={{ flex: 1, gap: 2 }}>
430
473
  <View style={{
431
- flexDirection: 'row',
432
- alignItems: 'center',
433
- gap: 6,
434
- flexWrap: 'wrap',
435
- }}>
474
+ flexDirection: 'row',
475
+ alignItems: 'center',
476
+ gap: 6,
477
+ flexWrap: 'wrap',
478
+ }}>
436
479
  <Text style={{
437
- fontFamily: AppFonts.interBold,
438
- fontSize: 14,
439
- lineHeight: 18,
440
- color: isLocked
441
- ? AppColors.grayText
442
- : AppColors.primaryBlack,
443
- }}>
480
+ fontFamily: AppFonts.interBold,
481
+ fontSize: 14,
482
+ lineHeight: 18,
483
+ color: isLocked
484
+ ? AppColors.grayText
485
+ : AppColors.primaryBlack,
486
+ }}>
444
487
  {moduleItem.label}
445
488
  </Text>
446
489
 
447
490
  {/* Default Startup Tab Badge */}
448
491
  {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
- }}>
492
+ !isUnavailable && (<View style={{
493
+ flexDirection: 'row',
494
+ alignItems: 'center',
495
+ backgroundColor: `${AppColors.purple}14`,
496
+ borderRadius: 12,
497
+ paddingHorizontal: 5,
498
+ paddingVertical: 1.5,
499
+ borderWidth: 1,
500
+ borderColor: `${AppColors.purple}2E`,
501
+ gap: 3,
502
+ }}>
460
503
  <View style={{
461
- width: 4,
462
- height: 4,
463
- borderRadius: 2,
464
- backgroundColor: AppColors.purple,
465
- }}/>
504
+ width: 4,
505
+ height: 4,
506
+ borderRadius: 2,
507
+ backgroundColor: AppColors.purple,
508
+ }}/>
466
509
  <Text style={{
467
- fontFamily: AppFonts.interBold,
468
- fontSize: 8,
469
- lineHeight: 11,
470
- color: AppColors.purple,
471
- letterSpacing: 0.4,
472
- }}>
510
+ fontFamily: AppFonts.interBold,
511
+ fontSize: 8,
512
+ lineHeight: 11,
513
+ color: AppColors.purple,
514
+ letterSpacing: 0.4,
515
+ }}>
473
516
  {t('settings.defaultBadge')}
474
517
  </Text>
475
518
  </View>)}
476
519
 
477
520
  {/* Status Pill */}
478
521
  {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
- }}>
522
+ backgroundColor: `${AppColors.blue500}14`,
523
+ borderRadius: 4,
524
+ paddingHorizontal: 5,
525
+ paddingVertical: 1.5,
526
+ borderWidth: 1,
527
+ borderColor: `${AppColors.blue500}33`,
528
+ }}>
486
529
  <Text style={{
487
- fontFamily: AppFonts.interBold,
488
- fontSize: 8,
489
- lineHeight: 11,
490
- color: AppColors.blue500,
491
- letterSpacing: 0.3,
492
- }}>
530
+ fontFamily: AppFonts.interBold,
531
+ fontSize: 8,
532
+ lineHeight: 11,
533
+ color: AppColors.blue500,
534
+ letterSpacing: 0.3,
535
+ }}>
493
536
  {t('settings.coreBadge')}
494
537
  </Text>
495
538
  </View>) : isUnavailable ? (<View style={{
496
- backgroundColor: `${AppColors.amber500}18`,
497
- borderRadius: 4,
498
- paddingHorizontal: 5,
499
- paddingVertical: 1.5,
500
- borderWidth: 1,
501
- borderColor: `${AppColors.amber500}35`,
502
- }}>
539
+ backgroundColor: `${AppColors.amber500}18`,
540
+ borderRadius: 4,
541
+ paddingHorizontal: 5,
542
+ paddingVertical: 1.5,
543
+ borderWidth: 1,
544
+ borderColor: `${AppColors.amber500}35`,
545
+ }}>
503
546
  <Text style={{
504
- fontFamily: AppFonts.interBold,
505
- fontSize: 8,
506
- lineHeight: 11,
507
- color: AppColors.amber700,
508
- letterSpacing: 0.3,
509
- }}>
547
+ fontFamily: AppFonts.interBold,
548
+ fontSize: 8,
549
+ lineHeight: 11,
550
+ color: AppColors.amber700,
551
+ letterSpacing: 0.3,
552
+ }}>
510
553
  {moduleItem.key === 'redux'
511
- ? t('settings.notConnectedBadge')
512
- : t('settings.notDetectedBadge')}
554
+ ? t('settings.notConnectedBadge')
555
+ : t('settings.notDetectedBadge')}
513
556
  </Text>
514
557
  </View>) : isChecked ? (<View style={{
515
- backgroundColor: `${AppColors.liveGreen}18`,
516
- borderRadius: 4,
517
- paddingHorizontal: 5,
518
- paddingVertical: 1.5,
519
- borderWidth: 1,
520
- borderColor: `${AppColors.liveGreen}38`,
521
- }}>
558
+ backgroundColor: `${AppColors.liveGreen}18`,
559
+ borderRadius: 4,
560
+ paddingHorizontal: 5,
561
+ paddingVertical: 1.5,
562
+ borderWidth: 1,
563
+ borderColor: `${AppColors.liveGreen}38`,
564
+ }}>
522
565
  <Text style={{
523
- fontFamily: AppFonts.interBold,
524
- fontSize: 8,
525
- lineHeight: 11,
526
- color: AppColors.green700,
527
- letterSpacing: 0.3,
528
- }}>
566
+ fontFamily: AppFonts.interBold,
567
+ fontSize: 8,
568
+ lineHeight: 11,
569
+ color: AppColors.green700,
570
+ letterSpacing: 0.3,
571
+ }}>
529
572
  {t('settings.activeBadge')}
530
573
  </Text>
531
574
  </View>) : (<View style={{
532
- backgroundColor: `${AppColors.grayTextWeak}18`,
533
- borderRadius: 4,
534
- paddingHorizontal: 5,
535
- paddingVertical: 1.5,
536
- borderWidth: 1,
537
- borderColor: `${AppColors.grayTextWeak}2E`,
538
- }}>
575
+ backgroundColor: `${AppColors.grayTextWeak}18`,
576
+ borderRadius: 4,
577
+ paddingHorizontal: 5,
578
+ paddingVertical: 1.5,
579
+ borderWidth: 1,
580
+ borderColor: `${AppColors.grayTextWeak}2E`,
581
+ }}>
539
582
  <Text style={{
540
- fontFamily: AppFonts.interBold,
541
- fontSize: 8,
542
- lineHeight: 11,
543
- color: AppColors.grayTextWeak,
544
- letterSpacing: 0.3,
545
- }}>
583
+ fontFamily: AppFonts.interBold,
584
+ fontSize: 8,
585
+ lineHeight: 11,
586
+ color: AppColors.grayTextWeak,
587
+ letterSpacing: 0.3,
588
+ }}>
546
589
  {t('settings.dormantBadge')}
547
590
  </Text>
548
591
  </View>)}
549
592
  </View>
550
593
 
551
594
  <Text style={{
552
- fontFamily: AppFonts.interRegular,
553
- fontSize: 11,
554
- color: AppColors.grayText,
555
- lineHeight: 15,
556
- marginTop: 1,
557
- }}>
595
+ fontFamily: AppFonts.interRegular,
596
+ fontSize: 11,
597
+ color: AppColors.grayText,
598
+ lineHeight: 15,
599
+ marginTop: 1,
600
+ }}>
558
601
  {moduleItem.desc}
559
602
  </Text>
560
603
  </View>
561
604
  </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,
579
- borderColor: isLocked
580
- ? AppColors.dividerColor
581
- : AppColors.grayBorderSecondary,
582
- backgroundColor: moduleItem.key === 'apis'
583
- ? `${AppColors.blue500}22`
584
- : isChecked
585
- ? AppColors.purple
586
- : AppColors.grayBackground,
587
- alignItems: 'center',
588
- justifyContent: 'center',
589
- shadowColor: isChecked
590
- ? AppColors.purple
591
- : 'transparent',
592
- shadowOpacity: isChecked ? 0.3 : 0,
593
- shadowRadius: 3,
594
- shadowOffset: { width: 0, height: 1 },
595
- elevation: isChecked ? 2 : 0,
596
- }}>
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">
598
- <Path d="M7 10V7a5 5 0 0 1 10 0v3" stroke={AppColors.grayText} strokeWidth="2.2" strokeLinecap="round"/>
599
- <Path d="M5 10h14v9a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1z" fill={AppColors.grayText}/>
600
- </Svg>) : null}
601
- </TouchableScale>
602
605
  </View>
603
606
 
604
607
  {/* Card Footer: Live Stats + Configure Button */}
605
608
  {!isUnavailable && (<View style={{
606
- flexDirection: 'row',
607
- alignItems: 'center',
608
- justifyContent: 'space-between',
609
- paddingTop: 8,
610
- borderTopWidth: 1,
611
- borderTopColor: AppColors.dividerColor,
612
- }}>
609
+ flexDirection: 'row',
610
+ alignItems: 'center',
611
+ justifyContent: 'space-between',
612
+ paddingTop: 8,
613
+ borderTopWidth: 1,
614
+ borderTopColor: AppColors.dividerColor,
615
+ }}>
613
616
  {/* Live Info Pill */}
614
617
  <View style={{
615
- flexDirection: 'row',
616
- alignItems: 'center',
617
- gap: 6,
618
- flex: 1,
619
- }}>
618
+ flexDirection: 'row',
619
+ alignItems: 'center',
620
+ gap: 6,
621
+ flex: 1,
622
+ }}>
620
623
  <View style={{
621
- width: 6,
622
- height: 6,
623
- borderRadius: 3,
624
- backgroundColor: isChecked
625
- ? AppColors.liveGreen
626
- : AppColors.grayTextWeak,
627
- }}/>
624
+ width: 6,
625
+ height: 6,
626
+ borderRadius: 3,
627
+ backgroundColor: isChecked
628
+ ? AppColors.liveGreen
629
+ : AppColors.grayTextWeak,
630
+ }}/>
628
631
  <Text style={{
629
- fontFamily: AppFonts.interMedium,
630
- fontSize: 11,
631
- color: AppColors.grayText,
632
- lineHeight: 14,
633
- }}>
632
+ fontFamily: AppFonts.interMedium,
633
+ fontSize: 11,
634
+ color: AppColors.grayText,
635
+ lineHeight: 14,
636
+ }}>
634
637
  {liveStats}
635
638
  </Text>
636
639
  </View>
637
640
 
638
641
  {/* Sleek Configure Button */}
639
642
  <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
- }}>
643
+ animateNextLayout();
644
+ setSettingsPage(moduleItem.key);
645
+ }} style={{
646
+ flexDirection: 'row',
647
+ alignItems: 'center',
648
+ gap: 5,
649
+ backgroundColor: `${AppColors.purple}12`,
650
+ paddingHorizontal: 10,
651
+ paddingVertical: 5,
652
+ borderRadius: 8,
653
+ borderWidth: 1,
654
+ borderColor: `${AppColors.purple}26`,
655
+ }}>
653
656
  <SettingsIcon color={AppColors.purple} size={11}/>
654
657
  <Text style={{
655
- fontFamily: AppFonts.interBold,
656
- fontSize: 11,
657
- lineHeight: 14,
658
- color: AppColors.purple,
659
- }}>
658
+ fontFamily: AppFonts.interBold,
659
+ fontSize: 11,
660
+ lineHeight: 14,
661
+ color: AppColors.purple,
662
+ }}>
660
663
  {t('settings.configure')}
661
664
  </Text>
662
665
  <ForwardChevronIcon color={AppColors.purple} size={9}/>
663
666
  </TouchableScale>
664
667
  </View>)}
665
668
  </View>);
666
- })}
669
+ })}
667
670
  </View>
668
671
 
669
672
  {/* Section 3.5: RAM-Based Auto Limits */}
670
673
  <View style={{
671
- backgroundColor: AppColors.primaryLight,
672
- borderRadius: 14,
673
- borderWidth: 1,
674
- borderColor: AppColors.grayBorderSecondary,
675
- overflow: 'hidden',
676
- padding: 14,
677
- gap: 12,
678
- }}>
674
+ backgroundColor: AppColors.primaryLight,
675
+ borderRadius: 14,
676
+ borderWidth: 1,
677
+ borderColor: AppColors.grayBorderSecondary,
678
+ overflow: 'hidden',
679
+ padding: 14,
680
+ gap: 12,
681
+ }}>
679
682
  <Text style={{
680
- fontFamily: AppFonts.interBold,
681
- fontSize: 11,
682
- lineHeight: 14,
683
- color: AppColors.grayTextWeak,
684
- letterSpacing: 0.8,
685
- }}>
683
+ fontFamily: AppFonts.interBold,
684
+ fontSize: 11,
685
+ lineHeight: 14,
686
+ color: AppColors.grayTextWeak,
687
+ letterSpacing: 0.8,
688
+ }}>
686
689
  RAM-Based Auto Limits
687
690
  </Text>
688
691
 
689
692
  {/* Auto RAM Limit Toggle */}
690
693
  <View style={{
691
- flexDirection: 'row',
692
- alignItems: 'center',
693
- justifyContent: 'space-between',
694
- }}>
694
+ flexDirection: 'row',
695
+ alignItems: 'center',
696
+ justifyContent: 'space-between',
697
+ }}>
695
698
  <View style={{
696
- flexDirection: 'row',
697
- alignItems: 'center',
698
- gap: 10,
699
- flex: 1,
700
- }}>
699
+ flexDirection: 'row',
700
+ alignItems: 'center',
701
+ gap: 10,
702
+ flex: 1,
703
+ }}>
701
704
  <View style={{
702
- width: 32,
703
- height: 32,
704
- borderRadius: 8,
705
- backgroundColor: AppColors.purpleShade50,
706
- alignItems: 'center',
707
- justifyContent: 'center',
708
- }}>
705
+ width: 32,
706
+ height: 32,
707
+ borderRadius: 8,
708
+ backgroundColor: AppColors.purpleShade50,
709
+ alignItems: 'center',
710
+ justifyContent: 'center',
711
+ }}>
709
712
  <PerformanceIcon color={AppColors.purple} size={15}/>
710
713
  </View>
711
714
  <View style={{ flex: 1 }}>
712
715
  <Text style={{
713
- fontFamily: AppFonts.interBold,
714
- fontSize: 13.5,
715
- lineHeight: 18,
716
- color: AppColors.primaryBlack,
717
- }}>
716
+ fontFamily: AppFonts.interBold,
717
+ fontSize: 13.5,
718
+ lineHeight: 18,
719
+ color: AppColors.primaryBlack,
720
+ }}>
718
721
  Auto-Calculate Limits from Device RAM
719
722
  </Text>
720
723
  <Text style={{
721
- fontFamily: AppFonts.interRegular,
722
- fontSize: 11,
723
- lineHeight: 15,
724
- color: AppColors.grayText,
725
- marginTop: 1,
726
- }}>
724
+ fontFamily: AppFonts.interRegular,
725
+ fontSize: 11,
726
+ lineHeight: 15,
727
+ color: AppColors.grayText,
728
+ marginTop: 1,
729
+ }}>
727
730
  Automatically set API, Logs, Analytics & Crash limits based on available memory
728
731
  </Text>
729
732
  </View>
730
733
  </View>
731
734
 
732
735
  <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
- }}>
736
+ width: 42,
737
+ height: 24,
738
+ borderRadius: 12,
739
+ backgroundColor: isAutoRamLimitEnabled
740
+ ? AppColors.purple
741
+ : AppColors.grayBorderSecondary,
742
+ padding: 2,
743
+ justifyContent: 'center',
744
+ alignItems: isAutoRamLimitEnabled ? 'flex-end' : 'flex-start',
745
+ }}>
743
746
  <View style={{
744
- width: 20,
745
- height: 20,
746
- borderRadius: 10,
747
- backgroundColor: AppColors.white,
748
- shadowColor: AppColors.black,
749
- shadowOpacity: 0.18,
750
- shadowRadius: 2,
751
- shadowOffset: { width: 0, height: 1 },
752
- }}/>
747
+ width: 20,
748
+ height: 20,
749
+ borderRadius: 10,
750
+ backgroundColor: AppColors.white,
751
+ shadowColor: AppColors.black,
752
+ shadowOpacity: 0.18,
753
+ shadowRadius: 2,
754
+ shadowOffset: { width: 0, height: 1 },
755
+ }}/>
753
756
  </TouchableScale>
754
757
  </View>
755
758
 
@@ -758,58 +761,58 @@ const SettingsPanel = () => {
758
761
  {/* Device Free RAM Display */}
759
762
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
760
763
  <View style={{
761
- width: 32,
762
- height: 32,
763
- borderRadius: 8,
764
- backgroundColor: isAutoRamLimitEnabled
765
- ? `${AppColors.purple}14`
766
- : AppColors.grayBackground,
767
- alignItems: 'center',
768
- justifyContent: 'center',
769
- }}>
764
+ width: 32,
765
+ height: 32,
766
+ borderRadius: 8,
767
+ backgroundColor: isAutoRamLimitEnabled
768
+ ? `${AppColors.purple}14`
769
+ : AppColors.grayBackground,
770
+ alignItems: 'center',
771
+ justifyContent: 'center',
772
+ }}>
770
773
  <SignalIcon color={isAutoRamLimitEnabled ? AppColors.purple : AppColors.grayTextWeak} size={15}/>
771
774
  </View>
772
775
  <View style={{ flex: 1 }}>
773
776
  <Text style={{
774
- fontFamily: AppFonts.interBold,
775
- fontSize: 13.5,
776
- lineHeight: 18,
777
- color: isAutoRamLimitEnabled
778
- ? AppColors.primaryBlack
779
- : AppColors.grayText,
780
- }}>
777
+ fontFamily: AppFonts.interBold,
778
+ fontSize: 13.5,
779
+ lineHeight: 18,
780
+ color: isAutoRamLimitEnabled
781
+ ? AppColors.primaryBlack
782
+ : AppColors.grayText,
783
+ }}>
781
784
  Detected Free RAM
782
785
  </Text>
783
786
  <Text style={{
784
- fontFamily: AppFonts.interRegular,
785
- fontSize: 11,
786
- lineHeight: 15,
787
- color: AppColors.grayText,
788
- marginTop: 1,
789
- }}>
787
+ fontFamily: AppFonts.interRegular,
788
+ fontSize: 11,
789
+ lineHeight: 15,
790
+ color: AppColors.grayText,
791
+ marginTop: 1,
792
+ }}>
790
793
  {deviceFreeRamMb} MB available
791
794
  </Text>
792
795
  </View>
793
796
  <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
- }}>
797
+ paddingHorizontal: 10,
798
+ paddingVertical: 5,
799
+ borderRadius: 8,
800
+ backgroundColor: isAutoRamLimitEnabled
801
+ ? `${AppColors.purple}12`
802
+ : AppColors.grayBackground,
803
+ borderWidth: 1,
804
+ borderColor: isAutoRamLimitEnabled
805
+ ? `${AppColors.purple}26`
806
+ : AppColors.dividerColor,
807
+ }}>
805
808
  <Text style={{
806
- fontFamily: AppFonts.interBold,
807
- fontSize: 12,
808
- lineHeight: 15,
809
- color: isAutoRamLimitEnabled
810
- ? AppColors.purple
811
- : AppColors.grayText,
812
- }}>
809
+ fontFamily: AppFonts.interBold,
810
+ fontSize: 12,
811
+ lineHeight: 15,
812
+ color: isAutoRamLimitEnabled
813
+ ? AppColors.purple
814
+ : AppColors.grayText,
815
+ }}>
813
816
  {deviceFreeRamMb} MB
814
817
  </Text>
815
818
  </View>
@@ -818,44 +821,44 @@ const SettingsPanel = () => {
818
821
  {/* Current Auto-Calculated Limits Preview */}
819
822
  {isAutoRamLimitEnabled && (<View style={{ marginTop: 8, paddingTop: 8, borderTopWidth: 1, borderTopColor: AppColors.dividerColor, gap: 6 }}>
820
823
  <Text style={{
821
- fontFamily: AppFonts.interBold,
822
- fontSize: 11.5,
823
- lineHeight: 15,
824
- color: AppColors.purple,
825
- }}>
824
+ fontFamily: AppFonts.interBold,
825
+ fontSize: 11.5,
826
+ lineHeight: 15,
827
+ color: AppColors.purple,
828
+ }}>
826
829
  Auto Profile: {autoRamProfile.profileName}
827
830
  </Text>
828
831
  <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>
829
832
  {[
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
- }}>
833
+ { label: 'APIs', value: autoRamProfile.maxNetworkLogs, color: AppColors.blue500 },
834
+ { label: 'Logs', value: autoRamProfile.maxConsoleLogs, color: AppColors.sky500 },
835
+ { label: 'Analytics', value: autoRamProfile.maxAnalyticsEvents, color: AppColors.purple },
836
+ { label: 'Crash', value: autoRamProfile.maxCrashRecords, color: AppColors.errorColor },
837
+ ].map(item => (<View key={item.label} style={{
838
+ flexDirection: 'row',
839
+ alignItems: 'center',
840
+ gap: 4,
841
+ paddingHorizontal: 8,
842
+ paddingVertical: 4,
843
+ borderRadius: 6,
844
+ backgroundColor: `${item.color}14`,
845
+ borderWidth: 1,
846
+ borderColor: `${item.color}33`,
847
+ }}>
848
+ <Text style={{
849
+ fontFamily: AppFonts.interBold,
850
+ fontSize: 10.5,
851
+ lineHeight: 14,
852
+ color: item.color,
853
+ }}>
851
854
  {item.label}
852
855
  </Text>
853
856
  <Text style={{
854
- fontFamily: AppFonts.interBold,
855
- fontSize: 10.5,
856
- lineHeight: 14,
857
- color: item.color,
858
- }}>
857
+ fontFamily: AppFonts.interBold,
858
+ fontSize: 10.5,
859
+ lineHeight: 14,
860
+ color: item.color,
861
+ }}>
859
862
  {item.value}
860
863
  </Text>
861
864
  </View>))}
@@ -865,184 +868,189 @@ const SettingsPanel = () => {
865
868
  </View>) : (<View style={{ gap: 14 }}>
866
869
  {/* Section 1: Appearance */}
867
870
  <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
- }}>
871
+ backgroundColor: AppColors.primaryLight,
872
+ borderRadius: 14,
873
+ borderWidth: 1,
874
+ borderColor: AppColors.grayBorderSecondary,
875
+ overflow: 'hidden',
876
+ padding: 14,
877
+ gap: 12,
878
+ }}>
876
879
  <Text style={{
877
- fontFamily: AppFonts.interBold,
878
- fontSize: 11,
879
- lineHeight: 14,
880
- color: AppColors.grayTextWeak,
881
- letterSpacing: 0.8,
882
- }}>
880
+ fontFamily: AppFonts.interBold,
881
+ fontSize: 11,
882
+ lineHeight: 14,
883
+ color: AppColors.grayTextWeak,
884
+ letterSpacing: 0.8,
885
+ }}>
883
886
  {t('settings.appearanceTheme')}
884
887
  </Text>
885
888
 
886
889
  <View style={{
887
- flexDirection: 'row',
888
- alignItems: 'center',
889
- justifyContent: 'space-between',
890
- }}>
890
+ flexDirection: 'row',
891
+ alignItems: 'center',
892
+ justifyContent: 'space-between',
893
+ }}>
891
894
  <View style={{
892
- flexDirection: 'row',
893
- alignItems: 'center',
894
- gap: 10,
895
- flex: 1,
896
- }}>
895
+ flexDirection: 'row',
896
+ alignItems: 'center',
897
+ gap: 10,
898
+ flex: 1,
899
+ }}>
897
900
  <View style={{
898
- width: 32,
899
- height: 32,
900
- borderRadius: 8,
901
- backgroundColor: AppColors.purpleShade50,
902
- alignItems: 'center',
903
- justifyContent: 'center',
904
- }}>
901
+ width: 32,
902
+ height: 32,
903
+ borderRadius: 8,
904
+ backgroundColor: AppColors.purpleShade50,
905
+ alignItems: 'center',
906
+ justifyContent: 'center',
907
+ }}>
905
908
  {isDark ? (<SunIcon color={AppColors.purple} size={15}/>) : (<MoonIcon color={AppColors.purple} size={15}/>)}
906
909
  </View>
907
910
  <View style={{ flex: 1 }}>
908
911
  <Text style={{
909
- fontFamily: AppFonts.interBold,
910
- fontSize: 13.5,
911
- lineHeight: 18,
912
- color: AppColors.primaryBlack,
913
- }}>
912
+ fontFamily: AppFonts.interBold,
913
+ fontSize: 13.5,
914
+ lineHeight: 18,
915
+ color: AppColors.primaryBlack,
916
+ }}>
914
917
  {t('settings.general.darkMode')}
915
918
  </Text>
916
919
  <Text style={{
917
- fontFamily: AppFonts.interRegular,
918
- fontSize: 11,
919
- lineHeight: 15,
920
- color: AppColors.grayText,
921
- marginTop: 1,
922
- }}>
920
+ fontFamily: AppFonts.interRegular,
921
+ fontSize: 11,
922
+ lineHeight: 15,
923
+ color: AppColors.grayText,
924
+ marginTop: 1,
925
+ }}>
923
926
  {t('settings.general.darkModeDescription')}
924
927
  </Text>
925
928
  </View>
926
929
  </View>
927
930
 
928
931
  <TouchableScale accessible={true} accessibilityRole="switch" accessibilityLabel="Toggle Dark Theme" accessibilityState={{ checked: isDark }} onPress={() => {
929
- const newTheme = !isDark;
930
- setIsDark(newTheme);
931
- toggleGlobalTheme(newTheme);
932
- }} style={{
933
- width: 42,
934
- height: 24,
935
- borderRadius: 12,
936
- backgroundColor: isDark
937
- ? AppColors.purple
938
- : AppColors.grayBorderSecondary,
939
- padding: 2,
940
- justifyContent: 'center',
941
- alignItems: isDark ? 'flex-end' : 'flex-start',
942
- }}>
932
+ const newTheme = !isDark;
933
+ setIsDark(newTheme);
934
+ toggleGlobalTheme(newTheme);
935
+ }} style={{
936
+ width: 42,
937
+ height: 24,
938
+ borderRadius: 12,
939
+ backgroundColor: isDark
940
+ ? AppColors.purple
941
+ : AppColors.grayBorderSecondary,
942
+ padding: 2,
943
+ justifyContent: 'center',
944
+ alignItems: isDark ? 'flex-end' : 'flex-start',
945
+ }}>
943
946
  <View style={{
944
- width: 20,
945
- height: 20,
946
- borderRadius: 10,
947
- backgroundColor: AppColors.white,
948
- shadowColor: AppColors.black,
949
- shadowOpacity: 0.18,
950
- shadowRadius: 2,
951
- shadowOffset: { width: 0, height: 1 },
952
- }}/>
947
+ width: 20,
948
+ height: 20,
949
+ borderRadius: 10,
950
+ backgroundColor: AppColors.white,
951
+ shadowColor: AppColors.black,
952
+ shadowOpacity: 0.18,
953
+ shadowRadius: 2,
954
+ shadowOffset: { width: 0, height: 1 },
955
+ }}/>
953
956
  </TouchableScale>
954
957
  </View>
955
958
  </View>
956
959
 
957
960
  {/* Section 2: Window & Layout */}
958
961
  <View style={{
959
- backgroundColor: AppColors.primaryLight,
960
- borderRadius: 14,
961
- borderWidth: 1,
962
- borderColor: AppColors.grayBorderSecondary,
963
- overflow: 'hidden',
964
- padding: 14,
965
- gap: 14,
966
- }}>
962
+ backgroundColor: AppColors.primaryLight,
963
+ borderRadius: 14,
964
+ borderWidth: 1,
965
+ borderColor: AppColors.grayBorderSecondary,
966
+ overflow: 'hidden',
967
+ padding: 14,
968
+ gap: 14,
969
+ }}>
967
970
  <Text style={{
968
- fontFamily: AppFonts.interBold,
969
- fontSize: 11,
970
- lineHeight: 14,
971
- color: AppColors.grayTextWeak,
972
- letterSpacing: 0.8,
973
- }}>
971
+ fontFamily: AppFonts.interBold,
972
+ fontSize: 11,
973
+ lineHeight: 14,
974
+ color: AppColors.grayTextWeak,
975
+ letterSpacing: 0.8,
976
+ }}>
974
977
  {t('settings.windowLayout')}
975
978
  </Text>
976
979
 
977
980
  {/* Modal Height */}
978
981
  <View style={{ gap: 8 }}>
979
982
  <View style={{
980
- flexDirection: 'row',
981
- alignItems: 'center',
982
- gap: 10,
983
- }}>
983
+ flexDirection: 'row',
984
+ alignItems: 'center',
985
+ justifyContent: 'space-between',
986
+ gap: 10,
987
+ }}>
984
988
  <View style={{
985
- width: 32,
986
- height: 32,
987
- borderRadius: 8,
988
- backgroundColor: AppColors.purpleShade50,
989
+ flexDirection: 'row',
990
+ alignItems: 'center',
991
+ gap: 10,
992
+ flex: 1,
993
+ }}>
994
+ <View style={{
995
+ width: 32,
996
+ height: 32,
997
+ borderRadius: 8,
998
+ backgroundColor: AppColors.purpleShade50,
999
+ alignItems: 'center',
1000
+ justifyContent: 'center',
1001
+ }}>
1002
+ <ScreenIcon color={AppColors.purple} size={15}/>
1003
+ </View>
1004
+ <View style={{ flex: 1 }}>
1005
+ <Text style={{
1006
+ fontFamily: AppFonts.interBold,
1007
+ fontSize: 13.5,
1008
+ lineHeight: 18,
1009
+ color: AppColors.primaryBlack,
1010
+ }}>
1011
+ {t('settings.general.modalHeight')}
1012
+ </Text>
1013
+ <Text style={{
1014
+ fontFamily: AppFonts.interRegular,
1015
+ fontSize: 11,
1016
+ lineHeight: 15,
1017
+ color: AppColors.grayText,
1018
+ marginTop: 1,
1019
+ }}>
1020
+ {t('settings.general.modalHeightDescription')}
1021
+ </Text>
1022
+ </View>
1023
+ </View>
1024
+
1025
+ {stagedHeight !== modalHeightPercent && (<TouchableScale onPress={() => {
1026
+ setModalHeightPercent(stagedHeight);
1027
+ }} style={{
1028
+ flexDirection: 'row',
989
1029
  alignItems: 'center',
990
- justifyContent: 'center',
1030
+ gap: 5,
1031
+ backgroundColor: AppColors.purple,
1032
+ paddingHorizontal: 10,
1033
+ paddingVertical: 5,
1034
+ borderRadius: 7,
1035
+ shadowColor: AppColors.purple,
1036
+ shadowOffset: { width: 0, height: 1 },
1037
+ shadowOpacity: 0.28,
1038
+ shadowRadius: 2,
1039
+ elevation: 2,
991
1040
  }}>
992
- <ScreenIcon color={AppColors.purple} size={15}/>
993
- </View>
994
- <View style={{ flex: 1 }}>
995
- <Text style={{
1041
+ <CheckIcon size={11} color={AppColors.white}/>
1042
+ <Text style={{
996
1043
  fontFamily: AppFonts.interBold,
997
- fontSize: 13.5,
998
- lineHeight: 18,
999
- color: AppColors.primaryBlack,
1000
- }}>
1001
- {t('settings.general.modalHeight')}
1002
- </Text>
1003
- <Text style={{
1004
- fontFamily: AppFonts.interRegular,
1005
1044
  fontSize: 11,
1006
- lineHeight: 15,
1007
- color: AppColors.grayText,
1008
- marginTop: 1,
1045
+ color: AppColors.white,
1009
1046
  }}>
1010
- {t('settings.general.modalHeightDescription')}
1011
- </Text>
1012
- </View>
1047
+ Save ({stagedHeight}%)
1048
+ </Text>
1049
+ </TouchableScale>)}
1013
1050
  </View>
1014
1051
 
1015
- <View style={{
1016
- flexDirection: 'row',
1017
- backgroundColor: AppColors.grayBackground,
1018
- borderRadius: 10,
1019
- padding: 3,
1020
- borderWidth: 1,
1021
- borderColor: AppColors.dividerColor,
1022
- }}>
1023
- {[50, 70, 90, 100].map(opt => {
1024
- const isActive = modalHeightPercent === opt;
1025
- return (<TouchableScale key={opt} accessible={true} accessibilityRole="button" accessibilityLabel={`Set window height to ${opt} percent`} accessibilityState={{ selected: isActive }} onPress={() => setModalHeightPercent(opt)} style={{
1026
- flex: 1,
1027
- paddingVertical: 7,
1028
- alignItems: 'center',
1029
- borderRadius: 8,
1030
- backgroundColor: isActive
1031
- ? AppColors.purple
1032
- : 'transparent',
1033
- }}>
1034
- <Text style={{
1035
- fontFamily: AppFonts.interBold,
1036
- fontSize: 11.5,
1037
- lineHeight: 15,
1038
- color: isActive
1039
- ? AppColors.white
1040
- : AppColors.grayText,
1041
- }}>
1042
- {opt}%
1043
- </Text>
1044
- </TouchableScale>);
1045
- })}
1052
+ <View style={{ marginTop: 6 }}>
1053
+ <Slider value={stagedHeight} onValueChange={setStagedHeight} min={50} max={90} step={5} quickPresets={[50, 60, 70, 80, 90]} formatLabel={val => `${Math.round(val)}%`}/>
1046
1054
  </View>
1047
1055
  </View>
1048
1056
 
@@ -1051,694 +1059,330 @@ const SettingsPanel = () => {
1051
1059
  {/* Modal Animation */}
1052
1060
  <View style={{ gap: 8 }}>
1053
1061
  <View style={{
1054
- flexDirection: 'row',
1055
- alignItems: 'center',
1056
- gap: 10,
1057
- }}>
1062
+ flexDirection: 'row',
1063
+ alignItems: 'center',
1064
+ gap: 10,
1065
+ }}>
1058
1066
  <View style={{
1059
- width: 32,
1060
- height: 32,
1061
- borderRadius: 8,
1062
- backgroundColor: AppColors.purpleShade50,
1063
- alignItems: 'center',
1064
- justifyContent: 'center',
1065
- }}>
1067
+ width: 32,
1068
+ height: 32,
1069
+ borderRadius: 8,
1070
+ backgroundColor: AppColors.purpleShade50,
1071
+ alignItems: 'center',
1072
+ justifyContent: 'center',
1073
+ }}>
1066
1074
  <MotionIcon color={AppColors.purple} size={15}/>
1067
1075
  </View>
1068
1076
  <View style={{ flex: 1 }}>
1069
1077
  <Text style={{
1070
- fontFamily: AppFonts.interBold,
1071
- fontSize: 13.5,
1072
- lineHeight: 18,
1073
- color: AppColors.primaryBlack,
1074
- }}>
1078
+ fontFamily: AppFonts.interBold,
1079
+ fontSize: 13.5,
1080
+ lineHeight: 18,
1081
+ color: AppColors.primaryBlack,
1082
+ }}>
1075
1083
  {t('settings.general.modalAnimation')}
1076
1084
  </Text>
1077
1085
  <Text style={{
1078
- fontFamily: AppFonts.interRegular,
1079
- fontSize: 11,
1080
- lineHeight: 15,
1081
- color: AppColors.grayText,
1082
- marginTop: 1,
1083
- }}>
1086
+ fontFamily: AppFonts.interRegular,
1087
+ fontSize: 11,
1088
+ lineHeight: 15,
1089
+ color: AppColors.grayText,
1090
+ marginTop: 1,
1091
+ }}>
1084
1092
  {t('settings.general.modalAnimationDescription')}
1085
1093
  </Text>
1086
1094
  </View>
1087
1095
  </View>
1088
1096
 
1089
1097
  <View style={{
1090
- flexDirection: 'row',
1091
- backgroundColor: AppColors.grayBackground,
1092
- borderRadius: 10,
1093
- padding: 3,
1094
- borderWidth: 1,
1095
- borderColor: AppColors.dividerColor,
1096
- }}>
1098
+ flexDirection: 'row',
1099
+ backgroundColor: AppColors.grayBackground,
1100
+ borderRadius: 10,
1101
+ padding: 3,
1102
+ borderWidth: 1,
1103
+ borderColor: AppColors.dividerColor,
1104
+ }}>
1097
1105
  {[
1098
- { key: 'slide', label: 'Slide Up' },
1099
- { key: 'fade', label: 'Fade' },
1100
- { key: 'none', label: 'None' },
1101
- ].map(opt => {
1102
- const isActive = modalAnimationType === opt.key;
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={{
1104
- flex: 1,
1105
- paddingVertical: 7,
1106
- alignItems: 'center',
1107
- borderRadius: 8,
1108
- backgroundColor: isActive
1109
- ? AppColors.purple
1110
- : 'transparent',
1111
- }}>
1106
+ { key: 'slide', label: 'Slide Up' },
1107
+ { key: 'fade', label: 'Fade' },
1108
+ { key: 'none', label: 'None' },
1109
+ ].map(opt => {
1110
+ const isActive = modalAnimationType === opt.key;
1111
+ return (<TouchableScale key={opt.key} accessible={true} accessibilityRole="button" accessibilityLabel={`Set transition animation to ${opt.label}`} accessibilityState={{ selected: isActive }} onPress={() => setModalAnimationType(opt.key)} style={{
1112
+ flex: 1,
1113
+ paddingVertical: 7,
1114
+ alignItems: 'center',
1115
+ borderRadius: 8,
1116
+ backgroundColor: isActive
1117
+ ? AppColors.purple
1118
+ : 'transparent',
1119
+ }}>
1112
1120
  <Text style={{
1113
- fontFamily: AppFonts.interBold,
1114
- fontSize: 11.5,
1115
- lineHeight: 15,
1116
- color: isActive
1117
- ? AppColors.white
1118
- : AppColors.grayText,
1119
- }}>
1121
+ fontFamily: AppFonts.interBold,
1122
+ fontSize: 11.5,
1123
+ lineHeight: 15,
1124
+ color: isActive
1125
+ ? AppColors.white
1126
+ : AppColors.grayText,
1127
+ }}>
1120
1128
  {opt.label}
1121
1129
  </Text>
1122
1130
  </TouchableScale>);
1123
- })}
1131
+ })}
1124
1132
  </View>
1125
1133
  </View>
1126
1134
  </View>
1127
1135
 
1128
1136
  {/* Section 3: Default Startup Tab */}
1129
1137
  <View style={{
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,
1144
- }}>
1138
+ backgroundColor: AppColors.primaryLight,
1139
+ borderRadius: 14,
1140
+ borderWidth: 1,
1141
+ borderColor: AppColors.grayBorderSecondary,
1142
+ overflow: 'hidden',
1143
+ padding: 14,
1144
+ gap: 12,
1145
+ }}>
1146
+ <Text style={{
1147
+ fontFamily: AppFonts.interBold,
1148
+ fontSize: 11,
1149
+ lineHeight: 14,
1150
+ color: AppColors.grayTextWeak,
1151
+ letterSpacing: 0.8,
1152
+ }}>
1145
1153
  {t('settings.startupDefault')}
1146
1154
  </Text>
1147
1155
 
1148
1156
  <View style={{
1149
- flexDirection: 'row',
1150
- alignItems: 'center',
1151
- gap: 10,
1152
- }}>
1157
+ flexDirection: 'row',
1158
+ alignItems: 'center',
1159
+ gap: 10,
1160
+ }}>
1153
1161
  <View style={{
1154
- width: 32,
1155
- height: 32,
1156
- borderRadius: 8,
1157
- backgroundColor: AppColors.purpleShade50,
1158
- alignItems: 'center',
1159
- justifyContent: 'center',
1160
- }}>
1162
+ width: 32,
1163
+ height: 32,
1164
+ borderRadius: 8,
1165
+ backgroundColor: AppColors.purpleShade50,
1166
+ alignItems: 'center',
1167
+ justifyContent: 'center',
1168
+ }}>
1161
1169
  <LayersIcon color={AppColors.purple} size={15}/>
1162
1170
  </View>
1163
1171
  <View style={{ flex: 1 }}>
1164
1172
  <Text style={{
1165
- fontFamily: AppFonts.interBold,
1166
- fontSize: 13.5,
1167
- lineHeight: 18,
1168
- color: AppColors.primaryBlack,
1169
- }}>
1173
+ fontFamily: AppFonts.interBold,
1174
+ fontSize: 13.5,
1175
+ lineHeight: 18,
1176
+ color: AppColors.primaryBlack,
1177
+ }}>
1170
1178
  {t('settings.general.defaultOpeningTab')}
1171
1179
  </Text>
1172
1180
  <Text style={{
1173
- fontFamily: AppFonts.interRegular,
1174
- fontSize: 11,
1175
- lineHeight: 15,
1176
- color: AppColors.grayText,
1177
- marginTop: 1,
1178
- }}>
1181
+ fontFamily: AppFonts.interRegular,
1182
+ fontSize: 11,
1183
+ lineHeight: 15,
1184
+ color: AppColors.grayText,
1185
+ marginTop: 1,
1186
+ }}>
1179
1187
  {t('settings.general.defaultOpeningTabDesc')}
1180
1188
  </Text>
1181
1189
  </View>
1182
1190
  </View>
1183
1191
 
1184
- {/* Grid of Default Tab Cards */}
1185
- <View style={{
1186
- flexDirection: 'row',
1187
- flexWrap: 'wrap',
1188
- gap: 8,
1189
- marginTop: 4,
1190
- }}>
1191
- {allModules
1192
- .filter(tab => tab.key === 'apis' ||
1193
- stagedTabVisibility?.[tab.key] ||
1194
- tabVisibility?.[tab.key])
1195
- .map(tab => {
1196
- const isActive = defaultTab === tab.key;
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={{
1198
- flexDirection: 'row',
1199
- alignItems: 'center',
1200
- gap: 8,
1201
- paddingVertical: 10,
1202
- paddingHorizontal: 12,
1203
- borderRadius: 10,
1204
- borderWidth: 1.5,
1205
- borderColor: isActive
1206
- ? AppColors.purple
1207
- : AppColors.grayBorderSecondary,
1208
- backgroundColor: isActive
1209
- ? `${AppColors.purple}0F`
1210
- : AppColors.primaryLight,
1211
- minWidth: '47%',
1212
- flex: 1,
1213
- }}>
1214
- <Text style={{
1215
- fontFamily: AppFonts.interBold,
1216
- fontSize: 12.5,
1217
- lineHeight: 16,
1218
- color: isActive
1219
- ? AppColors.purple
1220
- : AppColors.primaryBlack,
1221
- flex: 1,
1222
- }}>
1223
- {tab.label}
1224
- </Text>
1225
- {isActive && (<View style={{
1226
- width: 16,
1227
- height: 16,
1228
- borderRadius: 8,
1229
- backgroundColor: AppColors.purple,
1230
- alignItems: 'center',
1231
- justifyContent: 'center',
1232
- }}>
1233
- <CheckIcon size={9} color={AppColors.white}/>
1234
- </View>)}
1235
- </TouchableScale>);
1236
- })}
1237
- </View>
1238
- </View>
1239
-
1240
- {/* Section 4: Log Deduplication & Levels */}
1241
- <View style={{
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>
1259
-
1260
- {/* Show Duplicate Logs */}
1261
- <View style={{
1262
- flexDirection: 'row',
1263
- alignItems: 'center',
1264
- justifyContent: 'space-between',
1265
- }}>
1266
- <View style={{
1267
- flexDirection: 'row',
1268
- alignItems: 'center',
1269
- gap: 10,
1270
- flex: 1,
1271
- }}>
1192
+ {/* Sleek Interactive Dropdown Picker */}
1193
+ <View style={{ marginTop: 6, gap: 6 }}>
1194
+ <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={`Default opening tab: ${allModules.find(m => m.key === defaultTab)?.label || 'APIs'}`} onPress={() => setIsDefaultTabDropdownOpen(prev => !prev)} style={{
1195
+ flexDirection: 'row',
1196
+ alignItems: 'center',
1197
+ justifyContent: 'space-between',
1198
+ backgroundColor: AppColors.grayBackground,
1199
+ borderRadius: 10,
1200
+ borderWidth: 1.5,
1201
+ borderColor: isDefaultTabDropdownOpen
1202
+ ? AppColors.purple
1203
+ : AppColors.grayBorderSecondary,
1204
+ paddingHorizontal: 14,
1205
+ paddingVertical: 11,
1206
+ }}>
1272
1207
  <View style={{
1273
- width: 32,
1274
- height: 32,
1275
- borderRadius: 8,
1276
- backgroundColor: AppColors.purpleShade50,
1277
- alignItems: 'center',
1278
- justifyContent: 'center',
1279
- }}>
1280
- <EyeIcon color={AppColors.purple} size={15}/>
1281
- </View>
1282
- <View style={{ flex: 1 }}>
1283
- <Text style={{
1284
- fontFamily: AppFonts.interBold,
1285
- fontSize: 13.5,
1286
- lineHeight: 18,
1287
- color: AppColors.primaryBlack,
1288
- }}>
1289
- {t('settings.general.duplicateLogs')}
1290
- </Text>
1208
+ flexDirection: 'row',
1209
+ alignItems: 'center',
1210
+ gap: 10,
1211
+ flex: 1,
1212
+ }}>
1213
+ <View style={{
1214
+ width: 8,
1215
+ height: 8,
1216
+ borderRadius: 4,
1217
+ backgroundColor: AppColors.purple,
1218
+ }}/>
1291
1219
  <Text style={{
1292
- fontFamily: AppFonts.interRegular,
1293
- fontSize: 11,
1294
- lineHeight: 15,
1295
- color: AppColors.grayText,
1296
- marginTop: 1,
1297
- }}>
1298
- {t('settings.general.duplicateLogsDescription')}
1220
+ fontFamily: AppFonts.interBold,
1221
+ fontSize: 13.5,
1222
+ lineHeight: 18,
1223
+ color: AppColors.primaryBlack,
1224
+ }}>
1225
+ {allModules.find(m => m.key === defaultTab)?.label || 'APIs (Network)'}
1299
1226
  </Text>
1227
+ <View style={{
1228
+ backgroundColor: `${AppColors.purple}14`,
1229
+ paddingHorizontal: 6,
1230
+ paddingVertical: 1.5,
1231
+ borderRadius: 6,
1232
+ }}>
1233
+ <Text style={{
1234
+ fontFamily: AppFonts.interBold,
1235
+ fontSize: 8.5,
1236
+ color: AppColors.purple,
1237
+ }}>
1238
+ DEFAULT
1239
+ </Text>
1240
+ </View>
1300
1241
  </View>
1301
- </View>
1302
1242
 
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,
1307
- backgroundColor: showDuplicateLogs
1308
- ? AppColors.purple
1309
- : AppColors.grayBorderSecondary,
1310
- padding: 2,
1311
- justifyContent: 'center',
1312
- alignItems: showDuplicateLogs
1313
- ? 'flex-end'
1314
- : 'flex-start',
1315
- }}>
1316
1243
  <View style={{
1317
- width: 20,
1318
- height: 20,
1319
- borderRadius: 10,
1320
- backgroundColor: AppColors.white,
1321
- shadowColor: AppColors.black,
1322
- shadowOpacity: 0.18,
1323
- shadowRadius: 2,
1324
- shadowOffset: { width: 0, height: 1 },
1325
- }}/>
1244
+ transform: [
1245
+ { rotate: isDefaultTabDropdownOpen ? '180deg' : '0deg' },
1246
+ ],
1247
+ }}>
1248
+ <ChevronDownIcon color={AppColors.grayText} size={15}/>
1249
+ </View>
1326
1250
  </TouchableScale>
1327
- </View>
1328
-
1329
- <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
1330
1251
 
1331
- {/* Console Log Levels */}
1332
- <View style={{ gap: 8 }}>
1333
- <View style={{
1334
- flexDirection: 'row',
1335
- alignItems: 'center',
1336
- gap: 10,
1337
- }}>
1338
- <View style={{
1339
- width: 32,
1340
- height: 32,
1341
- borderRadius: 8,
1342
- backgroundColor: AppColors.purpleShade50,
1343
- alignItems: 'center',
1344
- justifyContent: 'center',
1345
- }}>
1346
- <TerminalIcon color={AppColors.purple} size={15}/>
1347
- </View>
1348
- <View style={{ flex: 1 }}>
1349
- <Text style={{
1350
- fontFamily: AppFonts.interBold,
1351
- fontSize: 13.5,
1352
- lineHeight: 18,
1353
- color: AppColors.primaryBlack,
1354
- }}>
1355
- {t('settings.general.activeConsoleLogLevels')}
1356
- </Text>
1357
- <Text style={{
1358
- fontFamily: AppFonts.interRegular,
1359
- fontSize: 11,
1360
- lineHeight: 15,
1361
- color: AppColors.grayText,
1362
- marginTop: 1,
1252
+ {/* Dropdown Options List */}
1253
+ {isDefaultTabDropdownOpen && (<View style={{
1254
+ backgroundColor: AppColors.primaryLight,
1255
+ borderRadius: 10,
1256
+ borderWidth: 1,
1257
+ borderColor: AppColors.grayBorderSecondary,
1258
+ overflow: 'hidden',
1259
+ shadowColor: AppColors.black,
1260
+ shadowOpacity: 0.08,
1261
+ shadowRadius: 6,
1262
+ shadowOffset: { width: 0, height: 3 },
1263
+ elevation: 4,
1363
1264
  }}>
1364
- {t('settings.general.activeConsoleLogLevelsDesc')}
1365
- </Text>
1366
- </View>
1367
- </View>
1368
-
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 => {
1387
- const isActive = showConsoleLevels[level.key];
1388
- return (<TouchableScale key={level.key} accessible={true} accessibilityRole="checkbox" accessibilityLabel={`Toggle ${level.label} logs`} accessibilityState={{ checked: isActive }} onPress={() => setShowConsoleLevels(prev => ({
1389
- ...prev,
1390
- [level.key]: !prev[level.key],
1391
- }))} style={{
1392
- flex: 1,
1265
+ {allModules
1266
+ .filter(tab => tab.key === 'apis' ||
1267
+ stagedTabVisibility?.[tab.key] ||
1268
+ tabVisibility?.[tab.key])
1269
+ .map((tab, idx, arr) => {
1270
+ const isActive = defaultTab === tab.key;
1271
+ const isLast = idx === arr.length - 1;
1272
+ return (<TouchableScale key={tab.key} accessible={true} accessibilityRole="button" accessibilityLabel={`Select ${tab.label} as default tab`} onPress={() => {
1273
+ setDefaultTab(tab.key);
1274
+ setIsDefaultTabDropdownOpen(false);
1275
+ }} style={{
1393
1276
  flexDirection: 'row',
1394
1277
  alignItems: 'center',
1395
- justifyContent: 'center',
1396
- gap: 6,
1397
- paddingVertical: 8,
1398
- borderRadius: 9,
1399
- borderWidth: 1.5,
1400
- borderColor: isActive
1401
- ? level.color
1402
- : AppColors.grayBorderSecondary,
1278
+ justifyContent: 'space-between',
1279
+ paddingHorizontal: 14,
1280
+ paddingVertical: 11,
1403
1281
  backgroundColor: isActive
1404
- ? `${level.color}0D`
1405
- : AppColors.primaryLight,
1282
+ ? `${AppColors.purple}0F`
1283
+ : 'transparent',
1284
+ borderBottomWidth: isLast ? 0 : 1,
1285
+ borderBottomColor: AppColors.dividerColor,
1406
1286
  }}>
1407
- <View style={{
1408
- width: 8,
1409
- height: 8,
1410
- borderRadius: 4,
1287
+ <View style={{
1288
+ flexDirection: 'row',
1289
+ alignItems: 'center',
1290
+ gap: 10,
1291
+ }}>
1292
+ <View style={{
1293
+ width: 6,
1294
+ height: 6,
1295
+ borderRadius: 3,
1411
1296
  backgroundColor: isActive
1412
- ? level.color
1413
- : AppColors.grayBorderSecondary,
1297
+ ? AppColors.purple
1298
+ : AppColors.grayTextWeak,
1414
1299
  }}/>
1415
- <Text style={{
1416
- fontFamily: AppFonts.interBold,
1417
- fontSize: 11.5,
1418
- lineHeight: 15,
1300
+ <Text style={{
1301
+ fontFamily: isActive
1302
+ ? AppFonts.interBold
1303
+ : AppFonts.interMedium,
1304
+ fontSize: 13,
1419
1305
  color: isActive
1420
- ? level.color
1421
- : AppColors.grayText,
1306
+ ? AppColors.purple
1307
+ : AppColors.primaryBlack,
1422
1308
  }}>
1423
- {level.label}
1424
- </Text>
1425
- </TouchableScale>);
1309
+ {tab.label}
1310
+ </Text>
1311
+ </View>
1312
+ {isActive && (<CheckIcon size={14} color={AppColors.purple}/>)}
1313
+ </TouchableScale>);
1426
1314
  })}
1427
- </View>
1315
+ </View>)}
1428
1316
  </View>
1429
1317
  </View>
1430
-
1431
- {/* Section 5: Reset Settings */}
1432
- <View style={{
1433
- backgroundColor: AppColors.primaryLight,
1434
- borderRadius: 14,
1435
- borderWidth: 1,
1436
- borderColor: `${AppColors.errorColor}33`,
1437
- overflow: 'hidden',
1438
- padding: 14,
1439
- flexDirection: 'row',
1440
- alignItems: 'center',
1441
- gap: 12,
1442
- }}>
1443
- <View style={{
1444
- width: 34,
1445
- height: 34,
1446
- borderRadius: 9,
1447
- backgroundColor: `${AppColors.errorColor}14`,
1448
- alignItems: 'center',
1449
- justifyContent: 'center',
1450
- }}>
1451
- <TrashIcon color={AppColors.errorColor} size={15}/>
1452
- </View>
1453
- <View style={{ flex: 1 }}>
1454
- <Text style={{
1455
- fontFamily: AppFonts.interBold,
1456
- fontSize: 13.5,
1457
- lineHeight: 18,
1458
- color: AppColors.primaryBlack,
1459
- }}>
1460
- {t('settings.resetSettings')}
1461
- </Text>
1462
- <Text style={{
1463
- fontFamily: AppFonts.interRegular,
1464
- fontSize: 11,
1465
- lineHeight: 15,
1466
- color: AppColors.grayText,
1467
- marginTop: 1,
1468
- }}>
1469
- {t('settings.resetSettingsDesc')}
1470
- </Text>
1471
- </View>
1472
-
1473
- <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel="Reset all settings to default" onPress={resetToDefaults} style={{
1474
- paddingHorizontal: 12,
1475
- paddingVertical: 7,
1476
- borderRadius: 8,
1477
- backgroundColor: `${AppColors.errorColor}14`,
1478
- borderWidth: 1,
1479
- borderColor: `${AppColors.errorColor}38`,
1480
- }}>
1481
- <Text style={{
1482
- fontFamily: AppFonts.interBold,
1483
- fontSize: 11.5,
1484
- lineHeight: 15,
1485
- color: AppColors.errorColor,
1486
- }}>
1487
- {t('settings.reset')}
1488
- </Text>
1489
- </TouchableScale>
1490
- </View>
1491
1318
  </View>)}
1492
-
1493
- {/* Storage & Engine Status Card */}
1494
- <View style={{
1495
- backgroundColor: isPersistent
1496
- ? `${AppColors.liveGreen}12`
1497
- : `${AppColors.amber600}12`,
1498
- borderRadius: 12,
1499
- borderWidth: 1,
1500
- borderColor: isPersistent
1501
- ? `${AppColors.liveGreen}33`
1502
- : `${AppColors.amber600}33`,
1503
- padding: 12,
1504
- marginBottom: 10,
1505
- flexDirection: 'row',
1506
- alignItems: 'center',
1507
- gap: 10,
1508
- }}>
1509
- <View style={{
1510
- width: 8,
1511
- height: 8,
1512
- borderRadius: 4,
1513
- backgroundColor: isPersistent
1514
- ? AppColors.green500
1515
- : AppColors.amber600,
1516
- }}/>
1517
- <View style={{ flex: 1 }}>
1518
- <Text style={{
1519
- fontFamily: AppFonts.interBold,
1520
- fontSize: 12,
1521
- lineHeight: 16,
1522
- color: isPersistent
1523
- ? AppColors.green700
1524
- : AppColors.amber800,
1525
- }}>
1526
- {isPersistent
1527
- ? t('settings.general.storageStatusEnabled', {
1528
- type: storage ? 'Custom Storage' : 'iOS NSUserDefaults',
1529
- })
1530
- : t('settings.general.storageStatusDisabled')}
1531
- </Text>
1532
- <Text style={{
1533
- fontFamily: AppFonts.interRegular,
1534
- fontSize: 10.5,
1535
- lineHeight: 14,
1536
- color: isPersistent
1537
- ? AppColors.green800
1538
- : AppColors.amber800,
1539
- marginTop: 2,
1540
- opacity: 0.85,
1541
- }}>
1542
- {isPersistent
1543
- ? t('settings.general.storageStatusEnabledDesc')
1544
- : t('settings.general.storageStatusDisabledDesc')}
1545
- </Text>
1546
- </View>
1547
- </View>
1319
+ <View style={{ height: 48 }}/>
1548
1320
  </ScrollView>
1549
1321
 
1550
1322
  {/* Bottom Sticky Action Bar for Save Changes */}
1551
1323
  {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
- }}>
1324
+ paddingHorizontal: 16,
1325
+ paddingTop: 12,
1326
+ paddingBottom: Platform.OS === 'ios' ? 36 : 24,
1327
+ backgroundColor: AppColors.primaryLight,
1328
+ borderTopWidth: 1,
1329
+ borderTopColor: AppColors.dividerColor,
1330
+ flexDirection: 'row',
1331
+ alignItems: 'center',
1332
+ gap: 12,
1333
+ shadowColor: AppColors.black,
1334
+ shadowOpacity: 0.08,
1335
+ shadowRadius: 8,
1336
+ shadowOffset: { width: 0, height: -3 },
1337
+ elevation: 8,
1338
+ }}>
1567
1339
  <View style={{ flex: 1 }}>
1568
1340
  <Text style={{
1569
- fontFamily: AppFonts.interBold,
1570
- fontSize: 13,
1571
- lineHeight: 17,
1572
- color: AppColors.primaryBlack,
1573
- }}>
1341
+ fontFamily: AppFonts.interBold,
1342
+ fontSize: 13,
1343
+ lineHeight: 17,
1344
+ color: AppColors.primaryBlack,
1345
+ }}>
1574
1346
  {stagedActiveCount} of {allModules.length} Modules Active
1575
1347
  </Text>
1576
1348
  <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
- }}>
1349
+ fontFamily: AppFonts.interRegular,
1350
+ fontSize: 11,
1351
+ lineHeight: 15,
1352
+ color: hasUnsavedChanges
1353
+ ? AppColors.purple
1354
+ : AppColors.grayText,
1355
+ marginTop: 1,
1356
+ }}>
1585
1357
  {hasUnsavedChanges
1586
- ? t('settings.unsavedPending')
1587
- : t('settings.allSynchronized')}
1358
+ ? t('settings.unsavedPending')
1359
+ : t('settings.allSynchronized')}
1588
1360
  </Text>
1589
1361
  </View>
1590
1362
 
1591
1363
  <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')}
1610
- </Text>
1611
- </TouchableScale>
1612
- </View>)}
1613
- </View>);
1614
- }
1615
- const goBackToMain = () => {
1616
- animateNextLayout();
1617
- setSettingsPage('main');
1618
- };
1619
- // Helper: settings row with icon + label + optional description
1620
- const renderSettingRow = (opts) => {
1621
- return (<View style={{
1622
- paddingVertical: 12,
1623
- borderBottomWidth: opts.isLast ? 0 : 1,
1624
- borderBottomColor: AppColors.dividerColor,
1625
- }}>
1626
- <TouchableScale disabled={!opts.onPress} onPress={opts.onPress} style={{
1627
1364
  flexDirection: 'row',
1628
1365
  alignItems: 'center',
1629
- gap: 12,
1630
- }}>
1631
- <View style={{
1632
- width: 36,
1633
- height: 36,
1366
+ gap: 6,
1367
+ backgroundColor: hasUnsavedChanges
1368
+ ? AppColors.purple
1369
+ : `${AppColors.purple}22`,
1370
+ paddingHorizontal: 18,
1371
+ paddingVertical: 10,
1634
1372
  borderRadius: 10,
1635
- backgroundColor: AppColors.purpleShade50,
1636
- borderWidth: 1,
1637
- borderColor: `${AppColors.purple}2E`,
1638
- alignItems: 'center',
1639
- justifyContent: 'center',
1640
1373
  }}>
1641
- {opts.icon}
1642
- </View>
1643
- <View style={{ flex: 1 }}>
1644
- <Text style={{
1374
+ <CheckIcon size={14} color={hasUnsavedChanges ? AppColors.white : AppColors.purple}/>
1375
+ <Text style={{
1645
1376
  fontFamily: AppFonts.interBold,
1646
- fontSize: 14,
1647
- lineHeight: 18,
1648
- color: AppColors.primaryBlack,
1377
+ fontSize: 13,
1378
+ lineHeight: 17,
1379
+ color: hasUnsavedChanges ? AppColors.white : AppColors.purple,
1649
1380
  }}>
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
1381
+ {t('settings.saveChanges')}
1696
1382
  </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,
1703
- }}>
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
- })}
1383
+ </TouchableScale>
1739
1384
  </View>)}
1740
1385
  </View>);
1741
- };
1742
1386
  let content = null;
1743
1387
  let title = '';
1744
1388
  let icon = null;
@@ -1747,7 +1391,7 @@ const SettingsPanel = () => {
1747
1391
  title = t('settings.apis.title');
1748
1392
  icon = <SignalIcon color={AppColors.white} size={16}/>;
1749
1393
  rightInfo = t('settings.apis.total', { count: logs.length });
1750
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1394
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, paddingBottom: 100, gap: 12 }}>
1751
1395
  <View style={{
1752
1396
  backgroundColor: AppColors.primaryLight,
1753
1397
  padding: 16,
@@ -1763,8 +1407,8 @@ const SettingsPanel = () => {
1763
1407
  value: maxNetworkLogs,
1764
1408
  onChange: setMaxNetworkLogs,
1765
1409
  min: 10,
1766
- max: 5000,
1767
- placeholder: 'Enter max requests (10-5000)',
1410
+ max: 100,
1411
+ placeholder: 'Enter max requests (10-100)',
1768
1412
  },
1769
1413
  isLast: true,
1770
1414
  })}
@@ -1806,13 +1450,14 @@ const SettingsPanel = () => {
1806
1450
  </View>),
1807
1451
  })}
1808
1452
  </View>
1453
+ <View style={{ height: 48 }}/>
1809
1454
  </ScrollView>);
1810
1455
  }
1811
1456
  else if (settingsPage === 'logs') {
1812
1457
  title = t('settings.logs.title');
1813
1458
  icon = <TerminalIcon color={AppColors.white} size={16}/>;
1814
1459
  rightInfo = t('settings.logs.total', { count: consoleLogs.length });
1815
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1460
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, paddingBottom: 100, gap: 12 }}>
1816
1461
  <View style={{
1817
1462
  backgroundColor: AppColors.primaryLight,
1818
1463
  padding: 16,
@@ -1829,8 +1474,8 @@ const SettingsPanel = () => {
1829
1474
  value: maxConsoleLogs,
1830
1475
  onChange: setMaxConsoleLogs,
1831
1476
  min: 10,
1832
- max: 10000,
1833
- placeholder: 'Enter max logs (10-10000)',
1477
+ max: 100,
1478
+ placeholder: 'Enter max logs (10-100)',
1834
1479
  },
1835
1480
  })}
1836
1481
  <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
@@ -1894,6 +1539,96 @@ const SettingsPanel = () => {
1894
1539
  })}
1895
1540
  </View>
1896
1541
 
1542
+ {/* Log Deduplication Card */}
1543
+ <View style={{
1544
+ backgroundColor: AppColors.primaryLight,
1545
+ borderRadius: 14,
1546
+ borderWidth: 1,
1547
+ borderColor: AppColors.grayBorderSecondary,
1548
+ overflow: 'hidden',
1549
+ padding: 16,
1550
+ gap: 14,
1551
+ }}>
1552
+ <Text style={{
1553
+ fontFamily: AppFonts.interBold,
1554
+ fontSize: 11,
1555
+ lineHeight: 14,
1556
+ color: AppColors.grayTextWeak,
1557
+ letterSpacing: 0.8,
1558
+ }}>
1559
+ {t('settings.logFiltersDeduplication')}
1560
+ </Text>
1561
+
1562
+ {/* Show Duplicate Logs */}
1563
+ <View style={{
1564
+ flexDirection: 'row',
1565
+ alignItems: 'center',
1566
+ justifyContent: 'space-between',
1567
+ }}>
1568
+ <View style={{
1569
+ flexDirection: 'row',
1570
+ alignItems: 'center',
1571
+ gap: 10,
1572
+ flex: 1,
1573
+ }}>
1574
+ <View style={{
1575
+ width: 32,
1576
+ height: 32,
1577
+ borderRadius: 8,
1578
+ backgroundColor: AppColors.purpleShade50,
1579
+ alignItems: 'center',
1580
+ justifyContent: 'center',
1581
+ }}>
1582
+ <EyeIcon color={AppColors.purple} size={15}/>
1583
+ </View>
1584
+ <View style={{ flex: 1 }}>
1585
+ <Text style={{
1586
+ fontFamily: AppFonts.interBold,
1587
+ fontSize: 13.5,
1588
+ lineHeight: 18,
1589
+ color: AppColors.primaryBlack,
1590
+ }}>
1591
+ {t('settings.general.duplicateLogs')}
1592
+ </Text>
1593
+ <Text style={{
1594
+ fontFamily: AppFonts.interRegular,
1595
+ fontSize: 11,
1596
+ lineHeight: 15,
1597
+ color: AppColors.grayText,
1598
+ marginTop: 1,
1599
+ }}>
1600
+ {t('settings.general.duplicateLogsDescription')}
1601
+ </Text>
1602
+ </View>
1603
+ </View>
1604
+
1605
+ <TouchableScale accessible={true} accessibilityRole="switch" accessibilityLabel="Toggle show duplicate logs" accessibilityState={{ checked: showDuplicateLogs }} onPress={() => setShowDuplicateLogs(prev => !prev)} style={{
1606
+ width: 42,
1607
+ height: 24,
1608
+ borderRadius: 12,
1609
+ backgroundColor: showDuplicateLogs
1610
+ ? AppColors.purple
1611
+ : AppColors.grayBorderSecondary,
1612
+ padding: 2,
1613
+ justifyContent: 'center',
1614
+ alignItems: showDuplicateLogs
1615
+ ? 'flex-end'
1616
+ : 'flex-start',
1617
+ }}>
1618
+ <View style={{
1619
+ width: 20,
1620
+ height: 20,
1621
+ borderRadius: 10,
1622
+ backgroundColor: AppColors.white,
1623
+ shadowColor: AppColors.black,
1624
+ shadowOpacity: 0.18,
1625
+ shadowRadius: 2,
1626
+ shadowOffset: { width: 0, height: 1 },
1627
+ }}/>
1628
+ </TouchableScale>
1629
+ </View>
1630
+ </View>
1631
+
1897
1632
  <View style={{
1898
1633
  backgroundColor: AppColors.primaryLight,
1899
1634
  borderRadius: 14,
@@ -1929,13 +1664,14 @@ const SettingsPanel = () => {
1929
1664
  </View>),
1930
1665
  })}
1931
1666
  </View>
1667
+ <View style={{ height: 48 }}/>
1932
1668
  </ScrollView>);
1933
1669
  }
1934
1670
  else if (settingsPage === 'analytics') {
1935
1671
  title = t('settings.analytics.title');
1936
1672
  icon = <AnalyticsIcon color={AppColors.white} size={16}/>;
1937
1673
  rightInfo = t('settings.analytics.total', { count: analyticsEvents.length });
1938
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1674
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, paddingBottom: 100, gap: 12 }}>
1939
1675
  <View style={{
1940
1676
  backgroundColor: AppColors.primaryLight,
1941
1677
  padding: 16,
@@ -1951,8 +1687,8 @@ const SettingsPanel = () => {
1951
1687
  value: maxAnalyticsEventsLimit,
1952
1688
  onChange: setMaxAnalyticsEventsLimit,
1953
1689
  min: 10,
1954
- max: 5000,
1955
- placeholder: 'Enter max events (10-5000)',
1690
+ max: 75,
1691
+ placeholder: 'Enter max events (10-75)',
1956
1692
  },
1957
1693
  isLast: true,
1958
1694
  })}
@@ -1993,13 +1729,14 @@ const SettingsPanel = () => {
1993
1729
  </View>),
1994
1730
  })}
1995
1731
  </View>
1732
+ <View style={{ height: 48 }}/>
1996
1733
  </ScrollView>);
1997
1734
  }
1998
1735
  else if (settingsPage === 'redux') {
1999
1736
  title = t('settings.redux.title');
2000
1737
  icon = <ReduxIcon color={AppColors.white} size={16}/>;
2001
1738
  rightInfo = t('settings.redux.reducers', { count: Object.keys(reduxState || {}).length });
2002
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1739
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, paddingBottom: 100, gap: 12 }}>
2003
1740
  <View style={{
2004
1741
  backgroundColor: AppColors.primaryLight,
2005
1742
  padding: 16,
@@ -2081,13 +1818,14 @@ const SettingsPanel = () => {
2081
1818
  </View>),
2082
1819
  })}
2083
1820
  </View>
1821
+ <View style={{ height: 48 }}/>
2084
1822
  </ScrollView>);
2085
1823
  }
2086
1824
  else if (settingsPage === 'crash') {
2087
1825
  title = t('settings.crash.title');
2088
1826
  icon = <CrashIcon color={AppColors.white} size={16}/>;
2089
1827
  rightInfo = `Total: ${crashRecords?.length || 0}`;
2090
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1828
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, paddingBottom: 100, gap: 12 }}>
2091
1829
  <View style={{
2092
1830
  backgroundColor: AppColors.primaryLight,
2093
1831
  padding: 16,
@@ -2104,8 +1842,8 @@ const SettingsPanel = () => {
2104
1842
  value: maxCrashLogs,
2105
1843
  onChange: setMaxCrashLogs,
2106
1844
  min: 5,
2107
- max: 500,
2108
- placeholder: 'Enter max crashes (5-500)',
1845
+ max: 50,
1846
+ placeholder: 'Enter max crashes (5-50)',
2109
1847
  },
2110
1848
  })}
2111
1849
  <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
@@ -2167,13 +1905,14 @@ const SettingsPanel = () => {
2167
1905
  </View>),
2168
1906
  })}
2169
1907
  </View>
1908
+ <View style={{ height: 48 }}/>
2170
1909
  </ScrollView>);
2171
1910
  }
2172
1911
  else if (settingsPage === 'bundle') {
2173
1912
  title = t('settings.bundle.title');
2174
1913
  icon = <PackageIcon color={AppColors.white} size={16}/>;
2175
1914
  rightInfo = 'Metro';
2176
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1915
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, paddingBottom: 100, gap: 12 }}>
2177
1916
  <View style={{
2178
1917
  backgroundColor: AppColors.primaryLight,
2179
1918
  padding: 16,
@@ -2250,13 +1989,14 @@ const SettingsPanel = () => {
2250
1989
  </View>),
2251
1990
  })}
2252
1991
  </View>
1992
+ <View style={{ height: 48 }}/>
2253
1993
  </ScrollView>);
2254
1994
  }
2255
1995
  else if (settingsPage === 'performance') {
2256
1996
  title = t('settings.performance.title');
2257
1997
  icon = <PerformanceIcon color={AppColors.white} size={16}/>;
2258
1998
  rightInfo = '60 FPS Target';
2259
- content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1999
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, paddingBottom: 100, gap: 12 }}>
2260
2000
  <View style={{
2261
2001
  backgroundColor: AppColors.primaryLight,
2262
2002
  padding: 16,
@@ -2333,65 +2073,51 @@ const SettingsPanel = () => {
2333
2073
  </View>),
2334
2074
  })}
2335
2075
  </View>
2076
+ <View style={{ height: 48 }}/>
2336
2077
  </ScrollView>);
2337
2078
  }
2079
+ const subPageAnim = useRef(new Animated.Value(0)).current;
2080
+ useEffect(() => {
2081
+ if (settingsPage !== 'main') {
2082
+ subPageAnim.setValue(0);
2083
+ Animated.spring(subPageAnim, {
2084
+ toValue: 1,
2085
+ friction: 8,
2086
+ tension: 65,
2087
+ useNativeDriver: true,
2088
+ }).start();
2089
+ }
2090
+ }, [settingsPage]);
2338
2091
  return (<View style={{
2339
2092
  flex: 1,
2340
2093
  backgroundColor: AppColors.grayBackground,
2341
2094
  }}>
2342
- <LinearGradient colors={[AppColors.purple, AppColors.brandPurple]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.headerGradient}>
2343
- <View style={{ paddingTop: headerTopPadding, width: '100%' }}>
2344
- <View style={[styles.header, { paddingHorizontal: 16, gap: 12 }]}>
2345
- <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={t('settings.back')} onPress={goBackToMain} hitSlop={12} style={{
2346
- padding: 8,
2347
- borderRadius: 10,
2348
- backgroundColor: `${AppColors.white}26`,
2349
- borderWidth: 1,
2350
- borderColor: `${AppColors.white}14`,
2351
- }}>
2352
- <WhiteBackNavigation color={AppColors.white} size={16}/>
2353
- </TouchableScale>
2354
- {icon && (<View style={{
2355
- width: 30,
2356
- height: 30,
2357
- borderRadius: 8,
2358
- backgroundColor: `${AppColors.white}26`,
2359
- alignItems: 'center',
2360
- justifyContent: 'center',
2361
- }}>
2362
- {icon}
2363
- </View>)}
2364
- <View style={{ flex: 1 }}>
2365
- <Text style={{
2366
- fontFamily: AppFonts.interBold,
2367
- fontSize: 17,
2368
- lineHeight: 22,
2369
- color: AppColors.white,
2370
- }}>
2371
- {title}
2372
- </Text>
2373
- </View>
2374
- {rightInfo ? (<View style={{
2375
- backgroundColor: `${AppColors.white}26`,
2376
- paddingHorizontal: 8,
2377
- paddingVertical: 4,
2378
- borderRadius: 8,
2379
- borderWidth: 1,
2380
- borderColor: `${AppColors.white}1A`,
2381
- }}>
2382
- <Text style={{
2383
- fontFamily: AppFonts.interBold,
2384
- fontSize: 11,
2385
- lineHeight: 14,
2386
- color: AppColors.white,
2387
- }}>
2388
- {rightInfo}
2389
- </Text>
2390
- </View>) : null}
2391
- </View>
2392
- </View>
2393
- </LinearGradient>
2394
- {content}
2095
+ {/* ─── Persistent Main Settings (Retains 100% scroll position) ─── */}
2096
+ <View style={[
2097
+ StyleSheet.absoluteFill,
2098
+ settingsPage !== 'main' && { pointerEvents: 'none' },
2099
+ ]}>
2100
+ {renderMainSettingsContent()}
2101
+ </View>
2102
+
2103
+ {/* ─── Sub-module Settings Overlay (Smooth Animated Slide) ─── */}
2104
+ {settingsPage !== 'main' && (<Animated.View style={[
2105
+ StyleSheet.absoluteFill,
2106
+ {
2107
+ backgroundColor: AppColors.grayBackground,
2108
+ opacity: subPageAnim,
2109
+ transform: [
2110
+ {
2111
+ translateX: subPageAnim.interpolate({
2112
+ inputRange: [0, 1],
2113
+ outputRange: [30, 0],
2114
+ }),
2115
+ },
2116
+ ],
2117
+ },
2118
+ ]}>
2119
+ {content}
2120
+ </Animated.View>)}
2395
2121
  </View>);
2396
2122
  };
2397
2123
  export default SettingsPanel;