react-native-inapp-inspector 1.0.3 → 1.0.5

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 (47) hide show
  1. package/README.md +3 -3
  2. package/dist/commonjs/components/AnalyticsEventCard.js +10 -10
  3. package/dist/commonjs/components/CodeSnippet.js +233 -10
  4. package/dist/commonjs/components/ConsoleLogCard.js +55 -9
  5. package/dist/commonjs/components/CopyButton.js +2 -1
  6. package/dist/commonjs/components/ErrorBoundary.d.ts +20 -0
  7. package/dist/commonjs/components/ErrorBoundary.js +332 -0
  8. package/dist/commonjs/components/NetworkIcons.d.ts +5 -0
  9. package/dist/commonjs/components/NetworkIcons.js +45 -1
  10. package/dist/commonjs/customHooks/reduxLogger.d.ts +4 -0
  11. package/dist/commonjs/customHooks/reduxLogger.js +30 -0
  12. package/dist/commonjs/customHooks/webViewLogger.d.ts +2 -0
  13. package/dist/commonjs/customHooks/webViewLogger.js +281 -246
  14. package/dist/commonjs/helpers/index.js +2 -1
  15. package/dist/commonjs/index.d.ts +5 -3
  16. package/dist/commonjs/index.js +685 -911
  17. package/dist/commonjs/styles/AppColors.d.ts +29 -1
  18. package/dist/commonjs/styles/AppColors.js +38 -2
  19. package/dist/commonjs/styles/index.d.ts +438 -229
  20. package/dist/commonjs/styles/index.js +448 -209
  21. package/dist/commonjs/types/index.d.ts +2 -2
  22. package/dist/esm/components/AnalyticsEventCard.js +10 -10
  23. package/dist/esm/components/CodeSnippet.js +232 -12
  24. package/dist/esm/components/ConsoleLogCard.js +55 -9
  25. package/dist/esm/components/CopyButton.js +2 -1
  26. package/dist/esm/components/ErrorBoundary.d.ts +20 -0
  27. package/dist/esm/components/ErrorBoundary.js +295 -0
  28. package/dist/esm/components/NetworkIcons.d.ts +5 -0
  29. package/dist/esm/components/NetworkIcons.js +39 -0
  30. package/dist/esm/customHooks/reduxLogger.d.ts +4 -0
  31. package/dist/esm/customHooks/reduxLogger.js +23 -0
  32. package/dist/esm/customHooks/webViewLogger.d.ts +2 -0
  33. package/dist/esm/customHooks/webViewLogger.js +281 -246
  34. package/dist/esm/helpers/index.js +2 -1
  35. package/dist/esm/index.d.ts +5 -3
  36. package/dist/esm/index.js +683 -914
  37. package/dist/esm/styles/AppColors.d.ts +29 -1
  38. package/dist/esm/styles/AppColors.js +35 -1
  39. package/dist/esm/styles/index.d.ts +438 -229
  40. package/dist/esm/styles/index.js +412 -209
  41. package/dist/esm/types/index.d.ts +2 -2
  42. package/example/App.tsx +351 -127
  43. package/example/ios/Podfile.lock +26 -0
  44. package/example/metro.config.js +1 -1
  45. package/example/package-lock.json +20 -4
  46. package/example/package.json +4 -3
  47. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- export type ActiveTab = 'apis' | 'analytics' | 'logs' | 'webview';
