react-native-inapp-inspector 1.1.16 → 1.1.18

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 (67) hide show
  1. package/README.md +13 -0
  2. package/dist/commonjs/components/AnimatedEntrance.js +3 -0
  3. package/dist/commonjs/components/AppHeaderLogo.d.ts +8 -0
  4. package/dist/commonjs/components/AppHeaderLogo.js +85 -0
  5. package/dist/commonjs/components/Inspector/BundleTab.d.ts +46 -0
  6. package/dist/commonjs/components/Inspector/BundleTab.js +2439 -0
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +185 -52
  8. package/dist/commonjs/components/Inspector/MainScreen.js +93 -7
  9. package/dist/commonjs/components/Inspector/PerformanceTab.d.ts +22 -0
  10. package/dist/commonjs/components/Inspector/PerformanceTab.js +1143 -0
  11. package/dist/commonjs/components/Inspector/ReduxDetail.d.ts +3 -0
  12. package/dist/commonjs/components/Inspector/ReduxDetail.js +576 -0
  13. package/dist/commonjs/components/Inspector/ReduxTab.js +433 -49
  14. package/dist/commonjs/components/Inspector/SettingsPanel.js +13 -2
  15. package/dist/commonjs/components/Inspector/TabBar.js +16 -6
  16. package/dist/commonjs/components/JsonViewer.js +91 -30
  17. package/dist/commonjs/components/LogCard.js +3 -4
  18. package/dist/commonjs/components/NetworkIcons.d.ts +9 -0
  19. package/dist/commonjs/components/NetworkIcons.js +63 -1
  20. package/dist/commonjs/constants/version.d.ts +1 -1
  21. package/dist/commonjs/constants/version.js +1 -1
  22. package/dist/commonjs/customHooks/consoleLogger.js +37 -9
  23. package/dist/commonjs/customHooks/reduxLogger.js +4 -2
  24. package/dist/commonjs/helpers/index.js +6 -2
  25. package/dist/commonjs/index.js +18 -1
  26. package/dist/commonjs/styles/AppColors.js +163 -163
  27. package/dist/commonjs/styles/index.d.ts +14 -0
  28. package/dist/commonjs/styles/index.js +14 -0
  29. package/dist/commonjs/types/enums.d.ts +2 -2
  30. package/dist/commonjs/types/interfaces.d.ts +11 -0
  31. package/dist/esm/components/AnimatedEntrance.js +4 -1
  32. package/dist/esm/components/AppHeaderLogo.d.ts +8 -0
  33. package/dist/esm/components/AppHeaderLogo.js +45 -0
  34. package/dist/esm/components/Inspector/BundleTab.d.ts +46 -0
  35. package/dist/esm/components/Inspector/BundleTab.js +2401 -0
  36. package/dist/esm/components/Inspector/InspectorHeader.js +151 -51
  37. package/dist/esm/components/Inspector/MainScreen.js +60 -7
  38. package/dist/esm/components/Inspector/PerformanceTab.d.ts +22 -0
  39. package/dist/esm/components/Inspector/PerformanceTab.js +1105 -0
  40. package/dist/esm/components/Inspector/ReduxDetail.d.ts +3 -0
  41. package/dist/esm/components/Inspector/ReduxDetail.js +538 -0
  42. package/dist/esm/components/Inspector/ReduxTab.js +402 -51
  43. package/dist/esm/components/Inspector/SettingsPanel.js +14 -3
  44. package/dist/esm/components/Inspector/TabBar.js +17 -7
  45. package/dist/esm/components/JsonViewer.js +92 -31
  46. package/dist/esm/components/LogCard.js +3 -4
  47. package/dist/esm/components/NetworkIcons.d.ts +9 -0
  48. package/dist/esm/components/NetworkIcons.js +55 -1
  49. package/dist/esm/constants/version.d.ts +1 -1
  50. package/dist/esm/constants/version.js +1 -1
  51. package/dist/esm/customHooks/consoleLogger.js +37 -9
  52. package/dist/esm/customHooks/reduxLogger.js +4 -2
  53. package/dist/esm/helpers/index.js +6 -2
  54. package/dist/esm/index.js +18 -1
  55. package/dist/esm/styles/AppColors.js +163 -163
  56. package/dist/esm/styles/index.d.ts +14 -0
  57. package/dist/esm/styles/index.js +14 -0
  58. package/dist/esm/types/enums.d.ts +2 -2
  59. package/dist/esm/types/interfaces.d.ts +11 -0
  60. package/example/App.tsx +5 -1
  61. package/example/assets/app_icon.png +0 -0
  62. package/example/ios/Podfile.lock +3 -3
  63. package/example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json +18 -9
  64. package/example/ios/example/Images.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  65. package/example/package-lock.json +824 -10
  66. package/example/package.json +6 -2
  67. package/package.json +4 -2
