react-native-inapp-inspector 1.1.28 → 1.1.31

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 (77) hide show
  1. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +29 -80
  2. package/dist/commonjs/components/AnalyticsDetail.js +668 -108
  3. package/dist/commonjs/components/AppHeaderLogo.js +15 -15
  4. package/dist/commonjs/components/ConsoleLogCard.js +112 -66
  5. package/dist/commonjs/components/CopyButton.js +15 -1
  6. package/dist/commonjs/components/DomainHeader.js +64 -32
  7. package/dist/commonjs/components/HighlightText.js +7 -1
  8. package/dist/commonjs/components/Inspector/AnalyticsTab.js +332 -221
  9. package/dist/commonjs/components/Inspector/ConsoleTab.js +162 -56
  10. package/dist/commonjs/components/Inspector/InspectorHeader.js +70 -119
  11. package/dist/commonjs/components/Inspector/LogDetail.js +53 -17
  12. package/dist/commonjs/components/Inspector/MainScreen.js +4 -0
  13. package/dist/commonjs/components/Inspector/NetworkDetail.js +24 -24
  14. package/dist/commonjs/components/Inspector/NetworkTab.js +4 -1
  15. package/dist/commonjs/components/Inspector/ReduxDetail.js +237 -42
  16. package/dist/commonjs/components/Inspector/ReduxTab.js +456 -35
  17. package/dist/commonjs/components/JsonViewer.js +26 -4
  18. package/dist/commonjs/components/LogCard.js +103 -11
  19. package/dist/commonjs/components/NetworkIcons.js +19 -10
  20. package/dist/commonjs/components/Toast.d.ts +3 -0
  21. package/dist/commonjs/components/Toast.js +152 -0
  22. package/dist/commonjs/components/TouchableScale.d.ts +1 -0
  23. package/dist/commonjs/components/TouchableScale.js +3 -3
  24. package/dist/commonjs/components/TreeNode.js +12 -2
  25. package/dist/commonjs/constants/index.js +6 -6
  26. package/dist/commonjs/constants/version.d.ts +1 -1
  27. package/dist/commonjs/constants/version.js +1 -1
  28. package/dist/commonjs/customHooks/reduxLogger.js +128 -5
  29. package/dist/commonjs/helpers/index.d.ts +4 -0
  30. package/dist/commonjs/helpers/index.js +107 -92
  31. package/dist/commonjs/helpers/toast.d.ts +4 -0
  32. package/dist/commonjs/helpers/toast.js +20 -0
  33. package/dist/commonjs/i18n/locales/en.json +49 -3
  34. package/dist/commonjs/styles/AppColors.d.ts +6 -0
  35. package/dist/commonjs/styles/AppColors.js +6 -0
  36. package/dist/commonjs/styles/index.d.ts +82 -15
  37. package/dist/commonjs/styles/index.js +92 -26
  38. package/dist/commonjs/types/interfaces.d.ts +7 -0
  39. package/dist/esm/components/AnalyticsDetail.js +670 -110
  40. package/dist/esm/components/AppHeaderLogo.js +15 -15
  41. package/dist/esm/components/ConsoleLogCard.js +114 -68
  42. package/dist/esm/components/CopyButton.js +15 -1
  43. package/dist/esm/components/DomainHeader.js +64 -32
  44. package/dist/esm/components/HighlightText.js +7 -1
  45. package/dist/esm/components/Inspector/AnalyticsTab.js +334 -223
  46. package/dist/esm/components/Inspector/ConsoleTab.js +163 -57
  47. package/dist/esm/components/Inspector/InspectorHeader.js +71 -120
  48. package/dist/esm/components/Inspector/LogDetail.js +55 -19
  49. package/dist/esm/components/Inspector/MainScreen.js +4 -0
  50. package/dist/esm/components/Inspector/NetworkDetail.js +25 -25
  51. package/dist/esm/components/Inspector/NetworkTab.js +4 -1
  52. package/dist/esm/components/Inspector/ReduxDetail.js +239 -44
  53. package/dist/esm/components/Inspector/ReduxTab.js +458 -37
  54. package/dist/esm/components/JsonViewer.js +26 -4
  55. package/dist/esm/components/LogCard.js +105 -13
  56. package/dist/esm/components/NetworkIcons.js +19 -10
  57. package/dist/esm/components/Toast.d.ts +3 -0
  58. package/dist/esm/components/Toast.js +117 -0
  59. package/dist/esm/components/TouchableScale.d.ts +1 -0
  60. package/dist/esm/components/TouchableScale.js +3 -3
  61. package/dist/esm/components/TreeNode.js +12 -2
  62. package/dist/esm/constants/index.js +6 -6
  63. package/dist/esm/constants/version.d.ts +1 -1
  64. package/dist/esm/constants/version.js +1 -1
  65. package/dist/esm/customHooks/reduxLogger.js +128 -5
  66. package/dist/esm/helpers/index.d.ts +4 -0
  67. package/dist/esm/helpers/index.js +102 -92
  68. package/dist/esm/helpers/toast.d.ts +4 -0
  69. package/dist/esm/helpers/toast.js +15 -0
  70. package/dist/esm/i18n/locales/en.json +49 -3
  71. package/dist/esm/styles/AppColors.d.ts +6 -0
  72. package/dist/esm/styles/AppColors.js +6 -0
  73. package/dist/esm/styles/index.d.ts +82 -15
  74. package/dist/esm/styles/index.js +92 -26
  75. package/dist/esm/types/interfaces.d.ts +7 -0
  76. package/ios/NetworkInspectorModule.m +0 -10
  77. package/package.json +13 -10
