react-native-debug-toolkit 0.2.2 → 0.3.1

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.
@@ -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
@@ -0,0 +1,199 @@
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 NavigationLogDetails from './NavigationLogDetails'
10
+ import { getNavigationActionColor } from '../utils/DebugConst'
11
+
12
+ const SubViewNavigationLogs = ({ 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 navigation data for preview
22
+ const formatNavigationPreview = (action, from, to) => {
23
+ const fromName = from?.name || 'Unknown';
24
+ const toName = to?.name || 'Unknown';
25
+
26
+ let preview = `${action || 'Navigate'}: ${fromName} → ${toName}`;
27
+
28
+ // Add params summary if available
29
+ if (to?.params) {
30
+ try {
31
+ const paramsStr = JSON.stringify(to.params);
32
+ const shortParams = paramsStr.length > 30 ? paramsStr.substring(0, 27) + '...' : paramsStr;
33
+ preview += ` ${shortParams}`;
34
+ } catch (e) {
35
+ preview += ' [with params]';
36
+ }
37
+ }
38
+
39
+ return preview;
40
+ };
41
+
42
+ const renderLogItem = ({ item }) => {
43
+ const action = item.action || 'navigate';
44
+ const actionColor = getNavigationActionColor(action);
45
+ const previewText = formatNavigationPreview(action, item.from, item.to);
46
+ const timestamp = item.timestamp
47
+ ? new Date(item.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })
48
+ : '';
49
+
50
+ return (
51
+ <TouchableOpacity
52
+ style={styles.logItem}
53
+ onPress={() => setSelectedLog(item)}>
54
+ <View style={styles.logItemContainer}>
55
+ <View
56
+ style={[styles.actionIndicator, { backgroundColor: actionColor }]}
57
+ />
58
+
59
+ <View style={styles.logContent}>
60
+ <View style={styles.logHeader}>
61
+ <Text style={[styles.actionText, { color: actionColor }]}>
62
+ {action.toUpperCase()}
63
+ </Text>
64
+ <Text style={styles.time}>{timestamp}</Text>
65
+ </View>
66
+
67
+ <Text style={styles.logMessage} numberOfLines={2}>
68
+ {previewText}
69
+ </Text>
70
+
71
+ {item.duration && (
72
+ <Text style={styles.duration}>{`${item.duration} ms`}</Text>
73
+ )}
74
+ </View>
75
+ </View>
76
+ </TouchableOpacity>
77
+ )
78
+ }
79
+
80
+ // If a log is selected, show the details view
81
+ if (selectedLog) {
82
+ return (
83
+ <View style={styles.container}>
84
+ <View style={styles.detailsHeader}>
85
+ <TouchableOpacity
86
+ style={styles.backButton}
87
+ onPress={() => setSelectedLog(null)}>
88
+ <Text style={styles.backButtonText}>← Back</Text>
89
+ </TouchableOpacity>
90
+ <Text style={styles.headerTitle}>Navigation Details</Text>
91
+ </View>
92
+ <NavigationLogDetails log={selectedLog} />
93
+ </View>
94
+ )
95
+ }
96
+
97
+ // Otherwise show the list view
98
+ return (
99
+ <View style={styles.container}>
100
+ {sortedLogs.length === 0 ? (
101
+ <Text style={styles.emptyText}>No navigation events logged yet</Text>
102
+ ) : (
103
+ <FlatList
104
+ data={sortedLogs}
105
+ renderItem={renderLogItem}
106
+ keyExtractor={(item, index) => `${item.timestamp}-${index}`}
107
+ style={styles.list}
108
+ />
109
+ )}
110
+ </View>
111
+ )
112
+ }
113
+
114
+ // Adapted styles from SubViewConsoleLogs
115
+ const styles = StyleSheet.create({
116
+ container: {
117
+ flex: 1,
118
+ backgroundColor: '#fff',
119
+ },
120
+ list: {
121
+ flex: 1,
122
+ },
123
+ emptyText: {
124
+ textAlign: 'center',
125
+ color: '#999',
126
+ marginTop: 20,
127
+ },
128
+ logItem: {
129
+ borderBottomWidth: 1,
130
+ borderBottomColor: '#eee',
131
+ },
132
+ logItemContainer: {
133
+ flexDirection: 'row',
134
+ paddingVertical: 10,
135
+ paddingHorizontal: 12,
136
+ },
137
+ actionIndicator: {
138
+ width: 4,
139
+ borderRadius: 2,
140
+ marginRight: 10,
141
+ },
142
+ logContent: {
143
+ flex: 1,
144
+ },
145
+ logHeader: {
146
+ flexDirection: 'row',
147
+ justifyContent: 'space-between',
148
+ alignItems: 'center',
149
+ marginBottom: 5,
150
+ },
151
+ actionText: {
152
+ fontSize: 13,
153
+ fontWeight: 'bold',
154
+ },
155
+ time: {
156
+ fontSize: 12,
157
+ color: '#888',
158
+ },
159
+ logMessage: {
160
+ fontSize: 14,
161
+ color: '#333',
162
+ lineHeight: 18,
163
+ fontFamily: 'monospace',
164
+ },
165
+ duration: {
166
+ fontSize: 12,
167
+ color: '#888',
168
+ marginTop: 4,
169
+ },
170
+ // Details Header Styles
171
+ detailsHeader: {
172
+ flexDirection: 'row',
173
+ alignItems: 'center',
174
+ paddingVertical: 10,
175
+ paddingHorizontal: 15,
176
+ borderBottomWidth: 1,
177
+ borderBottomColor: '#eee',
178
+ backgroundColor: '#f8f9fa',
179
+ },
180
+ backButton: {
181
+ paddingHorizontal: 10,
182
+ paddingVertical: 5,
183
+ borderRadius: 4,
184
+ backgroundColor: '#eee',
185
+ marginRight: 15,
186
+ },
187
+ backButtonText: {
188
+ color: '#333',
189
+ fontWeight: '500',
190
+ fontSize: 14,
191
+ },
192
+ headerTitle: {
193
+ fontSize: 16,
194
+ fontWeight: 'bold',
195
+ color: '#333',
196
+ },
197
+ })
198
+
199
+ export default SubViewNavigationLogs