2
+ export type ActiveTab = 'insights' | 'apis' | 'analytics' | 'logs' | 'webview' | 'redux';
3
3
  export interface ConsoleLog {
4
4
  id: number;
5
5
  type: 'info' | 'warn' | 'error';
@@ -73,7 +73,7 @@ export type DiffResult = {
73
73
  newVal?: any;
74
74
  };
75
75
  export interface CopyButtonProps {
76
- value: unknown;
76
+ value: unknown | (() => unknown);
77
77
  label: string;
78
78
  iconType?: 'copy' | 'terminal' | 'fetch';
79
79
  }
@@ -171,8 +171,8 @@ const AnalyticsEventCard = React.memo(function AnalyticsEventCard({ event, onPre
171
171
  // ─── Styles ───────────────────────────────────────────────────────────────────
172
172
  const cardStyles = StyleSheet.create({
173
173
  container: {
174
- paddingHorizontal: 16,
175
- paddingVertical: 4,
174
+ paddingHorizontal: 12,
175
+ paddingVertical: 3,
176
176
  },
177
177
  gapContainer: {
178
178
  alignItems: 'center',
@@ -189,7 +189,7 @@ const cardStyles = StyleSheet.create({
189
189
  borderRadius: 8,
190
190
  borderWidth: 1,
191
191
  borderColor: '#EFEFEF',
192
- padding: 12,
192
+ padding: 8,
193
193
  shadowColor: '#000',
194
194
  shadowOpacity: 0.03,
195
195
  shadowRadius: 3,
@@ -201,11 +201,11 @@ const cardStyles = StyleSheet.create({
201
201
  flexDirection: 'row',
202
202
  justifyContent: 'space-between',
203
203
  alignItems: 'center',
204
- marginBottom: 8,
204
+ marginBottom: 6,
205
205
  },
206
206
  nameBadge: {
207
- paddingHorizontal: 8,
208
- paddingVertical: 4,
207
+ paddingHorizontal: 6,
208
+ paddingVertical: 2.5,
209
209
  borderRadius: 6,
210
210
  borderWidth: 1,
211
211
  },
@@ -240,8 +240,8 @@ const cardStyles = StyleSheet.create({
240
240
  flexDirection: 'row',
241
241
  alignItems: 'center',
242
242
  backgroundColor: AppColors.grayBackground,
243
- paddingHorizontal: 6,
244
- paddingVertical: 3,
243
+ paddingHorizontal: 5,
244
+ paddingVertical: 2,
245
245
  borderRadius: 4,
246
246
  borderWidth: 1,
247
247
  borderColor: AppColors.grayBorderSecondary,
@@ -261,8 +261,8 @@ const cardStyles = StyleSheet.create({
261
261
  backgroundColor: '#FFE4E6',
262
262
  borderColor: '#FCC2D7',
263
263
  borderWidth: 1,
264
- paddingHorizontal: 6,
265
- paddingVertical: 3,
264
+ paddingHorizontal: 5,
265
+ paddingVertical: 2,
266
266
  borderRadius: 4,
267
267
  },
268
268
  duplicateText: {
@@ -1,5 +1,11 @@
1
- import React, { useMemo } from 'react';
2
- import { StyleSheet, Text, View, Platform, FlatList } from 'react-native';
1
+ import React, { useMemo, useState, useRef, useEffect } from 'react';
2
+ import { StyleSheet, Text, View, Platform, FlatList, TextInput, Pressable } from 'react-native';
3
+ import Svg, { Path } from 'react-native-svg';
4
+ import { SearchIcon, ClearIcon } from './NetworkIcons';
5
+ import CopyButton from './CopyButton';
6
+ import TouchableScale from './TouchableScale';
7
+ import { AppColors } from '../styles/AppColors';
8
+ import { AppFonts } from '../styles/AppFonts';
3
9
  const escapeRegExp = (string) => {
4
10
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
5
11
  };
@@ -100,7 +106,7 @@ const formatJS = (js) => {
100
106
  };
101
107
  const tokenizeJS = (text) => {
102
108
  const tokens = [];
103
- const regex = /(\/\/.*|\/\*[\s\S]*?\*\/)|("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`)|(\b(?:const|let|var|function|return|if|else|for|while|import|export|class|try|catch|new|this|async|await|break|continue|switch|case|default|throw|typeof|yield|let)\b)|(\b(?:console|window|document|fetch|global|require|module|exports|Promise|Map|Set|Array|Object|String|Number|Boolean)\b)|(\b\d+(?:\.\d+)?\b)/g;
109
+ const regex = /(\/\/.*|\/\*[\s\S]*?\*\/)|(\"(?:\\.|[^\"\\])*\"|\'(?:\\.|[^\'\\])*\'|\`(?:\\.|[^\`\\])*\`)|(\b(const|let|var|function|return|if|else|for|while|import|export|class|try|catch|new|this|async|await|break|continue|switch|case|default|throw|typeof|yield)\b)|(\b(console|window|document|fetch|global|require|module|exports|Promise|Map|Set|Array|Object|String|Number|Boolean)\b)|(\b\d+(?:\.\d+)?\b)/g;
104
110
  let lastIndex = 0;
105
111
  let match;
106
112
  while ((match = regex.exec(text)) !== null) {
@@ -166,7 +172,7 @@ const tokenizeCSS = (text) => {
166
172
  };
167
173
  const tokenizeHTML = (text) => {
168
174
  const tokens = [];
169
- const regex = /(<!--[\s\S]*?-->)|(<\/?[a-zA-Z0-9:-]+>?)|("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')|([a-zA-Z0-9:-]+(?=\s*=))/g;
175
+ const regex = /(<!--[\s\S]*?-->)|(<\/?[a-zA-Z0-9:-]+>?)|(\"(?:\\.|[^\"\\])*\"|\'(?:\\.|[^\'\\])*\')|([a-zA-Z0-9:-]+(?=\s*=))/g;
170
176
  let lastIndex = 0;
171
177
  let match;
172
178
  while ((match = regex.exec(text)) !== null) {
@@ -221,7 +227,13 @@ const getStyleForType = (type) => {
221
227
  return styles.plain;
222
228
  }
223
229
  };
224
- const CodeSnippetLine = React.memo(({ line, lineIndex, language, search, }) => {
230
+ const ArrowUpIcon = ({ color = '#64748B', size = 16 }) => (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
231
+ <Path d="M18 15l-6-6-6 6" stroke={color} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/>
232
+ </Svg>);
233
+ const ArrowDownIcon = ({ color = '#64748B', size = 16 }) => (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
234
+ <Path d="M6 9l6 6 6-6" stroke={color} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/>
235
+ </Svg>);
236
+ const CodeSnippetLine = React.memo(({ line, lineIndex, language, search, isActiveMatch, }) => {
225
237
  const tokens = useMemo(() => {
226
238
  if (language === 'html')
227
239
  return tokenizeHTML(line);
@@ -244,10 +256,10 @@ const CodeSnippetLine = React.memo(({ line, lineIndex, language, search, }) => {
244
256
  })}
245
257
  </Text>);
246
258
  };
247
- return (<View style={styles.lineRow}>
259
+ return (<View style={[styles.lineRow, isActiveMatch && styles.activeMatchRow]}>
248
260
  {/* Gutter Line Number */}
249
- <View style={styles.gutter}>
250
- <Text style={styles.lineNumber}>{lineIndex + 1}</Text>
261
+ <View style={[styles.gutter, isActiveMatch && styles.activeMatchGutter]}>
262
+ <Text style={[styles.lineNumber, isActiveMatch && styles.activeMatchLineNumber]}>{lineIndex + 1}</Text>
251
263
  </View>
252
264
 
253
265
  {/* Highlighted Code Line */}
@@ -260,10 +272,27 @@ const CodeSnippetLine = React.memo(({ line, lineIndex, language, search, }) => {
260
272
  </View>
261
273
  </View>);
262
274
  });
263
- const CodeSnippet = ({ code, language, search = '', }) => {
275
+ const CodeSnippet = ({ code, language, }) => {
276
+ const [searchQuery, setSearchQuery] = useState('');
277
+ const [debouncedQuery, setDebouncedQuery] = useState('');
278
+ const [currentMatchIdx, setCurrentMatchIdx] = useState(-1);
279
+ const [visibleLinesCount, setVisibleLinesCount] = useState(200);
280
+ const flatListRef = useRef(null);
281
+ // Debounce search query updates
282
+ useEffect(() => {
283
+ const timer = setTimeout(() => {
284
+ setDebouncedQuery(searchQuery);
285
+ }, 300);
286
+ return () => clearTimeout(timer);
287
+ }, [searchQuery]);
288
+ // If code is extremely large (e.g. > 150KB), skip the pretty printers
289
+ // to avoid blocking the single JS thread.
264
290
  const formattedCode = useMemo(() => {
265
291
  if (!code)
266
292
  return '';
293
+ if (code.length > 150000) {
294
+ return code;
295
+ }
267
296
  if (language === 'html')
268
297
  return formatHTML(code);
269
298
  if (language === 'css')
@@ -277,9 +306,135 @@ const CodeSnippet = ({ code, language, search = '', }) => {
277
306
  return [];
278
307
  return formattedCode.split(/\r?\n/);
279
308
  }, [formattedCode]);
280
- const renderLine = ({ item, index }) => (<CodeSnippetLine line={item} lineIndex={index} language={language} search={search}/>);
281
- return (<View style={styles.container}>
282
- <FlatList data={lines} renderItem={renderLine} keyExtractor={(_, index) => String(index)} maxToRenderPerBatch={50} windowSize={10} initialNumToRender={30} removeClippedSubviews={Platform.OS === 'android'}/>
309
+ // Find all line indices matching the search query
310
+ const matches = useMemo(() => {
311
+ if (!debouncedQuery.trim() || debouncedQuery.length < 2)
312
+ return [];
313
+ const query = debouncedQuery.toLowerCase();
314
+ const indices = [];
315
+ for (let i = 0; i < lines.length; i++) {
316
+ if (lines[i].toLowerCase().includes(query)) {
317
+ indices.push(i);
318
+ }
319
+ }
320
+ return indices;
321
+ }, [debouncedQuery, lines]);
322
+ // Reset scroll and states on code/language changes
323
+ useEffect(() => {
324
+ setVisibleLinesCount(200);
325
+ setSearchQuery('');
326
+ setCurrentMatchIdx(-1);
327
+ }, [code, language]);
328
+ // Auto-scroll to the first match when search query returns matches
329
+ useEffect(() => {
330
+ if (matches.length > 0) {
331
+ setCurrentMatchIdx(0);
332
+ scrollToMatch(0);
333
+ }
334
+ else {
335
+ setCurrentMatchIdx(-1);
336
+ }
337
+ }, [matches]);
338
+ const scrollToMatch = (matchIdx) => {
339
+ if (matches.length === 0 || matchIdx < 0 || matchIdx >= matches.length)
340
+ return;
341
+ const lineIdx = matches[matchIdx];
342
+ // Ensure the targeted index is loaded in FlatList
343
+ if (lineIdx >= visibleLinesCount) {
344
+ setVisibleLinesCount(lineIdx + 100);
345
+ }
346
+ // Schedule scroll after state has applied
347
+ setTimeout(() => {
348
+ try {
349
+ flatListRef.current?.scrollToIndex({
350
+ index: lineIdx,
351
+ animated: true,
352
+ viewPosition: 0.5,
353
+ });
354
+ }
355
+ catch (e) {
356
+ // Fallback recovery is handled by onScrollToIndexFailed
357
+ }
358
+ }, 100);
359
+ };
360
+ const onScrollToIndexFailed = (error) => {
361
+ flatListRef.current?.scrollToOffset({
362
+ offset: error.averageItemLength * error.index,
363
+ animated: true,
364
+ });
365
+ setTimeout(() => {
366
+ try {
367
+ flatListRef.current?.scrollToIndex({
368
+ index: error.index,
369
+ animated: true,
370
+ viewPosition: 0.5,
371
+ });
372
+ }
373
+ catch (err) {
374
+ console.warn('Scroll to line index failed after fallback retry:', err);
375
+ }
376
+ }, 120);
377
+ };
378
+ const handleNextMatch = () => {
379
+ if (matches.length === 0)
380
+ return;
381
+ const nextIdx = (currentMatchIdx + 1) % matches.length;
382
+ setCurrentMatchIdx(nextIdx);
383
+ scrollToMatch(nextIdx);
384
+ };
385
+ const handlePrevMatch = () => {
386
+ if (matches.length === 0)
387
+ return;
388
+ const prevIdx = (currentMatchIdx - 1 + matches.length) % matches.length;
389
+ setCurrentMatchIdx(prevIdx);
390
+ scrollToMatch(prevIdx);
391
+ };
392
+ const visibleLines = useMemo(() => {
393
+ return lines.slice(0, visibleLinesCount);
394
+ }, [lines, visibleLinesCount]);
395
+ const handleEndReached = () => {
396
+ if (visibleLinesCount < lines.length) {
397
+ setVisibleLinesCount(prev => Math.min(prev + 150, lines.length));
398
+ }
399
+ };
400
+ const renderLine = ({ item, index }) => {
401
+ const isActiveMatch = matches.length > 0 && currentMatchIdx >= 0 && index === matches[currentMatchIdx];
402
+ return (<CodeSnippetLine line={item} lineIndex={index} language={language} search={debouncedQuery} isActiveMatch={isActiveMatch}/>);
403
+ };
404
+ return (<View style={{ flex: 1 }}>
405
+ {/* Search Header Row */}
406
+ <View style={styles.searchRow}>
407
+ <View style={styles.searchBar}>
408
+ <SearchIcon color={AppColors.grayTextWeak} size={15}/>
409
+ <TextInput placeholder={`Search ${language.toUpperCase()}...`} placeholderTextColor={AppColors.grayTextWeak} value={searchQuery} onChangeText={setSearchQuery} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
410
+
411
+ {/* Matches Info */}
412
+ {debouncedQuery.length >= 2 && (<Text style={styles.matchCountText}>
413
+ {matches.length > 0 ? `${currentMatchIdx + 1}/${matches.length}` : '0/0'}
414
+ </Text>)}
415
+
416
+ {searchQuery.length > 0 && (<Pressable onPress={() => setSearchQuery('')} hitSlop={8} style={styles.clearBtn}>
417
+ <ClearIcon color={AppColors.grayTextWeak} size={12}/>
418
+ </Pressable>)}
419
+ </View>
420
+
421
+ {/* Up / Down Arrow Navigation Buttons */}
422
+ {matches.length > 0 && (<View style={styles.navArrowsGroup}>
423
+ <TouchableScale onPress={handlePrevMatch} hitSlop={8} style={styles.navArrowBtn}>
424
+ <ArrowUpIcon color="#475569" size={14}/>
425
+ </TouchableScale>
426
+ <TouchableScale onPress={handleNextMatch} hitSlop={8} style={styles.navArrowBtn}>
427
+ <ArrowDownIcon color="#475569" size={14}/>
428
+ </TouchableScale>
429
+ </View>)}
430
+
431
+ <CopyButton value={code} label={`${language.toUpperCase()} Source`}/>
432
+ </View>
433
+
434
+ {/* Code Snippet list container */}
435
+ <View style={styles.container}>
436
+ <FlatList ref={flatListRef} data={visibleLines} renderItem={renderLine} keyExtractor={(_, index) => String(index)} maxToRenderPerBatch={30} windowSize={5} initialNumToRender={50} removeClippedSubviews={Platform.OS === 'android'} onEndReached={handleEndReached} onEndReachedThreshold={0.5} onScrollToIndexFailed={onScrollToIndexFailed}/>
437
+ </View>
283
438
  </View>);
284
439
  };
285
440
  const monoFont = Platform.OS === 'ios' ? 'Menlo' : 'monospace';
@@ -292,12 +447,69 @@ const styles = StyleSheet.create({
292
447
  borderColor: '#E2E8F0',
293
448
  overflow: 'hidden',
294
449
  },
450
+ searchRow: {
451
+ flexDirection: 'row',
452
+ alignItems: 'center',
453
+ marginBottom: 6,
454
+ gap: 8,
455
+ },
456
+ searchBar: {
457
+ flex: 1,
458
+ flexDirection: 'row',
459
+ alignItems: 'center',
460
+ backgroundColor: '#FFFFFF',
461
+ borderRadius: 8,
462
+ borderWidth: 1,
463
+ borderColor: '#E2E8F0',
464
+ paddingHorizontal: 8,
465
+ height: 32,
466
+ },
467
+ searchInput: {
468
+ flex: 1,
469
+ fontFamily: AppFonts.interRegular,
470
+ fontSize: 12,
471
+ color: '#0F172A',
472
+ marginLeft: 6,
473
+ paddingVertical: 0,
474
+ },
475
+ matchCountText: {
476
+ fontFamily: AppFonts.interMedium,
477
+ fontSize: 10,
478
+ color: '#64748B',
479
+ marginHorizontal: 6,
480
+ backgroundColor: '#F1F5F9',
481
+ paddingHorizontal: 6,
482
+ paddingVertical: 2,
483
+ borderRadius: 4,
484
+ },
485
+ clearBtn: {
486
+ padding: 4,
487
+ },
488
+ navArrowsGroup: {
489
+ flexDirection: 'row',
490
+ alignItems: 'center',
491
+ backgroundColor: '#FFFFFF',
492
+ borderRadius: 8,
493
+ borderWidth: 1,
494
+ borderColor: '#E2E8F0',
495
+ height: 32,
496
+ paddingHorizontal: 2,
497
+ },
498
+ navArrowBtn: {
499
+ padding: 5,
500
+ borderRadius: 6,
501
+ alignItems: 'center',
502
+ justifyContent: 'center',
503
+ },
295
504
  lineRow: {
296
505
  flexDirection: 'row',
297
506
  alignItems: 'stretch',
298
507
  minHeight: 20,
299
508
  paddingVertical: 1,
300
509
  },
510
+ activeMatchRow: {
511
+ backgroundColor: 'rgba(234, 179, 8, 0.15)',
512
+ },
301
513
  gutter: {
302
514
  width: 40,
303
515
  backgroundColor: '#F1F5F9',
@@ -308,11 +520,19 @@ const styles = StyleSheet.create({
308
520
  paddingRight: 6,
309
521
  paddingTop: 1,
310
522
  },
523
+ activeMatchGutter: {
524
+ backgroundColor: 'rgba(234, 179, 8, 0.25)',
525
+ borderRightColor: 'rgba(234, 179, 8, 0.4)',
526
+ },
311
527
  lineNumber: {
312
528
  fontFamily: monoFont,
313
529
  fontSize: 9,
314
530
  color: '#94A3B8',
315
531
  },
532
+ activeMatchLineNumber: {
533
+ color: '#854D0E',
534
+ fontWeight: 'bold',
535
+ },
316
536
  codeLine: {
317
537
  flex: 1,
318
538
  paddingLeft: 10,
@@ -60,6 +60,52 @@ const getJsonPreviewText = (data) => {
60
60
  };
61
61
  }
62
62
  };
63
+ const getLogMessageWithBadges = (message, searchStr, textStyle, highlightStyle, numberOfLines) => {
64
+ if (!message)
65
+ return null;
66
+ const prefixRegex = /^((?:\[[^\]]+\]\s*)+)/;
67
+ const match = message.match(prefixRegex);
68
+ if (match) {
69
+ const fullPrefix = match[1];
70
+ const remainingText = message.substring(fullPrefix.length);
71
+ const tags = fullPrefix.match(/\[[^\]]+\]/g) || [];
72
+ const getTagColor = (tag) => {
73
+ const cleanTag = tag.replace(/[\[\]]/g, '').trim().toUpperCase();
74
+ if (cleanTag === 'API')
75
+ return '#0284C7';
76
+ if (cleanTag === 'TEST')
77
+ return '#16A34A';
78
+ if (cleanTag === 'APP')
79
+ return '#4F46E5';
80
+ if (cleanTag === 'DETAILS')
81
+ return '#7C3AED';
82
+ if (cleanTag === 'WEBVIEW')
83
+ return '#EA580C';
84
+ if (cleanTag === 'MOCK REDUX' || cleanTag === 'REDUX')
85
+ return '#DB2777';
86
+ let hash = 0;
87
+ for (let i = 0; i < cleanTag.length; i++) {
88
+ hash = cleanTag.charCodeAt(i) + ((hash << 5) - hash);
89
+ }
90
+ const colors = ['#0891B2', '#0D9488', '#2563EB', '#D97706', '#E11D48', '#8B5CF6'];
91
+ return colors[Math.abs(hash) % colors.length];
92
+ };
93
+ return (<Text style={textStyle} numberOfLines={numberOfLines}>
94
+ {tags.map((tag, idx) => {
95
+ const color = getTagColor(tag);
96
+ return (<Text key={idx} style={{
97
+ fontWeight: 'bold',
98
+ color: color,
99
+ fontFamily: AppFonts.interBold,
100
+ }}>
101
+ {tag}{' '}
102
+ </Text>);
103
+ })}
104
+ <HighlightText text={remainingText} search={searchStr} style={textStyle} highlightStyle={highlightStyle} detectLinks={true}/>
105
+ </Text>);
106
+ }
107
+ return (<HighlightText text={message} search={searchStr} style={textStyle} highlightStyle={highlightStyle} numberOfLines={numberOfLines} detectLinks={true}/>);
108
+ };
63
109
  export const ConsoleLogCard = React.memo(function ConsoleLogCard({ item, searchStr = '', isWebView = false, }) {
64
110
  const [expanded, setExpanded] = useState(false);
65
111
  const jsonContent = getJsonContent(item.message);
@@ -216,7 +262,7 @@ export const ConsoleLogCard = React.memo(function ConsoleLogCard({ item, searchS
216
262
  <View style={styles.cardBody}>
217
263
  {jsonContent ? (<>
218
264
  {jsonContent.header ? (<Pressable onPress={() => setExpanded(prev => !prev)}>
219
- <HighlightText text={jsonContent.header} search={searchStr} style={styles.messageText} highlightStyle={styles.highlight} numberOfLines={numLines} detectLinks={true}/>
265
+ {getLogMessageWithBadges(jsonContent.header, searchStr, styles.messageText, styles.highlight, numLines)}
220
266
  </Pressable>) : null}
221
267
  {expanded ? (<View style={styles.jsonContainer}>
222
268
  <JsonViewer data={jsonContent.data} search={searchStr} forceOpen={expanded}/>
@@ -224,7 +270,7 @@ export const ConsoleLogCard = React.memo(function ConsoleLogCard({ item, searchS
224
270
  <HighlightText text={getJsonPreviewText(jsonContent.data).text} search={searchStr} style={styles.jsonPreviewText} highlightStyle={styles.highlight} detectLinks={true}/>
225
271
  </Pressable>)}
226
272
  </>) : (<Pressable onPress={() => setExpanded(prev => !prev)}>
227
- <HighlightText text={item.message} search={searchStr} style={styles.messageText} highlightStyle={styles.highlight} numberOfLines={numLines} detectLinks={true}/>
273
+ {getLogMessageWithBadges(item.message, searchStr, styles.messageText, styles.highlight, numLines)}
228
274
  </Pressable>)}
229
275
  {hasLongMessage && (<Pressable onPress={() => setExpanded(prev => !prev)} style={styles.seeMoreBtn} hitSlop={8}>
230
276
  <Text style={styles.seeMoreText}>
@@ -241,8 +287,8 @@ export const ConsoleLogCard = React.memo(function ConsoleLogCard({ item, searchS
241
287
  });
242
288
  const styles = StyleSheet.create({
243
289
  container: {
244
- paddingHorizontal: 16,
245
- paddingVertical: 4,
290
+ paddingHorizontal: 12,
291
+ paddingVertical: 3,
246
292
  },
247
293
  card: {
248
294
  alignSelf: 'stretch',
@@ -250,7 +296,7 @@ const styles = StyleSheet.create({
250
296
  borderRadius: 8,
251
297
  borderWidth: 1,
252
298
  borderColor: '#EFEFEF',
253
- padding: 12,
299
+ padding: 8,
254
300
  shadowColor: '#000',
255
301
  shadowOpacity: 0.03,
256
302
  shadowRadius: 3,
@@ -263,7 +309,7 @@ const styles = StyleSheet.create({
263
309
  flexDirection: 'row',
264
310
  justifyContent: 'space-between',
265
311
  alignItems: 'center',
266
- marginBottom: 8,
312
+ marginBottom: 6,
267
313
  },
268
314
  headerLeft: {
269
315
  flexDirection: 'row',
@@ -297,10 +343,10 @@ const styles = StyleSheet.create({
297
343
  maxWidth: '50%',
298
344
  },
299
345
  cardBody: {
300
- marginTop: 8,
346
+ marginTop: 6,
301
347
  backgroundColor: '#FFFFFF',
302
- paddingHorizontal: 10,
303
- paddingVertical: 8,
348
+ paddingHorizontal: 8,
349
+ paddingVertical: 5,
304
350
  borderRadius: 6,
305
351
  borderWidth: 1,
306
352
  borderColor: '#EBECEF',
@@ -11,7 +11,8 @@ import styles from '../styles';
11
11
  const CopyButton = ({ value, label, iconType = 'copy' }) => {
12
12
  const [copied, setCopied] = useState(false);
13
13
  const handlePress = () => {
14
- copyToClipboard(value, label);
14
+ const resolvedValue = typeof value === 'function' ? value() : value;
15
+ copyToClipboard(resolvedValue, label);
15
16
  setCopied(true);
16
17
  setTimeout(() => setCopied(false), 1200);
17
18
  };
@@ -0,0 +1,20 @@
1
+ import React, { Component, ErrorInfo, ReactNode } from 'react';
2
+ interface Props {
3
+ children: ReactNode;
4
+ onClose?: () => void;
5
+ onReset?: () => void;
6
+ fallbackType?: 'modal' | 'inline';
7
+ }
8
+ interface State {
9
+ hasError: boolean;
10
+ error: Error | null;
11
+ }
12
+ export declare class ErrorBoundary extends Component<Props, State> {
13
+ state: State;
14
+ static getDerivedStateFromError(error: Error): State;
15
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
16
+ private handleReset;
17
+ private handleCopyTrace;
18
+ render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode>> | React.JSX.Element;
19
+ }
20
+ export default ErrorBoundary;