react-native-inapp-inspector 1.1.28 → 1.1.29
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/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +29 -80
- package/dist/commonjs/components/AppHeaderLogo.js +12 -15
- package/dist/commonjs/components/ConsoleLogCard.js +31 -7
- package/dist/commonjs/components/CopyButton.js +15 -1
- package/dist/commonjs/components/DomainHeader.js +56 -28
- package/dist/commonjs/components/HighlightText.js +7 -1
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +35 -21
- package/dist/commonjs/components/Inspector/InspectorHeader.js +17 -3
- package/dist/commonjs/components/Inspector/LogDetail.js +8 -5
- package/dist/commonjs/components/Inspector/MainScreen.js +4 -0
- package/dist/commonjs/components/Inspector/NetworkDetail.js +24 -24
- package/dist/commonjs/components/JsonViewer.js +25 -3
- package/dist/commonjs/components/LogCard.js +103 -11
- package/dist/commonjs/components/Toast.d.ts +3 -0
- package/dist/commonjs/components/Toast.js +152 -0
- package/dist/commonjs/components/TouchableScale.d.ts +1 -0
- package/dist/commonjs/components/TouchableScale.js +3 -3
- package/dist/commonjs/components/TreeNode.js +1 -1
- package/dist/commonjs/constants/index.js +6 -6
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/helpers/index.d.ts +4 -0
- package/dist/commonjs/helpers/index.js +91 -92
- package/dist/commonjs/helpers/toast.d.ts +4 -0
- package/dist/commonjs/helpers/toast.js +20 -0
- package/dist/commonjs/i18n/locales/en.json +4 -1
- package/dist/commonjs/styles/index.d.ts +60 -14
- package/dist/commonjs/styles/index.js +71 -26
- package/dist/esm/components/AppHeaderLogo.js +12 -15
- package/dist/esm/components/ConsoleLogCard.js +33 -9
- package/dist/esm/components/CopyButton.js +15 -1
- package/dist/esm/components/DomainHeader.js +56 -28
- package/dist/esm/components/HighlightText.js +7 -1
- package/dist/esm/components/Inspector/AnalyticsTab.js +36 -22
- package/dist/esm/components/Inspector/InspectorHeader.js +18 -4
- package/dist/esm/components/Inspector/LogDetail.js +10 -7
- package/dist/esm/components/Inspector/MainScreen.js +4 -0
- package/dist/esm/components/Inspector/NetworkDetail.js +25 -25
- package/dist/esm/components/JsonViewer.js +25 -3
- package/dist/esm/components/LogCard.js +105 -13
- package/dist/esm/components/Toast.d.ts +3 -0
- package/dist/esm/components/Toast.js +117 -0
- package/dist/esm/components/TouchableScale.d.ts +1 -0
- package/dist/esm/components/TouchableScale.js +3 -3
- package/dist/esm/components/TreeNode.js +1 -1
- package/dist/esm/constants/index.js +6 -6
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/helpers/index.d.ts +4 -0
- package/dist/esm/helpers/index.js +86 -92
- package/dist/esm/helpers/toast.d.ts +4 -0
- package/dist/esm/helpers/toast.js +15 -0
- package/dist/esm/i18n/locales/en.json +4 -1
- package/dist/esm/styles/index.d.ts +60 -14
- package/dist/esm/styles/index.js +71 -26
- package/ios/NetworkInspectorModule.m +0 -10
- package/package.json +4 -1
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
package com.inappinspector;
|
|
2
2
|
|
|
3
|
-
import android.content.ClipData;
|
|
4
|
-
import android.content.ClipboardManager;
|
|
5
|
-
import android.content.Context;
|
|
6
3
|
import android.os.Handler;
|
|
7
4
|
import android.os.Looper;
|
|
8
5
|
import android.util.Log;
|
|
@@ -31,7 +28,6 @@ public class NetworkInspectorModule extends ReactContextBaseJavaModule {
|
|
|
31
28
|
public NetworkInspectorModule(ReactApplicationContext reactContext) {
|
|
32
29
|
super(reactContext);
|
|
33
30
|
this.reactContext = reactContext;
|
|
34
|
-
setupNativeCrashProtection();
|
|
35
31
|
}
|
|
36
32
|
|
|
37
33
|
@NonNull
|
|
@@ -41,55 +37,39 @@ public class NetworkInspectorModule extends ReactContextBaseJavaModule {
|
|
|
41
37
|
}
|
|
42
38
|
|
|
43
39
|
private void setupNativeCrashProtection() {
|
|
44
|
-
if (isProtectionEnabled)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
|
49
|
-
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
|
50
|
-
@Override
|
|
51
|
-
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
|
|
52
|
-
try {
|
|
53
|
-
StringWriter sw = new StringWriter();
|
|
54
|
-
PrintWriter pw = new PrintWriter(sw);
|
|
55
|
-
throwable.printStackTrace(pw);
|
|
56
|
-
String stackTrace = sw.toString();
|
|
57
|
-
String message = throwable.getMessage() != null ? throwable.getMessage() : throwable.toString();
|
|
58
|
-
|
|
59
|
-
Log.e(TAG, "Intercepted Android Native Crash: " + message, throwable);
|
|
60
|
-
|
|
61
|
-
sendCrashEventToJS(message, stackTrace, thread.getName());
|
|
62
|
-
} catch (Exception e) {
|
|
63
|
-
Log.e(TAG, "Failed to dispatch native crash event", e);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Keep main looper processing to prevent instant process exit
|
|
67
|
-
if (Looper.myLooper() != null) {
|
|
68
|
-
while (true) {
|
|
69
|
-
try {
|
|
70
|
-
Looper.loop();
|
|
71
|
-
} catch (Throwable inner) {
|
|
72
|
-
Log.e(TAG, "Caught inner loop exception", inner);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
} catch (Exception e) {
|
|
79
|
-
Log.e(TAG, "Error installing uncaught exception handler", e);
|
|
40
|
+
if (isProtectionEnabled) {
|
|
41
|
+
return;
|
|
80
42
|
}
|
|
43
|
+
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
|
44
|
+
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
|
45
|
+
try {
|
|
46
|
+
handleNativeCrash(thread, throwable);
|
|
47
|
+
} catch (Exception e) {
|
|
48
|
+
Log.e(TAG, "Error in native crash handler", e);
|
|
49
|
+
} finally {
|
|
50
|
+
if (defaultHandler != null) {
|
|
51
|
+
defaultHandler.uncaughtException(thread, throwable);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
isProtectionEnabled = true;
|
|
56
|
+
Log.i(TAG, "Native crash protection enabled");
|
|
81
57
|
}
|
|
82
58
|
|
|
83
|
-
private void
|
|
59
|
+
private void handleNativeCrash(Thread thread, Throwable throwable) {
|
|
84
60
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
61
|
+
StringWriter sw = new StringWriter();
|
|
62
|
+
PrintWriter pw = new PrintWriter(sw);
|
|
63
|
+
throwable.printStackTrace(pw);
|
|
64
|
+
String stackTrace = sw.toString();
|
|
65
|
+
|
|
66
|
+
WritableMap params = Arguments.createMap();
|
|
67
|
+
params.putString("error", throwable.getMessage() != null ? throwable.getMessage() : "Unknown native crash");
|
|
68
|
+
params.putString("name", throwable.getClass().getName());
|
|
69
|
+
params.putString("stack", stackTrace);
|
|
70
|
+
params.putDouble("timestamp", System.currentTimeMillis());
|
|
71
|
+
|
|
72
|
+
if (reactContext.hasActiveReactInstance()) {
|
|
93
73
|
reactContext
|
|
94
74
|
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
95
75
|
.emit("onNativeCrash", params);
|
|
@@ -105,37 +85,6 @@ public class NetworkInspectorModule extends ReactContextBaseJavaModule {
|
|
|
105
85
|
promise.resolve(true);
|
|
106
86
|
}
|
|
107
87
|
|
|
108
|
-
@ReactMethod
|
|
109
|
-
public void copyToClipboard(String text, Promise promise) {
|
|
110
|
-
try {
|
|
111
|
-
Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
112
|
-
mainHandler.post(() -> {
|
|
113
|
-
try {
|
|
114
|
-
ClipboardManager clipboard = (ClipboardManager) reactContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
115
|
-
if (clipboard != null) {
|
|
116
|
-
ClipData clip = ClipData.newPlainText("NetworkInspector", text != null ? text : "");
|
|
117
|
-
clipboard.setPrimaryClip(clip);
|
|
118
|
-
if (promise != null) {
|
|
119
|
-
promise.resolve(true);
|
|
120
|
-
}
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
if (promise != null) {
|
|
124
|
-
promise.resolve(false);
|
|
125
|
-
}
|
|
126
|
-
} catch (Exception e) {
|
|
127
|
-
if (promise != null) {
|
|
128
|
-
promise.reject("CLIPBOARD_ERROR", e.getMessage(), e);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
} catch (Exception e) {
|
|
133
|
-
if (promise != null) {
|
|
134
|
-
promise.reject("CLIPBOARD_ERROR", e.getMessage(), e);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
88
|
@ReactMethod
|
|
140
89
|
public void addListener(String eventName) {
|
|
141
90
|
// Required for RN built-in Event Emitter
|
|
@@ -40,13 +40,12 @@ exports.AppHeaderLogo = void 0;
|
|
|
40
40
|
const react_1 = __importStar(require("react"));
|
|
41
41
|
const react_native_1 = require("react-native");
|
|
42
42
|
const BrandSquareIcon_1 = __importDefault(require("./BrandSquareIcon"));
|
|
43
|
-
const AppColors_1 = require("../styles/AppColors");
|
|
44
43
|
const AppHeaderLogo = ({ size = 46, customIcon, }) => {
|
|
45
44
|
const [loadError, setLoadError] = (0, react_1.useState)(false);
|
|
46
|
-
// 1. If customIcon is explicitly provided, render it directly
|
|
45
|
+
// 1. If customIcon is explicitly provided, render it directly edge-to-edge
|
|
47
46
|
if (customIcon) {
|
|
48
47
|
return (<react_native_1.View style={[logoStyles.container, { width: size, height: size }]}>
|
|
49
|
-
<react_native_1.Image source={customIcon} style={
|
|
48
|
+
<react_native_1.Image source={customIcon} style={logoStyles.image} resizeMode="cover"/>
|
|
50
49
|
</react_native_1.View>);
|
|
51
50
|
}
|
|
52
51
|
// 2. Dynamically query installed native app icon (AppIcon on iOS, ic_launcher on Android)
|
|
@@ -57,29 +56,27 @@ const AppHeaderLogo = ({ size = 46, customIcon, }) => {
|
|
|
57
56
|
default: { uri: 'AppIcon' },
|
|
58
57
|
});
|
|
59
58
|
return (<react_native_1.View style={[logoStyles.container, { width: size, height: size }]}>
|
|
60
|
-
<react_native_1.Image source={nativeSource} style={
|
|
59
|
+
<react_native_1.Image source={nativeSource} style={logoStyles.image} resizeMode="cover" onError={() => setLoadError(true)}/>
|
|
61
60
|
</react_native_1.View>);
|
|
62
61
|
}
|
|
63
62
|
// 3. Fallback: Inspector's Signature Owl Brand Logo
|
|
64
63
|
return (<react_native_1.View style={[logoStyles.container, { width: size, height: size }]}>
|
|
65
|
-
<BrandSquareIcon_1.default size={size
|
|
64
|
+
<BrandSquareIcon_1.default size={size}/>
|
|
66
65
|
</react_native_1.View>);
|
|
67
66
|
};
|
|
68
67
|
exports.AppHeaderLogo = AppHeaderLogo;
|
|
69
68
|
const logoStyles = react_native_1.StyleSheet.create({
|
|
70
69
|
container: {
|
|
70
|
+
borderRadius: 10,
|
|
71
|
+
overflow: 'hidden',
|
|
72
|
+
backgroundColor: 'transparent',
|
|
71
73
|
alignItems: 'center',
|
|
72
74
|
justifyContent: 'center',
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
shadowColor: AppColors_1.AppColors.primaryBlack,
|
|
79
|
-
shadowOffset: { width: 0, height: 2 },
|
|
80
|
-
shadowOpacity: 0.15,
|
|
81
|
-
shadowRadius: 4,
|
|
82
|
-
elevation: 3,
|
|
75
|
+
},
|
|
76
|
+
image: {
|
|
77
|
+
width: '100%',
|
|
78
|
+
height: '100%',
|
|
79
|
+
borderRadius: 10,
|
|
83
80
|
},
|
|
84
81
|
});
|
|
85
82
|
exports.default = exports.AppHeaderLogo;
|
|
@@ -232,18 +232,34 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
232
232
|
<react_native_1.Text style={styles.footerText}>{(0, helpers_1.formatTime)(item.timestamp)}</react_native_1.Text>
|
|
233
233
|
{parsedCaller && (<>
|
|
234
234
|
<react_native_1.View style={styles.footerDot}/>
|
|
235
|
-
<react_native_1.
|
|
235
|
+
<react_native_1.Pressable onPress={e => {
|
|
236
|
+
e.stopPropagation?.();
|
|
237
|
+
(0, helpers_1.openInVSCode)(parsedCaller.rawFilePath ||
|
|
238
|
+
parsedCaller.fullPath ||
|
|
239
|
+
parsedCaller.fileName, parsedCaller.lineNumber, parsedCaller.columnNumber);
|
|
240
|
+
}} hitSlop={8} style={{
|
|
241
|
+
flexDirection: 'row',
|
|
242
|
+
alignItems: 'center',
|
|
243
|
+
gap: 4,
|
|
244
|
+
maxWidth: 185,
|
|
245
|
+
backgroundColor: `${AppColors_1.AppColors.sky600}10`,
|
|
246
|
+
borderColor: `${AppColors_1.AppColors.sky600}2B`,
|
|
247
|
+
borderWidth: 1,
|
|
248
|
+
borderRadius: 5,
|
|
249
|
+
paddingHorizontal: 5,
|
|
250
|
+
paddingVertical: 1.5,
|
|
251
|
+
}}>
|
|
236
252
|
{parsedCaller.fileExt && parsedCaller.fileExt !== 'other' && (<react_native_1.View style={{
|
|
237
253
|
backgroundColor: parsedCaller.fileExt === 'tsx' || parsedCaller.fileExt === 'ts'
|
|
238
|
-
? `${AppColors_1.AppColors.brandPurple}
|
|
239
|
-
: `${AppColors_1.AppColors.teal600}
|
|
254
|
+
? `${AppColors_1.AppColors.brandPurple}22`
|
|
255
|
+
: `${AppColors_1.AppColors.teal600}22`,
|
|
240
256
|
borderRadius: 3,
|
|
241
257
|
paddingHorizontal: 3.5,
|
|
242
|
-
paddingVertical:
|
|
258
|
+
paddingVertical: 0.5,
|
|
243
259
|
}}>
|
|
244
260
|
<react_native_1.Text style={{
|
|
245
261
|
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
246
|
-
fontSize: 8
|
|
262
|
+
fontSize: 8,
|
|
247
263
|
color: parsedCaller.fileExt === 'tsx' || parsedCaller.fileExt === 'ts'
|
|
248
264
|
? AppColors_1.AppColors.brandPurple
|
|
249
265
|
: AppColors_1.AppColors.teal600,
|
|
@@ -251,11 +267,19 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
251
267
|
{parsedCaller.fileExt.toUpperCase()}
|
|
252
268
|
</react_native_1.Text>
|
|
253
269
|
</react_native_1.View>)}
|
|
254
|
-
<react_native_1.Text style={[
|
|
270
|
+
<react_native_1.Text style={[
|
|
271
|
+
styles.footerText,
|
|
272
|
+
{
|
|
273
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
274
|
+
color: AppColors_1.AppColors.sky600,
|
|
275
|
+
fontSize: 9.5,
|
|
276
|
+
},
|
|
277
|
+
]} numberOfLines={1} ellipsizeMode="middle">
|
|
255
278
|
{parsedCaller.fileName}
|
|
256
279
|
{parsedCaller.lineNumber ? `:${parsedCaller.lineNumber}` : ''}
|
|
257
280
|
</react_native_1.Text>
|
|
258
|
-
|
|
281
|
+
<NetworkIcons_1.ExternalLinkIcon color={AppColors_1.AppColors.sky600} size={8.5}/>
|
|
282
|
+
</react_native_1.Pressable>
|
|
259
283
|
</>)}
|
|
260
284
|
</react_native_1.View>
|
|
261
285
|
|
|
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
const react_1 = __importStar(require("react"));
|
|
40
|
+
const react_native_1 = require("react-native");
|
|
40
41
|
// Components
|
|
41
42
|
const TouchableScale_1 = __importDefault(require("./TouchableScale"));
|
|
42
43
|
// Helpers
|
|
@@ -54,6 +55,19 @@ const CopyButton = react_1.default.memo(({ value, label, iconType = 'copy' }) =>
|
|
|
54
55
|
setCopied(true);
|
|
55
56
|
setTimeout(() => setCopied(false), 1200);
|
|
56
57
|
}, [value, label]);
|
|
58
|
+
const handleLongPress = (0, react_1.useCallback)(() => {
|
|
59
|
+
try {
|
|
60
|
+
const resolvedValue = typeof value === 'function' ? value() : value;
|
|
61
|
+
const textToShare = typeof resolvedValue === 'string'
|
|
62
|
+
? resolvedValue
|
|
63
|
+
: JSON.stringify(resolvedValue, null, 2);
|
|
64
|
+
react_native_1.Share.share({
|
|
65
|
+
message: textToShare,
|
|
66
|
+
title: label,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch { }
|
|
70
|
+
}, [value, label]);
|
|
57
71
|
const containerStyle = [
|
|
58
72
|
styles_1.default.iconSquareBtn,
|
|
59
73
|
copied && styles_1.default.iconSquareBtnSuccess,
|
|
@@ -63,7 +77,7 @@ const CopyButton = react_1.default.memo(({ value, label, iconType = 'copy' }) =>
|
|
|
63
77
|
: iconType === 'fetch'
|
|
64
78
|
? NetworkIcons_1.FetchIcon
|
|
65
79
|
: NetworkIcons_1.CopyIcon;
|
|
66
|
-
return (<TouchableScale_1.default onPress={handlePress} hitSlop={12} style={containerStyle}>
|
|
80
|
+
return (<TouchableScale_1.default onPress={handlePress} onLongPress={handleLongPress} hitSlop={12} style={containerStyle}>
|
|
67
81
|
{copied ? (<NetworkIcons_1.CheckIcon color={AppColors_1.AppColors.greenColor} size={14}/>) : (<IconComponent color={AppColors_1.AppColors.grayTextWeak} size={14}/>)}
|
|
68
82
|
</TouchableScale_1.default>);
|
|
69
83
|
});
|
|
@@ -40,6 +40,7 @@ const react_1 = __importStar(require("react"));
|
|
|
40
40
|
const react_native_1 = require("react-native");
|
|
41
41
|
// Stylesheet
|
|
42
42
|
const AppColors_1 = require("../styles/AppColors");
|
|
43
|
+
const AppFonts_1 = require("../styles/AppFonts");
|
|
43
44
|
// Helpers
|
|
44
45
|
const helpers_1 = require("../helpers");
|
|
45
46
|
// Assets
|
|
@@ -73,10 +74,61 @@ const DomainHeader = ({ pageName, color, stats, activeFilters, onToggleFilter, i
|
|
|
73
74
|
<react_native_1.View style={[styles_1.default.domainIconWrap, { backgroundColor: `${color}18` }]}>
|
|
74
75
|
<NetworkIcons_1.ScreenIcon color={color} size={14}/>
|
|
75
76
|
</react_native_1.View>
|
|
76
|
-
<react_native_1.View style={{ flex: 1, justifyContent: 'center' }}>
|
|
77
|
-
<react_native_1.
|
|
78
|
-
{
|
|
79
|
-
|
|
77
|
+
<react_native_1.View style={{ flex: 1, justifyContent: 'center', minWidth: 0 }}>
|
|
78
|
+
<react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
79
|
+
<react_native_1.Text style={[styles_1.default.domainTitleText, { flexShrink: 1 }]} numberOfLines={1}>
|
|
80
|
+
{pageName}
|
|
81
|
+
</react_native_1.Text>
|
|
82
|
+
{total > 0 && (<react_native_1.View style={{
|
|
83
|
+
flexDirection: 'row',
|
|
84
|
+
alignItems: 'center',
|
|
85
|
+
gap: 4,
|
|
86
|
+
backgroundColor: stats.failed > 0
|
|
87
|
+
? '#FEE2E2'
|
|
88
|
+
: stats.loading > 0
|
|
89
|
+
? '#FEF3C7'
|
|
90
|
+
: '#D1FAE5',
|
|
91
|
+
paddingHorizontal: 5,
|
|
92
|
+
paddingVertical: 1.5,
|
|
93
|
+
borderRadius: 4,
|
|
94
|
+
}}>
|
|
95
|
+
<react_native_1.View style={{
|
|
96
|
+
width: 24,
|
|
97
|
+
height: 3.5,
|
|
98
|
+
borderRadius: 2,
|
|
99
|
+
backgroundColor: '#E5E7EB',
|
|
100
|
+
flexDirection: 'row',
|
|
101
|
+
overflow: 'hidden',
|
|
102
|
+
}}>
|
|
103
|
+
{pctSuccess > 0 && (<react_native_1.View style={{
|
|
104
|
+
width: `${pctSuccess}%`,
|
|
105
|
+
height: '100%',
|
|
106
|
+
backgroundColor: '#10B981',
|
|
107
|
+
}}/>)}
|
|
108
|
+
{pctFailed > 0 && (<react_native_1.View style={{
|
|
109
|
+
width: `${pctFailed}%`,
|
|
110
|
+
height: '100%',
|
|
111
|
+
backgroundColor: '#EF4444',
|
|
112
|
+
}}/>)}
|
|
113
|
+
{pctLoading > 0 && (<react_native_1.View style={{
|
|
114
|
+
width: `${pctLoading}%`,
|
|
115
|
+
height: '100%',
|
|
116
|
+
backgroundColor: '#F59E0B',
|
|
117
|
+
}}/>)}
|
|
118
|
+
</react_native_1.View>
|
|
119
|
+
<react_native_1.Text style={{
|
|
120
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
121
|
+
fontSize: 8.5,
|
|
122
|
+
color: stats.failed > 0
|
|
123
|
+
? '#991B1B'
|
|
124
|
+
: stats.loading > 0
|
|
125
|
+
? '#92400E'
|
|
126
|
+
: '#065F46',
|
|
127
|
+
}}>
|
|
128
|
+
{pctSuccess}%
|
|
129
|
+
</react_native_1.Text>
|
|
130
|
+
</react_native_1.View>)}
|
|
131
|
+
</react_native_1.View>
|
|
80
132
|
<react_native_1.Text style={styles_1.default.domainSummaryText} numberOfLines={1}>
|
|
81
133
|
{(0, helpers_1.formatDateTime)(timestamp)}
|
|
82
134
|
</react_native_1.Text>
|
|
@@ -132,30 +184,6 @@ const DomainHeader = ({ pageName, color, stats, activeFilters, onToggleFilter, i
|
|
|
132
184
|
})}
|
|
133
185
|
</react_native_1.View>
|
|
134
186
|
</react_native_1.View>
|
|
135
|
-
|
|
136
|
-
{total > 0 && (<react_native_1.View style={styles_1.default.domainProgressTrack}>
|
|
137
|
-
{pctSuccess > 0 && (<react_native_1.View style={[
|
|
138
|
-
styles_1.default.domainProgressSegment,
|
|
139
|
-
{
|
|
140
|
-
width: `${pctSuccess}%`,
|
|
141
|
-
backgroundColor: AppColors_1.AppColors.greenColor,
|
|
142
|
-
},
|
|
143
|
-
]}/>)}
|
|
144
|
-
{pctFailed > 0 && (<react_native_1.View style={[
|
|
145
|
-
styles_1.default.domainProgressSegment,
|
|
146
|
-
{
|
|
147
|
-
width: `${pctFailed}%`,
|
|
148
|
-
backgroundColor: AppColors_1.AppColors.errorColor,
|
|
149
|
-
},
|
|
150
|
-
]}/>)}
|
|
151
|
-
{pctLoading > 0 && (<react_native_1.View style={[
|
|
152
|
-
styles_1.default.domainProgressSegment,
|
|
153
|
-
{
|
|
154
|
-
width: `${pctLoading}%`,
|
|
155
|
-
backgroundColor: AppColors_1.AppColors.darkOrange,
|
|
156
|
-
},
|
|
157
|
-
]}/>)}
|
|
158
|
-
</react_native_1.View>)}
|
|
159
187
|
</react_native_1.Pressable>);
|
|
160
188
|
};
|
|
161
189
|
exports.default = DomainHeader;
|
|
@@ -8,6 +8,12 @@ const react_native_1 = require("react-native");
|
|
|
8
8
|
// Helpers
|
|
9
9
|
const helpers_1 = require("../helpers");
|
|
10
10
|
const HighlightText = react_1.default.memo(function HighlightText({ text, search, style, highlightStyle, detectLinks, numberOfLines, ...rest }) {
|
|
11
|
+
// Optimization: For huge strings (> 20KB) with no active search, bypass all splitting/regex overhead
|
|
12
|
+
if (!text || (text.length > 20000 && !search && !detectLinks)) {
|
|
13
|
+
return (<react_native_1.Text style={style} numberOfLines={numberOfLines} {...rest}>
|
|
14
|
+
{text}
|
|
15
|
+
</react_native_1.Text>);
|
|
16
|
+
}
|
|
11
17
|
// Regex to detect absolute URLs
|
|
12
18
|
const URL_REGEX = /(https?:\/\/[^\s]+)/g;
|
|
13
19
|
const renderNormalOrHighlighted = (subText, keyPrefix) => {
|
|
@@ -22,7 +28,7 @@ const HighlightText = react_1.default.memo(function HighlightText({ text, search
|
|
|
22
28
|
</react_native_1.Text>) : (<react_native_1.Text key={i}>{part}</react_native_1.Text>))}
|
|
23
29
|
</react_native_1.Text>);
|
|
24
30
|
};
|
|
25
|
-
if (detectLinks && text) {
|
|
31
|
+
if (detectLinks && text && text.length < 20000) {
|
|
26
32
|
const parts = text.split(URL_REGEX);
|
|
27
33
|
return (<react_native_1.Text style={style} numberOfLines={numberOfLines} {...rest}>
|
|
28
34
|
{parts.map((part, i) => {
|
|
@@ -337,41 +337,55 @@ const AnalyticsHeader = react_1.default.memo(() => {
|
|
|
337
337
|
</react_native_1.View>
|
|
338
338
|
</react_native_1.View>
|
|
339
339
|
|
|
340
|
-
{/* ──
|
|
340
|
+
{/* ── Crashlytics-Style Multi-Bar Histogram with Trendline Overlay ── */}
|
|
341
341
|
<react_native_1.View style={{ height: chartHeight, position: 'relative' }}>
|
|
342
342
|
<react_native_svg_1.default width="100%" height={chartHeight} viewBox={`0 0 ${svgWidth} ${chartHeight}`}>
|
|
343
343
|
<react_native_svg_1.Defs>
|
|
344
|
-
<react_native_svg_1.LinearGradient id="
|
|
345
|
-
<react_native_svg_1.Stop offset="0%" stopColor=
|
|
346
|
-
<react_native_svg_1.Stop offset="
|
|
347
|
-
|
|
344
|
+
<react_native_svg_1.LinearGradient id="crashlyticsBarGrad" x1="0" y1="0" x2="0" y2="1">
|
|
345
|
+
<react_native_svg_1.Stop offset="0%" stopColor="#8B5CF6" stopOpacity="0.9"/>
|
|
346
|
+
<react_native_svg_1.Stop offset="100%" stopColor="#6366F1" stopOpacity="0.6"/>
|
|
347
|
+
</react_native_svg_1.LinearGradient>
|
|
348
|
+
<react_native_svg_1.LinearGradient id="crashlyticsAreaGrad" x1="0" y1="0" x2="0" y2="1">
|
|
349
|
+
<react_native_svg_1.Stop offset="0%" stopColor="#8B5CF6" stopOpacity="0.25"/>
|
|
350
|
+
<react_native_svg_1.Stop offset="80%" stopColor="#8B5CF6" stopOpacity="0.04"/>
|
|
351
|
+
<react_native_svg_1.Stop offset="100%" stopColor="#8B5CF6" stopOpacity="0.0"/>
|
|
348
352
|
</react_native_svg_1.LinearGradient>
|
|
349
353
|
</react_native_svg_1.Defs>
|
|
350
354
|
|
|
351
|
-
{/*
|
|
352
|
-
<react_native_svg_1.Line x1="
|
|
353
|
-
<react_native_svg_1.Line x1="
|
|
354
|
-
|
|
355
|
-
{/* Glowing Area Fill */}
|
|
356
|
-
<react_native_svg_1.Path d={histogramData.areaPath} fill="url(#telemetryAreaGrad)"/>
|
|
355
|
+
{/* Subtle Grid Guidelines */}
|
|
356
|
+
<react_native_svg_1.Line x1="6" y1={chartHeight - 6} x2={svgWidth - 6} y2={chartHeight - 6} stroke="#E2E8F0" strokeWidth="1"/>
|
|
357
|
+
<react_native_svg_1.Line x1="6" y1={chartHeight / 2} x2={svgWidth - 6} y2={chartHeight / 2} stroke="#F1F5F9" strokeWidth="1" strokeDasharray="4,4"/>
|
|
357
358
|
|
|
358
|
-
{/*
|
|
359
|
-
<react_native_svg_1.Path d={histogramData.
|
|
359
|
+
{/* Glowing Area Underlay */}
|
|
360
|
+
<react_native_svg_1.Path d={histogramData.areaPath} fill="url(#crashlyticsAreaGrad)"/>
|
|
360
361
|
|
|
361
|
-
{/*
|
|
362
|
+
{/* Vertical Crashlytics Histogram Bars */}
|
|
362
363
|
{histogramData.buckets.map((b, idx) => {
|
|
363
364
|
const isSelected = selectedBucketIdx === idx;
|
|
364
|
-
|
|
365
|
-
|
|
365
|
+
const barWidth = 10;
|
|
366
|
+
const barX = b.x - barWidth / 2;
|
|
367
|
+
const barHeight = Math.max(3, chartHeight - 8 - b.y);
|
|
368
|
+
const barY = chartHeight - 8 - barHeight;
|
|
369
|
+
return (<react_native_svg_1.G key={`bar-${b.index}`}>
|
|
370
|
+
{/* Background slot */}
|
|
371
|
+
<react_native_svg_1.Rect x={barX} y={6} width={barWidth} height={chartHeight - 14} rx={3} fill={isSelected ? '#EDE9FE' : '#F8FAFC'}/>
|
|
366
372
|
|
|
367
|
-
{
|
|
373
|
+
{/* Active Event Bar */}
|
|
374
|
+
{b.total > 0 && (<react_native_svg_1.Rect x={barX} y={barY} width={barWidth} height={barHeight} rx={3} fill="url(#crashlyticsBarGrad)"/>)}
|
|
368
375
|
|
|
369
|
-
{
|
|
370
|
-
|
|
371
|
-
<react_native_svg_1.Circle cx={b.x} cy={b.y} r="3" fill={AppColors_1.AppColors.brandPurple}/>
|
|
372
|
-
</react_native_svg_1.G>)}
|
|
376
|
+
{/* Selected Cursor Line */}
|
|
377
|
+
{isSelected && (<react_native_svg_1.Line x1={b.x} y1={4} x2={b.x} y2={chartHeight - 6} stroke="#8B5CF6" strokeWidth="1.5" strokeDasharray="2,2"/>)}
|
|
373
378
|
</react_native_svg_1.G>);
|
|
374
379
|
})}
|
|
380
|
+
|
|
381
|
+
{/* Crashlytics Apex Trendline */}
|
|
382
|
+
<react_native_svg_1.Path d={histogramData.linePath} fill="none" stroke="#8B5CF6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
383
|
+
|
|
384
|
+
{/* Pulsing Dot on Current Active Time Bucket */}
|
|
385
|
+
{histogramData.buckets.length > 0 && (<react_native_svg_1.G>
|
|
386
|
+
<react_native_svg_1.Circle cx={histogramData.buckets[histogramData.buckets.length - 1].x} cy={histogramData.buckets[histogramData.buckets.length - 1].y} r="5.5" fill="#8B5CF6" opacity="0.25"/>
|
|
387
|
+
<react_native_svg_1.Circle cx={histogramData.buckets[histogramData.buckets.length - 1].x} cy={histogramData.buckets[histogramData.buckets.length - 1].y} r="3" fill="#7C3AED"/>
|
|
388
|
+
</react_native_svg_1.G>)}
|
|
375
389
|
</react_native_svg_1.default>
|
|
376
390
|
|
|
377
391
|
{/* Invisible Touch Columns for Interactive Bucket Selection */}
|
|
@@ -146,9 +146,9 @@ const InspectorHeader = react_1.default.memo(() => {
|
|
|
146
146
|
flex: 1,
|
|
147
147
|
}}>
|
|
148
148
|
<AppHeaderLogo_1.default size={46} customIcon={appIcon}/>
|
|
149
|
-
<react_native_1.View style={{ gap: 2, flex: 1 }}>
|
|
150
|
-
<react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: '
|
|
151
|
-
<react_native_1.Text style={[styles_1.default.headerTitle]} numberOfLines={1}>
|
|
149
|
+
<react_native_1.View style={{ gap: 2, flex: 1, minWidth: 0 }}>
|
|
150
|
+
<react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'nowrap', minWidth: 0 }}>
|
|
151
|
+
<react_native_1.Text style={[styles_1.default.headerTitle, { flexShrink: 1 }]} numberOfLines={1}>
|
|
152
152
|
{(0, helpers_1.getAppName)()}
|
|
153
153
|
</react_native_1.Text>
|
|
154
154
|
<react_native_1.View style={[
|
|
@@ -372,6 +372,20 @@ const InspectorHeader = react_1.default.memo(() => {
|
|
|
372
372
|
: '—'}
|
|
373
373
|
</react_native_1.Text>
|
|
374
374
|
</react_native_1.View>
|
|
375
|
+
{selected.response != null && (<react_native_1.View style={{
|
|
376
|
+
flexDirection: 'row',
|
|
377
|
+
alignItems: 'center',
|
|
378
|
+
gap: 4,
|
|
379
|
+
paddingHorizontal: 8,
|
|
380
|
+
paddingVertical: 3,
|
|
381
|
+
borderRadius: 20,
|
|
382
|
+
backgroundColor: `${AppColors_1.AppColors.white}29`,
|
|
383
|
+
}}>
|
|
384
|
+
<NetworkIcons_1.SizeIcon color={AppColors_1.AppColors.white} size={11}/>
|
|
385
|
+
<react_native_1.Text style={styles_1.default.headerSubTitle}>
|
|
386
|
+
{(0, helpers_1.getSize)(selected.response)}
|
|
387
|
+
</react_native_1.Text>
|
|
388
|
+
</react_native_1.View>)}
|
|
375
389
|
</react_native_1.View>
|
|
376
390
|
</react_native_1.View>) : activeTab === 'analytics' && selectedEvent != null ? (<react_native_1.View style={styles_1.default.headerDetailCenter}>
|
|
377
391
|
<react_native_1.View style={styles_1.default.headerDetailRow}>
|
|
@@ -307,16 +307,18 @@ const LogDetail = react_1.default.memo(() => {
|
|
|
307
307
|
: originFrame?.fileName || t('console.consoleLog')}
|
|
308
308
|
</react_native_1.Text>
|
|
309
309
|
|
|
310
|
-
{originFrame?.fileName && (<react_native_1.
|
|
310
|
+
{originFrame?.fileName && (<react_native_1.Pressable onPress={() => (0, helpers_1.openInVSCode)(originFrame.rawFilePath ||
|
|
311
|
+
originFrame.fullPath ||
|
|
312
|
+
originFrame.fileName, originFrame.lineNumber, originFrame.columnNumber)} hitSlop={8} style={[
|
|
311
313
|
styles_1.default.metaChip,
|
|
312
314
|
{
|
|
313
|
-
backgroundColor: `${AppColors_1.AppColors.brandPurple}
|
|
314
|
-
borderColor: `${AppColors_1.AppColors.brandPurple}
|
|
315
|
+
backgroundColor: `${AppColors_1.AppColors.brandPurple}15`,
|
|
316
|
+
borderColor: `${AppColors_1.AppColors.brandPurple}40`,
|
|
315
317
|
paddingHorizontal: 7,
|
|
316
318
|
paddingVertical: 2,
|
|
317
319
|
},
|
|
318
320
|
]}>
|
|
319
|
-
<react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap:
|
|
321
|
+
<react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 4, flexShrink: 1 }}>
|
|
320
322
|
<NetworkIcons_1.DocIcon color={AppColors_1.AppColors.brandPurple} size={10}/>
|
|
321
323
|
<react_native_1.Text style={[
|
|
322
324
|
styles_1.default.metaChipText,
|
|
@@ -326,8 +328,9 @@ const LogDetail = react_1.default.memo(() => {
|
|
|
326
328
|
{originFrame.lineNumber ? `:${originFrame.lineNumber}` : ''}
|
|
327
329
|
{originFrame.columnNumber ? `:${originFrame.columnNumber}` : ''}
|
|
328
330
|
</react_native_1.Text>
|
|
331
|
+
<NetworkIcons_1.ExternalLinkIcon color={AppColors_1.AppColors.brandPurple} size={9}/>
|
|
329
332
|
</react_native_1.View>
|
|
330
|
-
</react_native_1.
|
|
333
|
+
</react_native_1.Pressable>)}
|
|
331
334
|
</react_native_1.View>
|
|
332
335
|
|
|
333
336
|
{/* Enhanced metadata metrics row */}
|
|
@@ -56,6 +56,7 @@ const PerformanceTab_1 = __importDefault(require("./PerformanceTab"));
|
|
|
56
56
|
const CrashTab_1 = __importDefault(require("./CrashTab"));
|
|
57
57
|
const CrashDetail_1 = __importDefault(require("./CrashDetail"));
|
|
58
58
|
const SettingsPanel_1 = __importDefault(require("./SettingsPanel"));
|
|
59
|
+
const Toast_1 = __importDefault(require("../Toast"));
|
|
59
60
|
const styles_1 = __importDefault(require("../../styles"));
|
|
60
61
|
const AppColors_1 = require("../../styles/AppColors");
|
|
61
62
|
const NavigationTracker_1 = __importDefault(require("./NavigationTracker"));
|
|
@@ -153,6 +154,9 @@ const MainScreen = () => {
|
|
|
153
154
|
]}>
|
|
154
155
|
<SettingsPanel_1.default />
|
|
155
156
|
</react_native_1.View>)}
|
|
157
|
+
|
|
158
|
+
{/* Bottom floating toast notification */}
|
|
159
|
+
<Toast_1.default />
|
|
156
160
|
</react_native_1.View>
|
|
157
161
|
</react_native_1.View>
|
|
158
162
|
</ErrorBoundary_1.default>)}
|