@@ -14,6 +14,7 @@
14
14
  // and per-reducer "last action" consistently.
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.connectReduxStore = exports.inspectorReduxMiddleware = exports.subscribeReduxState = exports.setReduxState = exports.clearActionHistory = exports.getActionHistory = exports.clearLastActionForReducer = exports.getLastActionForReducer = exports.getReduxAutoRefresh = exports.setReduxAutoRefresh = exports.getReduxState = exports.isReduxConnected = void 0;
17
+ const helpers_1 = require("../helpers");
17
18
  let currentReduxState = null;
18
19
  const listeners = new Set();
19
20
  let globalReduxAutoRefresh = true;
@@ -76,7 +77,106 @@ function actionTypeOf(action) {
76
77
  }
77
78
  return 'UNKNOWN_ACTION';
78
79
  }
79
- function recordAction(action, prevState, nextState) {
80
+ function detectActionOrigin(stack, actionType, actionObj) {
81
+ const stackLower = (stack || '').toLowerCase();
82
+ const typeLower = (actionType || '').toLowerCase();
83
+ // Explicit hint on action if developer tagged it
84
+ if (actionObj && actionObj.__origin) {
85
+ const raw = String(actionObj.__origin).toLowerCase();
86
+ if (raw.includes('saga'))
87
+ return { originType: 'saga' };
88
+ if (raw.includes('thunk'))
89
+ return { originType: 'thunk' };
90
+ if (raw.includes('ui'))
91
+ return { originType: 'ui' };
92
+ }
93
+ // 1. Saga Detection (Stack + Action naming conventions)
94
+ const isSaga = stackLower.includes('redux-saga') ||
95
+ stackLower.includes('runputeffect') ||
96
+ stackLower.includes('proc.js') ||
97
+ stackLower.includes('effects.js') ||
98
+ stackLower.includes('sagamiddleware') ||
99
+ stackLower.includes('saga.ts') ||
100
+ stackLower.includes('saga.js') ||
101
+ stackLower.includes('sagas') ||
102
+ typeLower.includes('saga') ||
103
+ typeLower.startsWith('saga/') ||
104
+ typeLower.startsWith('@saga/');
105
+ // 2. Thunk / RTK Query Detection
106
+ const isThunk = stackLower.includes('createasyncthunk') ||
107
+ stackLower.includes('redux-thunk') ||
108
+ stackLower.includes('rtk-query') ||
109
+ stackLower.includes('thunkmiddleware') ||
110
+ stackLower.includes('actioncreator') ||
111
+ typeLower.includes('thunk') ||
112
+ typeLower.endsWith('/pending') ||
113
+ typeLower.endsWith('/fulfilled') ||
114
+ typeLower.endsWith('/rejected');
115
+ // 3. Parse Stack Frames to find best application caller
116
+ const lines = (stack || '').split('\n').map(l => l.trim()).filter(Boolean);
117
+ let bestFrame = null;
118
+ for (let i = 0; i < lines.length; i++) {
119
+ const line = lines[i];
120
+ // Ignore logger / middleware lines inside inspector
121
+ if (line.includes('reduxLogger') ||
122
+ line.includes('inspectorReduxMiddleware') ||
123
+ line.includes('recordAction') ||
124
+ line.includes('redux.js') ||
125
+ line.includes('redux.cjs')) {
126
+ continue;
127
+ }
128
+ const parsed = (0, helpers_1.parseStackLine)(line, false);
129
+ if (parsed.frameType === 'app' || (!parsed.isRuntimeNoise && parsed.fileName !== 'Unknown')) {
130
+ bestFrame = parsed;
131
+ break;
132
+ }
133
+ }
134
+ if (!bestFrame && lines.length > 0) {
135
+ for (let i = 0; i < lines.length; i++) {
136
+ if (!lines[i].includes('reduxLogger') && !lines[i].includes('inspectorReduxMiddleware')) {
137
+ bestFrame = (0, helpers_1.parseStackLine)(lines[i], false);
138
+ break;
139
+ }
140
+ }
141
+ }
142
+ let originType = 'direct';
143
+ if (isSaga || (bestFrame && bestFrame.fileName?.toLowerCase().includes('saga'))) {
144
+ originType = 'saga';
145
+ }
146
+ else if (isThunk || (bestFrame && bestFrame.fileName?.toLowerCase().includes('thunk'))) {
147
+ originType = 'thunk';
148
+ }
149
+ else if (bestFrame &&
150
+ (bestFrame.fileName?.endsWith('.tsx') ||
151
+ bestFrame.functionName?.includes('onPress') ||
152
+ bestFrame.functionName?.includes('touchable') ||
153
+ bestFrame.functionName?.includes('Effect'))) {
154
+ originType = 'ui';
155
+ }
156
+ else if (stackLower.includes('listener') || stackLower.includes('subscribe')) {
157
+ originType = 'listener';
158
+ }
159
+ return {
160
+ originType,
161
+ caller: bestFrame?.copyableLocation || bestFrame?.raw || undefined,
162
+ callerFile: bestFrame?.fullPath || bestFrame?.fileName || undefined,
163
+ callerLine: bestFrame?.lineNumber ? Number(bestFrame.lineNumber) : undefined,
164
+ callerCol: bestFrame?.columnNumber ? Number(bestFrame.columnNumber) : undefined,
165
+ };
166
+ }
167
+ function extractSliceName(actionType, affectedSlices) {
168
+ if (affectedSlices && affectedSlices.length > 0) {
169
+ return affectedSlices[0];
170
+ }
171
+ // If Redux Toolkit action format: e.g. "users/fetchById" -> "users"
172
+ const match = actionType.match(/^([a-zA-Z0-9_\-]+)\//);
173
+ if (match && match[1] && !match[1].startsWith('@@')) {
174
+ return match[1];
175
+ }
176
+ return undefined;
177
+ }
178
+ function recordAction(action, prevState, nextState, rawStack) {
179
+ const stack = rawStack || new Error().stack || '';
80
180
  const type = actionTypeOf(action);
81
181
  const payload = action && typeof action === 'object' && action.payload !== undefined
82
182
  ? action.payload
@@ -84,13 +184,26 @@ function recordAction(action, prevState, nextState) {
84
184
  const now = Date.now();
85
185
  const timestamp = new Date(now).toLocaleTimeString();
86
186
  const affectedSlices = [];
187
+ const origin = detectActionOrigin(stack, type, action);
188
+ const sliceName = extractSliceName(type, affectedSlices);
87
189
  if (prevState &&
88
190
  nextState &&
89
191
  typeof prevState === 'object' &&
90
192
  typeof nextState === 'object') {
91
193
  Object.keys(nextState).forEach(key => {
92
194
  if (prevState[key] !== nextState[key]) {
93
- lastActionForReducer[key] = { type, payload, timestamp, updatedAt: now };
195
+ lastActionForReducer[key] = {
196
+ type,
197
+ payload,
198
+ timestamp,
199
+ updatedAt: now,
200
+ originType: origin.originType,
201
+ callerFile: origin.callerFile,
202
+ callerLine: origin.callerLine,
203
+ callerCol: origin.callerCol,
204
+ caller: origin.caller,
205
+ stack,
206
+ };
94
207
  affectedSlices.push(key);
95
208
  }
96
209
  });
@@ -104,6 +217,13 @@ function recordAction(action, prevState, nextState) {
104
217
  affectedSlices,
105
218
  prevState,
106
219
  nextState,
220
+ stack,
221
+ caller: origin.caller,
222
+ callerFile: origin.callerFile,
223
+ callerLine: origin.callerLine,
224
+ callerCol: origin.callerCol,
225
+ originType: origin.originType,
226
+ sliceName,
107
227
  });
108
228
  if (actionHistory.length > MAX_HISTORY) {
109
229
  actionHistory.length = MAX_HISTORY;
@@ -129,6 +249,7 @@ function recordAction(action, prevState, nextState) {
129
249
  */
130
250
  const inspectorReduxMiddleware = (storeApi) => (next) => (action) => {
131
251
  middlewareAttached = true;
252
+ const capturedStack = new Error().stack || '';
132
253
  // Thunks are functions — let them run; their inner plain-action dispatches
133
254
  // pass back through this same middleware, so nothing is lost.
134
255
  if (typeof action === 'function') {
@@ -139,7 +260,7 @@ const inspectorReduxMiddleware = (storeApi) => (next) => (action) => {
139
260
  const nextState = storeApi.getState();
140
261
  if (currentReduxState == null)
141
262
  currentReduxState = nextState;
142
- recordAction(action, prevState, nextState);
263
+ recordAction(action, prevState, nextState, capturedStack);
143
264
  return result;
144
265
  };
145
266
  exports.inspectorReduxMiddleware = inspectorReduxMiddleware;
@@ -162,6 +283,7 @@ const connectReduxStore = (store) => {
162
283
  const originalDispatch = store.dispatch.bind(store);
163
284
  let inWrappedDispatch = false;
164
285
  store.dispatch = (action) => {
286
+ const capturedStack = new Error().stack || '';
165
287
  if (middlewareAttached || typeof action === 'function') {
166
288
  // Middleware handles recording, or it's a thunk whose inner dispatches
167
289
  // will be picked up individually.
@@ -182,7 +304,7 @@ const connectReduxStore = (store) => {
182
304
  finally {
183
305
  inWrappedDispatch = false;
184
306
  }
185
- recordAction(action, prevState, store.getState());
307
+ recordAction(action, prevState, store.getState(), capturedStack);
186
308
  return result;
187
309
  };
188
310
  (0, exports.setReduxState)(store.getState());
@@ -206,7 +328,8 @@ const connectReduxStore = (store) => {
206
328
  }
207
329
  // Change arrived outside the wrapped dispatch — record it so the
208
330
  // timeline stays consistent, even without the original action type.
209
- recordAction({ type: '@@inspector/EXTERNAL_STATE_CHANGE' }, prevState, nextState);
331
+ const subStack = new Error().stack || '';
332
+ recordAction({ type: '@@inspector/EXTERNAL_STATE_CHANGE' }, prevState, nextState, subStack);
210
333
  });
211
334
  };
212
335
  exports.connectReduxStore = connectReduxStore;
@@ -1,6 +1,7 @@
1
1
  import { NetworkLog, RouteInfo, DiffResult, JsonContent, StackFrameType } from '../types';
2
2
  export declare const getDomainColor: (domain: string) => string;
3
3
  export declare const formatDateTime: (timestamp: number) => string;
4
+ export declare const formatTimestamp: (timestamp: number) => string;
4
5
  export declare const getStatusColor: (status: number | null) => string;
5
6
  export declare const getDurationColor: (duration: number | null) => string;
6
7
  export declare const getSize: (data: unknown) => string;
@@ -44,6 +45,7 @@ export interface ParsedStackFrame {
44
45
  frameType: StackFrameType | 'app' | 'dependency' | 'runtime' | 'native';
45
46
  isUserCode: boolean;
46
47
  isRuntimeNoise: boolean;
48
+ rawFilePath?: string;
47
49
  lineNumber?: string;
48
50
  columnNumber?: string;
49
51
  isOrigin?: boolean;
@@ -51,6 +53,8 @@ export interface ParsedStackFrame {
51
53
  }
52
54
  /** Parses a stack trace line to extract function name, file name, extension (.tsx/.jsx/.ts), line, and column numbers */
53
55
  export declare const parseStackLine: (rawLine: string, isOrigin?: boolean) => ParsedStackFrame;
56
+ /** Opens a file and line number directly in VS Code / system editor */
57
+ export declare const openInVSCode: (filePath: string, lineNumber?: string | number, columnNumber?: string | number) => void;
54
58
  export declare const ANALYTICS_EVENT_PALETTE: string[];
55
59
  export declare const getEventColor: (name: string) => string;
56
60
  export { getEventCategory, registerGAPlugin, type GAEventCategory, type GAPlugin, } from './gaAnalyticsRegistry';
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getRuntimeDiagnostics = exports.getCategoryColors = exports.registerGAPlugin = exports.getEventCategory = exports.getEventColor = exports.ANALYTICS_EVENT_PALETTE = exports.parseStackLine = exports.getJsonPreviewText = exports.getJsonContent = exports.formatGap = exports.formatTime = exports.formatTimeShort = exports.handleOpenExternalLink = exports.getAppName = exports.getBundleIdentifier = 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;
6
+ exports.getRuntimeDiagnostics = exports.getCategoryColors = exports.registerGAPlugin = exports.getEventCategory = exports.getEventColor = exports.ANALYTICS_EVENT_PALETTE = exports.openInVSCode = exports.parseStackLine = exports.getJsonPreviewText = exports.getJsonContent = exports.formatGap = exports.formatTime = exports.formatTimeShort = exports.handleOpenExternalLink = exports.getAppName = exports.getBundleIdentifier = 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.formatTimestamp = exports.formatDateTime = exports.getDomainColor = void 0;
4
7
  const react_native_1 = require("react-native");
8
+ const clipboard_1 = __importDefault(require("@react-native-clipboard/clipboard"));
9
+ const toast_1 = require("./toast");
5
10
  // Stylesheet
6
11
  const AppColors_1 = require("../styles/AppColors");
7
12
  // Constants
@@ -28,6 +33,20 @@ const formatDateTime = (timestamp) => {
28
33
  return `${day}/${month}/${year} ${hours}:${minutes}:${seconds}`;
29
34
  };
30
35
  exports.formatDateTime = formatDateTime;
36
+ const formatTimestamp = (timestamp) => {
37
+ try {
38
+ const date = new Date(timestamp);
39
+ const hours = date.getHours().toString().padStart(2, '0');
40
+ const minutes = date.getMinutes().toString().padStart(2, '0');
41
+ const seconds = date.getSeconds().toString().padStart(2, '0');
42
+ const ms = date.getMilliseconds().toString().padStart(3, '0');
43
+ return `${hours}:${minutes}:${seconds}.${ms}`;
44
+ }
45
+ catch {
46
+ return '—';
47
+ }
48
+ };
49
+ exports.formatTimestamp = formatTimestamp;
31
50
  const getStatusColor = (status) => {
32
51
  if (!status || status === 0)
33
52
  return AppColors_1.AppColors.errorColor;
@@ -60,104 +79,43 @@ const getSize = (data) => {
60
79
  }
61
80
  };
62
81
  exports.getSize = getSize;
63
- // Safely detect and load clipboard natively without static require() calls that Metro fails on
64
- const getClipboardModule = () => {
65
- // 1. Check our built-in native module (NetworkInspectorModule)
66
- try {
67
- const tmReg = react_native_1.TurboModuleRegistry || globalThis.__turboModuleProxy;
68
- const inspectorMod = react_native_1.NativeModules?.NetworkInspectorModule ||
69
- (tmReg?.get ? tmReg.get('NetworkInspectorModule') : null);
70
- if (inspectorMod && typeof inspectorMod.copyToClipboard === 'function') {
71
- return {
72
- setString: (str) => inspectorMod.copyToClipboard(str),
73
- setStringAsync: (str) => inspectorMod.copyToClipboard(str),
74
- };
75
- }
82
+ const copyToClipboard = (value, label) => {
83
+ const resolved = typeof value === 'function' ? value() : value;
84
+ let textToCopy = '';
85
+ if (typeof resolved === 'string') {
86
+ textToCopy = resolved;
76
87
  }
77
- catch { }
78
- // 2. Check TurboModuleRegistry (TurboModules / New Architecture)
79
- try {
80
- const tmReg = react_native_1.TurboModuleRegistry || globalThis.__turboModuleProxy;
81
- if (tmReg?.get) {
82
- const tm = tmReg.get('RNCClipboard') ||
83
- tmReg.get('NativeClipboard') ||
84
- tmReg.get('ExpoClipboard');
85
- if (tm &&
86
- (typeof tm.setString === 'function' ||
87
- typeof tm.setStringAsync === 'function')) {
88
- return tm;
89
- }
88
+ else {
89
+ try {
90
+ textToCopy = JSON.stringify(resolved, null, 2);
91
+ }
92
+ catch {
93
+ textToCopy = String(resolved);
90
94
  }
91
95
  }
92
- catch { }
93
- // 3. Check legacy NativeModules table (Bridge / Old Architecture)
96
+ // Use @react-native-clipboard/clipboard npm package
94
97
  try {
95
- if (react_native_1.NativeModules?.RNCClipboard &&
96
- typeof react_native_1.NativeModules.RNCClipboard.setString === 'function') {
97
- return react_native_1.NativeModules.RNCClipboard;
98
- }
99
- if (react_native_1.NativeModules?.ExpoClipboard &&
100
- typeof react_native_1.NativeModules.ExpoClipboard.setStringAsync === 'function') {
101
- return react_native_1.NativeModules.ExpoClipboard;
98
+ if (typeof clipboard_1.default?.setString === 'function') {
99
+ clipboard_1.default.setString(textToCopy);
102
100
  }
103
- if (react_native_1.NativeModules?.Clipboard &&
104
- typeof react_native_1.NativeModules.Clipboard.setString === 'function') {
105
- return react_native_1.NativeModules.Clipboard;
101
+ else if (typeof clipboard_1.default?.default?.setString === 'function') {
102
+ clipboard_1.default.default.setString(textToCopy);
106
103
  }
107
104
  }
108
- catch { }
109
- // 4. Check legacy React Native core Clipboard
110
- try {
111
- if (react_native_1.Clipboard && typeof react_native_1.Clipboard.setString === 'function') {
112
- return react_native_1.Clipboard;
105
+ catch (err) {
106
+ if (__DEV__) {
107
+ console.warn('[NetworkInspector] Clipboard.setString failed:', err);
113
108
  }
114
109
  }
115
- catch { }
116
- return null;
117
- };
118
- const copyToClipboard = (value, label) => {
119
- const resolved = typeof value === 'function' ? value() : value;
120
- const text = typeof resolved === 'string' ? resolved : JSON.stringify(resolved, null, 2);
121
- const textToCopy = text ?? '';
122
- let copied = false;
110
+ // Trigger floating in-app bottom toast notification
123
111
  try {
124
- const cb = getClipboardModule();
125
- if (cb) {
126
- if (typeof cb.setStringAsync === 'function') {
127
- cb.setStringAsync(textToCopy);
128
- copied = true;
129
- }
130
- else if (typeof cb.setString === 'function') {
131
- cb.setString(textToCopy);
132
- copied = true;
133
- }
134
- }
112
+ (0, toast_1.showToast)(label ? `${label} copied to clipboard` : 'Copied to clipboard');
135
113
  }
136
114
  catch { }
137
- // Built-in native module direct invocation
138
- if (!copied) {
139
- try {
140
- const inspectorMod = react_native_1.NativeModules?.NetworkInspectorModule;
141
- if (inspectorMod && typeof inspectorMod.copyToClipboard === 'function') {
142
- inspectorMod.copyToClipboard(textToCopy);
143
- copied = true;
144
- }
145
- }
146
- catch { }
147
- }
148
- if (!copied) {
149
- try {
150
- if (typeof navigator !== 'undefined' && navigator?.clipboard?.writeText) {
151
- navigator.clipboard.writeText(textToCopy);
152
- copied = true;
153
- }
154
- }
155
- catch { }
156
- }
157
- // Visual toast feedback on Android if ToastAndroid exists
115
+ // Native Android Toast fallback
158
116
  try {
159
117
  if (react_native_1.Platform.OS === 'android' && react_native_1.ToastAndroid?.show) {
160
- react_native_1.ToastAndroid.show(`${label} copied`, react_native_1.ToastAndroid.SHORT);
118
+ react_native_1.ToastAndroid.show(label ? `${label} copied to clipboard` : 'Copied to clipboard', react_native_1.ToastAndroid.SHORT);
161
119
  }
162
120
  }
163
121
  catch { }
@@ -566,11 +524,16 @@ const parseStackLine = (rawLine, isOrigin = false) => {
566
524
  (functionName === 'next' && isNative);
567
525
  const isDependency = cleanPath.includes('node_modules') ||
568
526
  cleanPath.includes('react-native/Libraries') ||
569
- (cleanPath.includes('react-native-inapp-inspector') && !cleanPath.includes('/example/'));
527
+ (cleanPath.includes('react-native-inapp-inspector') &&
528
+ !cleanPath.includes('/example/'));
570
529
  const isUserCode = !isNative &&
571
530
  !isInternalBytecode &&
572
531
  !isDependency &&
573
- (fileExt === 'tsx' || fileExt === 'jsx' || fileExt === 'ts' || fileExt === 'js' || !fileName.includes('.bundle'));
532
+ (fileExt === 'tsx' ||
533
+ fileExt === 'jsx' ||
534
+ fileExt === 'ts' ||
535
+ fileExt === 'js' ||
536
+ !fileName.includes('.bundle'));
574
537
  const frameType = isUserCode
575
538
  ? 'app'
576
539
  : isDependency
@@ -578,7 +541,10 @@ const parseStackLine = (rawLine, isOrigin = false) => {
578
541
  : isNative
579
542
  ? 'native'
580
543
  : 'runtime';
581
- const isRuntimeNoise = isInternalBytecode || isNative || functionName === 'asyncGeneratorStep' || functionName === '_next';
544
+ const isRuntimeNoise = isInternalBytecode ||
545
+ isNative ||
546
+ functionName === 'asyncGeneratorStep' ||
547
+ functionName === '_next';
582
548
  // Format clean relative project path
583
549
  let relativePath = cleanPath;
584
550
  if (relativePath.includes('/example/')) {
@@ -598,6 +564,7 @@ const parseStackLine = (rawLine, isOrigin = false) => {
598
564
  functionName,
599
565
  fileName,
600
566
  fullPath: relativePath,
567
+ rawFilePath: fullPath,
601
568
  fileExt,
602
569
  frameType,
603
570
  isUserCode,
@@ -609,6 +576,47 @@ const parseStackLine = (rawLine, isOrigin = false) => {
609
576
  };
610
577
  };
611
578
  exports.parseStackLine = parseStackLine;
579
+ /** Opens a file and line number directly in VS Code / system editor */
580
+ const openInVSCode = (filePath, lineNumber, columnNumber) => {
581
+ const numLine = lineNumber ? Number(lineNumber) : 1;
582
+ const numCol = columnNumber ? Number(columnNumber) : 1;
583
+ const line = lineNumber ? `:${lineNumber}` : '';
584
+ const col = columnNumber ? `:${columnNumber}` : '';
585
+ const cleanPath = filePath.replace(/^file:\/\//, '');
586
+ // 1. Notify Metro dev server on host to launch editor directly on local system
587
+ const metroHosts = [
588
+ 'http://localhost:8081',
589
+ 'http://127.0.0.1:8081',
590
+ 'http://10.0.2.2:8081',
591
+ ];
592
+ metroHosts.forEach(host => {
593
+ try {
594
+ fetch(`${host}/open-stack-frame`, {
595
+ method: 'POST',
596
+ headers: { 'Content-Type': 'application/json' },
597
+ body: JSON.stringify({
598
+ file: cleanPath,
599
+ lineNumber: numLine,
600
+ column: numCol,
601
+ }),
602
+ }).catch(() => { });
603
+ }
604
+ catch { }
605
+ });
606
+ // 2. Also invoke native URL scheme handlers
607
+ const vscodeUrl = `vscode://file/${cleanPath.replace(/^\/+/, '')}${line}${col}`;
608
+ const cursorUrl = `cursor://file/${cleanPath.replace(/^\/+/, '')}${line}${col}`;
609
+ const vscodeInsidersUrl = `vscode-insiders://file/${cleanPath.replace(/^\/+/, '')}${line}${col}`;
610
+ react_native_1.Linking.openURL(vscodeUrl).catch(() => {
611
+ react_native_1.Linking.openURL(cursorUrl).catch(() => {
612
+ react_native_1.Linking.openURL(vscodeInsidersUrl).catch(() => {
613
+ // Fallback: Copy to clipboard so user can jump immediately
614
+ (0, exports.copyToClipboard)(`${cleanPath}${line}${col}`, 'Location');
615
+ });
616
+ });
617
+ });
618
+ };
619
+ exports.openInVSCode = openInVSCode;
612
620
  // ─── Analytics Helpers ────────────────────────────────────────────────────────
613
621
  exports.ANALYTICS_EVENT_PALETTE = [
614
622
  AppColors_1.AppColors.googleBlue,
@@ -667,7 +675,11 @@ exports.getCategoryColors = getCategoryColors;
667
675
  const getRuntimeDiagnostics = () => {
668
676
  const isHermes = typeof global.HermesInternal !== 'undefined';
669
677
  const isV8 = typeof global._v8runtime !== 'undefined';
670
- const engineType = isHermes ? 'hermes' : isV8 ? 'v8' : 'jsc';
678
+ const engineType = isHermes
679
+ ? 'hermes'
680
+ : isV8
681
+ ? 'v8'
682
+ : 'jsc';
671
683
  const isFabric = typeof global.nativeFabricUIManager !== 'undefined' ||
672
684
  Boolean(global.__turboModuleProxy);
673
685
  const archType = isFabric ? 'fabric' : 'paper';
@@ -677,11 +689,14 @@ const getRuntimeDiagnostics = () => {
677
689
  const hermesStats = global.HermesInternal?.getInstrumentedStats?.();
678
690
  if (hermesStats?.js_heap_size) {
679
691
  usedHeapMb = Number((hermesStats.js_heap_size / (1024 * 1024)).toFixed(1));
680
- totalAllocMb = Number(((hermesStats.js_allocated_bytes || hermesStats.js_heap_size * 1.6) / (1024 * 1024)).toFixed(1));
692
+ totalAllocMb = Number(((hermesStats.js_allocated_bytes || hermesStats.js_heap_size * 1.6) /
693
+ (1024 * 1024)).toFixed(1));
681
694
  }
682
695
  else if (global.performance?.memory?.usedJSHeapSize) {
683
- usedHeapMb = Number((global.performance.memory.usedJSHeapSize / (1024 * 1024)).toFixed(1));
684
- totalAllocMb = Number((global.performance.memory.totalJSHeapSize / (1024 * 1024)).toFixed(1));
696
+ usedHeapMb = Number((global.performance.memory.usedJSHeapSize /
697
+ (1024 * 1024)).toFixed(1));
698
+ totalAllocMb = Number((global.performance.memory.totalJSHeapSize /
699
+ (1024 * 1024)).toFixed(1));
685
700
  }
686
701
  }
687
702
  catch { }
@@ -0,0 +1,4 @@
1
+ type ToastListener = (message: string) => void;
2
+ export declare const showToast: (message: string) => void;
3
+ export declare const subscribeToast: (listener: ToastListener) => () => void;
4
+ export {};
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.subscribeToast = exports.showToast = void 0;
4
+ const listeners = new Set();
5
+ const showToast = (message) => {
6
+ listeners.forEach(fn => {
7
+ try {
8
+ fn(message);
9
+ }
10
+ catch { }
11
+ });
12
+ };
13
+ exports.showToast = showToast;
14
+ const subscribeToast = (listener) => {
15
+ listeners.add(listener);
16
+ return () => {
17
+ listeners.delete(listener);
18
+ };
19
+ };
20
+ exports.subscribeToast = subscribeToast;
@@ -15,7 +15,10 @@
15
15
  "expandAll": "Expand All",
16
16
  "viewOnNpm": "View on NPM",
17
17
  "later": "Later",
18
- "source": "SOURCE"
18
+ "source": "SOURCE",
19
+ "open": "Open",
20
+ "openInBrowser": "Open in Browser",
21
+ "openInBrowserPrompt": "Are you sure you want to open this URL in your external browser?"
19
22
  },
20
23
  "header": {
21
24
  "updateAvailableTitle": "Update Available",
@@ -279,7 +282,19 @@
279
282
  "timeNow": "NOW",
280
283
  "selectedBucket": "Bucket: {{time}} ({{count}} events)",
281
284
  "totalRevenue": "Revenue",
282
- "activeDistribution": "Category Split"
285
+ "activeDistribution": "Category Split",
286
+ "allCategory": "All",
287
+ "screensCategory": "Screens",
288
+ "overviewTab": "Overview",
289
+ "jsonTreeTab": "JSON Tree",
290
+ "rawPayloadTab": "Raw Payload",
291
+ "sessionContext": "Session Context",
292
+ "userPropertiesSnapshot": "User Properties Snapshot",
293
+ "parameterKeys": "Parameters ({{count}})",
294
+ "searchParameters": "Search parameters...",
295
+ "noParamsFound": "No parameters match your search",
296
+ "sourceFirebase": "FIREBASE GA4",
297
+ "sourceCustom": "CUSTOM / MANUAL"
283
298
  },
284
299
  "redux": {
285
300
  "title": "Redux",
@@ -305,7 +320,38 @@
305
320
  "connectionStatus": "Connected",
306
321
  "connectionStatusNone": "Not connected",
307
322
  "autoRefresh": "Auto-refresh",
308
- "paused": "Paused"
323
+ "paused": "Paused",
324
+ "liveState": "Live State",
325
+ "timeline": "Timeline",
326
+ "persisted": "Persisted",
327
+ "storage": "Storage",
328
+ "metadata": "Metadata",
329
+ "inMemory": "In-Memory",
330
+ "slice": "SLICE",
331
+ "rootKeys": "Root Keys",
332
+ "size": "Size",
333
+ "actions": "actions",
334
+ "keys": "Keys",
335
+ "last": "Last",
336
+ "live": "Live",
337
+ "loading": "Loading",
338
+ "error": "Error",
339
+ "empty": "Empty",
340
+ "payload": "Payload",
341
+ "actionPayload": "Action Payload:",
342
+ "stateChangesDiff": "State Changes (Diff):",
343
+ "tapToInspectAction": "Tap to inspect action payload & diff changes",
344
+ "noDispatchedActions": "No dispatched actions recorded for this slice yet.",
345
+ "sliceJson": "Slice JSON",
346
+ "originSaga": "SAGA",
347
+ "originThunk": "THUNK",
348
+ "originUi": "UI",
349
+ "originDirect": "DIRECT",
350
+ "originListener": "LISTENER",
351
+ "triggeredFrom": "Triggered From:",
352
+ "openInEditor": "Open in Editor",
353
+ "callStack": "Call Stack Trace",
354
+ "sliceOrigin": "Slice / Origin"
309
355
  },
310
356
  "performance": {
311
357
  "title": "Performance",
@@ -124,6 +124,9 @@ declare const LightColors: {
124
124
  emerald700: string;
125
125
  pink100: string;
126
126
  sky100: string;
127
+ teal100: string;
128
+ teal700: string;
129
+ purple700: string;
127
130
  indigo50: string;
128
131
  gray100: string;
129
132
  gray200: string;
@@ -279,6 +282,9 @@ export declare const getThemeColors: (isDark: boolean) => {
279
282
  emerald700: string;
280
283
  pink100: string;
281
284
  sky100: string;
285
+ teal100: string;
286
+ teal700: string;
287
+ purple700: string;
282
288
  indigo50: string;
283
289
  gray100: string;
284
290
  gray200: string;
@@ -133,6 +133,9 @@ const LightColors = {
133
133
  emerald700: '#047857',
134
134
  pink100: '#FCE7F3',
135
135
  sky100: '#E0F2FE',
136
+ teal100: '#CCFBF1',
137
+ teal700: '#0F766E',
138
+ purple700: '#6D28D9',
136
139
  indigo50: '#EEF2FF',
137
140
  gray100: '#F3F4F6',
138
141
  gray200: '#E5E7EB',
@@ -303,6 +306,9 @@ const DarkColors = {
303
306
  emerald700: '#34D399',
304
307
  pink100: '#341626',
305
308
  sky100: '#0C2744',
309
+ teal100: '#134E4A',
310
+ teal700: '#2DD4BF',
311
+ purple700: '#8B5CF6',
306
312
  indigo50: '#1E1B3A',
307
313
  gray100: '#2A2438',
308
314
  gray200: '#362C4E',