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.
- package/index.js +8 -0
- package/lib/features/ConsoleLogFeature.js +1 -1
- package/lib/features/NavigationLogFeature.js +47 -0
- package/lib/features/NetworkFeature.js +1 -1
- package/lib/features/ZustandLogFeature.js +46 -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 +37 -5
- package/lib/utils/StorageUtils.js +80 -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/SubViewZustandLogs.js +279 -0
- package/lib/views/ZustandLogDetails.js +355 -0
- package/package.json +2 -2
|
@@ -0,0 +1,279 @@
|
|
|
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 ZustandLogDetails from './ZustandLogDetails'
|
|
10
|
+
import { getZustandActionColor } from '../utils/DebugConst'
|
|
11
|
+
|
|
12
|
+
const SubViewZustandLogs = ({ logs = [] }) => {
|
|
13
|
+
const [selectedLog, setSelectedLog] = useState(null)
|
|
14
|
+
|
|
15
|
+
// Memoize the sorted logs
|
|
16
|
+
const sortedLogs = useMemo(() => {
|
|
17
|
+
// Create a stable copy before sorting
|
|
18
|
+
return [...logs].sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
|
|
19
|
+
}, [logs]);
|
|
20
|
+
|
|
21
|
+
// Helper to format preview of state changes
|
|
22
|
+
const formatActionPreview = (log) => {
|
|
23
|
+
if (!log) return '';
|
|
24
|
+
|
|
25
|
+
const { action = 'unknown', storeName } = log;
|
|
26
|
+
return `Action: ${action}${storeName ? ` (${storeName})` : ''}`;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Safely stringify value for display
|
|
30
|
+
const safeStringify = (value) => {
|
|
31
|
+
if (value === undefined) return 'undefined';
|
|
32
|
+
if (value === null) return 'null';
|
|
33
|
+
|
|
34
|
+
const type = typeof value;
|
|
35
|
+
if (type === 'string') return `"${value.length > 10 ? value.substring(0, 10) + '...' : value}"`;
|
|
36
|
+
if (type === 'number' || type === 'boolean') return String(value);
|
|
37
|
+
if (Array.isArray(value)) return '[...]';
|
|
38
|
+
if (type === 'object') return '{...}';
|
|
39
|
+
|
|
40
|
+
return String(value);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Helper to find key changes between states with more details
|
|
44
|
+
const findChangedKeys = (prevState, nextState) => {
|
|
45
|
+
if (!prevState || !nextState) return [];
|
|
46
|
+
|
|
47
|
+
const changes = [];
|
|
48
|
+
const allKeys = new Set([...Object.keys(prevState), ...Object.keys(nextState)]);
|
|
49
|
+
|
|
50
|
+
allKeys.forEach(key => {
|
|
51
|
+
const prevValue = prevState[key];
|
|
52
|
+
const nextValue = nextState[key];
|
|
53
|
+
|
|
54
|
+
// Check if key is added, removed, or value changed
|
|
55
|
+
if (!(key in prevState)) {
|
|
56
|
+
changes.push({
|
|
57
|
+
key,
|
|
58
|
+
type: 'added',
|
|
59
|
+
display: `+${key}: ${safeStringify(nextValue)}`
|
|
60
|
+
});
|
|
61
|
+
} else if (!(key in nextState)) {
|
|
62
|
+
changes.push({
|
|
63
|
+
key,
|
|
64
|
+
type: 'removed',
|
|
65
|
+
display: `-${key}: ${safeStringify(prevValue)}`
|
|
66
|
+
});
|
|
67
|
+
} else if (JSON.stringify(prevValue) !== JSON.stringify(nextValue)) {
|
|
68
|
+
// For primitive types, show the before and after values
|
|
69
|
+
const isPrimitive =
|
|
70
|
+
typeof prevValue !== 'object' ||
|
|
71
|
+
prevValue === null ||
|
|
72
|
+
typeof nextValue !== 'object' ||
|
|
73
|
+
nextValue === null;
|
|
74
|
+
|
|
75
|
+
if (isPrimitive) {
|
|
76
|
+
changes.push({
|
|
77
|
+
key,
|
|
78
|
+
type: 'changed',
|
|
79
|
+
display: `~${key}: ${safeStringify(prevValue)} → ${safeStringify(nextValue)}`
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
changes.push({
|
|
83
|
+
key,
|
|
84
|
+
type: 'changed',
|
|
85
|
+
display: `~${key}`
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return changes;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const renderLogItem = ({ item }) => {
|
|
95
|
+
const actionPreview = formatActionPreview(item);
|
|
96
|
+
const actionColor = getZustandActionColor(item.action);
|
|
97
|
+
const timestamp = item.timestamp
|
|
98
|
+
? new Date(item.timestamp).toLocaleTimeString([], {
|
|
99
|
+
hour: '2-digit',
|
|
100
|
+
minute: '2-digit',
|
|
101
|
+
second: '2-digit',
|
|
102
|
+
hour12: false
|
|
103
|
+
})
|
|
104
|
+
: '';
|
|
105
|
+
|
|
106
|
+
// Get changed keys with details
|
|
107
|
+
const changes = findChangedKeys(item.prevState, item.nextState);
|
|
108
|
+
|
|
109
|
+
// Create a formatted display of changes
|
|
110
|
+
const changesText = changes.length
|
|
111
|
+
? changes.map(change => change.display).join(', ')
|
|
112
|
+
: 'No changes detected';
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<TouchableOpacity
|
|
116
|
+
style={styles.logItem}
|
|
117
|
+
onPress={() => setSelectedLog(item)}>
|
|
118
|
+
<View style={styles.logItemContainer}>
|
|
119
|
+
<View
|
|
120
|
+
style={[styles.actionIndicator, { backgroundColor: actionColor }]}
|
|
121
|
+
/>
|
|
122
|
+
|
|
123
|
+
<View style={styles.logContent}>
|
|
124
|
+
<View style={styles.logHeader}>
|
|
125
|
+
<Text style={[styles.actionText, { color: actionColor }]}>
|
|
126
|
+
{actionPreview}
|
|
127
|
+
</Text>
|
|
128
|
+
<Text style={styles.time}>{timestamp}</Text>
|
|
129
|
+
</View>
|
|
130
|
+
|
|
131
|
+
<Text style={styles.statePathsText} numberOfLines={2}>
|
|
132
|
+
{changesText}
|
|
133
|
+
</Text>
|
|
134
|
+
</View>
|
|
135
|
+
</View>
|
|
136
|
+
</TouchableOpacity>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// If a log is selected, show the details view
|
|
141
|
+
if (selectedLog) {
|
|
142
|
+
const timestamp = selectedLog.timestamp
|
|
143
|
+
? new Date(selectedLog.timestamp).toLocaleTimeString([], {
|
|
144
|
+
hour: '2-digit',
|
|
145
|
+
minute: '2-digit',
|
|
146
|
+
second: '2-digit',
|
|
147
|
+
hour12: false
|
|
148
|
+
})
|
|
149
|
+
: '';
|
|
150
|
+
const actionColor = getZustandActionColor(selectedLog.action);
|
|
151
|
+
const actionDisplay = selectedLog.storeName
|
|
152
|
+
? `${selectedLog.action} (${selectedLog.storeName})`
|
|
153
|
+
: selectedLog.action || 'Unknown Action';
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<View style={styles.container}>
|
|
157
|
+
<View style={styles.detailsHeader}>
|
|
158
|
+
<TouchableOpacity
|
|
159
|
+
style={styles.backButton}
|
|
160
|
+
onPress={() => setSelectedLog(null)}>
|
|
161
|
+
<Text style={styles.backButtonText}>← Back</Text>
|
|
162
|
+
</TouchableOpacity>
|
|
163
|
+
<View style={styles.headerActionTime}>
|
|
164
|
+
<Text style={[styles.headerAction, { color: actionColor }]}>
|
|
165
|
+
{actionDisplay}
|
|
166
|
+
</Text>
|
|
167
|
+
<Text style={styles.headerTimestamp}>{timestamp}</Text>
|
|
168
|
+
</View>
|
|
169
|
+
</View>
|
|
170
|
+
<ZustandLogDetails log={selectedLog} />
|
|
171
|
+
</View>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Otherwise show the list view
|
|
176
|
+
return (
|
|
177
|
+
<View style={styles.container}>
|
|
178
|
+
{sortedLogs.length === 0 ? (
|
|
179
|
+
<Text style={styles.emptyText}>No Zustand state changes logged yet</Text>
|
|
180
|
+
) : (
|
|
181
|
+
<FlatList
|
|
182
|
+
data={sortedLogs}
|
|
183
|
+
renderItem={renderLogItem}
|
|
184
|
+
keyExtractor={(item, index) => `${item.timestamp}-${index}`}
|
|
185
|
+
style={styles.list}
|
|
186
|
+
/>
|
|
187
|
+
)}
|
|
188
|
+
</View>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Adapted styles from SubViewConsoleLogs
|
|
193
|
+
const styles = StyleSheet.create({
|
|
194
|
+
container: {
|
|
195
|
+
flex: 1,
|
|
196
|
+
backgroundColor: '#fff',
|
|
197
|
+
},
|
|
198
|
+
list: {
|
|
199
|
+
flex: 1,
|
|
200
|
+
},
|
|
201
|
+
emptyText: {
|
|
202
|
+
textAlign: 'center',
|
|
203
|
+
color: '#999',
|
|
204
|
+
marginTop: 20,
|
|
205
|
+
},
|
|
206
|
+
logItem: {
|
|
207
|
+
borderBottomWidth: 1,
|
|
208
|
+
borderBottomColor: '#eee',
|
|
209
|
+
},
|
|
210
|
+
logItemContainer: {
|
|
211
|
+
flexDirection: 'row',
|
|
212
|
+
paddingVertical: 10,
|
|
213
|
+
paddingHorizontal: 12,
|
|
214
|
+
},
|
|
215
|
+
actionIndicator: {
|
|
216
|
+
width: 4,
|
|
217
|
+
borderRadius: 2,
|
|
218
|
+
marginRight: 10,
|
|
219
|
+
},
|
|
220
|
+
logContent: {
|
|
221
|
+
flex: 1,
|
|
222
|
+
},
|
|
223
|
+
logHeader: {
|
|
224
|
+
flexDirection: 'row',
|
|
225
|
+
justifyContent: 'space-between',
|
|
226
|
+
alignItems: 'center',
|
|
227
|
+
marginBottom: 5,
|
|
228
|
+
},
|
|
229
|
+
actionText: {
|
|
230
|
+
fontSize: 13,
|
|
231
|
+
fontWeight: 'bold',
|
|
232
|
+
},
|
|
233
|
+
time: {
|
|
234
|
+
fontSize: 12,
|
|
235
|
+
color: '#888',
|
|
236
|
+
},
|
|
237
|
+
statePathsText: {
|
|
238
|
+
fontSize: 14,
|
|
239
|
+
color: '#555',
|
|
240
|
+
fontFamily: 'monospace',
|
|
241
|
+
},
|
|
242
|
+
// Details Header Styles
|
|
243
|
+
detailsHeader: {
|
|
244
|
+
flexDirection: 'row',
|
|
245
|
+
alignItems: 'center',
|
|
246
|
+
paddingVertical: 10,
|
|
247
|
+
paddingHorizontal: 15,
|
|
248
|
+
borderBottomWidth: 1,
|
|
249
|
+
borderBottomColor: '#eee',
|
|
250
|
+
backgroundColor: '#f8f9fa',
|
|
251
|
+
},
|
|
252
|
+
backButton: {
|
|
253
|
+
paddingHorizontal: 10,
|
|
254
|
+
paddingVertical: 5,
|
|
255
|
+
borderRadius: 4,
|
|
256
|
+
backgroundColor: '#eee',
|
|
257
|
+
marginRight: 15,
|
|
258
|
+
},
|
|
259
|
+
backButtonText: {
|
|
260
|
+
color: '#333',
|
|
261
|
+
fontWeight: '500',
|
|
262
|
+
fontSize: 14,
|
|
263
|
+
},
|
|
264
|
+
headerActionTime: {
|
|
265
|
+
flexDirection: 'row',
|
|
266
|
+
alignItems: 'baseline',
|
|
267
|
+
},
|
|
268
|
+
headerAction: {
|
|
269
|
+
fontSize: 15,
|
|
270
|
+
fontWeight: 'bold',
|
|
271
|
+
marginRight: 8,
|
|
272
|
+
},
|
|
273
|
+
headerTimestamp: {
|
|
274
|
+
fontSize: 13,
|
|
275
|
+
color: '#666',
|
|
276
|
+
},
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
export default SubViewZustandLogs
|
|
@@ -0,0 +1,355 @@
|
|
|
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 { getZustandActionColor } 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
|
+
const ZustandLogDetails = ({ log }) => {
|
|
62
|
+
if (!log) {
|
|
63
|
+
return (
|
|
64
|
+
<View style={styles.errorContainer}>
|
|
65
|
+
<Text style={styles.errorText}>Log data is missing</Text>
|
|
66
|
+
</View>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const { timestamp, action, prevState, nextState, actionCompleteTime, storeName } = log
|
|
71
|
+
const actionColor = getZustandActionColor(action);
|
|
72
|
+
|
|
73
|
+
// Format data for display and copying
|
|
74
|
+
const formatStateToString = (state) => {
|
|
75
|
+
try {
|
|
76
|
+
return JSON.stringify(state, null, 2);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return '[unserializable state]';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const formattedPrevState = formatStateToString(prevState);
|
|
83
|
+
const formattedNextState = formatStateToString(nextState);
|
|
84
|
+
|
|
85
|
+
// Format for the full log copy
|
|
86
|
+
const formattedLog = `Action: ${action}${storeName ? ` (${storeName})` : ''}
|
|
87
|
+
Previous State: ${formattedPrevState}
|
|
88
|
+
Next State: ${formattedNextState}${actionCompleteTime ? `\nAction Complete Time: ${actionCompleteTime}ms` : ''}`;
|
|
89
|
+
|
|
90
|
+
// Function to find differences between states
|
|
91
|
+
const findDifferences = () => {
|
|
92
|
+
const changes = [];
|
|
93
|
+
|
|
94
|
+
if (!prevState || !nextState) return changes;
|
|
95
|
+
|
|
96
|
+
// Helper to find differences in objects recursively
|
|
97
|
+
const findChangesRecursive = (prev, next, path = '') => {
|
|
98
|
+
if (prev === next) return;
|
|
99
|
+
|
|
100
|
+
if (typeof prev !== 'object' || typeof next !== 'object' ||
|
|
101
|
+
prev === null || next === null) {
|
|
102
|
+
changes.push({
|
|
103
|
+
path: path || 'root',
|
|
104
|
+
prevValue: prev,
|
|
105
|
+
nextValue: next
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check for object keys
|
|
111
|
+
const allKeys = [...new Set([...Object.keys(prev || {}), ...Object.keys(next || {})])];
|
|
112
|
+
|
|
113
|
+
for (const key of allKeys) {
|
|
114
|
+
const newPath = path ? `${path}.${key}` : key;
|
|
115
|
+
if (!(key in prev)) {
|
|
116
|
+
changes.push({
|
|
117
|
+
path: newPath,
|
|
118
|
+
prevValue: undefined,
|
|
119
|
+
nextValue: next[key]
|
|
120
|
+
});
|
|
121
|
+
} else if (!(key in next)) {
|
|
122
|
+
changes.push({
|
|
123
|
+
path: newPath,
|
|
124
|
+
prevValue: prev[key],
|
|
125
|
+
nextValue: undefined
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
findChangesRecursive(prev[key], next[key], newPath);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
findChangesRecursive(prevState, nextState);
|
|
134
|
+
return changes;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const stateChanges = findDifferences();
|
|
138
|
+
const hasChanges = stateChanges.length > 0;
|
|
139
|
+
|
|
140
|
+
const actionDisplay = storeName ? `${action} (${storeName})` : action;
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<ScrollView
|
|
144
|
+
style={styles.container}
|
|
145
|
+
contentContainerStyle={styles.contentContainer}
|
|
146
|
+
showsVerticalScrollIndicator={true}
|
|
147
|
+
scrollEventThrottle={16}
|
|
148
|
+
keyboardShouldPersistTaps='handled'>
|
|
149
|
+
|
|
150
|
+
<View style={styles.header}>
|
|
151
|
+
<View style={styles.headerInfo}>
|
|
152
|
+
<Text style={styles.actionIndicator}>
|
|
153
|
+
Action: <Text style={[styles.actionName, { color: actionColor }]}>{actionDisplay}</Text>
|
|
154
|
+
</Text>
|
|
155
|
+
<Text style={styles.timestamp}>
|
|
156
|
+
{timestamp
|
|
157
|
+
? new Date(timestamp).toLocaleString()
|
|
158
|
+
: 'Unknown time'}
|
|
159
|
+
</Text>
|
|
160
|
+
{actionCompleteTime && (
|
|
161
|
+
<Text style={styles.actionTime}>
|
|
162
|
+
Completed in: {actionCompleteTime}ms
|
|
163
|
+
</Text>
|
|
164
|
+
)}
|
|
165
|
+
</View>
|
|
166
|
+
<CopyButton text={formattedLog} />
|
|
167
|
+
</View>
|
|
168
|
+
|
|
169
|
+
{hasChanges && (
|
|
170
|
+
<CollapsibleSection title='State Changes' initiallyExpanded={true}>
|
|
171
|
+
<View style={styles.dataContentWrapper}>
|
|
172
|
+
<View style={styles.dataContent}>
|
|
173
|
+
{stateChanges.map((change, index) => (
|
|
174
|
+
<View key={index} style={[styles.changeItem, index === stateChanges.length - 1 && styles.changeItemLast]}>
|
|
175
|
+
<Text style={styles.changePath}>{change.path}</Text>
|
|
176
|
+
<View style={styles.changeValues}>
|
|
177
|
+
<View style={styles.changeValue}>
|
|
178
|
+
<Text style={styles.changeLabel}>From:</Text>
|
|
179
|
+
<JSONTree
|
|
180
|
+
data={change.prevValue}
|
|
181
|
+
theme={theme}
|
|
182
|
+
invertTheme={true}
|
|
183
|
+
hideRoot={true}
|
|
184
|
+
shouldExpandNode={() => true}
|
|
185
|
+
/>
|
|
186
|
+
</View>
|
|
187
|
+
<View style={styles.changeValue}>
|
|
188
|
+
<Text style={styles.changeLabel}>To:</Text>
|
|
189
|
+
<JSONTree
|
|
190
|
+
data={change.nextValue}
|
|
191
|
+
theme={theme}
|
|
192
|
+
invertTheme={true}
|
|
193
|
+
hideRoot={true}
|
|
194
|
+
shouldExpandNode={() => true}
|
|
195
|
+
/>
|
|
196
|
+
</View>
|
|
197
|
+
</View>
|
|
198
|
+
</View>
|
|
199
|
+
))}
|
|
200
|
+
</View>
|
|
201
|
+
</View>
|
|
202
|
+
</CollapsibleSection>
|
|
203
|
+
)}
|
|
204
|
+
|
|
205
|
+
<CollapsibleSection title='Previous State' initiallyExpanded={false}>
|
|
206
|
+
<View style={styles.dataContentWrapper}>
|
|
207
|
+
<View style={styles.dataContent}>
|
|
208
|
+
<JSONTree
|
|
209
|
+
data={prevState || {}}
|
|
210
|
+
theme={theme}
|
|
211
|
+
invertTheme={true}
|
|
212
|
+
hideRoot={true}
|
|
213
|
+
shouldExpandNode={(keyPath, data, level) => level < 1}
|
|
214
|
+
/>
|
|
215
|
+
</View>
|
|
216
|
+
</View>
|
|
217
|
+
</CollapsibleSection>
|
|
218
|
+
|
|
219
|
+
<CollapsibleSection title='Next State' initiallyExpanded={true}>
|
|
220
|
+
<View style={styles.dataContentWrapper}>
|
|
221
|
+
<View style={styles.dataContent}>
|
|
222
|
+
<JSONTree
|
|
223
|
+
data={nextState || {}}
|
|
224
|
+
theme={theme}
|
|
225
|
+
invertTheme={true}
|
|
226
|
+
hideRoot={true}
|
|
227
|
+
shouldExpandNode={(keyPath, data, level) => level < 1}
|
|
228
|
+
/>
|
|
229
|
+
</View>
|
|
230
|
+
</View>
|
|
231
|
+
</CollapsibleSection>
|
|
232
|
+
|
|
233
|
+
</ScrollView>
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Re-using and adapting styles from ConsoleLogDetails
|
|
238
|
+
const styles = StyleSheet.create({
|
|
239
|
+
container: {
|
|
240
|
+
flex: 1,
|
|
241
|
+
backgroundColor: '#fff',
|
|
242
|
+
},
|
|
243
|
+
contentContainer: {
|
|
244
|
+
paddingBottom: 20,
|
|
245
|
+
},
|
|
246
|
+
header: {
|
|
247
|
+
flexDirection: 'row',
|
|
248
|
+
padding: 15,
|
|
249
|
+
borderBottomWidth: 1,
|
|
250
|
+
borderBottomColor: '#eee',
|
|
251
|
+
alignItems: 'center',
|
|
252
|
+
justifyContent: 'space-between',
|
|
253
|
+
},
|
|
254
|
+
headerInfo: {
|
|
255
|
+
flexShrink: 1,
|
|
256
|
+
marginRight: 10,
|
|
257
|
+
},
|
|
258
|
+
actionIndicator: {
|
|
259
|
+
fontSize: 14,
|
|
260
|
+
marginBottom: 5,
|
|
261
|
+
},
|
|
262
|
+
actionName: {
|
|
263
|
+
fontWeight: 'bold',
|
|
264
|
+
},
|
|
265
|
+
timestamp: {
|
|
266
|
+
fontSize: 13,
|
|
267
|
+
color: '#666',
|
|
268
|
+
},
|
|
269
|
+
actionTime: {
|
|
270
|
+
fontSize: 13,
|
|
271
|
+
color: '#666',
|
|
272
|
+
marginTop: 2,
|
|
273
|
+
},
|
|
274
|
+
collapsibleSection: {
|
|
275
|
+
marginBottom: 1,
|
|
276
|
+
backgroundColor: '#fff',
|
|
277
|
+
},
|
|
278
|
+
sectionHeader: {
|
|
279
|
+
flexDirection: 'row',
|
|
280
|
+
justifyContent: 'space-between',
|
|
281
|
+
alignItems: 'center',
|
|
282
|
+
padding: 15,
|
|
283
|
+
backgroundColor: '#f5f5f5',
|
|
284
|
+
},
|
|
285
|
+
sectionTitle: {
|
|
286
|
+
fontSize: 15,
|
|
287
|
+
fontWeight: 'bold',
|
|
288
|
+
color: '#333',
|
|
289
|
+
},
|
|
290
|
+
expandIcon: {
|
|
291
|
+
fontSize: 14,
|
|
292
|
+
color: '#666',
|
|
293
|
+
},
|
|
294
|
+
dataContentWrapper: {
|
|
295
|
+
flex: 1,
|
|
296
|
+
padding: 10,
|
|
297
|
+
},
|
|
298
|
+
dataContent: {
|
|
299
|
+
backgroundColor: '#f8f9fa',
|
|
300
|
+
padding: 10,
|
|
301
|
+
borderRadius: 4,
|
|
302
|
+
borderWidth: 1,
|
|
303
|
+
borderColor: '#e9ecef',
|
|
304
|
+
},
|
|
305
|
+
errorContainer: {
|
|
306
|
+
flex: 1,
|
|
307
|
+
justifyContent: 'center',
|
|
308
|
+
alignItems: 'center',
|
|
309
|
+
padding: 20,
|
|
310
|
+
},
|
|
311
|
+
errorText: {
|
|
312
|
+
color: '#ff4444',
|
|
313
|
+
fontSize: 16,
|
|
314
|
+
},
|
|
315
|
+
copyButton: {
|
|
316
|
+
backgroundColor: '#e9ecef',
|
|
317
|
+
paddingHorizontal: 10,
|
|
318
|
+
paddingVertical: 5,
|
|
319
|
+
borderRadius: 4,
|
|
320
|
+
flexShrink: 0,
|
|
321
|
+
},
|
|
322
|
+
copyButtonText: {
|
|
323
|
+
fontSize: 12,
|
|
324
|
+
color: '#666',
|
|
325
|
+
},
|
|
326
|
+
changeItem: {
|
|
327
|
+
borderBottomWidth: 1,
|
|
328
|
+
borderBottomColor: '#eee',
|
|
329
|
+
paddingVertical: 8,
|
|
330
|
+
},
|
|
331
|
+
changeItemLast: {
|
|
332
|
+
borderBottomWidth: 0,
|
|
333
|
+
},
|
|
334
|
+
changePath: {
|
|
335
|
+
fontWeight: 'bold',
|
|
336
|
+
color: '#333',
|
|
337
|
+
marginBottom: 5,
|
|
338
|
+
fontFamily: 'monospace',
|
|
339
|
+
},
|
|
340
|
+
changeValues: {
|
|
341
|
+
flexDirection: 'row',
|
|
342
|
+
justifyContent: 'space-between',
|
|
343
|
+
},
|
|
344
|
+
changeValue: {
|
|
345
|
+
flex: 1,
|
|
346
|
+
padding: 5,
|
|
347
|
+
},
|
|
348
|
+
changeLabel: {
|
|
349
|
+
color: '#666',
|
|
350
|
+
fontSize: 12,
|
|
351
|
+
marginBottom: 3,
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
export default ZustandLogDetails
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-debug-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A simple yet powerful debugging toolkit for React Native with a convenient floating UI for development",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"author": "zcj",
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"
|
|
26
|
+
"react-native-mmkv": "2.12.2",
|
|
27
27
|
"react-native-json-tree": "^1.5.0",
|
|
28
28
|
"react-native-performance": "5.1.2",
|
|
29
29
|
"react-native-root-siblings": "^4.0.0"
|