react-native-inapp-inspector 1.1.23 → 1.1.25

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 (79) hide show
  1. package/dist/commonjs/components/AnalyticsEventCard.js +94 -88
  2. package/dist/commonjs/components/ConsoleLogCard.js +49 -25
  3. package/dist/commonjs/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
  4. package/dist/commonjs/components/Inspector/AnalyticsFilterModal.js +1002 -0
  5. package/dist/commonjs/components/Inspector/AnalyticsTab.js +637 -10
  6. package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -1
  7. package/dist/commonjs/components/Inspector/BundleTab.js +859 -402
  8. package/dist/commonjs/components/Inspector/InspectorContext.js +6 -1
  9. package/dist/commonjs/components/Inspector/InspectorHeader.js +13 -5
  10. package/dist/commonjs/components/Inspector/LogDetail.js +223 -111
  11. package/dist/commonjs/components/Inspector/MainScreen.js +1 -1
  12. package/dist/commonjs/components/Inspector/NetworkDetail.js +131 -81
  13. package/dist/commonjs/components/Inspector/PerformanceTab.js +20 -10
  14. package/dist/commonjs/components/NetworkIcons.d.ts +14 -0
  15. package/dist/commonjs/components/NetworkIcons.js +93 -1
  16. package/dist/commonjs/components/SegmentedTabs.js +31 -22
  17. package/dist/commonjs/components/TreeNode.js +13 -12
  18. package/dist/commonjs/constants/version.d.ts +1 -1
  19. package/dist/commonjs/constants/version.js +1 -1
  20. package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +6 -2
  21. package/dist/commonjs/customHooks/bundleAnalyzer.js +552 -114
  22. package/dist/commonjs/customHooks/consoleLogger.js +83 -45
  23. package/dist/commonjs/customHooks/networkLogger.js +10 -1
  24. package/dist/commonjs/customHooks/performanceTracker.d.ts +8 -0
  25. package/dist/commonjs/customHooks/performanceTracker.js +44 -3
  26. package/dist/commonjs/decorators/index.d.ts +49 -0
  27. package/dist/commonjs/decorators/index.js +142 -0
  28. package/dist/commonjs/helpers/gaAnalyticsRegistry.d.ts +18 -0
  29. package/dist/commonjs/helpers/gaAnalyticsRegistry.js +174 -0
  30. package/dist/commonjs/helpers/index.d.ts +7 -4
  31. package/dist/commonjs/helpers/index.js +102 -64
  32. package/dist/commonjs/i18n/locales/en.json +53 -4
  33. package/dist/commonjs/index.d.ts +4 -0
  34. package/dist/commonjs/index.js +216 -8
  35. package/dist/commonjs/styles/index.d.ts +4 -1
  36. package/dist/commonjs/styles/index.js +22 -19
  37. package/dist/commonjs/types/enums.d.ts +115 -9
  38. package/dist/commonjs/types/enums.js +103 -2
  39. package/dist/commonjs/types/interfaces.d.ts +19 -0
  40. package/dist/esm/components/AnalyticsEventCard.js +95 -89
  41. package/dist/esm/components/ConsoleLogCard.js +49 -25
  42. package/dist/esm/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
  43. package/dist/esm/components/Inspector/AnalyticsFilterModal.js +964 -0
  44. package/dist/esm/components/Inspector/AnalyticsTab.js +639 -12
  45. package/dist/esm/components/Inspector/BundleTab.d.ts +1 -1
  46. package/dist/esm/components/Inspector/BundleTab.js +860 -403
  47. package/dist/esm/components/Inspector/InspectorContext.js +1 -0
  48. package/dist/esm/components/Inspector/InspectorHeader.js +14 -6
  49. package/dist/esm/components/Inspector/LogDetail.js +223 -111
  50. package/dist/esm/components/Inspector/MainScreen.js +2 -2
  51. package/dist/esm/components/Inspector/NetworkDetail.js +132 -49
  52. package/dist/esm/components/Inspector/PerformanceTab.js +20 -10
  53. package/dist/esm/components/NetworkIcons.d.ts +14 -0
  54. package/dist/esm/components/NetworkIcons.js +78 -0
  55. package/dist/esm/components/SegmentedTabs.js +31 -22
  56. package/dist/esm/components/TreeNode.js +13 -12
  57. package/dist/esm/constants/version.d.ts +1 -1
  58. package/dist/esm/constants/version.js +1 -1
  59. package/dist/esm/customHooks/bundleAnalyzer.d.ts +6 -2
  60. package/dist/esm/customHooks/bundleAnalyzer.js +549 -114
  61. package/dist/esm/customHooks/consoleLogger.js +83 -45
  62. package/dist/esm/customHooks/networkLogger.js +10 -1
  63. package/dist/esm/customHooks/performanceTracker.d.ts +8 -0
  64. package/dist/esm/customHooks/performanceTracker.js +38 -1
  65. package/dist/esm/decorators/index.d.ts +49 -0
  66. package/dist/esm/decorators/index.js +137 -0
  67. package/dist/esm/helpers/gaAnalyticsRegistry.d.ts +18 -0
  68. package/dist/esm/helpers/gaAnalyticsRegistry.js +169 -0
  69. package/dist/esm/helpers/index.d.ts +7 -4
  70. package/dist/esm/helpers/index.js +100 -63
  71. package/dist/esm/i18n/locales/en.json +53 -4
  72. package/dist/esm/index.d.ts +4 -0
  73. package/dist/esm/index.js +190 -8
  74. package/dist/esm/styles/index.d.ts +4 -1
  75. package/dist/esm/styles/index.js +22 -19
  76. package/dist/esm/types/enums.d.ts +115 -9
  77. package/dist/esm/types/enums.js +102 -3
  78. package/dist/esm/types/interfaces.d.ts +19 -0
  79. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { createContext, useContext } from 'react';
