UNPKG

react-native-debug-toolkit

Version:

A simple yet powerful debugging toolkit for React Native with a convenient floating UI for development

315 lines (288 loc) 7.72 kB
import React from 'react' import { View, Text, StyleSheet, Clipboard } from 'react-native' import { ScrollView, Pressable } from 'react-native' import JSONTree from 'react-native-json-tree' // Re-using the theme from HttpLogDetails for consistency const theme = { scheme: 'monokai', author: 'wimer hazenberg (http://www.monokai.nl)', base00: '#272822', base01: '#383830', base02: '#49483e', base03: '#75715e', base04: '#a59f85', base05: '#f8f8f2', base06: '#f5f4f1', base07: '#f9f8f5', base08: '#f92672', base09: '#fd971f', base0A: '#f4bf75', base0B: '#a6e22e', base0C: '#a1efe4', base0D: '#66d9ef', base0E: '#ae81ff', base0F: '#cc6633' }; const CopyButton = ({ text, style }) => { const [copied, setCopied] = React.useState(false) const handleCopy = async () => { await Clipboard.setString(text) setCopied(true) setTimeout(() => setCopied(false), 2000) } return ( <Pressable style={[styles.copyButton, style]} onPress={handleCopy}> <Text style={styles.copyButtonText}>{copied ? 'Copied!' : 'Copy'}</Text> </Pressable> ) } const CollapsibleSection = ({ title, children, initiallyExpanded = false }) => { const [expanded, setExpanded] = React.useState(initiallyExpanded) return ( <View style={styles.collapsibleSection}> <Pressable style={styles.sectionHeader} onPress={() => setExpanded(!expanded)}> <Text style={styles.sectionTitle}>{title}</Text> <Text style={styles.expandIcon}>{expanded ? '▼' : '▶'}</Text> </Pressable> {expanded && children} </View> ) } // Basic JSONValue renderer, might need adjustments for console log specifics const JSONValue = ({ value }) => { if (value === null) { return <Text style={styles.jsonNull}>null</Text> } if (value === undefined) { return <Text style={styles.jsonNull}>undefined</Text> } if (typeof value === 'boolean') { return <Text style={styles.jsonBoolean}>{value.toString()}</Text> } if (typeof value === 'number') { return <Text style={styles.jsonNumber}>{value}</Text> } if (typeof value === 'string') { return ( <Text style={styles.jsonString} selectable={true}> "{value}" </Text> ) } if (typeof value === 'object') { return ( <JSONTree data={value} theme={theme} invertTheme={true} hideRoot={true} shouldExpandNode={(keyPath, nodeData, currentLevel) => currentLevel < 1} /> ) } return <Text style={styles.jsonOther}>{String(value)}</Text> } const ConsoleLogDetails = ({ log }) => { if (!log) { return ( <View style={styles.errorContainer}> <Text style={styles.errorText}>Log data is missing</Text> </View> ) } const logData = log.data || [] // Console logs can have multiple arguments const timestamp = log.timestamp const level = log.level || 'log' // Default to 'log' if level is not provided // Simple function to format log data for display and copying const formatLogData = (dataArray) => { return dataArray.map(item => { if (typeof item === 'object') { try { return JSON.stringify(item, null, 2); } catch (e) { return '[unserializable object]'; } } return String(item); }).join(' '); } const formattedLog = formatLogData(logData); return ( <ScrollView style={styles.container} contentContainerStyle={styles.contentContainer} showsVerticalScrollIndicator={true} scrollEventThrottle={16} keyboardShouldPersistTaps='handled'> <View style={styles.header}> <View style={styles.headerInfo}> <Text style={[styles.levelIndicator, { color: getLevelColor(level) }]}> {level.toUpperCase()} </Text> <Text style={styles.timestamp}> {timestamp ? new Date(timestamp).toLocaleString() : 'Unknown time'} </Text> </View> <CopyButton text={formattedLog} /> </View> <CollapsibleSection title='Log Data' initiallyExpanded={true}> <View style={styles.dataContentWrapper}> <View style={styles.dataContent}> {logData.map((item, index) => ( <View key={index} style={[styles.logDataItem, index === logData.length - 1 && styles.logDataItemLast]}> <JSONValue value={item} /> </View> ))} </View> </View> </CollapsibleSection> </ScrollView> ) } const getLevelColor = (level) => { switch (level?.toLowerCase()) { case 'log': return '#333'; // Dark gray for standard log case 'info': return '#0D96F2'; // Blue for info case 'warn': return '#FCA130'; // Orange for warning case 'error': return '#F93E3E'; // Red for error default: return '#666666'; // Default gray } }; const getLevelTextStyle = (level) => { const baseStyle = { fontSize: 14, fontFamily: 'monospace' }; // Use monospace font return { ...baseStyle, color: getLevelColor(level) }; } // Re-using and adapting styles from HttpLogDetails const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', }, contentContainer: { paddingBottom: 20, }, header: { flexDirection: 'row', padding: 15, borderBottomWidth: 1, borderBottomColor: '#eee', alignItems: 'center', // Align items vertically justifyContent: 'space-between', // Space out items }, headerInfo: { flexDirection: 'row', // Arrange level and timestamp horizontally alignItems: 'center', flexShrink: 1, // Allow info to shrink if needed marginRight: 10, }, levelIndicator: { fontSize: 14, fontWeight: 'bold', marginRight: 10, }, timestamp: { fontSize: 13, color: '#666', }, collapsibleSection: { marginBottom: 1, backgroundColor: '#fff', }, sectionHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 15, backgroundColor: '#f5f5f5', }, sectionTitle: { fontSize: 15, fontWeight: 'bold', color: '#333', }, expandIcon: { fontSize: 14, color: '#666', }, content: { padding: 15, }, dataContentWrapper: { flex: 1, padding: 10, // Add padding around the scroll view }, dataContent: { backgroundColor: '#f8f9fa', padding: 10, borderRadius: 4, borderWidth: 1, borderColor: '#e9ecef', }, logDataItem: { borderBottomWidth: 1, borderBottomColor: '#eee', paddingVertical: 8, }, logDataItemLast: { borderBottomWidth: 0, }, errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20, }, errorText: { color: '#ff4444', fontSize: 16, }, copyButton: { backgroundColor: '#e9ecef', paddingHorizontal: 10, paddingVertical: 5, borderRadius: 4, flexShrink: 0, // Prevent button from shrinking }, copyButtonText: { fontSize: 12, color: '#666', }, // JSON Value Styles (simplified) jsonString: { color: '#CB772F', fontFamily: 'monospace', fontSize: 13, }, jsonNumber: { color: '#AE81FF', fontFamily: 'monospace', fontSize: 13, }, jsonBoolean: { color: '#66D9EF', fontWeight: 'bold', fontFamily: 'monospace', fontSize: 13, }, jsonNull: { color: '#F92672', fontStyle: 'italic', fontFamily: 'monospace', fontSize: 13, }, jsonOther: { color: '#75715e', fontFamily: 'monospace', fontSize: 13, }, }); export default ConsoleLogDetails