@@ -1,70 +1,421 @@
1
- import React from 'react';
2
- import { Pressable, ScrollView, Text, TextInput, View, } from 'react-native';
1
+ import React, { useCallback, useMemo, useState } from 'react';
2
+ import { FlatList, Pressable, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native';
3
3
  import { useInspector } from './InspectorContext';
4
- import { ReduxTreeView } from '../ReduxTreeView';
4
+ import TouchableScale from '../TouchableScale';
5
+ import AnimatedEntrance from '../AnimatedEntrance';
6
+ import EmptyState from '../EmptyState';
7
+ import EndOfListFooter from '../EndOfListFooter';
8
+ import HighlightText from '../HighlightText';
9
+ import { getSize } from '../../helpers';
5
10
  import styles from '../../styles';
6
11
  import { AppColors } from '../../styles/AppColors';
7
12
  import { AppFonts } from '../../styles/AppFonts';
8
- import { TerminalIcon, ClearIcon } from '../NetworkIcons';
13
+ import { TerminalIcon, SearchIcon, ClearIcon, ClockIcon, ForwardChevronIcon, SortArrowIcon, } from '../NetworkIcons';
9
14
  const ReduxTab = React.memo(() => {
10
- const { reduxState, reduxLastActionMap, reduxSearch, setReduxSearch, } = useInspector();
15
+ const { reduxState, reduxLastActionMap, reduxSearch, setReduxSearch, setSelectedReduxSlice, } = useInspector();
16
+ // Sort mode: 'latest' (newest updated first) vs 'alpha' (A-Z)
17
+ const [sortMode, setSortMode] = useState('latest');
18
+ // Total state size calculation
19
+ const totalStateSize = useMemo(() => {
20
+ return getSize(reduxState);
21
+ }, [reduxState]);
22
+ // Compute slice items with exact timestamps
23
+ const sliceItems = useMemo(() => {
24
+ if (!reduxState || typeof reduxState !== 'object')
25
+ return [];
26
+ const keys = Object.keys(reduxState);
27
+ return keys.map(key => {
28
+ const sliceVal = reduxState[key];
29
+ const keysCount = sliceVal && typeof sliceVal === 'object'
30
+ ? Object.keys(sliceVal).length
31
+ : typeof sliceVal !== 'undefined'
32
+ ? 1
33
+ : 0;
34
+ const lastAction = reduxLastActionMap[key];
35
+ const updatedAt = lastAction?.updatedAt || 0;
36
+ return {
37
+ id: `slice-${key}`,
38
+ name: key,
39
+ keysCount,
40
+ sizeStr: getSize(sliceVal),
41
+ lastAction,
42
+ updatedAt,
43
+ };
44
+ });
45
+ }, [reduxState, reduxLastActionMap]);
46
+ // Filter & Sort slices: by default, the most recently updated item is at the FIRST position
47
+ const filteredSlices = useMemo(() => {
48
+ let list = sliceItems;
49
+ if (reduxSearch.trim().length > 0) {
50
+ const q = reduxSearch.toLowerCase();
51
+ list = list.filter(item => item.name.toLowerCase().includes(q) ||
52
+ (item.lastAction && item.lastAction.type.toLowerCase().includes(q)));
53
+ }
54
+ return [...list].sort((a, b) => {
55
+ if (sortMode === 'latest') {
56
+ // Most recently updated item first
57
+ if (b.updatedAt !== a.updatedAt) {
58
+ return b.updatedAt - a.updatedAt;
59
+ }
60
+ return a.name.localeCompare(b.name);
61
+ }
62
+ else {
63
+ // Alphabetical A-Z
64
+ return a.name.localeCompare(b.name);
65
+ }
66
+ });
67
+ }, [sliceItems, reduxSearch, sortMode]);
68
+ const lastGlobalAction = useMemo(() => {
69
+ const actions = Object.values(reduxLastActionMap);
70
+ if (actions.length === 0)
71
+ return null;
72
+ return actions.reduce((latest, curr) => {
73
+ if (!latest)
74
+ return curr;
75
+ return (curr.updatedAt || 0) > (latest.updatedAt || 0) ? curr : latest;
76
+ }, null);
77
+ }, [reduxLastActionMap]);
78
+ const renderSliceItem = useCallback(({ item, index }) => {
79
+ const isRecentlyUpdated = item.updatedAt > 0 && Date.now() - item.updatedAt < 5000;
80
+ return (<AnimatedEntrance index={index} distance={8}>
81
+ <TouchableScale onPress={() => setSelectedReduxSlice(item.name)} style={[
82
+ reduxTabStyles.card,
83
+ isRecentlyUpdated && reduxTabStyles.cardRecentlyUpdated,
84
+ ]}>
85
+ {/* Top row: Badge + Slice Name + Timestamp + Chevron */}
86
+ <View style={reduxTabStyles.cardHeader}>
87
+ <View style={reduxTabStyles.sliceBadge}>
88
+ <Text style={reduxTabStyles.sliceBadgeText}>SLICE</Text>
89
+ </View>
90
+
91
+ <HighlightText text={item.name} search={reduxSearch} style={reduxTabStyles.sliceName} highlightStyle={reduxTabStyles.highlight}/>
92
+
93
+ {item.lastAction?.timestamp && (<View style={reduxTabStyles.timePill}>
94
+ <ClockIcon color={AppColors.purple} size={10}/>
95
+ <Text style={reduxTabStyles.timeText}>
96
+ {item.lastAction.timestamp}
97
+ </Text>
98
+ </View>)}
99
+
100
+ <ForwardChevronIcon color={AppColors.grayTextWeak} size={13}/>
101
+ </View>
102
+
103
+ {/* Middle row: Stats pills */}
104
+ <View style={reduxTabStyles.statsRow}>
105
+ <View style={reduxTabStyles.pill}>
106
+ <Text style={reduxTabStyles.pillLabel}>Keys:</Text>
107
+ <Text style={reduxTabStyles.pillValue}>{item.keysCount}</Text>
108
+ </View>
109
+ <View style={reduxTabStyles.pill}>
110
+ <Text style={reduxTabStyles.pillLabel}>Size:</Text>
111
+ <Text style={reduxTabStyles.pillValue}>{item.sizeStr}</Text>
112
+ </View>
113
+ <View style={reduxTabStyles.livePill}>
114
+ <View style={reduxTabStyles.liveDot}/>
115
+ <Text style={reduxTabStyles.livePillText}>Live</Text>
116
+ </View>
117
+ </View>
118
+
119
+ {/* Bottom row: Last Dispatched Action */}
120
+ {item.lastAction && (<View style={reduxTabStyles.lastActionRow}>
121
+ <Text style={reduxTabStyles.lastActionLabel}>Last Action:</Text>
122
+ <HighlightText text={item.lastAction.type} search={reduxSearch} style={reduxTabStyles.lastActionValue} highlightStyle={reduxTabStyles.highlight} numberOfLines={1}/>
123
+ </View>)}
124
+ </TouchableScale>
125
+ </AnimatedEntrance>);
126
+ }, [reduxSearch, setSelectedReduxSlice]);
11
127
  if (!reduxState) {
12
128
  return (<View style={styles.emptyContainer}>
13
129
  <View style={styles.emptyIconWrap}>
14
130
  <TerminalIcon color={AppColors.purple} size={32}/>
15
131
  </View>
16
- <Text style={styles.emptyTitle}>No Redux Store</Text>
132
+ <Text style={styles.emptyTitle}>No Redux Store Connected</Text>
17
133
  <Text style={styles.emptySub}>
18
- To inspect Redux store, call connectReduxStore(store) at app start.
134
+ Call connectReduxStore(store) or use inspectorReduxMiddleware at app initialization.
19
135
  </Text>
20
136
  </View>);
21
137
  }
22
- const reducerKeys = Object.keys(reduxState);
23
- if (reducerKeys.length === 0) {
24
- return (<View style={styles.emptyContainer}>
25
- <Text style={styles.emptyTitle}>Empty Store</Text>
26
- <Text style={styles.emptySub}>Connected store state is empty.</Text>
27
- </View>);
28
- }
29
- const lastActionMap = reduxLastActionMap;
30
- return (<ScrollView style={styles.detailScroll} contentContainerStyle={{ paddingBottom: 24 }}>
31
- {/* Search Bar */}
32
- <View style={{
33
- flexDirection: 'row',
34
- alignItems: 'center',
35
- backgroundColor: AppColors.grayBackground,
36
- borderRadius: 8,
37
- marginHorizontal: 16,
38
- marginTop: 16,
39
- marginBottom: 12,
40
- paddingHorizontal: 10,
41
- borderWidth: 1,
42
- borderColor: AppColors.dividerColor,
43
- height: 36,
44
- }}>
45
- <TextInput placeholder="Search Redux keys or values..." placeholderTextColor={AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={{
46
- flex: 1,
47
- fontFamily: AppFonts.interRegular,
48
- fontSize: 12,
49
- color: AppColors.grayTextStrong,
50
- padding: 0,
51
- }} autoCorrect={false} autoCapitalize="none"/>
52
- {reduxSearch.length > 0 && (<Pressable onPress={() => setReduxSearch('')} hitSlop={10}>
53
- <ClearIcon color={AppColors.grayTextWeak} size={14}/>
54
- </Pressable>)}
138
+ return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
139
+ {/* ─── Top Stats Overview Card ─── */}
140
+ <View style={reduxTabStyles.overviewCard}>
141
+ <View style={reduxTabStyles.statCol}>
142
+ <Text style={reduxTabStyles.statHeading}>ACTIVE SLICES</Text>
143
+ <Text style={reduxTabStyles.statBigVal}>{sliceItems.length}</Text>
144
+ </View>
145
+ <View style={reduxTabStyles.statDivider}/>
146
+ <View style={reduxTabStyles.statCol}>
147
+ <Text style={reduxTabStyles.statHeading}>TOTAL STATE</Text>
148
+ <Text style={reduxTabStyles.statBigVal}>{totalStateSize}</Text>
149
+ </View>
150
+ <View style={reduxTabStyles.statDivider}/>
151
+ <View style={[reduxTabStyles.statCol, { flex: 1.5 }]}>
152
+ <Text style={reduxTabStyles.statHeading}>LAST ACTION</Text>
153
+ <Text style={reduxTabStyles.statLastAction} numberOfLines={1}>
154
+ {lastGlobalAction ? lastGlobalAction.type : 'Initial'}
155
+ </Text>
156
+ {lastGlobalAction?.timestamp && (<Text style={reduxTabStyles.statLastTime}>
157
+ {lastGlobalAction.timestamp}
158
+ </Text>)}
159
+ </View>
55
160
  </View>
56
161
 
57
- {/* Main Content Card */}
58
- <View style={{
59
- backgroundColor: AppColors.primaryLight,
60
- borderRadius: 12,
61
- borderWidth: 1,
62
- borderColor: AppColors.grayBorderSecondary,
63
- marginHorizontal: 16,
64
- padding: 12,
65
- }}>
66
- <ReduxTreeView state={reduxState} lastActionMap={lastActionMap} search={reduxSearch}/>
162
+ {/* ─── Search & Sort Bar ─── */}
163
+ <View style={reduxTabStyles.toolbarContainer}>
164
+ <View style={reduxTabStyles.searchRow}>
165
+ <View style={styles.searchContainer}>
166
+ <SearchIcon color={AppColors.grayTextWeak} size={15}/>
167
+ <TextInput placeholder="Search Redux slices or actions..." placeholderTextColor={AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
168
+ {reduxSearch.length > 0 && (<Pressable onPress={() => setReduxSearch('')} hitSlop={10}>
169
+ <ClearIcon color={AppColors.grayTextWeak} size={14}/>
170
+ </Pressable>)}
171
+ </View>
172
+
173
+ {/* Sort Button */}
174
+ <TouchableOpacity style={[
175
+ styles.toolbarBtn,
176
+ sortMode === 'latest' && { backgroundColor: `${AppColors.brandPurple}1F`, borderColor: AppColors.brandPurple },
177
+ ]} onPress={() => setSortMode(prev => (prev === 'latest' ? 'alpha' : 'latest'))} hitSlop={6}>
178
+ <SortArrowIcon color={sortMode === 'latest' ? AppColors.brandPurple : AppColors.grayTextStrong} size={16}/>
179
+ </TouchableOpacity>
180
+ </View>
181
+
182
+ <View style={reduxTabStyles.headerSubRow}>
183
+ <Text style={reduxTabStyles.resultCount}>
184
+ Showing {filteredSlices.length} of {sliceItems.length} state slices
185
+ </Text>
186
+ <Text style={reduxTabStyles.sortLabel}>
187
+ {sortMode === 'latest' ? '⚡ Newest Updates First' : '🔤 Alphabetical (A-Z)'}
188
+ </Text>
189
+ </View>
67
190
  </View>
68
- </ScrollView>);
191
+
192
+ {/* ─── FlatList of Slices ─── */}
193
+ <FlatList data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={15} maxToRenderPerBatch={10} windowSize={7} removeClippedSubviews={true} ListEmptyComponent={<EmptyState isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
194
+ reduxTabStyles.listContent,
195
+ filteredSlices.length === 0 && { flexGrow: 1 },
196
+ ]} keyboardShouldPersistTaps="handled"/>
197
+ </View>);
198
+ });
199
+ const reduxTabStyles = StyleSheet.create({
200
+ overviewCard: {
201
+ flexDirection: 'row',
202
+ alignItems: 'center',
203
+ backgroundColor: AppColors.white,
204
+ marginHorizontal: 12,
205
+ marginTop: 10,
206
+ borderRadius: 12,
207
+ padding: 12,
208
+ borderWidth: 1,
209
+ borderColor: AppColors.dividerColor,
210
+ shadowColor: AppColors.primaryBlack,
211
+ shadowOffset: { width: 0, height: 1 },
212
+ shadowOpacity: 0.03,
213
+ shadowRadius: 3,
214
+ elevation: 1,
215
+ },
216
+ statCol: {
217
+ flex: 1,
218
+ },
219
+ statDivider: {
220
+ width: 1,
221
+ height: 28,
222
+ backgroundColor: AppColors.dividerColor,
223
+ marginHorizontal: 8,
224
+ },
225
+ statHeading: {
226
+ fontFamily: AppFonts.interBold,
227
+ fontSize: 9,
228
+ color: AppColors.grayTextWeak,
229
+ letterSpacing: 0.5,
230
+ },
231
+ statBigVal: {
232
+ fontFamily: AppFonts.interBold,
233
+ fontSize: 14,
234
+ color: AppColors.primaryBlack,
235
+ marginTop: 2,
236
+ },
237
+ statLastAction: {
238
+ fontFamily: AppFonts.interBold,
239
+ fontSize: 11,
240
+ color: AppColors.purple,
241
+ marginTop: 2,
242
+ },
243
+ statLastTime: {
244
+ fontFamily: AppFonts.interRegular,
245
+ fontSize: 9.5,
246
+ color: AppColors.grayTextWeak,
247
+ },
248
+ toolbarContainer: {
249
+ backgroundColor: AppColors.white,
250
+ borderBottomWidth: 1,
251
+ borderBottomColor: AppColors.dividerColor,
252
+ paddingHorizontal: 12,
253
+ paddingTop: 8,
254
+ paddingBottom: 6,
255
+ marginTop: 8,
256
+ },
257
+ searchRow: {
258
+ flexDirection: 'row',
259
+ alignItems: 'center',
260
+ gap: 8,
261
+ },
262
+ headerSubRow: {
263
+ flexDirection: 'row',
264
+ justifyContent: 'space-between',
265
+ alignItems: 'center',
266
+ marginTop: 6,
267
+ paddingHorizontal: 2,
268
+ },
269
+ resultCount: {
270
+ fontFamily: AppFonts.interMedium,
271
+ fontSize: 11,
272
+ color: AppColors.grayTextWeak,
273
+ },
274
+ sortLabel: {
275
+ fontFamily: AppFonts.interMedium,
276
+ fontSize: 10.5,
277
+ color: AppColors.brandPurple,
278
+ },
279
+ listContent: {
280
+ padding: 12,
281
+ paddingBottom: 36,
282
+ },
283
+ card: {
284
+ backgroundColor: AppColors.white,
285
+ borderRadius: 12,
286
+ padding: 12,
287
+ marginBottom: 10,
288
+ borderWidth: 1,
289
+ borderColor: AppColors.dividerColor,
290
+ shadowColor: AppColors.primaryBlack,
291
+ shadowOffset: { width: 0, height: 1 },
292
+ shadowOpacity: 0.04,
293
+ shadowRadius: 4,
294
+ elevation: 2,
295
+ },
296
+ cardRecentlyUpdated: {
297
+ borderColor: `${AppColors.brandPurple}66`,
298
+ backgroundColor: '#FAFAFF',
299
+ },
300
+ cardHeader: {
301
+ flexDirection: 'row',
302
+ alignItems: 'center',
303
+ gap: 8,
304
+ marginBottom: 8,
305
+ },
306
+ sliceBadge: {
307
+ backgroundColor: '#EDE9FE',
308
+ paddingHorizontal: 6,
309
+ paddingVertical: 3,
310
+ borderRadius: 5,
311
+ borderWidth: 1,
312
+ borderColor: '#DDD6FE',
313
+ },
314
+ sliceBadgeText: {
315
+ fontFamily: AppFonts.interBold,
316
+ fontSize: 9.5,
317
+ color: '#6D28D9',
318
+ letterSpacing: 0.4,
319
+ },
320
+ sliceName: {
321
+ flex: 1,
322
+ fontFamily: AppFonts.interBold,
323
+ fontSize: 13.5,
324
+ color: AppColors.primaryBlack,
325
+ },
326
+ timePill: {
327
+ flexDirection: 'row',
328
+ alignItems: 'center',
329
+ gap: 3,
330
+ backgroundColor: `${AppColors.purple}14`,
331
+ paddingHorizontal: 6,
332
+ paddingVertical: 2,
333
+ borderRadius: 4,
334
+ borderWidth: 1,
335
+ borderColor: `${AppColors.purple}2E`,
336
+ },
337
+ timeText: {
338
+ fontFamily: AppFonts.interMedium,
339
+ fontSize: 10,
340
+ color: AppColors.purple,
341
+ },
342
+ statsRow: {
343
+ flexDirection: 'row',
344
+ alignItems: 'center',
345
+ gap: 6,
346
+ flexWrap: 'wrap',
347
+ marginBottom: 6,
348
+ },
349
+ pill: {
350
+ flexDirection: 'row',
351
+ alignItems: 'center',
352
+ gap: 4,
353
+ backgroundColor: AppColors.grayBackground,
354
+ paddingHorizontal: 7,
355
+ paddingVertical: 3,
356
+ borderRadius: 5,
357
+ borderWidth: 1,
358
+ borderColor: AppColors.dividerColor,
359
+ },
360
+ pillLabel: {
361
+ fontFamily: AppFonts.interMedium,
362
+ fontSize: 10.5,
363
+ color: AppColors.grayTextWeak,
364
+ },
365
+ pillValue: {
366
+ fontFamily: AppFonts.interBold,
367
+ fontSize: 10.5,
368
+ color: AppColors.primaryBlack,
369
+ },
370
+ livePill: {
371
+ flexDirection: 'row',
372
+ alignItems: 'center',
373
+ gap: 4,
374
+ backgroundColor: '#ECFDF5',
375
+ paddingHorizontal: 6,
376
+ paddingVertical: 3,
377
+ borderRadius: 5,
378
+ borderWidth: 1,
379
+ borderColor: '#A7F3D0',
380
+ },
381
+ liveDot: {
382
+ width: 6,
383
+ height: 6,
384
+ borderRadius: 3,
385
+ backgroundColor: '#10B981',
386
+ },
387
+ livePillText: {
388
+ fontFamily: AppFonts.interBold,
389
+ fontSize: 10,
390
+ color: '#047857',
391
+ },
392
+ lastActionRow: {
393
+ flexDirection: 'row',
394
+ alignItems: 'center',
395
+ gap: 6,
396
+ backgroundColor: AppColors.grayBackground,
397
+ borderRadius: 6,
398
+ paddingHorizontal: 8,
399
+ paddingVertical: 4,
400
+ marginTop: 2,
401
+ borderWidth: 1,
402
+ borderColor: AppColors.dividerColor,
403
+ },
404
+ lastActionLabel: {
405
+ fontFamily: AppFonts.interMedium,
406
+ fontSize: 10.5,
407
+ color: AppColors.grayTextWeak,
408
+ },
409
+ lastActionValue: {
410
+ flex: 1,
411
+ fontFamily: AppFonts.interBold,
412
+ fontSize: 11,
413
+ color: AppColors.primaryBlack,
414
+ },
415
+ highlight: {
416
+ backgroundColor: '#FEF08A',
417
+ color: '#854D0E',
418
+ fontFamily: AppFonts.interBold,
419
+ },
69
420
  });
70
421
  export default ReduxTab;
@@ -12,9 +12,10 @@ import { isPersistentStorageAvailable } from '../../helpers/settingsStore';
12
12
  import { clearNetworkLogs } from '../../customHooks/networkLogger';
13
13
  import { clearConsoleLogs } from '../../customHooks/consoleLogger';
14
14
  import { clearAnalyticsEvents } from '../../customHooks/analyticsLogger';
15
- import { SignalIcon, TerminalIcon, AnalyticsIcon, SettingsIcon, SunIcon, MoonIcon, ScreenIcon, MotionIcon, LayersIcon, EyeIcon, CheckIcon, TrashIcon, WhiteBackNavigation, } from '../NetworkIcons';
15
+ import { SignalIcon, TerminalIcon, AnalyticsIcon, SettingsIcon, SunIcon, MoonIcon, ScreenIcon, MotionIcon, LayersIcon, EyeIcon, CheckIcon, TrashIcon, WhiteBackNavigation, PackageIcon, ReduxIcon, PerformanceIcon, } from '../NetworkIcons';
16
16
  const SettingsPanel = () => {
17
17
  const { settingsPage, setSettingsPage, settingsActiveSubTab, setSettingsActiveSubTab, tabVisibility, toggleTabVisibility, defaultTab, setDefaultTab, isDark, setIsDark, modalHeightPercent, setModalHeightPercent, modalAnimationType, setModalAnimationType, showDuplicateLogs, setShowDuplicateLogs, showConsoleLevels, setShowConsoleLevels, resetToDefaults, storage, logs, consoleLogs, analyticsEvents, reduxState, maxNetworkLogs, setMaxNetworkLogs, maxConsoleLogs, setMaxConsoleLogs, reduxAutoRefresh, setReduxAutoRefreshState, reduxExpandDepth, setReduxExpandDepth, switchActiveTab, setSelected, setSelectedEvent, setReduxState, } = useInspector();
18
+ const isPersistent = isPersistentStorageAvailable();
18
19
  const headerTopPadding = Platform.OS === 'ios' && modalHeightPercent >= 95 ? 44 : 0;
19
20
  if (settingsPage === 'main') {
20
21
  const settingsTabs = [
@@ -22,6 +23,8 @@ const SettingsPanel = () => {
22
23
  { key: 'logs', label: 'Logs', icon: 'logs' },
23
24
  { key: 'analytics', label: 'Analytics', icon: 'analytics' },
24
25
  { key: 'redux', label: 'Redux', icon: 'redux' },
26
+ { key: 'bundle', label: 'Bundle', icon: 'bundle' },
27
+ { key: 'performance', label: 'Performance', icon: 'performance' },
25
28
  ];
26
29
  return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
27
30
  {/* Settings Header with back button */}
@@ -223,7 +226,13 @@ const SettingsPanel = () => {
223
226
  {tab.icon === 'analytics' && (<AnalyticsIcon color={isLocked
224
227
  ? AppColors.grayTextWeak
225
228
  : AppColors.purple} size={11}/>)}
226
- {tab.icon === 'redux' && (<TerminalIcon color={isLocked
229
+ {tab.icon === 'redux' && (<ReduxIcon color={isLocked
230
+ ? AppColors.grayTextWeak
231
+ : AppColors.purple} size={11}/>)}
232
+ {tab.icon === 'bundle' && (<PackageIcon color={isLocked
233
+ ? AppColors.grayTextWeak
234
+ : AppColors.purple} size={11}/>)}
235
+ {tab.icon === 'performance' && (<PerformanceIcon color={isLocked
227
236
  ? AppColors.grayTextWeak
228
237
  : AppColors.purple} size={11}/>)}
229
238
  </View>
@@ -662,7 +671,9 @@ const SettingsPanel = () => {
662
671
  {tab.icon === 'apis' && (<SignalIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
663
672
  {tab.icon === 'logs' && (<TerminalIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
664
673
  {tab.icon === 'analytics' && (<AnalyticsIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
665
- {tab.icon === 'redux' && (<TerminalIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
674
+ {tab.icon === 'redux' && (<ReduxIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
675
+ {tab.icon === 'bundle' && (<PackageIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
676
+ {tab.icon === 'performance' && (<PerformanceIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
666
677
  </View>
667
678
  <Text style={{
668
679
  fontFamily: AppFonts.interBold,
@@ -4,7 +4,7 @@ import { useInspector } from './InspectorContext';
4
4
  import TouchableScale from '../TouchableScale';
5
5
  import styles from '../../styles';
6
6
  import { AppColors } from '../../styles/AppColors';
7
- import { SignalIcon, TerminalIcon, AnalyticsIcon, } from '../NetworkIcons';
7
+ import { SignalIcon, TerminalIcon, AnalyticsIcon, PackageIcon, ReduxIcon, PerformanceIcon, } from '../NetworkIcons';
8
8
  const TabBar = React.memo(() => {
9
9
  const { activeTab, switchActiveTab, tabVisibility, logs, consoleLogs, analyticsEvents, lastReadApisCount, lastReadLogsCount, unreadPulseAnim, } = useInspector();
10
10
  return (<View style={styles.tabBarContainer}>
@@ -34,6 +34,18 @@ const TabBar = React.memo(() => {
34
34
  count: 0,
35
35
  icon: 'redux',
36
36
  },
37
+ {
38
+ key: 'bundle',
39
+ label: 'Bundle',
40
+ count: 0,
41
+ icon: 'bundle',
42
+ },
43
+ {
44
+ key: 'performance',
45
+ label: 'Performance',
46
+ count: 0,
47
+ icon: 'performance',
48
+ },
37
49
  ]
38
50
  .filter(tab => tabVisibility?.[tab.key])
39
51
  .map(tab => {
@@ -46,11 +58,7 @@ const TabBar = React.memo(() => {
46
58
  logs.length > lastReadApisCount;
47
59
  const hasUnreadLogs = activeTab !== 'logs' &&
48
60
  consoleLogs.length > lastReadLogsCount;
49
- return (<TouchableScale key={tab.key} onPress={() => {
50
- requestAnimationFrame(() => {
51
- switchActiveTab(tab.key);
52
- });
53
- }} style={[
61
+ return (<TouchableScale key={tab.key} onPress={() => switchActiveTab(tab.key)} style={[
54
62
  styles.contentTabButton,
55
63
  isActive && styles.contentTabButtonActive,
56
64
  ]}>
@@ -62,7 +70,9 @@ const TabBar = React.memo(() => {
62
70
  {tab.icon === 'apis' && (<SignalIcon color={iconColor} size={14}/>)}
63
71
  {tab.icon === 'logs' && (<TerminalIcon color={iconColor} size={14}/>)}
64
72
  {tab.icon === 'analytics' && (<AnalyticsIcon color={iconColor} size={14}/>)}
65
- {tab.icon === 'redux' && (<TerminalIcon color={iconColor} size={14}/>)}
73
+ {tab.icon === 'redux' && (<ReduxIcon color={iconColor} size={14}/>)}
74
+ {tab.icon === 'bundle' && (<PackageIcon color={iconColor} size={14}/>)}
75
+ {tab.icon === 'performance' && (<PerformanceIcon color={iconColor} size={14}/>)}
66
76
  <Text numberOfLines={1} ellipsizeMode="tail" style={[
67
77
  styles.contentTabButtonText,
68
78
  isActive &&