2
+ export { ModalAnimationType, SettingsPage, SettingsSubTab, LogFilter, } from '../../types';
2
3
  export const InspectorContext = createContext(null);
3
4
  export const useInspector = () => {
4
5
  const ctx = useContext(InspectorContext);
@@ -10,7 +10,7 @@ import { AppFonts } from '../../styles/AppFonts';
10
10
  import { METHOD_COLORS } from '../../constants';
11
11
  import { LIB_VERSION } from '../../constants';
12
12
  import { getStatusColor, getAppName, getBundleIdentifier, formatTime, getSize } from '../../helpers';
13
- import { WhiteBackNavigation, TrashIcon, SettingsIcon, CloseWhite, ChevronIcon, ClockIcon, } from '../NetworkIcons';
13
+ import { WhiteBackNavigation, TrashIcon, SettingsIcon, CloseWhite, ChevronIcon, ClockIcon, AppleIcon, AndroidIcon, PackageBoxIcon, } from '../NetworkIcons';
14
14
  const InspectorHeader = React.memo(() => {
15
15
  const { modalHeightPercent, appIcon, selected, setSelected, selectedEvent, setSelectedEvent, selectedLog, setSelectedLog, selectedReduxSlice, setSelectedReduxSlice, selectedReduxAction, setSelectedReduxAction, reduxState, reduxLastActionMap, showHeaderInfo, setShowHeaderInfo, updateAvailable, latestNpmVersion, clearAnim, activePulseAnim, unreadPulseAnim, runClearAllWithAnimation, setSettingsPage, closeModal, detailTitle, activeTab, environment, } = useInspector();
16
16
  const envConfig = useMemo(() => {
@@ -194,10 +194,14 @@ const InspectorHeader = React.memo(() => {
194
194
  borderColor: `${AppColors.white}2E`,
195
195
  }}>
196
196
  <View style={{
197
- paddingHorizontal: 5,
198
- paddingVertical: 2,
197
+ flexDirection: 'row',
198
+ alignItems: 'center',
199
+ gap: 3.5,
200
+ paddingHorizontal: 6,
201
+ paddingVertical: 2.5,
199
202
  backgroundColor: `${AppColors.white}47`,
200
203
  }}>
204
+ {Platform.OS === 'ios' ? (<AppleIcon color={AppColors.white} size={10}/>) : (<AndroidIcon color={AppColors.white} size={10}/>)}
201
205
  <Text style={{
202
206
  fontFamily: AppFonts.interBold,
203
207
  fontSize: 9,
@@ -209,7 +213,7 @@ const InspectorHeader = React.memo(() => {
209
213
  </View>
210
214
  <View style={{
211
215
  paddingHorizontal: 5,
212
- paddingVertical: 2,
216
+ paddingVertical: 2.5,
213
217
  backgroundColor: `${AppColors.white}1F`,
214
218
  }}>
215
219
  <Text style={{
@@ -230,10 +234,14 @@ const InspectorHeader = React.memo(() => {
230
234
  borderColor: `${AppColors.white}2E`,
231
235
  }}>
232
236
  <View style={{
233
- paddingHorizontal: 5,
234
- paddingVertical: 2,
237
+ flexDirection: 'row',
238
+ alignItems: 'center',
239
+ gap: 3.5,
240
+ paddingHorizontal: 6,
241
+ paddingVertical: 2.5,
235
242
  backgroundColor: `${AppColors.white}47`,
236
243
  }}>
244
+ <PackageBoxIcon color={AppColors.white} size={10}/>
237
245
  <Text style={{
238
246
  fontFamily: AppFonts.interBold,
239
247
  fontSize: 9,
@@ -35,6 +35,7 @@ const LogDetail = React.memo(() => {
35
35
  const jsonContent = useMemo(() => (selectedLog ? getJsonContent(selectedLog.message) : null), [selectedLog]);
36
36
  const [stackViewMode, setStackViewMode] = useState('structured');
37
37
  const [stackSource, setStackSource] = useState('caller');
38
+ const [stackFilter, setStackFilter] = useState('app');
38
39
  const activeStackString = useMemo(() => {
39
40
  if (!selectedLog)
40
41
  return '';
@@ -43,21 +44,6 @@ const LogDetail = React.memo(() => {
43
44
  }
44
45
  return selectedLog.stack || selectedLog.caller || '';
45
46
  }, [selectedLog, stackSource]);
46
- const originFrame = useMemo(() => {
47
- if (!selectedLog)
48
- return null;
49
- if (selectedLog.caller && selectedLog.caller !== 'Unknown') {
50
- return parseStackLine(selectedLog.caller, true);
51
- }
52
- if (selectedLog.stack) {
53
- const firstLine = selectedLog.stack
54
- .split('\n')
55
- .find(l => l.trim().length > 0);
56
- if (firstLine)
57
- return parseStackLine(firstLine, true);
58
- }
59
- return null;
60
- }, [selectedLog]);
61
47
  const parsedStackFrames = useMemo(() => {
62
48
  if (!activeStackString)
63
49
  return [];
@@ -67,6 +53,29 @@ const LogDetail = React.memo(() => {
67
53
  .filter(l => l.length > 0)
68
54
  .map((line, idx) => parseStackLine(line, idx === 0));
69
55
  }, [activeStackString]);
56
+ const appStackFrames = useMemo(() => {
57
+ return parsedStackFrames.filter(f => f.frameType === 'app' || !f.isRuntimeNoise);
58
+ }, [parsedStackFrames]);
59
+ const displayedFrames = useMemo(() => {
60
+ if (stackFilter === 'app' && appStackFrames.length > 0) {
61
+ return appStackFrames;
62
+ }
63
+ return parsedStackFrames;
64
+ }, [stackFilter, appStackFrames, parsedStackFrames]);
65
+ const originFrame = useMemo(() => {
66
+ if (!selectedLog)
67
+ return null;
68
+ if (appStackFrames.length > 0) {
69
+ return appStackFrames[0];
70
+ }
71
+ if (selectedLog.caller && selectedLog.caller !== 'Unknown') {
72
+ return parseStackLine(selectedLog.caller, true);
73
+ }
74
+ if (parsedStackFrames.length > 0) {
75
+ return parsedStackFrames[0];
76
+ }
77
+ return null;
78
+ }, [selectedLog, appStackFrames, parsedStackFrames]);
70
79
  if (!selectedLog)
71
80
  return null;
72
81
  const isDark = AppColors.primaryLight !== AppColors.white;
@@ -100,14 +109,14 @@ const LogDetail = React.memo(() => {
100
109
  const subTabs = [
101
110
  {
102
111
  key: 'output',
103
- label: 'Output',
112
+ label: t('console.tabOutput'),
104
113
  icon: (isActive) => (<TerminalIcon color={isActive ? AppColors.white : AppColors.grayTextWeak} size={12}/>),
105
114
  },
106
115
  ...(hasMultipleArgs
107
116
  ? [
108
117
  {
109
118
  key: 'arguments',
110
- label: `Args (${selectedLog.rawArgs?.length})`,
119
+ label: t('console.tabArgs', { count: selectedLog.rawArgs?.length }),
111
120
  icon: (isActive) => (<RequestIcon color={isActive ? AppColors.white : AppColors.grayTextWeak} size={12}/>),
112
121
  },
113
122
  ]
@@ -116,14 +125,14 @@ const LogDetail = React.memo(() => {
116
125
  ? [
117
126
  {
118
127
  key: 'stack',
119
- label: 'Stack Trace',
128
+ label: t('console.tabStack'),
120
129
  icon: (isActive) => (<LayersIcon color={isActive ? AppColors.white : AppColors.grayTextWeak} size={12}/>),
121
130
  },
122
131
  ]
123
132
  : []),
124
133
  {
125
134
  key: 'metadata',
126
- label: 'Metadata',
135
+ label: t('console.tabMetadata'),
127
136
  icon: (isActive) => (<InfoCircleIcon color={isActive ? AppColors.white : AppColors.grayTextWeak} size={12}/>),
128
137
  },
129
138
  ];
@@ -575,10 +584,12 @@ const LogDetail = React.memo(() => {
575
584
  ? AppColors.primaryLight
576
585
  : AppColors.grayBackground,
577
586
  }}>
587
+ {/* Header Top Row: Title, Mode switcher & Full copy */}
578
588
  <View style={{
579
589
  flexDirection: 'row',
580
590
  alignItems: 'center',
581
591
  justifyContent: 'space-between',
592
+ gap: 8,
582
593
  }}>
583
594
  <Text style={{
584
595
  fontFamily: AppFonts.interBold,
@@ -587,156 +598,257 @@ const LogDetail = React.memo(() => {
587
598
  textTransform: 'uppercase',
588
599
  letterSpacing: 0.4,
589
600
  }}>
590
- {stackSource === 'error' ? 'Error Exception Stack' : 'Call Stack'} ({parsedStackFrames.length} frames)
601
+ {stackSource === 'error'
602
+ ? t('console.errorStack', { count: parsedStackFrames.length })
603
+ : t('console.callStack', { count: parsedStackFrames.length })}
591
604
  </Text>
592
- <CopyButton value={activeStackString} label="Full Stack Trace"/>
605
+
606
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
607
+ <View style={{
608
+ flexDirection: 'row',
609
+ backgroundColor: isDark ? AppColors.primaryLight : AppColors.white,
610
+ borderRadius: 7,
611
+ padding: 2,
612
+ borderWidth: 1,
613
+ borderColor: AppColors.dividerColor,
614
+ }}>
615
+ <Pressable onPress={() => setStackViewMode('structured')} style={{
616
+ flexDirection: 'row',
617
+ alignItems: 'center',
618
+ gap: 3,
619
+ paddingVertical: 3.5,
620
+ paddingHorizontal: 7,
621
+ borderRadius: 5,
622
+ backgroundColor: stackViewMode === 'structured'
623
+ ? AppColors.brandPurple
624
+ : 'transparent',
625
+ }}>
626
+ <LayersIcon color={stackViewMode === 'structured'
627
+ ? AppColors.white
628
+ : AppColors.grayTextWeak} size={11}/>
629
+ <Text style={{
630
+ fontFamily: AppFonts.interBold,
631
+ fontSize: 10,
632
+ color: stackViewMode === 'structured'
633
+ ? AppColors.white
634
+ : AppColors.grayTextStrong,
635
+ }}>
636
+ {t('console.cardsView')}
637
+ </Text>
638
+ </Pressable>
639
+
640
+ <Pressable onPress={() => setStackViewMode('raw')} style={{
641
+ flexDirection: 'row',
642
+ alignItems: 'center',
643
+ gap: 3,
644
+ paddingVertical: 3.5,
645
+ paddingHorizontal: 7,
646
+ borderRadius: 5,
647
+ backgroundColor: stackViewMode === 'raw'
648
+ ? AppColors.brandPurple
649
+ : 'transparent',
650
+ }}>
651
+ <RawIcon color={stackViewMode === 'raw'
652
+ ? AppColors.white
653
+ : AppColors.grayTextWeak} size={11}/>
654
+ <Text style={{
655
+ fontFamily: AppFonts.interBold,
656
+ fontSize: 10,
657
+ color: stackViewMode === 'raw'
658
+ ? AppColors.white
659
+ : AppColors.grayTextStrong,
660
+ }}>
661
+ {t('console.rawTraceView')}
662
+ </Text>
663
+ </Pressable>
664
+ </View>
665
+
666
+ <CopyButton value={activeStackString} label={t('console.fullStackTrace')}/>
667
+ </View>
593
668
  </View>
594
669
 
595
670
  {/* Source Switcher if Error Stack exists */}
596
671
  {selectedLog.errorStack && (<SegmentedTabs tabs={[
597
672
  {
598
673
  key: 'caller',
599
- label: 'Call Origin Stack',
674
+ label: t('console.callOriginStack'),
600
675
  },
601
676
  {
602
677
  key: 'error',
603
- label: 'Error Thrown Stack',
678
+ label: t('console.errorThrownStack'),
604
679
  },
605
680
  ]} activeKey={stackSource} onChange={key => setStackSource(key)}/>)}
606
681
 
607
- {/* Structured vs Raw View Mode Tabs */}
682
+ {/* Scope Filter Tabs (Full Width) */}
608
683
  <SegmentedTabs tabs={[
609
684
  {
610
- key: 'structured',
611
- label: 'Structured Cards',
612
- icon: (isActive) => (<LayersIcon color={isActive ? AppColors.white : AppColors.grayTextWeak} size={12}/>),
685
+ key: 'app',
686
+ label: t('console.appCodeScope', { count: appStackFrames.length }),
613
687
  },
614
688
  {
615
- key: 'raw',
616
- label: 'Raw Trace',
617
- icon: (isActive) => (<RawIcon color={isActive ? AppColors.white : AppColors.grayTextWeak} size={12}/>),
689
+ key: 'all',
690
+ label: t('console.allFramesScope', { count: parsedStackFrames.length }),
618
691
  },
619
- ]} activeKey={stackViewMode} onChange={key => setStackViewMode(key)}/>
692
+ ]} activeKey={stackFilter} onChange={key => setStackFilter(key)}/>
620
693
  </View>
621
694
 
622
695
  {/* Stack Content: Structured Cards or Raw Full Trace */}
623
696
  {stackViewMode === 'structured' ? (<View style={{ padding: 12, gap: 10 }}>
624
- {parsedStackFrames.length > 0 ? (parsedStackFrames.map((frame, frameIdx) => {
697
+ {displayedFrames.length > 0 ? (displayedFrames.map((frame, frameIdx) => {
625
698
  const isTopFrame = frameIdx === 0;
699
+ const frameBadgeBg = frame.frameType === 'app'
700
+ ? `${AppColors.brandPurple}15`
701
+ : frame.frameType === 'dependency'
702
+ ? `${AppColors.amber500}18`
703
+ : frame.frameType === 'native'
704
+ ? `${AppColors.sky500}18`
705
+ : AppColors.grayBorderSecondary;
706
+ const frameBadgeText = frame.frameType === 'app'
707
+ ? AppColors.brandPurple
708
+ : frame.frameType === 'dependency'
709
+ ? AppColors.amber700
710
+ : frame.frameType === 'native'
711
+ ? AppColors.sky500
712
+ : AppColors.grayTextWeak;
626
713
  return (<View key={frameIdx} style={{
627
714
  backgroundColor: isTopFrame
628
715
  ? `${AppColors.brandPurple}0A`
629
716
  : AppColors.primaryLight,
630
- borderRadius: 8,
717
+ borderRadius: 10,
631
718
  borderWidth: 1,
632
719
  borderColor: isTopFrame
633
- ? `${AppColors.brandPurple}30`
720
+ ? `${AppColors.brandPurple}35`
634
721
  : AppColors.dividerColor,
635
- padding: 10,
636
- gap: 5,
722
+ padding: 11,
723
+ gap: 6,
637
724
  }}>
638
- {/* Frame Header Row */}
725
+ {/* Frame Top Row: Origin / Category, File, Line badge, Copy */}
639
726
  <View style={{
640
727
  flexDirection: 'row',
641
728
  alignItems: 'center',
642
729
  justifyContent: 'space-between',
643
730
  gap: 6,
644
731
  }}>
645
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flex: 1 }}>
646
- <View style={{
647
- backgroundColor: isTopFrame
648
- ? AppColors.brandPurple
649
- : AppColors.grayBorderSecondary,
650
- borderRadius: 4,
651
- paddingHorizontal: 5,
652
- paddingVertical: 1.5,
653
- }}>
654
- <Text style={{
655
- fontFamily: AppFonts.interBold,
656
- fontSize: 9,
657
- color: isTopFrame
658
- ? AppColors.white
659
- : AppColors.grayTextStrong,
660
- }}>
661
- #{frameIdx + 1}
662
- {isTopFrame ? ' ORIGIN' : ''}
663
- </Text>
664
- </View>
665
-
666
- <HighlightText text={frame.functionName !== '<anonymous>' ? `${frame.functionName}()` : '<anonymous>'} search={detailSearch} style={{
667
- fontFamily: AppFonts.interBold,
668
- fontSize: 12,
669
- color: isTopFrame
670
- ? AppColors.primaryBlack
671
- : AppColors.grayTextStrong,
672
- }} highlightStyle={{
673
- backgroundColor: AppColors.yellowHighlight,
674
- color: AppColors.primaryBlack,
675
- }}/>
676
- </View>
677
-
678
- {frame.lineNumber && (<View style={{
679
- backgroundColor: `${AppColors.teal600}15`,
680
- borderColor: `${AppColors.teal600}30`,
681
- borderWidth: 1,
732
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 5, flex: 1, flexWrap: 'wrap' }}>
733
+ {isTopFrame && (<View style={{
734
+ backgroundColor: AppColors.brandPurple,
682
735
  borderRadius: 4,
683
- paddingHorizontal: 6,
736
+ paddingHorizontal: 5,
684
737
  paddingVertical: 1.5,
685
738
  }}>
686
- <Text style={{
739
+ <Text style={{
687
740
  fontFamily: AppFonts.interBold,
688
- fontSize: 9.5,
689
- color: AppColors.teal600,
741
+ fontSize: 8.5,
742
+ color: AppColors.white,
690
743
  }}>
691
- L{frame.lineNumber}
692
- {frame.columnNumber ? `:C${frame.columnNumber}` : ''}
693
- </Text>
694
- </View>)}
695
- </View>
744
+ {t('console.originBadge')}
745
+ </Text>
746
+ </View>)}
696
747
 
697
- {/* File Path Row */}
698
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
699
- {frame.fileExt && frame.fileExt !== 'other' && (<View style={{
748
+ {frame.fileExt && frame.fileExt !== 'other' && (<View style={{
700
749
  backgroundColor: frame.fileExt === 'tsx' || frame.fileExt === 'ts'
701
750
  ? `${AppColors.brandPurple}18`
702
751
  : `${AppColors.teal600}18`,
703
- borderRadius: 3,
704
- paddingHorizontal: 4,
705
- paddingVertical: 1,
752
+ borderRadius: 4,
753
+ paddingHorizontal: 4.5,
754
+ paddingVertical: 1.5,
706
755
  }}>
707
- <Text style={{
756
+ <Text style={{
708
757
  fontFamily: AppFonts.interBold,
709
758
  fontSize: 8.5,
710
759
  color: frame.fileExt === 'tsx' || frame.fileExt === 'ts'
711
760
  ? AppColors.brandPurple
712
761
  : AppColors.teal600,
713
762
  }}>
714
- {frame.fileExt.toUpperCase()}
715
- </Text>
716
- </View>)}
717
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 3 }}>
718
- <DocIcon color={isTopFrame ? AppColors.brandPurple : AppColors.grayTextWeak} size={10}/>
719
- <Text style={{
720
- fontFamily: AppFonts.interMedium,
721
- fontSize: 10.5,
763
+ {frame.fileExt.toUpperCase()}
764
+ </Text>
765
+ </View>)}
766
+
767
+ <Text style={{
768
+ fontFamily: AppFonts.interBold,
769
+ fontSize: 11.5,
722
770
  color: isTopFrame
723
771
  ? AppColors.brandPurple
724
- : AppColors.grayTextWeak,
772
+ : AppColors.primaryBlack,
725
773
  }}>
726
- {frame.fileName}
727
- </Text>
774
+ {frame.fileName}
775
+ </Text>
776
+ </View>
777
+
778
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
779
+ {frame.lineNumber && (<View style={{
780
+ backgroundColor: `${AppColors.teal600}15`,
781
+ borderColor: `${AppColors.teal600}30`,
782
+ borderWidth: 1,
783
+ borderRadius: 4,
784
+ paddingHorizontal: 5,
785
+ paddingVertical: 1.5,
786
+ }}>
787
+ <Text style={{
788
+ fontFamily: AppFonts.interBold,
789
+ fontSize: 9.5,
790
+ color: AppColors.teal600,
791
+ }}>
792
+ L{frame.lineNumber}{frame.columnNumber ? `:C${frame.columnNumber}` : ''}
793
+ </Text>
794
+ </View>)}
795
+ <CopyButton value={frame.copyableLocation} label={frame.fileName}/>
796
+ </View>
728
797
  </View>
798
+
799
+ {/* Middle Row: Function / Symbol Invocation */}
800
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
801
+ <HighlightText text={frame.functionName !== '<anonymous>' ? `${frame.functionName}()` : '<anonymous>'} search={detailSearch} style={{
802
+ fontFamily: AppFonts.interBold,
803
+ fontSize: 12.5,
804
+ color: AppColors.primaryBlack,
805
+ }} highlightStyle={{
806
+ backgroundColor: AppColors.yellowHighlight,
807
+ color: AppColors.primaryBlack,
808
+ }}/>
729
809
  </View>
730
810
 
731
- {/* Full path */}
732
- {frame.fullPath && frame.fullPath !== frame.fileName && (<Text selectable style={{
733
- fontFamily: AppFonts.interRegular,
734
- fontSize: 9.5,
735
- color: AppColors.grayTextWeak,
736
- lineHeight: 14,
737
- }} numberOfLines={2}>
738
- {frame.fullPath}
739
- </Text>)}
811
+ {/* Bottom Row: Clean Relative Path & Execution Scope Tag */}
812
+ <View style={{
813
+ flexDirection: 'row',
814
+ alignItems: 'center',
815
+ justifyContent: 'space-between',
816
+ backgroundColor: isDark ? `${AppColors.primaryBlack}25` : AppColors.grayBackground,
817
+ borderRadius: 6,
818
+ paddingHorizontal: 8,
819
+ paddingVertical: 4.5,
820
+ gap: 6,
821
+ }}>
822
+ <Text selectable style={{
823
+ fontFamily: AppFonts.interRegular,
824
+ fontSize: 9.5,
825
+ color: AppColors.grayTextStrong,
826
+ flex: 1,
827
+ }} numberOfLines={1}>
828
+ 📂 {frame.fullPath}
829
+ </Text>
830
+
831
+ <View style={{
832
+ backgroundColor: frameBadgeBg,
833
+ borderRadius: 4,
834
+ paddingHorizontal: 5,
835
+ paddingVertical: 1.5,
836
+ }}>
837
+ <Text style={{
838
+ fontFamily: AppFonts.interBold,
839
+ fontSize: 8.5,
840
+ color: frameBadgeText,
841
+ }}>
842
+ {frame.frameType === 'app'
843
+ ? t('console.appCodeBadge')
844
+ : frame.frameType === 'dependency'
845
+ ? t('console.dependencyBadge')
846
+ : frame.frameType === 'native'
847
+ ? t('console.nativeBadge')
848
+ : t('console.hermesVmBadge')}
849
+ </Text>
850
+ </View>
851
+ </View>
740
852
  </View>);
741
853
  })) : (<Text style={{
742
854
  fontFamily: AppFonts.interRegular,
@@ -745,7 +857,7 @@ const LogDetail = React.memo(() => {
745
857
  textAlign: 'center',
746
858
  paddingVertical: 16,
747
859
  }}>
748
- No call stack trace available for this log.
860
+ {t('console.noStackAvailable')}
749
861
  </Text>)}
750
862
  </View>) : (
751
863
  /* Raw Full Trace Monospace Block */
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import { ActivityIndicator, Modal, Pressable, StatusBar, StyleSheet, Text, View, } from 'react-native';
2
+ import { ActivityIndicator, Modal, Platform, Pressable, StatusBar, StyleSheet, Text, View, } from 'react-native';
3
3
  import { useInspector } from './InspectorContext';
4
4
  import ErrorBoundary from '../ErrorBoundary';
5
5
  import FabLauncher from './FabLauncher';
@@ -37,7 +37,7 @@ const MainScreen = () => {
37
37
  (activeTab === 'logs' && selectedLog != null) ||
38
38
  (activeTab === 'redux' && (selectedReduxSlice != null || selectedReduxAction != null));
39
39
  return (<>
40
- {isEnabled && <FabLauncher />}
40
+ {(Platform.OS === 'ios' || Platform.OS === 'android') && isEnabled && <FabLauncher />}
41
41
  <Modal visible={visible} animationType={modalAnimationType} transparent>
42
42
  {visible && (<ErrorBoundary onClose={closeModal}>
43
43
  <View style={styles.modalBackdrop}>