react-native-debug-toolkit 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/android/src/main/java/com/debugtoolkit/BuildTypeModule.java +3 -27
- package/android/src/main/java/com/reactnative/debuglibs/RNDebugLibsModule.java +65 -0
- package/android/src/main/java/com/reactnative/debuglibs/RNDebugLibsPackage.java +28 -0
- package/index.js +6 -1
- package/ios/BuildTypeModule.h +5 -0
- package/ios/BuildTypeModule.m +6 -35
- package/ios/RNDebugLibs.h +10 -0
- package/ios/RNDebugLibs.m +79 -0
- package/lib/NativeDebugLibs.js +43 -0
- package/lib/features/NavigationLogFeature.js +47 -0
- package/lib/features/ThirdPartyLibsFeature.js +65 -0
- package/lib/hooks/useNavigationLogger.js +92 -0
- package/lib/index.js +16 -2
- package/lib/navigation/NavigationLogger.js +1 -0
- package/lib/utils/DebugConst.js +23 -5
- package/lib/utils/StorageUtils.js +1 -0
- package/lib/views/FloatPanelView.js +127 -10
- package/lib/views/NavigationLogDetails.js +294 -0
- package/lib/views/SubViewNavigationLogs.js +199 -0
- package/lib/views/SubViewThirdPartyLibs.js +239 -0
- package/package.json +2 -1
- package/react-native-debug-toolkit.podspec +25 -0
- package/react-native.config.js +10 -0
package/lib/utils/DebugConst.js
CHANGED
|
@@ -19,15 +19,15 @@ export const DebugColors = {
|
|
|
19
19
|
export const getLogLevelColor = (level) => {
|
|
20
20
|
switch (level?.toLowerCase()) {
|
|
21
21
|
case 'log':
|
|
22
|
-
return '#333';
|
|
22
|
+
return '#333'; // Dark gray for standard log
|
|
23
23
|
case 'info':
|
|
24
|
-
return '#0D96F2';
|
|
24
|
+
return '#0D96F2'; // Blue for info
|
|
25
25
|
case 'warn':
|
|
26
|
-
return '#FCA130';
|
|
26
|
+
return '#FCA130'; // Orange for warning
|
|
27
27
|
case 'error':
|
|
28
|
-
return '#F93E3E';
|
|
28
|
+
return '#F93E3E'; // Red for error
|
|
29
29
|
default:
|
|
30
|
-
return '#666666';
|
|
30
|
+
return '#666666'; // Default gray
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
33
|
export const DebugImgs = {
|
|
@@ -47,3 +47,21 @@ export const getZustandActionColor = (action) => {
|
|
|
47
47
|
|
|
48
48
|
return '#0D96F2'; // Default blue for other actions
|
|
49
49
|
};
|
|
50
|
+
|
|
51
|
+
// Navigation action colors
|
|
52
|
+
export const getNavigationActionColor = (action) => {
|
|
53
|
+
switch (action?.toLowerCase()) {
|
|
54
|
+
case 'push':
|
|
55
|
+
return '#0D96F2'; // Blue for push
|
|
56
|
+
case 'pop':
|
|
57
|
+
return '#6E7072'; // Gray for pop
|
|
58
|
+
case 'replace':
|
|
59
|
+
return '#4CAF50'; // Green for replace
|
|
60
|
+
case 'reset':
|
|
61
|
+
return '#FCA130'; // Orange for reset
|
|
62
|
+
case 'goback':
|
|
63
|
+
return '#9C27B0'; // Purple for go back
|
|
64
|
+
default:
|
|
65
|
+
return '#0D96F2'; // Default blue for navigation
|
|
66
|
+
}
|
|
67
|
+
};
|
|
@@ -8,15 +8,19 @@ import {
|
|
|
8
8
|
Dimensions,
|
|
9
9
|
Pressable,
|
|
10
10
|
SafeAreaView,
|
|
11
|
+
ScrollView,
|
|
12
|
+
TouchableOpacity,
|
|
13
|
+
Modal,
|
|
14
|
+
FlatList,
|
|
11
15
|
} from 'react-native'
|
|
12
16
|
// import AsyncStorage from '@react-native-async-storage/async-storage'
|
|
13
17
|
import { IconRadius, DebugColors } from '../utils/DebugConst'
|
|
14
18
|
import SubViewHTTPLogs from './SubViewHTTPLogs'
|
|
15
19
|
import SubViewPerformance from './SubViewPerformance'
|
|
16
|
-
import TabView from './TabView'
|
|
17
20
|
import SubViewConsoleLogs from './SubViewConsoleLogs'
|
|
18
21
|
import SubViewZustandLogs from './SubViewZustandLogs'
|
|
19
|
-
|
|
22
|
+
import SubViewNavigationLogs from './SubViewNavigationLogs'
|
|
23
|
+
import SubViewThirdPartyLibs from './SubViewThirdPartyLibs'
|
|
20
24
|
const { width: screenWidth, height: screenHeight } = Dimensions.get('window')
|
|
21
25
|
|
|
22
26
|
export default class FloatPanelView extends Component {
|
|
@@ -38,8 +42,12 @@ export default class FloatPanelView extends Component {
|
|
|
38
42
|
activeTab: 0,
|
|
39
43
|
panelTranslateY: new Animated.Value(screenHeight),
|
|
40
44
|
backdropOpacity: new Animated.Value(0),
|
|
45
|
+
isTabMenuVisible: false,
|
|
46
|
+
panelLayout: { width: 0, height: 0 },
|
|
41
47
|
}
|
|
42
48
|
|
|
49
|
+
this.tabScrollViewRef = React.createRef()
|
|
50
|
+
|
|
43
51
|
this.gestureResponder = PanResponder.create({
|
|
44
52
|
onStartShouldSetPanResponder: () => true,
|
|
45
53
|
onMoveShouldSetPanResponder: () => true,
|
|
@@ -197,6 +205,8 @@ export default class FloatPanelView extends Component {
|
|
|
197
205
|
}
|
|
198
206
|
|
|
199
207
|
_closePanel = () => {
|
|
208
|
+
this.setState({ isTabMenuVisible: false })
|
|
209
|
+
|
|
200
210
|
Animated.parallel([
|
|
201
211
|
Animated.spring(this.state.panelTranslateY, {
|
|
202
212
|
toValue: screenHeight,
|
|
@@ -214,6 +224,26 @@ export default class FloatPanelView extends Component {
|
|
|
214
224
|
})
|
|
215
225
|
}
|
|
216
226
|
|
|
227
|
+
_toggleTabMenu = () => {
|
|
228
|
+
this.setState(prevState => ({ isTabMenuVisible: !prevState.isTabMenuVisible }))
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
_handleTabChange = (index) => {
|
|
232
|
+
this.setState({
|
|
233
|
+
activeTab: index,
|
|
234
|
+
isTabMenuVisible: false
|
|
235
|
+
}, () => {
|
|
236
|
+
// Scroll to make active tab visible
|
|
237
|
+
if (this.tabScrollViewRef.current) {
|
|
238
|
+
const tabWidth = 80; // Approximate tab width
|
|
239
|
+
this.tabScrollViewRef.current.scrollTo({
|
|
240
|
+
x: Math.max(0, index * tabWidth - 50), // Center the tab
|
|
241
|
+
animated: true
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
217
247
|
renderFloatBtn() {
|
|
218
248
|
const { isOpen, toFloat } = this.state
|
|
219
249
|
|
|
@@ -287,6 +317,14 @@ export default class FloatPanelView extends Component {
|
|
|
287
317
|
return <SubViewZustandLogs logs={data} />
|
|
288
318
|
}
|
|
289
319
|
|
|
320
|
+
if (feature.name === 'navigation') {
|
|
321
|
+
return <SubViewNavigationLogs logs={data} />
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (feature.name === 'thirdPartyLibs') {
|
|
325
|
+
return <SubViewThirdPartyLibs libraries={data} />
|
|
326
|
+
}
|
|
327
|
+
|
|
290
328
|
// Generic fallback for other feature types
|
|
291
329
|
return (
|
|
292
330
|
<View style={styles.genericContent}>
|
|
@@ -309,9 +347,46 @@ export default class FloatPanelView extends Component {
|
|
|
309
347
|
return this.renderFeatureContent(tabs[activeTab].id)
|
|
310
348
|
}
|
|
311
349
|
|
|
350
|
+
renderTabBar() {
|
|
351
|
+
const tabs = this.getFeatureTabs()
|
|
352
|
+
const { activeTab } = this.state
|
|
353
|
+
|
|
354
|
+
if (!tabs.length) return null
|
|
355
|
+
|
|
356
|
+
return (
|
|
357
|
+
<View style={styles.tabBarContainer}>
|
|
358
|
+
<View style={styles.tabsRow}>
|
|
359
|
+
<ScrollView
|
|
360
|
+
ref={this.tabScrollViewRef}
|
|
361
|
+
horizontal
|
|
362
|
+
showsHorizontalScrollIndicator={false}
|
|
363
|
+
contentContainerStyle={styles.tabsScrollContainer}>
|
|
364
|
+
{tabs.map((tab, index) => {
|
|
365
|
+
const isActive = index === activeTab
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
<TouchableOpacity
|
|
369
|
+
key={`tab-${tab.id}`}
|
|
370
|
+
style={[styles.tab, isActive && styles.activeTab]}
|
|
371
|
+
onPress={() => this._handleTabChange(index)}>
|
|
372
|
+
<Text
|
|
373
|
+
style={[styles.tabText, isActive && styles.activeTabText]}
|
|
374
|
+
numberOfLines={1}
|
|
375
|
+
ellipsizeMode="tail">
|
|
376
|
+
{tab.label}
|
|
377
|
+
</Text>
|
|
378
|
+
</TouchableOpacity>
|
|
379
|
+
)
|
|
380
|
+
})}
|
|
381
|
+
</ScrollView>
|
|
382
|
+
|
|
383
|
+
</View>
|
|
384
|
+
</View>
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
|
|
312
388
|
renderPanel() {
|
|
313
389
|
const { isOpen, panelTranslateY, backdropOpacity } = this.state
|
|
314
|
-
const tabs = this.getFeatureTabs()
|
|
315
390
|
|
|
316
391
|
if (!isOpen) {
|
|
317
392
|
return null
|
|
@@ -329,7 +404,11 @@ export default class FloatPanelView extends Component {
|
|
|
329
404
|
style={[
|
|
330
405
|
styles.panel,
|
|
331
406
|
{ transform: [{ translateY: panelTranslateY }] },
|
|
332
|
-
]}
|
|
407
|
+
]}
|
|
408
|
+
onLayout={(e) => {
|
|
409
|
+
const { width, height } = e.nativeEvent.layout
|
|
410
|
+
this.setState({ panelLayout: { width, height } })
|
|
411
|
+
}}>
|
|
333
412
|
<View {...this.panelResponder.panHandlers} style={styles.dragHandle}>
|
|
334
413
|
<View style={styles.dragIndicator} />
|
|
335
414
|
</View>
|
|
@@ -340,12 +419,12 @@ export default class FloatPanelView extends Component {
|
|
|
340
419
|
<Text style={styles.closeButtonText}>×</Text>
|
|
341
420
|
</Pressable>
|
|
342
421
|
</View>
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
422
|
+
|
|
423
|
+
{this.renderTabBar()}
|
|
424
|
+
|
|
425
|
+
<View style={styles.contentContainer}>
|
|
347
426
|
{this.renderContent()}
|
|
348
|
-
</
|
|
427
|
+
</View>
|
|
349
428
|
</SafeAreaView>
|
|
350
429
|
</Animated.View>
|
|
351
430
|
</View>
|
|
@@ -445,6 +524,9 @@ const styles = StyleSheet.create({
|
|
|
445
524
|
panelContent: {
|
|
446
525
|
flex: 1,
|
|
447
526
|
},
|
|
527
|
+
contentContainer: {
|
|
528
|
+
flex: 1,
|
|
529
|
+
},
|
|
448
530
|
header: {
|
|
449
531
|
flexDirection: 'row',
|
|
450
532
|
justifyContent: 'space-between',
|
|
@@ -467,6 +549,41 @@ const styles = StyleSheet.create({
|
|
|
467
549
|
color: DebugColors.text,
|
|
468
550
|
fontWeight: 'bold',
|
|
469
551
|
},
|
|
552
|
+
tabBarContainer: {
|
|
553
|
+
borderBottomWidth: 1,
|
|
554
|
+
borderBottomColor: DebugColors.border,
|
|
555
|
+
backgroundColor: DebugColors.background,
|
|
556
|
+
},
|
|
557
|
+
tabsRow: {
|
|
558
|
+
flexDirection: 'row',
|
|
559
|
+
alignItems: 'center',
|
|
560
|
+
},
|
|
561
|
+
tabsScrollContainer: {
|
|
562
|
+
paddingHorizontal: 15,
|
|
563
|
+
flexDirection: 'row',
|
|
564
|
+
alignItems: 'center',
|
|
565
|
+
},
|
|
566
|
+
tab: {
|
|
567
|
+
paddingVertical: 12,
|
|
568
|
+
paddingHorizontal: 8,
|
|
569
|
+
marginRight: 4,
|
|
570
|
+
alignItems: 'center',
|
|
571
|
+
justifyContent: 'center',
|
|
572
|
+
minWidth: 50,
|
|
573
|
+
},
|
|
574
|
+
activeTab: {
|
|
575
|
+
borderBottomWidth: 2,
|
|
576
|
+
borderBottomColor: DebugColors.blue,
|
|
577
|
+
},
|
|
578
|
+
tabText: {
|
|
579
|
+
fontSize: 13,
|
|
580
|
+
color: DebugColors.textLight,
|
|
581
|
+
textAlign: 'center',
|
|
582
|
+
},
|
|
583
|
+
activeTabText: {
|
|
584
|
+
color: DebugColors.blue,
|
|
585
|
+
fontWeight: '600',
|
|
586
|
+
},
|
|
470
587
|
emptyText: {
|
|
471
588
|
padding: 20,
|
|
472
589
|
textAlign: 'center',
|
|
@@ -501,5 +618,5 @@ const styles = StyleSheet.create({
|
|
|
501
618
|
fontFamily: 'monospace',
|
|
502
619
|
fontSize: 12,
|
|
503
620
|
color: DebugColors.text,
|
|
504
|
-
}
|
|
621
|
+
}
|
|
505
622
|
})
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, Text, StyleSheet, Clipboard } from 'react-native'
|
|
3
|
+
import { ScrollView, Pressable } from 'react-native'
|
|
4
|
+
import JSONTree from 'react-native-json-tree'
|
|
5
|
+
import { getNavigationActionColor } from '../utils/DebugConst'
|
|
6
|
+
|
|
7
|
+
// Re-using the theme from ConsoleLogDetails for consistency
|
|
8
|
+
const theme = {
|
|
9
|
+
scheme: 'monokai',
|
|
10
|
+
author: 'wimer hazenberg (http://www.monokai.nl)',
|
|
11
|
+
base00: '#272822',
|
|
12
|
+
base01: '#383830',
|
|
13
|
+
base02: '#49483e',
|
|
14
|
+
base03: '#75715e',
|
|
15
|
+
base04: '#a59f85',
|
|
16
|
+
base05: '#f8f8f2',
|
|
17
|
+
base06: '#f5f4f1',
|
|
18
|
+
base07: '#f9f8f5',
|
|
19
|
+
base08: '#f92672',
|
|
20
|
+
base09: '#fd971f',
|
|
21
|
+
base0A: '#f4bf75',
|
|
22
|
+
base0B: '#a6e22e',
|
|
23
|
+
base0C: '#a1efe4',
|
|
24
|
+
base0D: '#66d9ef',
|
|
25
|
+
base0E: '#ae81ff',
|
|
26
|
+
base0F: '#cc6633'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const CopyButton = ({ text, style }) => {
|
|
30
|
+
const [copied, setCopied] = React.useState(false)
|
|
31
|
+
|
|
32
|
+
const handleCopy = async () => {
|
|
33
|
+
await Clipboard.setString(text)
|
|
34
|
+
setCopied(true)
|
|
35
|
+
setTimeout(() => setCopied(false), 2000)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Pressable style={[styles.copyButton, style]} onPress={handleCopy}>
|
|
40
|
+
<Text style={styles.copyButtonText}>{copied ? 'Copied!' : 'Copy'}</Text>
|
|
41
|
+
</Pressable>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const CollapsibleSection = ({ title, children, initiallyExpanded = false }) => {
|
|
46
|
+
const [expanded, setExpanded] = React.useState(initiallyExpanded)
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<View style={styles.collapsibleSection}>
|
|
50
|
+
<Pressable
|
|
51
|
+
style={styles.sectionHeader}
|
|
52
|
+
onPress={() => setExpanded(!expanded)}>
|
|
53
|
+
<Text style={styles.sectionTitle}>{title}</Text>
|
|
54
|
+
<Text style={styles.expandIcon}>{expanded ? '▼' : '▶'}</Text>
|
|
55
|
+
</Pressable>
|
|
56
|
+
{expanded && children}
|
|
57
|
+
</View>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Basic JSONValue renderer
|
|
62
|
+
const JSONValue = ({ value }) => {
|
|
63
|
+
if (value === null) {
|
|
64
|
+
return <Text style={styles.jsonNull}>null</Text>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (value === undefined) {
|
|
68
|
+
return <Text style={styles.jsonNull}>undefined</Text>
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof value === 'boolean') {
|
|
72
|
+
return <Text style={styles.jsonBoolean}>{value.toString()}</Text>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (typeof value === 'number') {
|
|
76
|
+
return <Text style={styles.jsonNumber}>{value}</Text>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (typeof value === 'string') {
|
|
80
|
+
return (
|
|
81
|
+
<Text style={styles.jsonString} selectable={true}>
|
|
82
|
+
"{value}"
|
|
83
|
+
</Text>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (typeof value === 'object') {
|
|
88
|
+
return (
|
|
89
|
+
<JSONTree
|
|
90
|
+
data={value}
|
|
91
|
+
theme={theme}
|
|
92
|
+
invertTheme={true}
|
|
93
|
+
hideRoot={true}
|
|
94
|
+
shouldExpandNode={(keyPath, nodeData, currentLevel) => currentLevel < 1}
|
|
95
|
+
/>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return <Text style={styles.jsonOther}>{String(value)}</Text>
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const NavigationLogDetails = ({ log }) => {
|
|
103
|
+
if (!log) {
|
|
104
|
+
return (
|
|
105
|
+
<View style={styles.errorContainer}>
|
|
106
|
+
<Text style={styles.errorText}>Navigation log data is missing</Text>
|
|
107
|
+
</View>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const { action, from, to, timestamp, startTime, duration } = log
|
|
112
|
+
const actionColor = getNavigationActionColor(action)
|
|
113
|
+
|
|
114
|
+
// Format the log data for text display and copying
|
|
115
|
+
const formatLogForCopy = () => {
|
|
116
|
+
try {
|
|
117
|
+
return `Navigation: ${action}
|
|
118
|
+
From: ${JSON.stringify(from, null, 2)}
|
|
119
|
+
To: ${JSON.stringify(to, null, 2)}
|
|
120
|
+
Time: ${timestamp ? new Date(timestamp).toLocaleString() : 'Unknown'}
|
|
121
|
+
Duration: ${duration || 'N/A'} ms`;
|
|
122
|
+
} catch (e) {
|
|
123
|
+
return 'Failed to format navigation log';
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<ScrollView
|
|
129
|
+
style={styles.container}
|
|
130
|
+
contentContainerStyle={styles.contentContainer}
|
|
131
|
+
showsVerticalScrollIndicator={true}
|
|
132
|
+
scrollEventThrottle={16}
|
|
133
|
+
keyboardShouldPersistTaps='handled'>
|
|
134
|
+
|
|
135
|
+
<View style={styles.header}>
|
|
136
|
+
<View style={styles.headerInfo}>
|
|
137
|
+
<Text style={[styles.actionIndicator, { color: actionColor }]}>
|
|
138
|
+
{action?.toUpperCase() || 'UNKNOWN'}
|
|
139
|
+
</Text>
|
|
140
|
+
<Text style={styles.timestamp}>
|
|
141
|
+
{timestamp
|
|
142
|
+
? new Date(timestamp).toLocaleString()
|
|
143
|
+
: 'Unknown time'}
|
|
144
|
+
</Text>
|
|
145
|
+
{duration && (
|
|
146
|
+
<Text style={styles.duration}>
|
|
147
|
+
{`${duration} ms`}
|
|
148
|
+
</Text>
|
|
149
|
+
)}
|
|
150
|
+
</View>
|
|
151
|
+
<CopyButton text={formatLogForCopy()} />
|
|
152
|
+
</View>
|
|
153
|
+
|
|
154
|
+
<CollapsibleSection title='From Route' initiallyExpanded={true}>
|
|
155
|
+
<View style={styles.dataContentWrapper}>
|
|
156
|
+
<View style={styles.dataContent}>
|
|
157
|
+
<JSONValue value={from} />
|
|
158
|
+
</View>
|
|
159
|
+
</View>
|
|
160
|
+
</CollapsibleSection>
|
|
161
|
+
|
|
162
|
+
<CollapsibleSection title='To Route' initiallyExpanded={true}>
|
|
163
|
+
<View style={styles.dataContentWrapper}>
|
|
164
|
+
<View style={styles.dataContent}>
|
|
165
|
+
<JSONValue value={to} />
|
|
166
|
+
</View>
|
|
167
|
+
</View>
|
|
168
|
+
</CollapsibleSection>
|
|
169
|
+
|
|
170
|
+
</ScrollView>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Styles adapted from ConsoleLogDetails
|
|
175
|
+
const styles = StyleSheet.create({
|
|
176
|
+
container: {
|
|
177
|
+
flex: 1,
|
|
178
|
+
backgroundColor: '#fff',
|
|
179
|
+
},
|
|
180
|
+
contentContainer: {
|
|
181
|
+
paddingBottom: 20,
|
|
182
|
+
},
|
|
183
|
+
header: {
|
|
184
|
+
flexDirection: 'row',
|
|
185
|
+
padding: 15,
|
|
186
|
+
borderBottomWidth: 1,
|
|
187
|
+
borderBottomColor: '#eee',
|
|
188
|
+
alignItems: 'center',
|
|
189
|
+
justifyContent: 'space-between',
|
|
190
|
+
},
|
|
191
|
+
headerInfo: {
|
|
192
|
+
flexShrink: 1,
|
|
193
|
+
marginRight: 10,
|
|
194
|
+
},
|
|
195
|
+
actionIndicator: {
|
|
196
|
+
fontSize: 14,
|
|
197
|
+
fontWeight: 'bold',
|
|
198
|
+
marginBottom: 4,
|
|
199
|
+
},
|
|
200
|
+
timestamp: {
|
|
201
|
+
fontSize: 13,
|
|
202
|
+
color: '#666',
|
|
203
|
+
marginBottom: 2,
|
|
204
|
+
},
|
|
205
|
+
duration: {
|
|
206
|
+
fontSize: 12,
|
|
207
|
+
color: '#888',
|
|
208
|
+
},
|
|
209
|
+
collapsibleSection: {
|
|
210
|
+
marginBottom: 1,
|
|
211
|
+
backgroundColor: '#fff',
|
|
212
|
+
},
|
|
213
|
+
sectionHeader: {
|
|
214
|
+
flexDirection: 'row',
|
|
215
|
+
justifyContent: 'space-between',
|
|
216
|
+
alignItems: 'center',
|
|
217
|
+
padding: 15,
|
|
218
|
+
backgroundColor: '#f5f5f5',
|
|
219
|
+
},
|
|
220
|
+
sectionTitle: {
|
|
221
|
+
fontSize: 15,
|
|
222
|
+
fontWeight: 'bold',
|
|
223
|
+
color: '#333',
|
|
224
|
+
},
|
|
225
|
+
expandIcon: {
|
|
226
|
+
fontSize: 14,
|
|
227
|
+
color: '#666',
|
|
228
|
+
},
|
|
229
|
+
content: {
|
|
230
|
+
padding: 15,
|
|
231
|
+
},
|
|
232
|
+
dataContentWrapper: {
|
|
233
|
+
flex: 1,
|
|
234
|
+
padding: 10,
|
|
235
|
+
},
|
|
236
|
+
dataContent: {
|
|
237
|
+
backgroundColor: '#f8f9fa',
|
|
238
|
+
padding: 10,
|
|
239
|
+
borderRadius: 4,
|
|
240
|
+
borderWidth: 1,
|
|
241
|
+
borderColor: '#e9ecef',
|
|
242
|
+
},
|
|
243
|
+
errorContainer: {
|
|
244
|
+
flex: 1,
|
|
245
|
+
justifyContent: 'center',
|
|
246
|
+
alignItems: 'center',
|
|
247
|
+
padding: 20,
|
|
248
|
+
},
|
|
249
|
+
errorText: {
|
|
250
|
+
color: '#ff4444',
|
|
251
|
+
fontSize: 16,
|
|
252
|
+
},
|
|
253
|
+
copyButton: {
|
|
254
|
+
backgroundColor: '#e9ecef',
|
|
255
|
+
paddingHorizontal: 10,
|
|
256
|
+
paddingVertical: 5,
|
|
257
|
+
borderRadius: 4,
|
|
258
|
+
flexShrink: 0,
|
|
259
|
+
},
|
|
260
|
+
copyButtonText: {
|
|
261
|
+
fontSize: 12,
|
|
262
|
+
color: '#666',
|
|
263
|
+
},
|
|
264
|
+
// JSON Value Styles (reused from ConsoleLogDetails)
|
|
265
|
+
jsonString: {
|
|
266
|
+
color: '#CB772F',
|
|
267
|
+
fontFamily: 'monospace',
|
|
268
|
+
fontSize: 13,
|
|
269
|
+
},
|
|
270
|
+
jsonNumber: {
|
|
271
|
+
color: '#AE81FF',
|
|
272
|
+
fontFamily: 'monospace',
|
|
273
|
+
fontSize: 13,
|
|
274
|
+
},
|
|
275
|
+
jsonBoolean: {
|
|
276
|
+
color: '#66D9EF',
|
|
277
|
+
fontWeight: 'bold',
|
|
278
|
+
fontFamily: 'monospace',
|
|
279
|
+
fontSize: 13,
|
|
280
|
+
},
|
|
281
|
+
jsonNull: {
|
|
282
|
+
color: '#F92672',
|
|
283
|
+
fontStyle: 'italic',
|
|
284
|
+
fontFamily: 'monospace',
|
|
285
|
+
fontSize: 13,
|
|
286
|
+
},
|
|
287
|
+
jsonOther: {
|
|
288
|
+
color: '#75715e',
|
|
289
|
+
fontFamily: 'monospace',
|
|
290
|
+
fontSize: 13,
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
export default NavigationLogDetails
|