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,5 +1,5 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import { ScrollView, View, Text, StyleSheet } from 'react-native';
2
+ import { ScrollView, View, Text, StyleSheet, TouchableOpacity } from 'react-native';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  // Components
5
5
  import TreeNode from './TreeNode';
@@ -10,10 +10,63 @@ import { PrettyIcon, RawIcon, TableIcon } from './NetworkIcons';
10
10
  import { AppColors } from '../styles/AppColors';
11
11
  import { AppFonts } from '../styles/AppFonts';
12
12
  import styles from '../styles';
13
+ // ── Table Row Component with 3-Line Clamp & Show More/Less ───────────────────
14
+ const JsonTableRow = React.memo(({ itemKey, val, search, }) => {
15
+ const [expanded, setExpanded] = useState(false);
16
+ const isObject = val !== null && typeof val === 'object';
17
+ let rawStr = '';
18
+ if (val === null) {
19
+ rawStr = 'null';
20
+ }
21
+ else if (val === undefined) {
22
+ rawStr = 'undefined';
23
+ }
24
+ else if (isObject) {
25
+ try {
26
+ rawStr = JSON.stringify(val, null, 2);
27
+ }
28
+ catch {
29
+ rawStr = String(val);
30
+ }
31
+ }
32
+ else {
33
+ rawStr = String(val);
34
+ }
35
+ const isMultiline = rawStr.includes('\n') || rawStr.length > 90;
36
+ return (<View style={localStyles.tableRow}>
37
+ <View style={{ flex: 2, paddingRight: 8 }}>
38
+ <HighlightText text={itemKey} search={search} style={localStyles.tableCellKey} highlightStyle={localStyles.highlight}/>
39
+ {isObject && (<Text style={localStyles.tableTypeBadge}>
40
+ {Array.isArray(val) ? `Array(${val.length})` : 'Object'}
41
+ </Text>)}
42
+ </View>
43
+
44
+ <View style={{ flex: 3 }}>
45
+ <HighlightText text={rawStr} search={search} style={[
46
+ localStyles.tableCellValue,
47
+ isObject && localStyles.tableCellRawCode,
48
+ ]} highlightStyle={localStyles.highlight} numberOfLines={expanded ? undefined : 3} selectable={true}/>
49
+ {isMultiline && (<TouchableOpacity onPress={() => setExpanded(prev => !prev)} hitSlop={6} style={localStyles.showMoreBtn}>
50
+ <Text style={localStyles.showMoreText}>
51
+ {expanded ? 'Show less ▲' : 'Show more ▼'}
52
+ </Text>
53
+ </TouchableOpacity>)}
54
+ </View>
55
+ </View>);
56
+ });
13
57
  // ── JsonViewer Component ─────────────────────────────────────────────────────
