react-native-inapp-inspector 1.1.6 → 1.1.9

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.
package/README.md CHANGED
@@ -125,6 +125,32 @@ If no `storage` prop is passed, Android will fall back to an in-memory store (se
125
125
 
126
126
  ---
127
127
 
128
+ ## Navigation & Screen Tracking
129
+
130
+ To group network requests, console logs, and analytics events by the specific navigation screen where they occurred, the inspector automatically hooks into React Navigation context.
131
+
132
+ If you mount `<NetworkInspector />` globally at the root of your application (outside of the `<NavigationContainer>`), it won't have access to React Navigation's context. To enable accurate screen tracking in this setup, create a navigation ref and pass it to the `navigationRef` prop:
133
+
134
+ ```tsx
135
+ import { NavigationContainer, createNavigationContainerRef } from '@react-navigation/native';
136
+ import NetworkInspector from 'react-native-inapp-inspector';
137
+
138
+ const navigationRef = createNavigationContainerRef();
139
+
140
+ const App = () => {
141
+ return (
142
+ <>
143
+ <NavigationContainer ref={navigationRef}>
144
+ {/* Navigators / Screens */}
145
+ </NavigationContainer>
146
+ <NetworkInspector navigationRef={navigationRef} />
147
+ </>
148
+ );
149
+ };
150
+ ```
151
+
152
+ ---
153
+
128
154
  ## Network Logging
129
155
 
130
156
  `setupNetworkLogger()` patches global `fetch`, the default axios instance, and future axios instances created with `axios.create()`.
@@ -272,8 +298,11 @@ import {ErrorBoundary} from 'react-native-inapp-inspector';
272
298
  | `clearAnalyticsEvents()` | function | Clears captured analytics events. |
273
299
  | `subscribeAnalyticsEvents(callback)` | function | Subscribes to analytics event updates and returns an unsubscribe function. |
274
300
  | `connectReduxStore(store)` | function | Connects a Redux store for live state and action inspection. |
301
+ | `inspectorReduxMiddleware` | middleware | Redux middleware to capture actions dispatched from thunks/sagas. |
275
302
  | `getReduxState()` | function | Returns the latest captured Redux state. |
276
303
  | `subscribeReduxState(callback)` | function | Subscribes to Redux state updates and returns an unsubscribe function. |
304
+ | `getActionHistory()` | function | Returns the recent dispatched Redux actions. |
305
+ | `clearActionHistory()` | function | Clears the Redux action history. |
277
306
  | `WebView` | component | Instrumented WebView wrapper. |
278
307
  | `getWebViewLogs()` | function | Returns captured WebView console logs. |
279
308
  | `getWebViewNavHistory()` | function | Returns captured WebView navigation history. |
@@ -300,11 +329,21 @@ npm run ios
300
329
 
301
330
  ---
302
331
 
303
- ## Support
332
+ ## Contributing 🤝
333
+
334
+ Contributions are welcome! Please check out our [Contributing Guidelines](CONTRIBUTING.md) to learn how to get started, set up the development environment, or request features.
335
+
336
+ ---
337
+
338
+ ## Support & Sponsoring 💖
339
+
340
+ This library is a completely free, open-source utility maintained in the author's spare time. If this tool has saved you or your team hours of debugging, please consider supporting its continuous development.
341
+
342
+ Sponsoring helps prioritize your bug fixes, maintain compatibility with new React Native versions, and support new features.
304
343
 
305
- If you find this project useful, consider sponsoring its development:
344
+ 👉 **[Sponsor @vengatmacuser on GitHub Sponsors](https://github.com/sponsors/vengatmacuser)**
306
345
 
307
- [![GitHub Sponsors](https://img.shields.io/badge/Sponsor-@vengatmacuser-ea4aaa?logo=githubsponsors&logoColor=white)](https://github.com/sponsors/vengatmacuser)
346
+ [![GitHub Sponsors](https://img.shields.io/badge/Sponsor-@vengatmacuser-ea4aaa?logo=githubsponsors&logoColor=white&style=for-the-badge)](https://github.com/sponsors/vengatmacuser)
308
347
 
309
348
  ---
310
349
 
@@ -43,6 +43,7 @@ const AppColors_1 = require("../styles/AppColors");
43
43
  const constants_1 = require("../constants");
44
44
  const helpers_1 = require("../helpers");
45
45
  const NetworkIcons_1 = require("./NetworkIcons");
46
+ const AppFonts_1 = require("../styles/AppFonts");
46
47
  const styles_1 = __importDefault(require("../styles"));
47
48
  const HighlightText_1 = __importDefault(require("./HighlightText"));
48
49
  const TouchableScale_1 = __importDefault(require("./TouchableScale"));
@@ -74,6 +75,8 @@ const LogCard = react_1.default.memo(function LogCard({ item, onPress, timelineM
74
75
  }
75
76
  }, [isNew]);
76
77
  const path = (0, helpers_1.getPath)(item.url);
78
+ const baseUrl = (0, helpers_1.getBaseUrl)(item.url) || item.url;
79
+ const showPathRow = baseUrl !== item.url && path !== '/';
77
80
  const triggeredAt = (0, helpers_1.formatDateTime)(item.startTime);
78
81
  const isJson = item.url.split('?')[0].toLowerCase().endsWith('.json');
79
82
  return (<TouchableScale_1.default onPress={onPress} style={[
@@ -102,7 +105,7 @@ const LogCard = react_1.default.memo(function LogCard({ item, onPress, timelineM
102
105
  </react_native_1.Text>
103
106
  </react_native_1.View>
104
107
 
105
- <HighlightText_1.default text={path} search={searchStr} style={styles_1.default.urlPathText} highlightStyle={styles_1.default.highlight}/>
108
+ <HighlightText_1.default text={baseUrl} search={searchStr} style={styles_1.default.urlPathText} highlightStyle={styles_1.default.highlight} numberOfLines={0}/>
106
109
 
107
110
  {isJson && (<react_native_1.View style={[
108
111
  styles_1.default.chip,
@@ -135,6 +138,16 @@ const LogCard = react_1.default.memo(function LogCard({ item, onPress, timelineM
135
138
  </react_native_1.View>)}
136
139
  </react_native_1.View>
137
140
 
141
+ {showPathRow && (<react_native_1.View style={{ flexDirection: 'row', alignItems: 'flex-start', marginTop: 3, marginBottom: 5, paddingLeft: 30, gap: 6 }}>
142
+ <react_native_1.Text style={{ color: AppColors_1.AppColors.grayTextWeak, fontSize: 9, fontFamily: AppFonts_1.AppFonts.interBold, letterSpacing: 0.5, marginTop: 2.5 }}>PATH</react_native_1.Text>
143
+ <HighlightText_1.default text={path} search={searchStr} style={{
144
+ fontFamily: AppFonts_1.AppFonts.Sfprotext || 'System',
145
+ fontSize: 12,
146
+ color: AppColors_1.AppColors.grayText,
147
+ flex: 1,
148
+ }} highlightStyle={styles_1.default.highlight} numberOfLines={0}/>
149
+ </react_native_1.View>)}
150
+
138
151
  <react_native_1.View style={styles_1.default.cardBottomRow}>
139
152
  <react_native_1.View style={styles_1.default.cardDateRow}>
140
153
  <NetworkIcons_1.CalendarIcon color={AppColors_1.AppColors.grayTextWeak} size={11}/>
@@ -49,3 +49,4 @@ export declare const InfoCircleIcon: ({ color, size, }: any) => React.JSX.Elemen
49
49
  export declare const WarningTriangleIcon: ({ color, size, }: any) => React.JSX.Element;
50
50
  export declare const ErrorCircleIcon: ({ color, size, }: any) => React.JSX.Element;
51
51
  export declare const TrendingUpIcon: ({ color, size, }: any) => React.JSX.Element;
52
+ export declare const MotionIcon: ({ color, size, }: any) => React.JSX.Element;
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.TrendingUpIcon = exports.ErrorCircleIcon = exports.WarningTriangleIcon = exports.InfoCircleIcon = exports.UserIcon = exports.LayersIcon = exports.WipeIcon = exports.FolderIcon = exports.SettingsIcon = exports.EyeIcon = exports.JsIcon = exports.CssIcon = exports.HtmlIcon = exports.BrandSquareIcon = exports.BrandCircleIcon = exports.MoonIcon = exports.SunIcon = exports.DebugIcon = exports.InsightsIcon = exports.AnalyticsIcon = exports.WhiteBackNavigation = exports.CloseWhite = exports.DownloadIcon = exports.FilterIcon = exports.ChevronIcon = exports.SortArrowIcon = exports.GlobeIcon = exports.DiffIcon = exports.SignalIcon = exports.ExportIcon = exports.HeaderPauseIcon = exports.TrashIcon = exports.FailIcon = exports.CheckIcon = exports.TerminalIcon = exports.FetchIcon = exports.CopyIcon = exports.HeadersIcon = exports.ResponseIcon = exports.RequestIcon = exports.SizeIcon = exports.StatusIcon = exports.CalendarIcon = exports.ClockIcon = exports.ClearIcon = exports.SearchIcon = exports.ExpandCollapseIcon = exports.ScreenIcon = exports.MapPinIcon = exports.EmptyRadarIcon = void 0;
40
+ exports.MotionIcon = void 0;
40
41
  const react_1 = __importDefault(require("react"));
41
42
  const react_native_svg_1 = __importStar(require("react-native-svg"));
42
43
  // Stylesheet
@@ -386,3 +387,9 @@ const TrendingUpIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 12,
386
387
  </react_native_svg_1.default>);
387
388
  };
388
389
  exports.TrendingUpIcon = TrendingUpIcon;
390
+ const MotionIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 14, }) => {
391
+ return (<react_native_svg_1.default width={size} height={size} viewBox="0 0 24 24" fill="none">
392
+ <react_native_svg_1.Path d="M5 3l14 9-14 9V3z" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
393
+ </react_native_svg_1.default>);
394
+ };
395
+ exports.MotionIcon = MotionIcon;
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "1.1.6";
1
+ export declare const LIB_VERSION = "1.1.9";
@@ -3,4 +3,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LIB_VERSION = void 0;
4
4
  // AUTO-GENERATED FILE — do not edit by hand.
5
5
  // Regenerated from package.json on every build by scripts/gen-version.js.
6
- exports.LIB_VERSION = '1.1.6';
6
+ exports.LIB_VERSION = '1.1.9';
@@ -6,6 +6,7 @@ export declare const getDurationColor: (duration: number | null) => string;
6
6
  export declare const getSize: (data: unknown) => string;
7
7
  export declare const copyToClipboard: (value: unknown, label: string) => void;
8
8
  export declare const getPath: (url: string) => string;
9
+ export declare const getBaseUrl: (url: string) => string;
9
10
  export declare const getCurlCommand: (log: NetworkLog) => string;
10
11
  export declare const getFetchCommand: (log: NetworkLog) => string;
11
12
  export declare const deduplicateLogs: (raw: NetworkLog[]) => NetworkLog[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.formatDateTimeToAnalytics = exports.isAllValuesEmpty = exports.getLocalizedFilePathWithSlash = exports.getLocalizedFilePath = exports.formatDisplayUrl = exports.getDiff = exports.flattenObject = exports.getNavigationInfo = exports.escapeRegex = exports.deduplicateLogs = exports.getFetchCommand = exports.getCurlCommand = exports.getPath = exports.copyToClipboard = exports.getSize = exports.getDurationColor = exports.getStatusColor = exports.formatDateTime = exports.getDomainColor = void 0;
3
+ exports.formatDateTimeToAnalytics = exports.isAllValuesEmpty = exports.getLocalizedFilePathWithSlash = exports.getLocalizedFilePath = exports.formatDisplayUrl = exports.getDiff = exports.flattenObject = exports.getNavigationInfo = exports.escapeRegex = exports.deduplicateLogs = exports.getFetchCommand = exports.getCurlCommand = exports.getBaseUrl = exports.getPath = exports.copyToClipboard = exports.getSize = exports.getDurationColor = exports.getStatusColor = exports.formatDateTime = exports.getDomainColor = void 0;
4
4
  const react_native_1 = require("react-native");
5
5
  // Stylesheet
6
6
  const AppColors_1 = require("../styles/AppColors");
@@ -83,6 +83,17 @@ const getPath = (url) => {
83
83
  }
84
84
  };
85
85
  exports.getPath = getPath;
86
+ const getBaseUrl = (url) => {
87
+ try {
88
+ const u = new URL(url);
89
+ return `${u.protocol}//${u.host}`;
90
+ }
91
+ catch {
92
+ const match = url.match(/^(https?:\/\/[^/]+)/);
93
+ return match ? match[1] : '';
94
+ }
95
+ };
96
+ exports.getBaseUrl = getBaseUrl;
86
97
  const getCurlCommand = (log) => {
87
98
  let cmd = `curl -X ${log.method} "${log.url}"`;
88
99
  if (log.requestHeaders) {
@@ -6,6 +6,7 @@ export declare function setCustomStorage(storage: InspectorStorage | null): void
6
6
  export interface PersistedSettings {
7
7
  isDark?: boolean;
8
8
  modalHeightPercent?: number;
9
+ modalAnimationType?: string;
9
10
  tabVisibility?: Record<string, boolean>;
10
11
  defaultTab?: string;
11
12
  maxNetworkLogs?: number;
@@ -152,7 +152,7 @@ const NavigationTracker = ({ onStateChange }) => {
152
152
  const animateNextLayout = () => {
153
153
  react_native_1.LayoutAnimation.configureNext(react_native_1.LayoutAnimation.Presets.easeInEaseOut);
154
154
  };
155
- const NetworkInspector = ({ enabled = true, storage, }) => {
155
+ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
156
156
  // Set custom storage synchronously during render phase
157
157
  (0, settingsStore_1.setCustomStorage)(storage || null);
158
158
  const [isDark, setIsDark] = (0, react_1.useState)(false);
@@ -163,6 +163,8 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
163
163
  const [reduxLastActionMap, setReduxLastActionMap] = (0, react_1.useState)({});
164
164
  // Inspector panel height as a percentage of the screen (configurable in Settings).
165
165
  const [modalHeightPercent, setModalHeightPercent] = (0, react_1.useState)(90);
166
+ const [modalAnimationType, setModalAnimationType] = (0, react_1.useState)('slide');
167
+ const headerTopPadding = react_native_1.Platform.OS === 'ios' && modalHeightPercent >= 95 ? 44 : 0;
166
168
  const [expandedReducers, setExpandedReducers] = (0, react_1.useState)({});
167
169
  const [logs, setLogs] = (0, react_1.useState)([]);
168
170
  const [visible, setVisible] = (0, react_1.useState)(false);
@@ -290,6 +292,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
290
292
  setIsDark(false);
291
293
  (0, styles_1.toggleGlobalTheme)(false);
292
294
  setModalHeightPercent(90);
295
+ setModalAnimationType('slide');
293
296
  setTabVisibility({
294
297
  insights: true,
295
298
  apis: true,
@@ -327,6 +330,8 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
327
330
  }
328
331
  if (saved.modalHeightPercent != null)
329
332
  setModalHeightPercent(saved.modalHeightPercent);
333
+ if (saved.modalAnimationType)
334
+ setModalAnimationType(saved.modalAnimationType);
330
335
  if (saved.tabVisibility)
331
336
  setTabVisibility(prev => ({
332
337
  ...prev,
@@ -381,6 +386,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
381
386
  (0, settingsStore_1.saveSettings)({
382
387
  isDark,
383
388
  modalHeightPercent,
389
+ modalAnimationType,
384
390
  tabVisibility,
385
391
  defaultTab,
386
392
  maxNetworkLogs,
@@ -396,6 +402,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
396
402
  }, [
397
403
  isDark,
398
404
  modalHeightPercent,
405
+ modalAnimationType,
399
406
  tabVisibility,
400
407
  defaultTab,
401
408
  maxNetworkLogs,
@@ -538,6 +545,36 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
538
545
  currentRouteRef.current = (0, helpers_1.getNavigationInfo)(navState);
539
546
  }
540
547
  }, [navState]);
548
+ (0, react_1.useEffect)(() => {
549
+ if (!navigationRef)
550
+ return;
551
+ const updateState = () => {
552
+ try {
553
+ if (typeof navigationRef.isReady === 'function' && navigationRef.isReady()) {
554
+ const state = navigationRef.getRootState();
555
+ if (state) {
556
+ setNavState(state);
557
+ }
558
+ }
559
+ }
560
+ catch (err) {
561
+ // Safe check
562
+ }
563
+ };
564
+ // Initialize state
565
+ updateState();
566
+ // Listen to changes
567
+ const unsubscribe = typeof navigationRef.addListener === 'function'
568
+ ? navigationRef.addListener('state', () => {
569
+ updateState();
570
+ })
571
+ : null;
572
+ return () => {
573
+ if (unsubscribe) {
574
+ unsubscribe();
575
+ }
576
+ };
577
+ }, [navigationRef]);
541
578
  const logRouteMapRef = (0, react_1.useRef)(new Map());
542
579
  const prevLogIdsRef = (0, react_1.useRef)(new Set());
543
580
  const [newLogIds, setNewLogIds] = (0, react_1.useState)(new Set());
@@ -1362,7 +1399,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
1362
1399
  return (<react_native_1.View style={{ flex: 1, backgroundColor: AppColors_1.AppColors.grayBackground }}>
1363
1400
  {/* Settings Header with back button */}
1364
1401
  <react_native_linear_gradient_1.default colors={[AppColors_1.AppColors.purple, '#6B4EFF']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles_1.default.headerGradient}>
1365
- <react_native_1.SafeAreaView style={{ width: '100%' }}>
1402
+ <react_native_1.View style={{ paddingTop: headerTopPadding, width: '100%' }}>
1366
1403
  <react_native_1.View style={[styles_1.default.header, { paddingHorizontal: 16, gap: 12 }]}>
1367
1404
  <TouchableScale_1.default onPress={() => {
1368
1405
  animateNextLayout();
@@ -1411,7 +1448,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
1411
1448
  </react_native_1.Text>
1412
1449
  </react_native_1.View>
1413
1450
  </react_native_1.View>
1414
- </react_native_1.SafeAreaView>
1451
+ </react_native_1.View>
1415
1452
  </react_native_linear_gradient_1.default>
1416
1453
 
1417
1454
  <react_native_1.ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
@@ -1838,6 +1875,90 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
1838
1875
  </react_native_1.View>
1839
1876
  </react_native_1.View>
1840
1877
 
1878
+ {/* Divider */}
1879
+ <react_native_1.View style={{
1880
+ height: 1,
1881
+ backgroundColor: AppColors_1.AppColors.dividerColor,
1882
+ }}/>
1883
+
1884
+ {/* Modal Animation */}
1885
+ <react_native_1.View style={{
1886
+ paddingVertical: 12,
1887
+ paddingHorizontal: 14,
1888
+ }}>
1889
+ <react_native_1.View style={{
1890
+ flexDirection: 'row',
1891
+ alignItems: 'center',
1892
+ gap: 8,
1893
+ }}>
1894
+ <react_native_1.View style={{
1895
+ width: 20,
1896
+ height: 20,
1897
+ borderRadius: 6,
1898
+ backgroundColor: AppColors_1.AppColors.purpleShade50,
1899
+ borderWidth: 1,
1900
+ borderColor: 'rgba(104,75,155,0.2)',
1901
+ alignItems: 'center',
1902
+ justifyContent: 'center',
1903
+ }}>
1904
+ <NetworkIcons_1.MotionIcon color={AppColors_1.AppColors.purple} size={11}/>
1905
+ </react_native_1.View>
1906
+ <react_native_1.View style={{ flex: 1 }}>
1907
+ <react_native_1.Text style={{
1908
+ fontFamily: AppFonts_1.AppFonts.interBold,
1909
+ fontSize: 13,
1910
+ color: AppColors_1.AppColors.primaryBlack,
1911
+ }}>
1912
+ Modal Animation
1913
+ </react_native_1.Text>
1914
+ <react_native_1.Text style={{
1915
+ fontFamily: AppFonts_1.AppFonts.interRegular,
1916
+ fontSize: 11,
1917
+ color: AppColors_1.AppColors.grayText,
1918
+ marginTop: 1,
1919
+ }}>
1920
+ How the inspector panel enters and exits the screen
1921
+ </react_native_1.Text>
1922
+ </react_native_1.View>
1923
+ </react_native_1.View>
1924
+
1925
+ {/* Segmented picker */}
1926
+ <react_native_1.View style={{
1927
+ flexDirection: 'row',
1928
+ backgroundColor: AppColors_1.AppColors.grayBackground,
1929
+ borderRadius: 8,
1930
+ padding: 2.5,
1931
+ marginTop: 10,
1932
+ borderWidth: 1,
1933
+ borderColor: AppColors_1.AppColors.dividerColor,
1934
+ }}>
1935
+ {([
1936
+ { key: 'slide', label: 'Slide Up' },
1937
+ { key: 'fade', label: 'Fade' },
1938
+ { key: 'none', label: 'None' },
1939
+ ]).map(opt => {
1940
+ const isActive = modalAnimationType === opt.key;
1941
+ return (<TouchableScale_1.default key={opt.key} onPress={() => setModalAnimationType(opt.key)} style={{
1942
+ flex: 1,
1943
+ paddingVertical: 6,
1944
+ alignItems: 'center',
1945
+ borderRadius: 6,
1946
+ backgroundColor: isActive
1947
+ ? AppColors_1.AppColors.purple
1948
+ : 'transparent',
1949
+ }}>
1950
+ <react_native_1.Text style={{
1951
+ fontFamily: AppFonts_1.AppFonts.interBold,
1952
+ fontSize: 11,
1953
+ color: isActive ? '#FFFFFF' : AppColors_1.AppColors.grayText,
1954
+ }}>
1955
+ {opt.label}
1956
+ </react_native_1.Text>
1957
+ </TouchableScale_1.default>);
1958
+ })}
1959
+ </react_native_1.View>
1960
+ </react_native_1.View>
1961
+
1841
1962
  {/* Divider */}
1842
1963
  <react_native_1.View style={{
1843
1964
  height: 1,
@@ -2679,7 +2800,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
2679
2800
  }
2680
2801
  return (<react_native_1.View style={{ flex: 1, backgroundColor: AppColors_1.AppColors.grayBackground }}>
2681
2802
  <react_native_linear_gradient_1.default colors={[AppColors_1.AppColors.purple, '#6B4EFF']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles_1.default.headerGradient}>
2682
- <react_native_1.SafeAreaView style={{ width: '100%' }}>
2803
+ <react_native_1.View style={{ paddingTop: headerTopPadding, width: '100%' }}>
2683
2804
  <react_native_1.View style={[styles_1.default.header, { paddingHorizontal: 16, gap: 12 }]}>
2684
2805
  <TouchableScale_1.default onPress={goBackToMain} hitSlop={12} style={{
2685
2806
  padding: 8,
@@ -2726,7 +2847,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
2726
2847
  </react_native_1.Text>
2727
2848
  </react_native_1.View>) : null}
2728
2849
  </react_native_1.View>
2729
- </react_native_1.SafeAreaView>
2850
+ </react_native_1.View>
2730
2851
  </react_native_linear_gradient_1.default>
2731
2852
  {content}
2732
2853
  </react_native_1.View>);
@@ -3563,7 +3684,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
3563
3684
  </TouchableScale_1.default>
3564
3685
  </react_native_1.Animated.View>
3565
3686
 
3566
- <react_native_1.Modal visible={visible} animationType="slide" transparent>
3687
+ <react_native_1.Modal visible={visible} animationType={modalAnimationType} transparent>
3567
3688
  {visible && (<ErrorBoundary_1.default onClose={closeModal}>
3568
3689
  <react_native_1.View style={styles_1.default.modalBackdrop}>
3569
3690
  <react_native_1.Pressable style={styles_1.default.modalBackdropPressable} onPress={closeModal}/>
@@ -3574,7 +3695,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
3574
3695
  <react_native_1.StatusBar translucent backgroundColor="transparent" barStyle="light-content"/>
3575
3696
 
3576
3697
  <react_native_linear_gradient_1.default colors={[AppColors_1.AppColors.purple, '#6B4EFF']} style={styles_1.default.headerGradient}>
3577
- <react_native_1.SafeAreaView style={{ width: '100%' }}>
3698
+ <react_native_1.View style={{ paddingTop: headerTopPadding, width: '100%' }}>
3578
3699
  <react_native_1.View style={styles_1.default.header}>
3579
3700
  <react_native_1.View style={[
3580
3701
  styles_1.default.headerLeft,
@@ -3912,7 +4033,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
3912
4033
  </TouchableScale_1.default>
3913
4034
  </react_native_1.View>
3914
4035
  </react_native_1.View>
3915
- </react_native_1.SafeAreaView>
4036
+ </react_native_1.View>
3916
4037
  </react_native_linear_gradient_1.default>
3917
4038
 
3918
4039
  {/* ─── Horizontal Scrollable Tab Bar inside Content ─── */}
@@ -4037,8 +4158,25 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
4037
4158
  ? AppColors_1.AppColors.purple
4038
4159
  : AppColors_1.AppColors.grayTextStrong} size={18}/>
4039
4160
  </TouchableScale_1.default>
4040
- <TouchableScale_1.default style={styles_1.default.toolbarBtn} onPress={handleDelete} hitSlop={10}>
4041
- <NetworkIcons_1.TrashIcon color={AppColors_1.AppColors.grayTextStrong} size={18}/>
4161
+ <TouchableScale_1.default style={{
4162
+ flexDirection: 'row',
4163
+ alignItems: 'center',
4164
+ gap: 4,
4165
+ paddingHorizontal: 10,
4166
+ paddingVertical: 5,
4167
+ borderRadius: 8,
4168
+ backgroundColor: 'rgba(255,46,87,0.06)',
4169
+ borderWidth: 1,
4170
+ borderColor: 'rgba(255,46,87,0.15)',
4171
+ }} onPress={handleDelete} hitSlop={6}>
4172
+ <NetworkIcons_1.WipeIcon color={AppColors_1.AppColors.errorColor} size={13}/>
4173
+ <react_native_1.Text style={{
4174
+ fontFamily: AppFonts_1.AppFonts.interBold,
4175
+ fontSize: 10.5,
4176
+ color: AppColors_1.AppColors.errorColor,
4177
+ }}>
4178
+ Clear
4179
+ </react_native_1.Text>
4042
4180
  </TouchableScale_1.default>
4043
4181
  </react_native_1.View>)}
4044
4182
  </react_native_1.View>
@@ -4264,13 +4402,27 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
4264
4402
  </react_native_1.View>
4265
4403
 
4266
4404
  <react_native_1.View style={styles_1.default.toolbarRight}>
4267
- <TouchableScale_1.default style={styles_1.default.toolbarBtn} onPress={handleDelete} hitSlop={10}>
4268
- <NetworkIcons_1.TrashIcon color={AppColors_1.AppColors.grayTextStrong} size={18}/>
4269
- {selectedLogs.size > 0 && (<react_native_1.View style={styles_1.default.trashBadge}>
4270
- <react_native_1.Text style={styles_1.default.trashBadgeText}>
4271
- {selectedLogs.size}
4272
- </react_native_1.Text>
4273
- </react_native_1.View>)}
4405
+ <TouchableScale_1.default style={{
4406
+ flexDirection: 'row',
4407
+ alignItems: 'center',
4408
+ gap: 4,
4409
+ paddingHorizontal: 10,
4410
+ paddingVertical: 5,
4411
+ borderRadius: 8,
4412
+ backgroundColor: 'rgba(255,46,87,0.06)',
4413
+ borderWidth: 1,
4414
+ borderColor: 'rgba(255,46,87,0.15)',
4415
+ }} onPress={handleDelete} hitSlop={6}>
4416
+ <NetworkIcons_1.WipeIcon color={AppColors_1.AppColors.errorColor} size={13}/>
4417
+ <react_native_1.Text style={{
4418
+ fontFamily: AppFonts_1.AppFonts.interBold,
4419
+ fontSize: 10.5,
4420
+ color: AppColors_1.AppColors.errorColor,
4421
+ }}>
4422
+ {selectedLogs.size > 0
4423
+ ? `Delete (${selectedLogs.size})`
4424
+ : 'Clear'}
4425
+ </react_native_1.Text>
4274
4426
  </TouchableScale_1.default>
4275
4427
 
4276
4428
  <TouchableScale_1.default style={styles_1.default.toolbarBtn} onPress={() => setSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
@@ -4438,8 +4590,25 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
4438
4590
  <TouchableScale_1.default style={styles_1.default.toolbarBtn} onPress={() => setLogSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
4439
4591
  <NetworkIcons_1.SortArrowIcon color={AppColors_1.AppColors.grayTextStrong} size={18} direction={logSortOrder === 'newest' ? 'down' : 'up'}/>
4440
4592
  </TouchableScale_1.default>
4441
- <TouchableScale_1.default style={styles_1.default.toolbarBtn} onPress={handleDelete} hitSlop={10}>
4442
- <NetworkIcons_1.TrashIcon color={AppColors_1.AppColors.grayTextStrong} size={18}/>
4593
+ <TouchableScale_1.default style={{
4594
+ flexDirection: 'row',
4595
+ alignItems: 'center',
4596
+ gap: 4,
4597
+ paddingHorizontal: 10,
4598
+ paddingVertical: 5,
4599
+ borderRadius: 8,
4600
+ backgroundColor: 'rgba(255,46,87,0.06)',
4601
+ borderWidth: 1,
4602
+ borderColor: 'rgba(255,46,87,0.15)',
4603
+ }} onPress={handleDelete} hitSlop={6}>
4604
+ <NetworkIcons_1.WipeIcon color={AppColors_1.AppColors.errorColor} size={13}/>
4605
+ <react_native_1.Text style={{
4606
+ fontFamily: AppFonts_1.AppFonts.interBold,
4607
+ fontSize: 10.5,
4608
+ color: AppColors_1.AppColors.errorColor,
4609
+ }}>
4610
+ Clear
4611
+ </react_native_1.Text>
4443
4612
  </TouchableScale_1.default>
4444
4613
  </react_native_1.View>
4445
4614
  </react_native_1.View>
@@ -724,6 +724,9 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
724
724
  paddingVertical: number;
725
725
  borderRadius: number;
726
726
  marginRight: number;
727
+ minWidth: number;
728
+ alignItems: string;
729
+ justifyContent: string;
727
730
  shadowColor: string;
728
731
  shadowOffset: {
729
732
  width: number;
@@ -689,10 +689,13 @@ const getRawStyles = (colors) => ({
689
689
  fontSize: 10,
690
690
  },
691
691
  methodBadge: {
692
- paddingHorizontal: 10,
693
- paddingVertical: 5,
694
- borderRadius: 8,
695
- marginRight: 8,
692
+ paddingHorizontal: 6,
693
+ paddingVertical: 3,
694
+ borderRadius: 6,
695
+ marginRight: 6,
696
+ minWidth: 46,
697
+ alignItems: 'center',
698
+ justifyContent: 'center',
696
699
  shadowColor: '#000000',
697
700
  shadowOffset: { width: 0, height: 1 },
698
701
  shadowOpacity: 0.08,
@@ -701,8 +704,8 @@ const getRawStyles = (colors) => ({
701
704
  },
702
705
  methodBadgeText: {
703
706
  fontFamily: AppFonts_1.AppFonts.interBold,
704
- fontSize: 11,
705
- letterSpacing: 0.6,
707
+ fontSize: 9.5,
708
+ letterSpacing: 0.4,
706
709
  },
707
710
  timelineTrack: {
708
711
  height: 3,
@@ -3,8 +3,9 @@ import { Animated, View, Pressable, Text, StyleSheet } from 'react-native';
3
3
  import Svg, { Path } from 'react-native-svg';
4
4
  import { AppColors } from '../styles/AppColors';
5
5
  import { METHOD_COLORS } from '../constants';
6
- import { getStatusColor, getDurationColor, getPath, formatDateTime, } from '../helpers';
6
+ import { getStatusColor, getDurationColor, getPath, getBaseUrl, formatDateTime, } from '../helpers';
7
7
  import { CalendarIcon, ClockIcon } from './NetworkIcons';
8
+ import { AppFonts } from '../styles/AppFonts';
8
9
  import styles from '../styles';
9
10
  import HighlightText from './HighlightText';
10
11
  import TouchableScale from './TouchableScale';
@@ -36,6 +37,8 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
36
37
  }
37
38
  }, [isNew]);
38
39
  const path = getPath(item.url);
40
+ const baseUrl = getBaseUrl(item.url) || item.url;
41
+ const showPathRow = baseUrl !== item.url && path !== '/';
39
42
  const triggeredAt = formatDateTime(item.startTime);
40
43
  const isJson = item.url.split('?')[0].toLowerCase().endsWith('.json');
41
44
  return (<TouchableScale onPress={onPress} style={[
@@ -64,7 +67,7 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
64
67
  </Text>
65
68
  </View>
66
69
 
67
- <HighlightText text={path} search={searchStr} style={styles.urlPathText} highlightStyle={styles.highlight}/>
70
+ <HighlightText text={baseUrl} search={searchStr} style={styles.urlPathText} highlightStyle={styles.highlight} numberOfLines={0}/>
68
71
 
69
72
  {isJson && (<View style={[
70
73
  styles.chip,
@@ -97,6 +100,16 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
97
100
  </View>)}
98
101
  </View>
99
102
 
103
+ {showPathRow && (<View style={{ flexDirection: 'row', alignItems: 'flex-start', marginTop: 3, marginBottom: 5, paddingLeft: 30, gap: 6 }}>
104
+ <Text style={{ color: AppColors.grayTextWeak, fontSize: 9, fontFamily: AppFonts.interBold, letterSpacing: 0.5, marginTop: 2.5 }}>PATH</Text>
105
+ <HighlightText text={path} search={searchStr} style={{
106
+ fontFamily: AppFonts.Sfprotext || 'System',
107
+ fontSize: 12,
108
+ color: AppColors.grayText,
109
+ flex: 1,
110
+ }} highlightStyle={styles.highlight} numberOfLines={0}/>
111
+ </View>)}
112
+
100
113
  <View style={styles.cardBottomRow}>
101
114
  <View style={styles.cardDateRow}>
102
115
  <CalendarIcon color={AppColors.grayTextWeak} size={11}/>
@@ -49,3 +49,4 @@ export declare const InfoCircleIcon: ({ color, size, }: any) => React.JSX.Elemen
49
49
  export declare const WarningTriangleIcon: ({ color, size, }: any) => React.JSX.Element;
50
50
  export declare const ErrorCircleIcon: ({ color, size, }: any) => React.JSX.Element;
51
51
  export declare const TrendingUpIcon: ({ color, size, }: any) => React.JSX.Element;
52
+ export declare const MotionIcon: ({ color, size, }: any) => React.JSX.Element;
@@ -297,3 +297,8 @@ export const TrendingUpIcon = ({ color = AppColors.grayTextWeak, size = 12, }) =
297
297
  <Path d="M17 6h6v6" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
298
298
  </Svg>);
299
299
  };
300
+ export const MotionIcon = ({ color = AppColors.grayTextWeak, size = 14, }) => {
301
+ return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
302
+ <Path d="M5 3l14 9-14 9V3z" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
303
+ </Svg>);
304
+ };
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "1.1.6";
1
+ export declare const LIB_VERSION = "1.1.9";
@@ -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.6';
3
+ export const LIB_VERSION = '1.1.9';
@@ -6,6 +6,7 @@ export declare const getDurationColor: (duration: number | null) => string;
6
6
  export declare const getSize: (data: unknown) => string;
7
7
  export declare const copyToClipboard: (value: unknown, label: string) => void;
8
8
  export declare const getPath: (url: string) => string;
9
+ export declare const getBaseUrl: (url: string) => string;
9
10
  export declare const getCurlCommand: (log: NetworkLog) => string;
10
11
  export declare const getFetchCommand: (log: NetworkLog) => string;
11
12
  export declare const deduplicateLogs: (raw: NetworkLog[]) => NetworkLog[];
@@ -73,6 +73,16 @@ export const getPath = (url) => {
73
73
  return withoutDomain || '/';
74
74
  }
75
75
  };
76
+ export const getBaseUrl = (url) => {
77
+ try {
78
+ const u = new URL(url);
79
+ return `${u.protocol}//${u.host}`;
80
+ }
81
+ catch {
82
+ const match = url.match(/^(https?:\/\/[^/]+)/);
83
+ return match ? match[1] : '';
84
+ }
85
+ };
76
86
  export const getCurlCommand = (log) => {
77
87
  let cmd = `curl -X ${log.method} "${log.url}"`;
78
88
  if (log.requestHeaders) {
@@ -6,6 +6,7 @@ export declare function setCustomStorage(storage: InspectorStorage | null): void
6
6
  export interface PersistedSettings {
7
7
  isDark?: boolean;
8
8
  modalHeightPercent?: number;
9
+ modalAnimationType?: string;
9
10
  tabVisibility?: Record<string, boolean>;
10
11
  defaultTab?: string;
11
12
  maxNetworkLogs?: number;
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useMemo, useRef, useState, useCallback } from 'react';
2
- import { Alert, Animated, StyleSheet, FlatList, LayoutAnimation, Modal, PanResponder, Platform, Pressable, ScrollView, Text, TextInput, View, Linking, Image, InteractionManager, ActivityIndicator, StatusBar, TouchableOpacity, UIManager, LogBox, SafeAreaView, } from 'react-native';
2
+ import { Alert, Animated, StyleSheet, FlatList, LayoutAnimation, Modal, PanResponder, Platform, Pressable, ScrollView, Text, TextInput, View, Linking, Image, InteractionManager, ActivityIndicator, StatusBar, TouchableOpacity, UIManager, LogBox, } from 'react-native';
3
3
  import Svg, { Circle, Path } from 'react-native-svg';
4
4
  import LinearGradient from 'react-native-linear-gradient';
5
5
  import { useNavigationState, NavigationContext } from '@react-navigation/native';
@@ -26,7 +26,7 @@ import { formatDateTime, getStatusColor, getNavigationInfo, deduplicateLogs, get
26
26
  // #5 — settings persistence
27
27
  import { loadSettings, saveSettings, setCustomStorage, isPersistentStorageAvailable, clearPersistedSettings, } from './helpers/settingsStore';
28
28
  // Assets
29
- import { EmptyRadarIcon, FailIcon, SearchIcon, ScreenIcon, ClearIcon, SortArrowIcon, FilterIcon, InsightsIcon, GlobeIcon, DownloadIcon, CloseWhite, TrashIcon, WhiteBackNavigation, TerminalIcon, SignalIcon, AnalyticsIcon, SunIcon, MoonIcon, BrandCircleIcon, BrandSquareIcon, HtmlIcon, CssIcon, JsIcon, ClockIcon, EyeIcon, CheckIcon, SettingsIcon, RequestIcon, ResponseIcon, HeadersIcon, StatusIcon, ChevronIcon, WipeIcon, LayersIcon, UserIcon, InfoCircleIcon, WarningTriangleIcon, ErrorCircleIcon, TrendingUpIcon, } from './components/NetworkIcons';
29
+ import { EmptyRadarIcon, FailIcon, SearchIcon, ScreenIcon, ClearIcon, SortArrowIcon, FilterIcon, InsightsIcon, GlobeIcon, DownloadIcon, CloseWhite, TrashIcon, WhiteBackNavigation, TerminalIcon, SignalIcon, AnalyticsIcon, SunIcon, MoonIcon, BrandCircleIcon, BrandSquareIcon, HtmlIcon, CssIcon, JsIcon, ClockIcon, EyeIcon, CheckIcon, SettingsIcon, RequestIcon, ResponseIcon, HeadersIcon, StatusIcon, ChevronIcon, WipeIcon, LayersIcon, UserIcon, InfoCircleIcon, WarningTriangleIcon, ErrorCircleIcon, TrendingUpIcon, MotionIcon, } from './components/NetworkIcons';
30
30
  import ErrorBoundary from './components/ErrorBoundary';
31
31
  // Stylesheet
32
32
  import { AppColors } from './styles/AppColors';
@@ -113,7 +113,7 @@ const NavigationTracker = ({ onStateChange }) => {
113
113
  const animateNextLayout = () => {
114
114
  LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
115
115
  };
116
- const NetworkInspector = ({ enabled = true, storage, }) => {
116
+ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
117
117
  // Set custom storage synchronously during render phase
118
118
  setCustomStorage(storage || null);
119
119
  const [isDark, setIsDark] = useState(false);
@@ -124,6 +124,8 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
124
124
  const [reduxLastActionMap, setReduxLastActionMap] = useState({});
125
125
  // Inspector panel height as a percentage of the screen (configurable in Settings).
126
126
  const [modalHeightPercent, setModalHeightPercent] = useState(90);
127
+ const [modalAnimationType, setModalAnimationType] = useState('slide');
128
+ const headerTopPadding = Platform.OS === 'ios' && modalHeightPercent >= 95 ? 44 : 0;
127
129
  const [expandedReducers, setExpandedReducers] = useState({});
128
130
  const [logs, setLogs] = useState([]);
129
131
  const [visible, setVisible] = useState(false);
@@ -251,6 +253,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
251
253
  setIsDark(false);
252
254
  toggleGlobalTheme(false);
253
255
  setModalHeightPercent(90);
256
+ setModalAnimationType('slide');
254
257
  setTabVisibility({
255
258
  insights: true,
256
259
  apis: true,
@@ -288,6 +291,8 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
288
291
  }
289
292
  if (saved.modalHeightPercent != null)
290
293
  setModalHeightPercent(saved.modalHeightPercent);
294
+ if (saved.modalAnimationType)
295
+ setModalAnimationType(saved.modalAnimationType);
291
296
  if (saved.tabVisibility)
292
297
  setTabVisibility(prev => ({
293
298
  ...prev,
@@ -342,6 +347,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
342
347
  saveSettings({
343
348
  isDark,
344
349
  modalHeightPercent,
350
+ modalAnimationType,
345
351
  tabVisibility,
346
352
  defaultTab,
347
353
  maxNetworkLogs,
@@ -357,6 +363,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
357
363
  }, [
358
364
  isDark,
359
365
  modalHeightPercent,
366
+ modalAnimationType,
360
367
  tabVisibility,
361
368
  defaultTab,
362
369
  maxNetworkLogs,
@@ -499,6 +506,36 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
499
506
  currentRouteRef.current = getNavigationInfo(navState);
500
507
  }
501
508
  }, [navState]);
509
+ useEffect(() => {
510
+ if (!navigationRef)
511
+ return;
512
+ const updateState = () => {
513
+ try {
514
+ if (typeof navigationRef.isReady === 'function' && navigationRef.isReady()) {
515
+ const state = navigationRef.getRootState();
516
+ if (state) {
517
+ setNavState(state);
518
+ }
519
+ }
520
+ }
521
+ catch (err) {
522
+ // Safe check
523
+ }
524
+ };
525
+ // Initialize state
526
+ updateState();
527
+ // Listen to changes
528
+ const unsubscribe = typeof navigationRef.addListener === 'function'
529
+ ? navigationRef.addListener('state', () => {
530
+ updateState();
531
+ })
532
+ : null;
533
+ return () => {
534
+ if (unsubscribe) {
535
+ unsubscribe();
536
+ }
537
+ };
538
+ }, [navigationRef]);
502
539
  const logRouteMapRef = useRef(new Map());
503
540
  const prevLogIdsRef = useRef(new Set());
504
541
  const [newLogIds, setNewLogIds] = useState(new Set());
@@ -1323,7 +1360,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
1323
1360
  return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
1324
1361
  {/* Settings Header with back button */}
1325
1362
  <LinearGradient colors={[AppColors.purple, '#6B4EFF']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.headerGradient}>
1326
- <SafeAreaView style={{ width: '100%' }}>
1363
+ <View style={{ paddingTop: headerTopPadding, width: '100%' }}>
1327
1364
  <View style={[styles.header, { paddingHorizontal: 16, gap: 12 }]}>
1328
1365
  <TouchableScale onPress={() => {
1329
1366
  animateNextLayout();
@@ -1372,7 +1409,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
1372
1409
  </Text>
1373
1410
  </View>
1374
1411
  </View>
1375
- </SafeAreaView>
1412
+ </View>
1376
1413
  </LinearGradient>
1377
1414
 
1378
1415
  <ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
@@ -1799,6 +1836,90 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
1799
1836
  </View>
1800
1837
  </View>
1801
1838
 
1839
+ {/* Divider */}
1840
+ <View style={{
1841
+ height: 1,
1842
+ backgroundColor: AppColors.dividerColor,
1843
+ }}/>
1844
+
1845
+ {/* Modal Animation */}
1846
+ <View style={{
1847
+ paddingVertical: 12,
1848
+ paddingHorizontal: 14,
1849
+ }}>
1850
+ <View style={{
1851
+ flexDirection: 'row',
1852
+ alignItems: 'center',
1853
+ gap: 8,
1854
+ }}>
1855
+ <View style={{
1856
+ width: 20,
1857
+ height: 20,
1858
+ borderRadius: 6,
1859
+ backgroundColor: AppColors.purpleShade50,
1860
+ borderWidth: 1,
1861
+ borderColor: 'rgba(104,75,155,0.2)',
1862
+ alignItems: 'center',
1863
+ justifyContent: 'center',
1864
+ }}>
1865
+ <MotionIcon color={AppColors.purple} size={11}/>
1866
+ </View>
1867
+ <View style={{ flex: 1 }}>
1868
+ <Text style={{
1869
+ fontFamily: AppFonts.interBold,
1870
+ fontSize: 13,
1871
+ color: AppColors.primaryBlack,
1872
+ }}>
1873
+ Modal Animation
1874
+ </Text>
1875
+ <Text style={{
1876
+ fontFamily: AppFonts.interRegular,
1877
+ fontSize: 11,
1878
+ color: AppColors.grayText,
1879
+ marginTop: 1,
1880
+ }}>
1881
+ How the inspector panel enters and exits the screen
1882
+ </Text>
1883
+ </View>
1884
+ </View>
1885
+
1886
+ {/* Segmented picker */}
1887
+ <View style={{
1888
+ flexDirection: 'row',
1889
+ backgroundColor: AppColors.grayBackground,
1890
+ borderRadius: 8,
1891
+ padding: 2.5,
1892
+ marginTop: 10,
1893
+ borderWidth: 1,
1894
+ borderColor: AppColors.dividerColor,
1895
+ }}>
1896
+ {([
1897
+ { key: 'slide', label: 'Slide Up' },
1898
+ { key: 'fade', label: 'Fade' },
1899
+ { key: 'none', label: 'None' },
1900
+ ]).map(opt => {
1901
+ const isActive = modalAnimationType === opt.key;
1902
+ return (<TouchableScale key={opt.key} onPress={() => setModalAnimationType(opt.key)} style={{
1903
+ flex: 1,
1904
+ paddingVertical: 6,
1905
+ alignItems: 'center',
1906
+ borderRadius: 6,
1907
+ backgroundColor: isActive
1908
+ ? AppColors.purple
1909
+ : 'transparent',
1910
+ }}>
1911
+ <Text style={{
1912
+ fontFamily: AppFonts.interBold,
1913
+ fontSize: 11,
1914
+ color: isActive ? '#FFFFFF' : AppColors.grayText,
1915
+ }}>
1916
+ {opt.label}
1917
+ </Text>
1918
+ </TouchableScale>);
1919
+ })}
1920
+ </View>
1921
+ </View>
1922
+
1802
1923
  {/* Divider */}
1803
1924
  <View style={{
1804
1925
  height: 1,
@@ -2640,7 +2761,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
2640
2761
  }
2641
2762
  return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
2642
2763
  <LinearGradient colors={[AppColors.purple, '#6B4EFF']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.headerGradient}>
2643
- <SafeAreaView style={{ width: '100%' }}>
2764
+ <View style={{ paddingTop: headerTopPadding, width: '100%' }}>
2644
2765
  <View style={[styles.header, { paddingHorizontal: 16, gap: 12 }]}>
2645
2766
  <TouchableScale onPress={goBackToMain} hitSlop={12} style={{
2646
2767
  padding: 8,
@@ -2687,7 +2808,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
2687
2808
  </Text>
2688
2809
  </View>) : null}
2689
2810
  </View>
2690
- </SafeAreaView>
2811
+ </View>
2691
2812
  </LinearGradient>
2692
2813
  {content}
2693
2814
  </View>);
@@ -3524,7 +3645,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
3524
3645
  </TouchableScale>
3525
3646
  </Animated.View>
3526
3647
 
3527
- <Modal visible={visible} animationType="slide" transparent>
3648
+ <Modal visible={visible} animationType={modalAnimationType} transparent>
3528
3649
  {visible && (<ErrorBoundary onClose={closeModal}>
3529
3650
  <View style={styles.modalBackdrop}>
3530
3651
  <Pressable style={styles.modalBackdropPressable} onPress={closeModal}/>
@@ -3535,7 +3656,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
3535
3656
  <StatusBar translucent backgroundColor="transparent" barStyle="light-content"/>
3536
3657
 
3537
3658
  <LinearGradient colors={[AppColors.purple, '#6B4EFF']} style={styles.headerGradient}>
3538
- <SafeAreaView style={{ width: '100%' }}>
3659
+ <View style={{ paddingTop: headerTopPadding, width: '100%' }}>
3539
3660
  <View style={styles.header}>
3540
3661
  <View style={[
3541
3662
  styles.headerLeft,
@@ -3873,7 +3994,7 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
3873
3994
  </TouchableScale>
3874
3995
  </View>
3875
3996
  </View>
3876
- </SafeAreaView>
3997
+ </View>
3877
3998
  </LinearGradient>
3878
3999
 
3879
4000
  {/* ─── Horizontal Scrollable Tab Bar inside Content ─── */}
@@ -3998,8 +4119,25 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
3998
4119
  ? AppColors.purple
3999
4120
  : AppColors.grayTextStrong} size={18}/>
4000
4121
  </TouchableScale>
4001
- <TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
4002
- <TrashIcon color={AppColors.grayTextStrong} size={18}/>
4122
+ <TouchableScale style={{
4123
+ flexDirection: 'row',
4124
+ alignItems: 'center',
4125
+ gap: 4,
4126
+ paddingHorizontal: 10,
4127
+ paddingVertical: 5,
4128
+ borderRadius: 8,
4129
+ backgroundColor: 'rgba(255,46,87,0.06)',
4130
+ borderWidth: 1,
4131
+ borderColor: 'rgba(255,46,87,0.15)',
4132
+ }} onPress={handleDelete} hitSlop={6}>
4133
+ <WipeIcon color={AppColors.errorColor} size={13}/>
4134
+ <Text style={{
4135
+ fontFamily: AppFonts.interBold,
4136
+ fontSize: 10.5,
4137
+ color: AppColors.errorColor,
4138
+ }}>
4139
+ Clear
4140
+ </Text>
4003
4141
  </TouchableScale>
4004
4142
  </View>)}
4005
4143
  </View>
@@ -4225,13 +4363,27 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
4225
4363
  </View>
4226
4364
 
4227
4365
  <View style={styles.toolbarRight}>
4228
- <TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
4229
- <TrashIcon color={AppColors.grayTextStrong} size={18}/>
4230
- {selectedLogs.size > 0 && (<View style={styles.trashBadge}>
4231
- <Text style={styles.trashBadgeText}>
4232
- {selectedLogs.size}
4233
- </Text>
4234
- </View>)}
4366
+ <TouchableScale style={{
4367
+ flexDirection: 'row',
4368
+ alignItems: 'center',
4369
+ gap: 4,
4370
+ paddingHorizontal: 10,
4371
+ paddingVertical: 5,
4372
+ borderRadius: 8,
4373
+ backgroundColor: 'rgba(255,46,87,0.06)',
4374
+ borderWidth: 1,
4375
+ borderColor: 'rgba(255,46,87,0.15)',
4376
+ }} onPress={handleDelete} hitSlop={6}>
4377
+ <WipeIcon color={AppColors.errorColor} size={13}/>
4378
+ <Text style={{
4379
+ fontFamily: AppFonts.interBold,
4380
+ fontSize: 10.5,
4381
+ color: AppColors.errorColor,
4382
+ }}>
4383
+ {selectedLogs.size > 0
4384
+ ? `Delete (${selectedLogs.size})`
4385
+ : 'Clear'}
4386
+ </Text>
4235
4387
  </TouchableScale>
4236
4388
 
4237
4389
  <TouchableScale style={styles.toolbarBtn} onPress={() => setSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
@@ -4399,8 +4551,25 @@ const NetworkInspector = ({ enabled = true, storage, }) => {
4399
4551
  <TouchableScale style={styles.toolbarBtn} onPress={() => setLogSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
4400
4552
  <SortArrowIcon color={AppColors.grayTextStrong} size={18} direction={logSortOrder === 'newest' ? 'down' : 'up'}/>
4401
4553
  </TouchableScale>
4402
- <TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
4403
- <TrashIcon color={AppColors.grayTextStrong} size={18}/>
4554
+ <TouchableScale style={{
4555
+ flexDirection: 'row',
4556
+ alignItems: 'center',
4557
+ gap: 4,
4558
+ paddingHorizontal: 10,
4559
+ paddingVertical: 5,
4560
+ borderRadius: 8,
4561
+ backgroundColor: 'rgba(255,46,87,0.06)',
4562
+ borderWidth: 1,
4563
+ borderColor: 'rgba(255,46,87,0.15)',
4564
+ }} onPress={handleDelete} hitSlop={6}>
4565
+ <WipeIcon color={AppColors.errorColor} size={13}/>
4566
+ <Text style={{
4567
+ fontFamily: AppFonts.interBold,
4568
+ fontSize: 10.5,
4569
+ color: AppColors.errorColor,
4570
+ }}>
4571
+ Clear
4572
+ </Text>
4404
4573
  </TouchableScale>
4405
4574
  </View>
4406
4575
  </View>
@@ -724,6 +724,9 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
724
724
  paddingVertical: number;
725
725
  borderRadius: number;
726
726
  marginRight: number;
727
+ minWidth: number;
728
+ alignItems: string;
729
+ justifyContent: string;
727
730
  shadowColor: string;
728
731
  shadowOffset: {
729
732
  width: number;
@@ -653,10 +653,13 @@ export const getRawStyles = (colors) => ({
653
653
  fontSize: 10,
654
654
  },
655
655
  methodBadge: {
656
- paddingHorizontal: 10,
657
- paddingVertical: 5,
658
- borderRadius: 8,
659
- marginRight: 8,
656
+ paddingHorizontal: 6,
657
+ paddingVertical: 3,
658
+ borderRadius: 6,
659
+ marginRight: 6,
660
+ minWidth: 46,
661
+ alignItems: 'center',
662
+ justifyContent: 'center',
660
663
  shadowColor: '#000000',
661
664
  shadowOffset: { width: 0, height: 1 },
662
665
  shadowOpacity: 0.08,
@@ -665,8 +668,8 @@ export const getRawStyles = (colors) => ({
665
668
  },
666
669
  methodBadgeText: {
667
670
  fontFamily: AppFonts.interBold,
668
- fontSize: 11,
669
- letterSpacing: 0.6,
671
+ fontSize: 9.5,
672
+ letterSpacing: 0.4,
670
673
  },
671
674
  timelineTrack: {
672
675
  height: 3,
package/example/App.tsx CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  useColorScheme,
10
10
  } from 'react-native';
11
11
  import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
12
- import { NavigationContainer } from '@react-navigation/native';
12
+ import { NavigationContainer, createNavigationContainerRef } from '@react-navigation/native';
13
13
  import { createNativeStackNavigator } from '@react-navigation/native-stack';
14
14
  import axios from 'axios';
15
15
  import NetworkInspector, {
@@ -574,6 +574,7 @@ function WebViewScreen({ navigation }: any) {
574
574
  }
575
575
 
576
576
  const Stack = createNativeStackNavigator();
577
+ const navigationRef = createNavigationContainerRef();
577
578
 
578
579
  function App() {
579
580
  const isDarkMode = useColorScheme() === 'dark';
@@ -582,7 +583,7 @@ function App() {
582
583
  <SafeAreaProvider>
583
584
  <StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
584
585
  <ErrorBoundary>
585
- <NavigationContainer>
586
+ <NavigationContainer ref={navigationRef}>
586
587
  <Stack.Navigator
587
588
  screenOptions={{
588
589
  headerShown: false,
@@ -597,7 +598,7 @@ function App() {
597
598
  </Stack.Navigator>
598
599
  </NavigationContainer>
599
600
  {/* Render inspector globally exactly once at the root level */}
600
- <NetworkInspector />
601
+ <NetworkInspector navigationRef={navigationRef} />
601
602
  </ErrorBoundary>
602
603
  </SafeAreaProvider>
603
604
  );
@@ -38,7 +38,7 @@
38
38
  "eslint": "^8.19.0",
39
39
  "jest": "^29.6.3",
40
40
  "prettier": "2.8.8",
41
- "react-native-inapp-inspector": "^1.1.3",
41
+ "react-native-inapp-inspector": "^1.1.8",
42
42
  "react-test-renderer": "19.2.3",
43
43
  "typescript": "^5.8.3"
44
44
  },
@@ -10639,9 +10639,9 @@
10639
10639
  }
10640
10640
  },
10641
10641
  "node_modules/react-native-inapp-inspector": {
10642
- "version": "1.1.3",
10643
- "resolved": "https://registry.npmjs.org/react-native-inapp-inspector/-/react-native-inapp-inspector-1.1.3.tgz",
10644
- "integrity": "sha512-lO7SxdsyClNNtfgvmVuhR1TXsruY0KGXEgjpyBFlSsmfP0HVWHkHFgOfgZGHzelBNqpALcg/OrlfGE/mPLSAWg==",
10642
+ "version": "1.1.8",
10643
+ "resolved": "https://registry.npmjs.org/react-native-inapp-inspector/-/react-native-inapp-inspector-1.1.8.tgz",
10644
+ "integrity": "sha512-YxmzhE/qRz/KgGva4d0hrL5CpZKmNfAtxrUSTxNM+tD2D5ch686RwG9C+KyqqJvpfIBIxkZyBqoTp31NVGVgjA==",
10645
10645
  "dev": true,
10646
10646
  "license": "MIT",
10647
10647
  "dependencies": {
@@ -40,7 +40,7 @@
40
40
  "eslint": "^8.19.0",
41
41
  "jest": "^29.6.3",
42
42
  "prettier": "2.8.8",
43
- "react-native-inapp-inspector": "^1.1.3",
43
+ "react-native-inapp-inspector": "^1.1.8",
44
44
  "react-test-renderer": "19.2.3",
45
45
  "typescript": "^5.8.3"
46
46
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-inapp-inspector",
3
- "version": "1.1.6",
3
+ "version": "1.1.9",
4
4
  "description": "A self-contained network, console, analytics, and webview inspector for React Native applications.",
5
5
  "repository": {
6
6
  "type": "git",