react-native-debug-toolkit 0.1.7 → 0.2.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/index.js +11 -2
- package/lib/DebugToolKit.js +22 -15
- package/lib/features/ConsoleLogFeature.js +73 -0
- package/lib/features/PerformanceFeature.js +390 -0
- package/lib/index.js +72 -2
- package/lib/utils/DebugConst.js +14 -1
- package/lib/views/ConsoleLogDetails.js +314 -0
- package/lib/views/FloatPanelView.js +11 -0
- package/lib/views/SubViewConsoleLogs.js +209 -0
- package/lib/views/SubViewPerformance.js +515 -0
- package/package.json +3 -2
- package/react-native.config.js +5 -0
- package/index.d.ts +0 -155
|
@@ -0,0 +1,314 @@
|
|
|
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
|
+
|
|
6
|
+
// Re-using the theme from HttpLogDetails for consistency
|
|
7
|
+
const theme = {
|
|
8
|
+
scheme: 'monokai',
|
|
9
|
+
author: 'wimer hazenberg (http://www.monokai.nl)',
|
|
10
|
+
base00: '#272822',
|
|
11
|
+
base01: '#383830',
|
|
12
|
+
base02: '#49483e',
|
|
13
|
+
base03: '#75715e',
|
|
14
|
+
base04: '#a59f85',
|
|
15
|
+
base05: '#f8f8f2',
|
|
16
|
+
base06: '#f5f4f1',
|
|
17
|
+
base07: '#f9f8f5',
|
|
18
|
+
base08: '#f92672',
|
|
19
|
+
base09: '#fd971f',
|
|
20
|
+
base0A: '#f4bf75',
|
|
21
|
+
base0B: '#a6e22e',
|
|
22
|
+
base0C: '#a1efe4',
|
|
23
|
+
base0D: '#66d9ef',
|
|
24
|
+
base0E: '#ae81ff',
|
|
25
|
+
base0F: '#cc6633'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const CopyButton = ({ text, style }) => {
|
|
29
|
+
const [copied, setCopied] = React.useState(false)
|
|
30
|
+
|
|
31
|
+
const handleCopy = async () => {
|
|
32
|
+
await Clipboard.setString(text)
|
|
33
|
+
setCopied(true)
|
|
34
|
+
setTimeout(() => setCopied(false), 2000)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Pressable style={[styles.copyButton, style]} onPress={handleCopy}>
|
|
39
|
+
<Text style={styles.copyButtonText}>{copied ? 'Copied!' : 'Copy'}</Text>
|
|
40
|
+
</Pressable>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const CollapsibleSection = ({ title, children, initiallyExpanded = false }) => {
|
|
45
|
+
const [expanded, setExpanded] = React.useState(initiallyExpanded)
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<View style={styles.collapsibleSection}>
|
|
49
|
+
<Pressable
|
|
50
|
+
style={styles.sectionHeader}
|
|
51
|
+
onPress={() => setExpanded(!expanded)}>
|
|
52
|
+
<Text style={styles.sectionTitle}>{title}</Text>
|
|
53
|
+
<Text style={styles.expandIcon}>{expanded ? '▼' : '▶'}</Text>
|
|
54
|
+
</Pressable>
|
|
55
|
+
{expanded && children}
|
|
56
|
+
</View>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Basic JSONValue renderer, might need adjustments for console log specifics
|
|
61
|
+
const JSONValue = ({ value }) => {
|
|
62
|
+
if (value === null) {
|
|
63
|
+
return <Text style={styles.jsonNull}>null</Text>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (value === undefined) {
|
|
67
|
+
return <Text style={styles.jsonNull}>undefined</Text>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (typeof value === 'boolean') {
|
|
71
|
+
return <Text style={styles.jsonBoolean}>{value.toString()}</Text>
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (typeof value === 'number') {
|
|
75
|
+
return <Text style={styles.jsonNumber}>{value}</Text>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (typeof value === 'string') {
|
|
79
|
+
return (
|
|
80
|
+
<Text style={styles.jsonString} selectable={true}>
|
|
81
|
+
"{value}"
|
|
82
|
+
</Text>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (typeof value === 'object') {
|
|
87
|
+
return (
|
|
88
|
+
<JSONTree
|
|
89
|
+
data={value}
|
|
90
|
+
theme={theme}
|
|
91
|
+
invertTheme={true}
|
|
92
|
+
hideRoot={true}
|
|
93
|
+
shouldExpandNode={(keyPath, nodeData, currentLevel) => currentLevel < 1}
|
|
94
|
+
/>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return <Text style={styles.jsonOther}>{String(value)}</Text>
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
const ConsoleLogDetails = ({ log }) => {
|
|
103
|
+
if (!log) {
|
|
104
|
+
return (
|
|
105
|
+
<View style={styles.errorContainer}>
|
|
106
|
+
<Text style={styles.errorText}>Log data is missing</Text>
|
|
107
|
+
</View>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const logData = log.data || [] // Console logs can have multiple arguments
|
|
112
|
+
const timestamp = log.timestamp
|
|
113
|
+
const level = log.level || 'log' // Default to 'log' if level is not provided
|
|
114
|
+
|
|
115
|
+
// Simple function to format log data for display and copying
|
|
116
|
+
const formatLogData = (dataArray) => {
|
|
117
|
+
return dataArray.map(item => {
|
|
118
|
+
if (typeof item === 'object') {
|
|
119
|
+
try {
|
|
120
|
+
return JSON.stringify(item, null, 2);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
return '[unserializable object]';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return String(item);
|
|
126
|
+
}).join(' ');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const formattedLog = formatLogData(logData);
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<ScrollView
|
|
133
|
+
style={styles.container}
|
|
134
|
+
contentContainerStyle={styles.contentContainer}
|
|
135
|
+
showsVerticalScrollIndicator={true}
|
|
136
|
+
scrollEventThrottle={16}
|
|
137
|
+
keyboardShouldPersistTaps='handled'>
|
|
138
|
+
|
|
139
|
+
<View style={styles.header}>
|
|
140
|
+
<View style={styles.headerInfo}>
|
|
141
|
+
<Text style={[styles.levelIndicator, { color: getLevelColor(level) }]}>
|
|
142
|
+
{level.toUpperCase()}
|
|
143
|
+
</Text>
|
|
144
|
+
<Text style={styles.timestamp}>
|
|
145
|
+
{timestamp
|
|
146
|
+
? new Date(timestamp).toLocaleString()
|
|
147
|
+
: 'Unknown time'}
|
|
148
|
+
</Text>
|
|
149
|
+
</View>
|
|
150
|
+
<CopyButton text={formattedLog} />
|
|
151
|
+
</View>
|
|
152
|
+
|
|
153
|
+
<CollapsibleSection title='Log Data' initiallyExpanded={true}>
|
|
154
|
+
<View style={styles.dataContentWrapper}>
|
|
155
|
+
<View style={styles.dataContent}>
|
|
156
|
+
{logData.map((item, index) => (
|
|
157
|
+
<View key={index} style={[styles.logDataItem, index === logData.length - 1 && styles.logDataItemLast]}>
|
|
158
|
+
<JSONValue value={item} />
|
|
159
|
+
</View>
|
|
160
|
+
))}
|
|
161
|
+
</View>
|
|
162
|
+
</View>
|
|
163
|
+
</CollapsibleSection>
|
|
164
|
+
|
|
165
|
+
</ScrollView>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const getLevelColor = (level) => {
|
|
170
|
+
switch (level?.toLowerCase()) {
|
|
171
|
+
case 'log':
|
|
172
|
+
return '#333'; // Dark gray for standard log
|
|
173
|
+
case 'info':
|
|
174
|
+
return '#0D96F2'; // Blue for info
|
|
175
|
+
case 'warn':
|
|
176
|
+
return '#FCA130'; // Orange for warning
|
|
177
|
+
case 'error':
|
|
178
|
+
return '#F93E3E'; // Red for error
|
|
179
|
+
default:
|
|
180
|
+
return '#666666'; // Default gray
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const getLevelTextStyle = (level) => {
|
|
185
|
+
const baseStyle = { fontSize: 14, fontFamily: 'monospace' }; // Use monospace font
|
|
186
|
+
return { ...baseStyle, color: getLevelColor(level) };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Re-using and adapting styles from HttpLogDetails
|
|
190
|
+
const styles = StyleSheet.create({
|
|
191
|
+
container: {
|
|
192
|
+
flex: 1,
|
|
193
|
+
backgroundColor: '#fff',
|
|
194
|
+
},
|
|
195
|
+
contentContainer: {
|
|
196
|
+
paddingBottom: 20,
|
|
197
|
+
},
|
|
198
|
+
header: {
|
|
199
|
+
flexDirection: 'row',
|
|
200
|
+
padding: 15,
|
|
201
|
+
borderBottomWidth: 1,
|
|
202
|
+
borderBottomColor: '#eee',
|
|
203
|
+
alignItems: 'center', // Align items vertically
|
|
204
|
+
justifyContent: 'space-between', // Space out items
|
|
205
|
+
},
|
|
206
|
+
headerInfo: {
|
|
207
|
+
flexDirection: 'row', // Arrange level and timestamp horizontally
|
|
208
|
+
alignItems: 'center',
|
|
209
|
+
flexShrink: 1, // Allow info to shrink if needed
|
|
210
|
+
marginRight: 10,
|
|
211
|
+
},
|
|
212
|
+
levelIndicator: {
|
|
213
|
+
fontSize: 14,
|
|
214
|
+
fontWeight: 'bold',
|
|
215
|
+
marginRight: 10,
|
|
216
|
+
},
|
|
217
|
+
timestamp: {
|
|
218
|
+
fontSize: 13,
|
|
219
|
+
color: '#666',
|
|
220
|
+
},
|
|
221
|
+
collapsibleSection: {
|
|
222
|
+
marginBottom: 1,
|
|
223
|
+
backgroundColor: '#fff',
|
|
224
|
+
},
|
|
225
|
+
sectionHeader: {
|
|
226
|
+
flexDirection: 'row',
|
|
227
|
+
justifyContent: 'space-between',
|
|
228
|
+
alignItems: 'center',
|
|
229
|
+
padding: 15,
|
|
230
|
+
backgroundColor: '#f5f5f5',
|
|
231
|
+
},
|
|
232
|
+
sectionTitle: {
|
|
233
|
+
fontSize: 15,
|
|
234
|
+
fontWeight: 'bold',
|
|
235
|
+
color: '#333',
|
|
236
|
+
},
|
|
237
|
+
expandIcon: {
|
|
238
|
+
fontSize: 14,
|
|
239
|
+
color: '#666',
|
|
240
|
+
},
|
|
241
|
+
content: {
|
|
242
|
+
padding: 15,
|
|
243
|
+
},
|
|
244
|
+
dataContentWrapper: {
|
|
245
|
+
flex: 1,
|
|
246
|
+
padding: 10, // Add padding around the scroll view
|
|
247
|
+
},
|
|
248
|
+
dataContent: {
|
|
249
|
+
backgroundColor: '#f8f9fa',
|
|
250
|
+
padding: 10,
|
|
251
|
+
borderRadius: 4,
|
|
252
|
+
borderWidth: 1,
|
|
253
|
+
borderColor: '#e9ecef',
|
|
254
|
+
},
|
|
255
|
+
logDataItem: {
|
|
256
|
+
borderBottomWidth: 1,
|
|
257
|
+
borderBottomColor: '#eee',
|
|
258
|
+
paddingVertical: 8,
|
|
259
|
+
},
|
|
260
|
+
logDataItemLast: {
|
|
261
|
+
borderBottomWidth: 0,
|
|
262
|
+
},
|
|
263
|
+
errorContainer: {
|
|
264
|
+
flex: 1,
|
|
265
|
+
justifyContent: 'center',
|
|
266
|
+
alignItems: 'center',
|
|
267
|
+
padding: 20,
|
|
268
|
+
},
|
|
269
|
+
errorText: {
|
|
270
|
+
color: '#ff4444',
|
|
271
|
+
fontSize: 16,
|
|
272
|
+
},
|
|
273
|
+
copyButton: {
|
|
274
|
+
backgroundColor: '#e9ecef',
|
|
275
|
+
paddingHorizontal: 10,
|
|
276
|
+
paddingVertical: 5,
|
|
277
|
+
borderRadius: 4,
|
|
278
|
+
flexShrink: 0, // Prevent button from shrinking
|
|
279
|
+
},
|
|
280
|
+
copyButtonText: {
|
|
281
|
+
fontSize: 12,
|
|
282
|
+
color: '#666',
|
|
283
|
+
},
|
|
284
|
+
// JSON Value Styles (simplified)
|
|
285
|
+
jsonString: {
|
|
286
|
+
color: '#CB772F',
|
|
287
|
+
fontFamily: 'monospace',
|
|
288
|
+
fontSize: 13,
|
|
289
|
+
},
|
|
290
|
+
jsonNumber: {
|
|
291
|
+
color: '#AE81FF',
|
|
292
|
+
fontFamily: 'monospace',
|
|
293
|
+
fontSize: 13,
|
|
294
|
+
},
|
|
295
|
+
jsonBoolean: {
|
|
296
|
+
color: '#66D9EF',
|
|
297
|
+
fontWeight: 'bold',
|
|
298
|
+
fontFamily: 'monospace',
|
|
299
|
+
fontSize: 13,
|
|
300
|
+
},
|
|
301
|
+
jsonNull: {
|
|
302
|
+
color: '#F92672',
|
|
303
|
+
fontStyle: 'italic',
|
|
304
|
+
fontFamily: 'monospace',
|
|
305
|
+
fontSize: 13,
|
|
306
|
+
},
|
|
307
|
+
jsonOther: {
|
|
308
|
+
color: '#75715e',
|
|
309
|
+
fontFamily: 'monospace',
|
|
310
|
+
fontSize: 13,
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
export default ConsoleLogDetails
|
|
@@ -12,7 +12,9 @@ import {
|
|
|
12
12
|
} from 'react-native'
|
|
13
13
|
import { IconRadius, DebugColors } from '../utils/DebugConst'
|
|
14
14
|
import SubViewHTTPLogs from './SubViewHTTPLogs'
|
|
15
|
+
import SubViewPerformance from './SubViewPerformance'
|
|
15
16
|
import TabView from './TabView'
|
|
17
|
+
import SubViewConsoleLogs from './SubViewConsoleLogs'
|
|
16
18
|
|
|
17
19
|
const { width: screenWidth, height: screenHeight } = Dimensions.get('window')
|
|
18
20
|
|
|
@@ -272,6 +274,15 @@ export default class FloatPanelView extends Component {
|
|
|
272
274
|
if (feature.name === 'network') {
|
|
273
275
|
return <SubViewHTTPLogs logs={data} />
|
|
274
276
|
}
|
|
277
|
+
|
|
278
|
+
// Special handling for performance view
|
|
279
|
+
if (feature.name === 'performance') {
|
|
280
|
+
return <SubViewPerformance />
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (feature.name === 'console') {
|
|
284
|
+
return <SubViewConsoleLogs logs={data} />
|
|
285
|
+
}
|
|
275
286
|
|
|
276
287
|
// Generic fallback for other feature types
|
|
277
288
|
return (
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import React, { useState, useMemo } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
FlatList,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
} from 'react-native'
|
|
9
|
+
import ConsoleLogDetails from './ConsoleLogDetails' // Import the new details component
|
|
10
|
+
import { getLogLevelColor } from '../utils/DebugConst' // Assuming you move color logic
|
|
11
|
+
|
|
12
|
+
const SubViewConsoleLogs = ({ logs = [] }) => {
|
|
13
|
+
const [selectedLog, setSelectedLog] = useState(null)
|
|
14
|
+
|
|
15
|
+
// Memoize the sorted logs
|
|
16
|
+
const sortedLogs = useMemo(() => {
|
|
17
|
+
// Create a stable copy before sorting if FlatList relies on reference equality
|
|
18
|
+
return [...logs].sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
|
|
19
|
+
}, [logs]);
|
|
20
|
+
|
|
21
|
+
// Helper to format log data for preview
|
|
22
|
+
const formatLogPreview = (dataArray = []) => {
|
|
23
|
+
if (!Array.isArray(dataArray)) return ''; // Handle cases where data might not be an array
|
|
24
|
+
return dataArray.map(item => {
|
|
25
|
+
if (typeof item === 'string') return item;
|
|
26
|
+
if (typeof item === 'number' || typeof item === 'boolean') return String(item);
|
|
27
|
+
if (item === null) return 'null';
|
|
28
|
+
if (item === undefined) return 'undefined';
|
|
29
|
+
if (typeof item === 'object' && item !== null) {
|
|
30
|
+
try {
|
|
31
|
+
// Attempt a brief JSON stringification, handle potential circular refs?
|
|
32
|
+
const preview = JSON.stringify(item);
|
|
33
|
+
return preview.length > 50 ? preview.substring(0, 47) + '...' : preview;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
return '{...}'; // Fallback for complex/circular objects
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return String(item); // Fallback
|
|
39
|
+
}).join(' ');
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
const renderLogItem = ({ item }) => {
|
|
44
|
+
const level = item.level || 'log';
|
|
45
|
+
const levelColor = getLogLevelColor(level);
|
|
46
|
+
const previewText = formatLogPreview(item.data);
|
|
47
|
+
const timestamp = item.timestamp
|
|
48
|
+
? new Date(item.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })
|
|
49
|
+
: '';
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<TouchableOpacity
|
|
53
|
+
style={styles.logItem}
|
|
54
|
+
onPress={() => setSelectedLog(item)}>
|
|
55
|
+
<View style={styles.logItemContainer}>
|
|
56
|
+
<View
|
|
57
|
+
style={[styles.levelIndicator, { backgroundColor: levelColor }]}
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
<View style={styles.logContent}>
|
|
61
|
+
<View style={styles.logHeader}>
|
|
62
|
+
<Text style={[styles.levelText, { color: levelColor }]}>
|
|
63
|
+
{level.toUpperCase()}
|
|
64
|
+
</Text>
|
|
65
|
+
<Text style={styles.time}>{timestamp}</Text>
|
|
66
|
+
</View>
|
|
67
|
+
|
|
68
|
+
<Text style={styles.logMessage} numberOfLines={3}>
|
|
69
|
+
{previewText}
|
|
70
|
+
</Text>
|
|
71
|
+
|
|
72
|
+
</View>
|
|
73
|
+
</View>
|
|
74
|
+
</TouchableOpacity>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// If a log is selected, show the details view
|
|
79
|
+
if (selectedLog) {
|
|
80
|
+
const selectedLevel = selectedLog.level || 'log';
|
|
81
|
+
const timestamp = selectedLog.timestamp
|
|
82
|
+
? new Date(selectedLog.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })
|
|
83
|
+
: '';
|
|
84
|
+
return (
|
|
85
|
+
<View style={styles.container}>
|
|
86
|
+
<View style={styles.detailsHeader}>
|
|
87
|
+
<TouchableOpacity
|
|
88
|
+
style={styles.backButton}
|
|
89
|
+
onPress={() => setSelectedLog(null)}>
|
|
90
|
+
<Text style={styles.backButtonText}>← Back</Text>
|
|
91
|
+
</TouchableOpacity>
|
|
92
|
+
<View style={styles.headerLevelTime}>
|
|
93
|
+
<Text style={[styles.headerLevel, { color: getLogLevelColor(selectedLevel) }]}>
|
|
94
|
+
{selectedLevel.toUpperCase()}
|
|
95
|
+
</Text>
|
|
96
|
+
<Text style={styles.headerTimestamp}>{timestamp}</Text>
|
|
97
|
+
</View>
|
|
98
|
+
</View>
|
|
99
|
+
<ConsoleLogDetails log={selectedLog} />
|
|
100
|
+
</View>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Otherwise show the list view
|
|
105
|
+
return (
|
|
106
|
+
<View style={styles.container}>
|
|
107
|
+
{sortedLogs.length === 0 ? (
|
|
108
|
+
<Text style={styles.emptyText}>No console messages logged yet</Text>
|
|
109
|
+
) : (
|
|
110
|
+
<FlatList
|
|
111
|
+
data={sortedLogs} // Use the memoized sorted list
|
|
112
|
+
renderItem={renderLogItem}
|
|
113
|
+
keyExtractor={(item, index) => `${item.timestamp}-${index}`} // Use timestamp and index for key
|
|
114
|
+
style={styles.list}
|
|
115
|
+
/>
|
|
116
|
+
)}
|
|
117
|
+
</View>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Adapted styles from SubViewHTTPLogs
|
|
122
|
+
const styles = StyleSheet.create({
|
|
123
|
+
container: {
|
|
124
|
+
flex: 1,
|
|
125
|
+
backgroundColor: '#fff',
|
|
126
|
+
},
|
|
127
|
+
list: {
|
|
128
|
+
flex: 1,
|
|
129
|
+
},
|
|
130
|
+
emptyText: {
|
|
131
|
+
textAlign: 'center',
|
|
132
|
+
color: '#999',
|
|
133
|
+
marginTop: 20,
|
|
134
|
+
},
|
|
135
|
+
logItem: {
|
|
136
|
+
borderBottomWidth: 1,
|
|
137
|
+
borderBottomColor: '#eee',
|
|
138
|
+
},
|
|
139
|
+
logItemContainer: {
|
|
140
|
+
flexDirection: 'row',
|
|
141
|
+
paddingVertical: 10,
|
|
142
|
+
paddingHorizontal: 12,
|
|
143
|
+
},
|
|
144
|
+
levelIndicator: {
|
|
145
|
+
width: 4,
|
|
146
|
+
borderRadius: 2,
|
|
147
|
+
marginRight: 10,
|
|
148
|
+
},
|
|
149
|
+
logContent: {
|
|
150
|
+
flex: 1,
|
|
151
|
+
},
|
|
152
|
+
logHeader: {
|
|
153
|
+
flexDirection: 'row',
|
|
154
|
+
justifyContent: 'space-between',
|
|
155
|
+
alignItems: 'center',
|
|
156
|
+
marginBottom: 5,
|
|
157
|
+
},
|
|
158
|
+
levelText: {
|
|
159
|
+
fontSize: 13,
|
|
160
|
+
fontWeight: 'bold',
|
|
161
|
+
},
|
|
162
|
+
time: {
|
|
163
|
+
fontSize: 12,
|
|
164
|
+
color: '#888',
|
|
165
|
+
},
|
|
166
|
+
logMessage: {
|
|
167
|
+
fontSize: 14,
|
|
168
|
+
color: '#333',
|
|
169
|
+
lineHeight: 18,
|
|
170
|
+
fontFamily: 'monospace', // Use monospace for log messages
|
|
171
|
+
},
|
|
172
|
+
// Details Header Styles
|
|
173
|
+
detailsHeader: {
|
|
174
|
+
flexDirection: 'row',
|
|
175
|
+
alignItems: 'center',
|
|
176
|
+
paddingVertical: 10,
|
|
177
|
+
paddingHorizontal: 15,
|
|
178
|
+
borderBottomWidth: 1,
|
|
179
|
+
borderBottomColor: '#eee',
|
|
180
|
+
backgroundColor: '#f8f9fa',
|
|
181
|
+
},
|
|
182
|
+
backButton: {
|
|
183
|
+
paddingHorizontal: 10,
|
|
184
|
+
paddingVertical: 5,
|
|
185
|
+
borderRadius: 4,
|
|
186
|
+
backgroundColor: '#eee',
|
|
187
|
+
marginRight: 15, // More space after back button
|
|
188
|
+
},
|
|
189
|
+
backButtonText: {
|
|
190
|
+
color: '#333',
|
|
191
|
+
fontWeight: '500',
|
|
192
|
+
fontSize: 14,
|
|
193
|
+
},
|
|
194
|
+
headerLevelTime: {
|
|
195
|
+
flexDirection: 'row',
|
|
196
|
+
alignItems: 'baseline', // Align baseline of text
|
|
197
|
+
},
|
|
198
|
+
headerLevel: {
|
|
199
|
+
fontSize: 15,
|
|
200
|
+
fontWeight: 'bold',
|
|
201
|
+
marginRight: 8,
|
|
202
|
+
},
|
|
203
|
+
headerTimestamp: {
|
|
204
|
+
fontSize: 13,
|
|
205
|
+
color: '#666',
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
export default SubViewConsoleLogs
|