14
58
  const JsonViewer = React.memo(({ data, search, forceOpen, defaultExpandDepth, wrap, fullHeight = false, maxHeight, mode: externalMode, onModeChange, hideTabs = false, }) => {
15
59
  const { t } = useTranslation();
16
- const [mode, setMode] = useState(externalMode || 'pretty');
60
+ const [internalMode, setInternalMode] = useState('pretty');
61
+ const mode = externalMode ?? internalMode;
62
+ const setMode = (newMode) => {
63
+ if (onModeChange) {
64
+ onModeChange(newMode);
65
+ }
66
+ else {
67
+ setInternalMode(newMode);
68
+ }
69
+ };
17
70
  const rawText = React.useMemo(() => {
18
71
  if (typeof data === 'string') {
19
72
  return data;
@@ -27,12 +80,9 @@ const JsonViewer = React.memo(({ data, search, forceOpen, defaultExpandDepth, wr
27
80
  }, [data]);
28
81
  useEffect(() => {
29
82
  if (externalMode) {
30
- setMode(externalMode);
83
+ setInternalMode(externalMode);
31
84
  }
32
85
  }, [externalMode]);
33
- useEffect(() => {
34
- onModeChange?.(mode);
35
- }, [mode, onModeChange]);
36
86
  const contentWrapperStyle = [
37
87
  localStyles.contentWrapper,
38
88
  { backgroundColor: AppColors.contentBg },
@@ -45,7 +95,9 @@ const JsonViewer = React.memo(({ data, search, forceOpen, defaultExpandDepth, wr
45
95
  ];
46
96
  // Determine type and size details
47
97
  const isObject = typeof data === 'object' && data !== null;
48
- const isEmpty = isObject && Object.keys(data).length === 0;
98
+ const isEmpty = data === null ||
99
+ data === undefined ||
100
+ (isObject && Object.keys(data).length === 0);
49
101
  // Render Table view (Key-Value flat table for root keys)
50
102
  const renderTableMode = () => {
51
103
  if (!isObject) {
@@ -65,28 +117,7 @@ const JsonViewer = React.memo(({ data, search, forceOpen, defaultExpandDepth, wr
65
117
  <Text style={[localStyles.tableHeaderCell, { flex: 2 }]}>{t('network.jsonViewer.key')}</Text>
66
118
  <Text style={[localStyles.tableHeaderCell, { flex: 3 }]}>{t('network.jsonViewer.value')}</Text>
67
119
  </View>
68
- {keys.map((key) => {
69
- const val = data[key];
70
- let displayVal = '';
71
- if (val === null) {
72
- displayVal = 'null';
73
- }
74
- else if (val === undefined) {
75
- displayVal = 'undefined';
76
- }
77
- else if (typeof val === 'object') {
78
- displayVal = Array.isArray(val) ? `[Array(${val.length})]` : '{Object}';
79
- }
80
- else {
81
- displayVal = String(val);
82
- }
83
- return (<View key={key} style={localStyles.tableRow}>
84
- <Text style={[localStyles.tableCellKey, { flex: 2 }]}>{key}</Text>
85
- <Text style={[localStyles.tableCellValue, { flex: 3 }]} numberOfLines={2}>
86
- {displayVal}
87
- </Text>
88
- </View>);
89
- })}
120
+ {keys.map((key) => (<JsonTableRow key={key} itemKey={key} val={data[key]} search={search}/>))}
90
121
  </View>);
91
122
  };
92
123
  // Tree View Content
@@ -220,11 +251,36 @@ const localStyles = StyleSheet.create({
220
251
  fontSize: 12.5,
221
252
  color: AppColors.purple,
222
253
  },
254
+ tableTypeBadge: {
255
+ fontFamily: AppFonts.interMedium,
256
+ fontSize: 9.5,
257
+ color: AppColors.slate400,
258
+ marginTop: 2,
259
+ },
223
260
  tableCellValue: {
224
261
  fontFamily: AppFonts.interRegular,
225
- fontSize: 12.5,
262
+ fontSize: 12,
226
263
  color: AppColors.primaryBlack,
227
- lineHeight: 18,
264
+ lineHeight: 17,
265
+ },
266
+ tableCellRawCode: {
267
+ fontFamily: AppFonts.interRegular,
268
+ fontSize: 11.5,
269
+ color: AppColors.slate700,
270
+ backgroundColor: `${AppColors.slate100}80`,
271
+ padding: 6,
272
+ borderRadius: 6,
273
+ },
274
+ showMoreBtn: {
275
+ alignSelf: 'flex-start',
276
+ marginTop: 4,
277
+ paddingVertical: 2,
278
+ paddingHorizontal: 4,
279
+ },
280
+ showMoreText: {
281
+ fontFamily: AppFonts.interBold,
282
+ fontSize: 11,
283
+ color: AppColors.purple,
228
284
  },
229
285
  emptyTable: {
230
286
  padding: 24,
@@ -236,5 +292,10 @@ const localStyles = StyleSheet.create({
236
292
  fontSize: 12.5,
237
293
  color: AppColors.slate400,
238
294
  },
295
+ highlight: {
296
+ backgroundColor: '#FEF08A',
297
+ color: '#854D0E',
298
+ fontFamily: AppFonts.interBold,
299
+ },
239
300
  });
240
301
  export default JsonViewer;
@@ -39,9 +39,8 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
39
39
  }, [isNew]);
40
40
  const path = getPath(item.url);
41
41
  const baseUrl = getBaseUrl(item.url) || item.url;
42
- const cleanHost = baseUrl.replace(/^https?:\/\//, '');
43
42
  const slug = path && path !== '/' ? path : item.url;
44
- const showSlug = slug !== cleanHost;
43
+ const showSlug = slug !== baseUrl && slug !== item.url;
45
44
  const triggeredAt = formatDateTime(item.startTime);
46
45
  const isJson = item.url.split('?')[0].toLowerCase().endsWith('.json');
47
46
  return (<TouchableScale onPress={onPress} style={[
@@ -52,7 +51,7 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
52
51
  },
53
52
  ]}>
54
53
  <View style={styles.cardBody}>
55
- {/* Row 1: Header (Checkbox, Serial, Method Badge, Base URL / Host, Status Pill) */}
54
+ {/* Row 1: Header (Checkbox, Serial, Method Badge, Base URL / Host with HTTPS, Status Pill) */}
56
55
  <View style={styles.cardHeaderRow}>
57
56
  <View style={styles.cardHeaderLeft}>
58
57
  <Pressable onPress={() => onToggleSelect(item.id)} hitSlop={12} style={[
@@ -72,7 +71,7 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
72
71
  </Text>
73
72
  </View>
74
73
 
75
- <HighlightText text={cleanHost || item.url} search={searchStr} style={styles.cardHostText} highlightStyle={styles.highlight} numberOfLines={1} ellipsizeMode="tail"/>
74
+ <HighlightText text={baseUrl || item.url} search={searchStr} style={styles.cardHostText} highlightStyle={styles.highlight} numberOfLines={1} ellipsizeMode="tail"/>
76
75
  </View>
77
76
 
78
77
  <View style={[
@@ -57,3 +57,12 @@ export declare const SortIcon: ({ ascending, color, size, }: any) => React.JSX.E
57
57
  export declare const PrettyIcon: ({ color, size, }: any) => React.JSX.Element;
58
58
  export declare const RawIcon: ({ color, size, }: any) => React.JSX.Element;
59
59
  export declare const TableIcon: ({ color, size, }: any) => React.JSX.Element;
60
+ export declare const PackageIcon: ({ color, size, }: any) => React.JSX.Element;
61
+ export declare const ForwardChevronIcon: ({ color, size, }: any) => React.JSX.Element;
62
+ export declare const BundleIcon: ({ color, size, }: any) => React.JSX.Element;
63
+ export declare const LiveStateIcon: ({ color, size, }: any) => React.JSX.Element;
64
+ export declare const TimelineIcon: ({ color, size, }: any) => React.JSX.Element;
65
+ export declare const StorageIcon: ({ color, size, }: any) => React.JSX.Element;
66
+ export declare const MetadataIcon: ({ color, size, }: any) => React.JSX.Element;
67
+ export declare const ReduxIcon: ({ color, size, }: any) => React.JSX.Element;
68
+ export declare const PerformanceIcon: ({ color, size, }: any) => React.JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import Svg, { Circle, Line, Path, Rect } from 'react-native-svg';
2
+ import Svg, { Circle, Ellipse, Line, Path, Rect } from 'react-native-svg';
3
3
  // Stylesheet
4
4
  import { AppColors } from '../styles/AppColors';
5
5
  export const EmptyRadarIcon = ({ color = AppColors.purple, size = 32 }) => {
@@ -348,3 +348,57 @@ export const TableIcon = ({ color = AppColors.grayTextWeak, size = 14, }) => {
348
348
  <Rect x="3" y="14" width="7" height="7" rx="1.5" stroke={color} strokeWidth="2"/>
349
349
  </Svg>);
350
350
  };
351
+ export const PackageIcon = ({ color = AppColors.grayTextWeak, size = 14, }) => {
352
+ return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
353
+ <Path d="M16.5 9.4 7.55 4.24a1.78 1.78 0 0 0-2.5 1.55v12.42a1.78 1.78 0 0 0 .86 1.52l8.95 5.16a1.78 1.78 0 0 0 1.78 0l8.95-5.16a1.78 1.78 0 0 0 .86-1.52V5.79a1.78 1.78 0 0 0-2.5-1.55L16.5 9.4z" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
354
+ <Path d="M3.29 7 12 12.01 20.71 7M12 22.08V12" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
355
+ </Svg>);
356
+ };
357
+ export const ForwardChevronIcon = ({ color = AppColors.grayTextWeak, size = 14, }) => {
358
+ return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
359
+ <Path d="M9 18l6-6-6-6" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
360
+ </Svg>);
361
+ };
362
+ export const BundleIcon = PackageIcon;
363
+ export const LiveStateIcon = ({ color = AppColors.purple, size = 14, }) => {
364
+ return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
365
+ <Path d="M12 3v3M12 18v3M3 12h3M18 12h3" stroke={color} strokeWidth="2" strokeLinecap="round"/>
366
+ <Circle cx="12" cy="12" r="3.5" stroke={color} strokeWidth="1.8" fill={`${color}22`}/>
367
+ <Circle cx="12" cy="12" r="1.5" fill={color}/>
368
+ </Svg>);
369
+ };
370
+ export const TimelineIcon = ({ color = AppColors.purple, size = 14, }) => {
371
+ return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
372
+ <Circle cx="12" cy="12" r="8.5" stroke={color} strokeWidth="1.8"/>
373
+ <Path d="M12 7.5v4.5l3 2" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
374
+ </Svg>);
375
+ };
376
+ export const StorageIcon = ({ color = AppColors.purple, size = 14, }) => {
377
+ return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
378
+ <Ellipse cx="12" cy="6" rx="8" ry="2.8" stroke={color} strokeWidth="1.8"/>
379
+ <Path d="M20 12c0 1.55-3.58 2.8-8 2.8s-8-1.25-8-2.8" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
380
+ <Path d="M4 6v12c0 1.55 3.58 2.8 8 2.8s8-1.25 8-2.8V6" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
381
+ </Svg>);
382
+ };
383
+ export const MetadataIcon = ({ color = AppColors.purple, size = 14, }) => {
384
+ return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
385
+ <Circle cx="12" cy="12" r="8.5" stroke={color} strokeWidth="1.8"/>
386
+ <Path d="M12 16v-4M12 8.2h.01" stroke={color} strokeWidth="2" strokeLinecap="round"/>
387
+ </Svg>);
388
+ };
389
+ export const ReduxIcon = ({ color = AppColors.purple, size = 14, }) => {
390
+ return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
391
+ <Circle cx="12" cy="12" r="4" stroke={color} strokeWidth="1.8"/>
392
+ <Path d="M12 3a9 9 0 0 1 9 9M12 21a9 9 0 0 1-9-9" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
393
+ <Circle cx="21" cy="12" r="1.5" fill={color}/>
394
+ <Circle cx="3" cy="12" r="1.5" fill={color}/>
395
+ </Svg>);
396
+ };
397
+ export const PerformanceIcon = ({ color = AppColors.purple, size = 14, }) => {
398
+ return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
399
+ <Path d="M12 3v2M5.636 5.636l1.414 1.414M18.364 5.636l-1.414 1.414M3 12h2M19 12h2" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
400
+ <Path d="M12 19a7 7 0 1 0-7-7c0 1.93.784 3.68 2.05 4.95" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
401
+ <Path d="M12 12l3.5-3.5" stroke={color} strokeWidth="2" strokeLinecap="round"/>
402
+ <Circle cx="12" cy="12" r="1.5" fill={color}/>
403
+ </Svg>);
404
+ };
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "1.1.16";
1
+ export declare const LIB_VERSION = "1.1.18";
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED FILE — do not edit by hand.
2
2
  // Regenerated from package.json on every build by scripts/gen-version.js.
3
- export const LIB_VERSION = '1.1.16';
3
+ export const LIB_VERSION = '1.1.18';
@@ -63,6 +63,16 @@ const symbolicateStack = async (stackString) => {
63
63
  catch { }
64
64
  return null;
65
65
  };
66
+ const isLoggerInternal = (line) => {
67
+ const l = line.toLowerCase();
68
+ return (l.includes('getstackdetails') ||
69
+ l.includes('addlog') ||
70
+ l.includes('consolelogger') ||
71
+ l.includes('setupconsolelogger') ||
72
+ l.includes('formatargs') ||
73
+ l.includes('react-native-inapp-inspector') ||
74
+ l.trim() === 'error');
75
+ };
66
76
  const getStackDetails = (args) => {
67
77
  try {
68
78
  let errorStack;
@@ -89,11 +99,7 @@ const getStackDetails = (args) => {
89
99
  let fallbackCaller;
90
100
  for (let i = 0; i < lines.length; i++) {
91
101
  const line = lines[i];
92
- if (!line ||
93
- line.includes('consoleLogger') ||
94
- line.includes('setupConsoleLogger') ||
95
- line.includes('react-native-inapp-inspector') ||
96
- line.trim() === 'Error') {
102
+ if (!line || isLoggerInternal(line)) {
97
103
  continue;
98
104
  }
99
105
  const isInternal = line.includes('react-native/Libraries') ||
@@ -110,10 +116,7 @@ const getStackDetails = (args) => {
110
116
  break;
111
117
  }
112
118
  }
113
- const cleanedStack = lines
114
- .filter(l => !l.includes('consoleLogger') &&
115
- !l.includes('setupConsoleLogger'))
116
- .join('\n');
119
+ const cleanedStack = lines.filter(l => !isLoggerInternal(l)).join('\n');
117
120
  const caller = userCaller || fallbackCaller || 'Unknown';
118
121
  return { caller, stack: cleanedStack || stackToUse, errorStack, stackToUse };
119
122
  }
@@ -159,6 +162,31 @@ const addLog = (type, args, sourceMethod) => {
159
162
  logs.unshift(newLog);
160
163
  logs = logs.slice(0, MAX_LOGS);
161
164
  notify();
165
+ // Asynchronously symbolicate stack trace via Metro in development
166
+ if (newLog.stack && typeof __DEV__ !== 'undefined' && __DEV__) {
167
+ symbolicateStack(newLog.stack)
168
+ .then(symbolicatedFrames => {
169
+ if (symbolicatedFrames && symbolicatedFrames.length > 0) {
170
+ const validFrames = symbolicatedFrames.filter(f => f.file &&
171
+ !f.file.includes('react-native/Libraries') &&
172
+ !f.file.includes('node_modules') &&
173
+ !isLoggerInternal(f.file) &&
174
+ !isLoggerInternal(f.methodName || ''));
175
+ const topFrame = validFrames[0] || symbolicatedFrames[0];
176
+ if (topFrame) {
177
+ const cleanMethod = topFrame.methodName || 'anonymous';
178
+ const cleanFile = topFrame.file.split('/').pop() || topFrame.file;
179
+ newLog.caller = `${cleanMethod} (${cleanFile}:${topFrame.lineNumber || 1}:${topFrame.column || 1})`;
180
+ }
181
+ newLog.stack = symbolicatedFrames
182
+ .filter(f => !isLoggerInternal(f.file || '') && !isLoggerInternal(f.methodName || ''))
183
+ .map(f => ` at ${f.methodName || 'anonymous'} (${f.file}:${f.lineNumber || 1}:${f.column || 1})`)
184
+ .join('\n');
185
+ notify();
186
+ }
187
+ })
188
+ .catch(() => { });
189
+ }
162
190
  // Asynchronously symbolicate stack trace using Metro in DEV mode
163
191
  if (stackToUse && typeof __DEV__ !== 'undefined' && __DEV__) {
164
192
  const currentLogId = newLog.id;
@@ -67,7 +67,8 @@ function recordAction(action, prevState, nextState) {
67
67
  const payload = action && typeof action === 'object' && action.payload !== undefined
68
68
  ? action.payload
69
69
  : null;
70
- const timestamp = new Date().toLocaleTimeString();
70
+ const now = Date.now();
71
+ const timestamp = new Date(now).toLocaleTimeString();
71
72
  const affectedSlices = [];
72
73
  if (prevState &&
73
74
  nextState &&
@@ -75,7 +76,7 @@ function recordAction(action, prevState, nextState) {
75
76
  typeof nextState === 'object') {
76
77
  Object.keys(nextState).forEach(key => {
77
78
  if (prevState[key] !== nextState[key]) {
78
- lastActionForReducer[key] = { type, payload, timestamp };
79
+ lastActionForReducer[key] = { type, payload, timestamp, updatedAt: now };
79
80
  affectedSlices.push(key);
80
81
  }
81
82
  });
@@ -85,6 +86,7 @@ function recordAction(action, prevState, nextState) {
85
86
  type,
86
87
  payload,
87
88
  timestamp,
89
+ updatedAt: now,
88
90
  affectedSlices,
89
91
  prevState,
90
92
  nextState,
@@ -458,10 +458,14 @@ export const parseStackLine = (rawLine, isOrigin = false) => {
458
458
  columnNumber = locMatch[3];
459
459
  }
460
460
  // Clean file name (remove query strings, packager urls, and parent paths)
461
- let cleanPath = fullPath.split('?')[0].split('&')[0].replace(/[)]+$/, '');
461
+ let cleanPath = fullPath
462
+ .split('?')[0]
463
+ .split('&')[0]
464
+ .replace(/[)]+$/, '')
465
+ .replace(/\/\/+$/, '');
462
466
  // Remove protocol prefixes
463
467
  cleanPath = cleanPath.replace(/^(?:https?:\/\/[^\/]+\/|file:\/\/\/|webpack:\/\/\/?)/, '');
464
- const fileName = cleanPath.split('/').pop() || cleanPath;
468
+ const fileName = cleanPath.split('/').filter(Boolean).pop() || cleanPath;
465
469
  // Determine file extension (.tsx, .jsx, .ts, .js)
466
470
  const extMatch = fileName.match(/\.([a-z0-9]+)$/i);
467
471
  const ext = extMatch ? extMatch[1].toLowerCase() : '';
package/dist/esm/index.js CHANGED
@@ -22,7 +22,7 @@ import { getReduxState, subscribeReduxState, setReduxAutoRefresh, getLastActionF
22
22
  import { LIB_VERSION } from './constants';
23
23
  // Stylesheet
24
24
  import { toggleGlobalTheme } from './styles';
25
- const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigationRef, }) => {
25
+ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigationRef, appIcon, environment, }) => {
26
26
  // Set custom storage synchronously during render phase
27
27
  setCustomStorage(storage || null);
28
28
  const [isDark, setIsDark] = useState(false);
@@ -41,6 +41,9 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
41
41
  const [search, setSearch] = useState('');
42
42
  const [detailSearch, setDetailSearch] = useState('');
43
43
  const [reduxSearch, setReduxSearch] = useState('');
44
+ const [selectedReduxSlice, setSelectedReduxSlice] = useState(null);
45
+ const [selectedReduxAction, setSelectedReduxAction] = useState(null);
46
+ const [reduxActiveSubTab, setReduxActiveSubTab] = useState('state');
44
47
  const [apiDetailActiveTab, setApiDetailActiveTab] = useState('response');
45
48
  useEffect(() => {
46
49
  LogBox.ignoreAllLogs(enabled);
@@ -147,6 +150,8 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
147
150
  logs: true,
148
151
  analytics: true,
149
152
  redux: false,
153
+ bundle: false,
154
+ performance: false,
150
155
  });
151
156
  const [maxNetworkLogs, setMaxNetworkLogs] = useState(100);
152
157
  const [reduxAutoRefresh, setReduxAutoRefreshState] = useState(true);
@@ -165,6 +170,8 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
165
170
  logs: true,
166
171
  analytics: true,
167
172
  redux: false,
173
+ bundle: false,
174
+ performance: false,
168
175
  });
169
176
  setDefaultTab('apis');
170
177
  setMaxNetworkLogs(100);
@@ -222,6 +229,8 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
222
229
  logs: true,
223
230
  analytics: true,
224
231
  redux: false,
232
+ bundle: false,
233
+ performance: false,
225
234
  },
226
235
  ...(saved.tabVisibility || {}),
227
236
  apis: true,
@@ -1089,6 +1098,8 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1089
1098
  closeModal,
1090
1099
  isReady,
1091
1100
  isEnabled,
1101
+ appIcon,
1102
+ environment,
1092
1103
  modalHeightPercent,
1093
1104
  setModalHeightPercent,
1094
1105
  modalAnimationType,
@@ -1197,6 +1208,12 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1197
1208
  reduxLastActionMap,
1198
1209
  reduxSearch,
1199
1210
  setReduxSearch,
1211
+ selectedReduxSlice,
1212
+ setSelectedReduxSlice,
1213
+ selectedReduxAction,
1214
+ setSelectedReduxAction,
1215
+ reduxActiveSubTab,
1216
+ setReduxActiveSubTab,
1200
1217
  // ─── Settings ───────────────────────────────────────────────────────
1201
1218
  settingsActiveSubTab,
1202
1219
  setSettingsActiveSubTab,