react-native-prod-debugger 1.0.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/LICENSE +21 -0
- package/README.md +350 -0
- package/lib/commonjs/core/DebugBubble.js +139 -0
- package/lib/commonjs/core/DebugBubble.js.map +1 -0
- package/lib/commonjs/core/DebugOverlay.js +184 -0
- package/lib/commonjs/core/DebugOverlay.js.map +1 -0
- package/lib/commonjs/core/DebuggerProvider.js +174 -0
- package/lib/commonjs/core/DebuggerProvider.js.map +1 -0
- package/lib/commonjs/core/GestureDetector.js +83 -0
- package/lib/commonjs/core/GestureDetector.js.map +1 -0
- package/lib/commonjs/core/PluginRegistry.js +124 -0
- package/lib/commonjs/core/PluginRegistry.js.map +1 -0
- package/lib/commonjs/core/theme.js +50 -0
- package/lib/commonjs/core/theme.js.map +1 -0
- package/lib/commonjs/core/types.js +6 -0
- package/lib/commonjs/core/types.js.map +1 -0
- package/lib/commonjs/core/useDebugger.js +27 -0
- package/lib/commonjs/core/useDebugger.js.map +1 -0
- package/lib/commonjs/core/utils.js +239 -0
- package/lib/commonjs/core/utils.js.map +1 -0
- package/lib/commonjs/index.js +132 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/plugins/console/ConsoleInterceptor.js +103 -0
- package/lib/commonjs/plugins/console/ConsoleInterceptor.js.map +1 -0
- package/lib/commonjs/plugins/console/ConsoleViewerPlugin.js +269 -0
- package/lib/commonjs/plugins/console/ConsoleViewerPlugin.js.map +1 -0
- package/lib/commonjs/plugins/crashReporter/CrashReporterPlugin.js +363 -0
- package/lib/commonjs/plugins/crashReporter/CrashReporterPlugin.js.map +1 -0
- package/lib/commonjs/plugins/customActions/CustomActionsPlugin.js +218 -0
- package/lib/commonjs/plugins/customActions/CustomActionsPlugin.js.map +1 -0
- package/lib/commonjs/plugins/customActions/actionStore.js +46 -0
- package/lib/commonjs/plugins/customActions/actionStore.js.map +1 -0
- package/lib/commonjs/plugins/deepLinkTester/DeepLinkTesterPlugin.js +268 -0
- package/lib/commonjs/plugins/deepLinkTester/DeepLinkTesterPlugin.js.map +1 -0
- package/lib/commonjs/plugins/deviceInfo/DeviceInfoPlugin.js +184 -0
- package/lib/commonjs/plugins/deviceInfo/DeviceInfoPlugin.js.map +1 -0
- package/lib/commonjs/plugins/featureFlags/FeatureFlagsPlugin.js +381 -0
- package/lib/commonjs/plugins/featureFlags/FeatureFlagsPlugin.js.map +1 -0
- package/lib/commonjs/plugins/featureFlags/flagStore.js +125 -0
- package/lib/commonjs/plugins/featureFlags/flagStore.js.map +1 -0
- package/lib/commonjs/plugins/navigationInspector/NavigationInspectorPlugin.js +250 -0
- package/lib/commonjs/plugins/navigationInspector/NavigationInspectorPlugin.js.map +1 -0
- package/lib/commonjs/plugins/navigationInspector/navigationStore.js +65 -0
- package/lib/commonjs/plugins/navigationInspector/navigationStore.js.map +1 -0
- package/lib/commonjs/plugins/network/NetworkInspectorPlugin.js +709 -0
- package/lib/commonjs/plugins/network/NetworkInspectorPlugin.js.map +1 -0
- package/lib/commonjs/plugins/network/NetworkInterceptor.js +305 -0
- package/lib/commonjs/plugins/network/NetworkInterceptor.js.map +1 -0
- package/lib/commonjs/plugins/performance/PerformanceMonitorPlugin.js +261 -0
- package/lib/commonjs/plugins/performance/PerformanceMonitorPlugin.js.map +1 -0
- package/lib/commonjs/plugins/remoteConfig/RemoteConfigPlugin.js +265 -0
- package/lib/commonjs/plugins/remoteConfig/RemoteConfigPlugin.js.map +1 -0
- package/lib/commonjs/plugins/remoteConfig/remoteConfigStore.js +50 -0
- package/lib/commonjs/plugins/remoteConfig/remoteConfigStore.js.map +1 -0
- package/lib/commonjs/plugins/stateInspector/StateInspectorPlugin.js +378 -0
- package/lib/commonjs/plugins/stateInspector/StateInspectorPlugin.js.map +1 -0
- package/lib/commonjs/plugins/stateInspector/stateAdapterRegistry.js +62 -0
- package/lib/commonjs/plugins/stateInspector/stateAdapterRegistry.js.map +1 -0
- package/lib/commonjs/plugins/storageBrowser/StorageBrowserPlugin.js +496 -0
- package/lib/commonjs/plugins/storageBrowser/StorageBrowserPlugin.js.map +1 -0
- package/lib/commonjs/plugins/storageBrowser/storageAdapterRegistry.js +44 -0
- package/lib/commonjs/plugins/storageBrowser/storageAdapterRegistry.js.map +1 -0
- package/lib/commonjs/plugins/timeline/TimelinePlugin.js +294 -0
- package/lib/commonjs/plugins/timeline/TimelinePlugin.js.map +1 -0
- package/lib/commonjs/plugins/timeline/timelineStore.js +61 -0
- package/lib/commonjs/plugins/timeline/timelineStore.js.map +1 -0
- package/lib/module/core/DebugBubble.js +131 -0
- package/lib/module/core/DebugBubble.js.map +1 -0
- package/lib/module/core/DebugOverlay.js +176 -0
- package/lib/module/core/DebugOverlay.js.map +1 -0
- package/lib/module/core/DebuggerProvider.js +167 -0
- package/lib/module/core/DebuggerProvider.js.map +1 -0
- package/lib/module/core/GestureDetector.js +75 -0
- package/lib/module/core/GestureDetector.js.map +1 -0
- package/lib/module/core/PluginRegistry.js +116 -0
- package/lib/module/core/PluginRegistry.js.map +1 -0
- package/lib/module/core/theme.js +43 -0
- package/lib/module/core/theme.js.map +1 -0
- package/lib/module/core/types.js +2 -0
- package/lib/module/core/types.js.map +1 -0
- package/lib/module/core/useDebugger.js +21 -0
- package/lib/module/core/useDebugger.js.map +1 -0
- package/lib/module/core/utils.js +219 -0
- package/lib/module/core/utils.js.map +1 -0
- package/lib/module/index.js +44 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/plugins/console/ConsoleInterceptor.js +97 -0
- package/lib/module/plugins/console/ConsoleInterceptor.js.map +1 -0
- package/lib/module/plugins/console/ConsoleViewerPlugin.js +262 -0
- package/lib/module/plugins/console/ConsoleViewerPlugin.js.map +1 -0
- package/lib/module/plugins/crashReporter/CrashReporterPlugin.js +357 -0
- package/lib/module/plugins/crashReporter/CrashReporterPlugin.js.map +1 -0
- package/lib/module/plugins/customActions/CustomActionsPlugin.js +211 -0
- package/lib/module/plugins/customActions/CustomActionsPlugin.js.map +1 -0
- package/lib/module/plugins/customActions/actionStore.js +38 -0
- package/lib/module/plugins/customActions/actionStore.js.map +1 -0
- package/lib/module/plugins/deepLinkTester/DeepLinkTesterPlugin.js +261 -0
- package/lib/module/plugins/deepLinkTester/DeepLinkTesterPlugin.js.map +1 -0
- package/lib/module/plugins/deviceInfo/DeviceInfoPlugin.js +177 -0
- package/lib/module/plugins/deviceInfo/DeviceInfoPlugin.js.map +1 -0
- package/lib/module/plugins/featureFlags/FeatureFlagsPlugin.js +374 -0
- package/lib/module/plugins/featureFlags/FeatureFlagsPlugin.js.map +1 -0
- package/lib/module/plugins/featureFlags/flagStore.js +117 -0
- package/lib/module/plugins/featureFlags/flagStore.js.map +1 -0
- package/lib/module/plugins/navigationInspector/NavigationInspectorPlugin.js +243 -0
- package/lib/module/plugins/navigationInspector/NavigationInspectorPlugin.js.map +1 -0
- package/lib/module/plugins/navigationInspector/navigationStore.js +58 -0
- package/lib/module/plugins/navigationInspector/navigationStore.js.map +1 -0
- package/lib/module/plugins/network/NetworkInspectorPlugin.js +703 -0
- package/lib/module/plugins/network/NetworkInspectorPlugin.js.map +1 -0
- package/lib/module/plugins/network/NetworkInterceptor.js +299 -0
- package/lib/module/plugins/network/NetworkInterceptor.js.map +1 -0
- package/lib/module/plugins/performance/PerformanceMonitorPlugin.js +254 -0
- package/lib/module/plugins/performance/PerformanceMonitorPlugin.js.map +1 -0
- package/lib/module/plugins/remoteConfig/RemoteConfigPlugin.js +258 -0
- package/lib/module/plugins/remoteConfig/RemoteConfigPlugin.js.map +1 -0
- package/lib/module/plugins/remoteConfig/remoteConfigStore.js +43 -0
- package/lib/module/plugins/remoteConfig/remoteConfigStore.js.map +1 -0
- package/lib/module/plugins/stateInspector/StateInspectorPlugin.js +372 -0
- package/lib/module/plugins/stateInspector/StateInspectorPlugin.js.map +1 -0
- package/lib/module/plugins/stateInspector/stateAdapterRegistry.js +54 -0
- package/lib/module/plugins/stateInspector/stateAdapterRegistry.js.map +1 -0
- package/lib/module/plugins/storageBrowser/StorageBrowserPlugin.js +489 -0
- package/lib/module/plugins/storageBrowser/StorageBrowserPlugin.js.map +1 -0
- package/lib/module/plugins/storageBrowser/storageAdapterRegistry.js +36 -0
- package/lib/module/plugins/storageBrowser/storageAdapterRegistry.js.map +1 -0
- package/lib/module/plugins/timeline/TimelinePlugin.js +287 -0
- package/lib/module/plugins/timeline/TimelinePlugin.js.map +1 -0
- package/lib/module/plugins/timeline/timelineStore.js +54 -0
- package/lib/module/plugins/timeline/timelineStore.js.map +1 -0
- package/lib/typescript/core/DebugBubble.d.ts +22 -0
- package/lib/typescript/core/DebugBubble.d.ts.map +1 -0
- package/lib/typescript/core/DebugOverlay.d.ts +18 -0
- package/lib/typescript/core/DebugOverlay.d.ts.map +1 -0
- package/lib/typescript/core/DebuggerProvider.d.ts +25 -0
- package/lib/typescript/core/DebuggerProvider.d.ts.map +1 -0
- package/lib/typescript/core/GestureDetector.d.ts +20 -0
- package/lib/typescript/core/GestureDetector.d.ts.map +1 -0
- package/lib/typescript/core/PluginRegistry.d.ts +41 -0
- package/lib/typescript/core/PluginRegistry.d.ts.map +1 -0
- package/lib/typescript/core/theme.d.ts +11 -0
- package/lib/typescript/core/theme.d.ts.map +1 -0
- package/lib/typescript/core/types.d.ts +269 -0
- package/lib/typescript/core/types.d.ts.map +1 -0
- package/lib/typescript/core/useDebugger.d.ts +14 -0
- package/lib/typescript/core/useDebugger.d.ts.map +1 -0
- package/lib/typescript/core/utils.d.ts +46 -0
- package/lib/typescript/core/utils.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +23 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/plugins/console/ConsoleInterceptor.d.ts +32 -0
- package/lib/typescript/plugins/console/ConsoleInterceptor.d.ts.map +1 -0
- package/lib/typescript/plugins/console/ConsoleViewerPlugin.d.ts +3 -0
- package/lib/typescript/plugins/console/ConsoleViewerPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/crashReporter/CrashReporterPlugin.d.ts +3 -0
- package/lib/typescript/plugins/crashReporter/CrashReporterPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/customActions/CustomActionsPlugin.d.ts +3 -0
- package/lib/typescript/plugins/customActions/CustomActionsPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/customActions/actionStore.d.ts +16 -0
- package/lib/typescript/plugins/customActions/actionStore.d.ts.map +1 -0
- package/lib/typescript/plugins/deepLinkTester/DeepLinkTesterPlugin.d.ts +3 -0
- package/lib/typescript/plugins/deepLinkTester/DeepLinkTesterPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/deviceInfo/DeviceInfoPlugin.d.ts +3 -0
- package/lib/typescript/plugins/deviceInfo/DeviceInfoPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/featureFlags/FeatureFlagsPlugin.d.ts +3 -0
- package/lib/typescript/plugins/featureFlags/FeatureFlagsPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/featureFlags/flagStore.d.ts +45 -0
- package/lib/typescript/plugins/featureFlags/flagStore.d.ts.map +1 -0
- package/lib/typescript/plugins/navigationInspector/NavigationInspectorPlugin.d.ts +3 -0
- package/lib/typescript/plugins/navigationInspector/NavigationInspectorPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/navigationInspector/navigationStore.d.ts +30 -0
- package/lib/typescript/plugins/navigationInspector/navigationStore.d.ts.map +1 -0
- package/lib/typescript/plugins/network/NetworkInspectorPlugin.d.ts +3 -0
- package/lib/typescript/plugins/network/NetworkInspectorPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/network/NetworkInterceptor.d.ts +42 -0
- package/lib/typescript/plugins/network/NetworkInterceptor.d.ts.map +1 -0
- package/lib/typescript/plugins/performance/PerformanceMonitorPlugin.d.ts +3 -0
- package/lib/typescript/plugins/performance/PerformanceMonitorPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/remoteConfig/RemoteConfigPlugin.d.ts +3 -0
- package/lib/typescript/plugins/remoteConfig/RemoteConfigPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/remoteConfig/remoteConfigStore.d.ts +20 -0
- package/lib/typescript/plugins/remoteConfig/remoteConfigStore.d.ts.map +1 -0
- package/lib/typescript/plugins/stateInspector/StateInspectorPlugin.d.ts +3 -0
- package/lib/typescript/plugins/stateInspector/StateInspectorPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/stateInspector/stateAdapterRegistry.d.ts +34 -0
- package/lib/typescript/plugins/stateInspector/stateAdapterRegistry.d.ts.map +1 -0
- package/lib/typescript/plugins/storageBrowser/StorageBrowserPlugin.d.ts +3 -0
- package/lib/typescript/plugins/storageBrowser/StorageBrowserPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/storageBrowser/storageAdapterRegistry.d.ts +16 -0
- package/lib/typescript/plugins/storageBrowser/storageAdapterRegistry.d.ts.map +1 -0
- package/lib/typescript/plugins/timeline/TimelinePlugin.d.ts +3 -0
- package/lib/typescript/plugins/timeline/TimelinePlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/timeline/timelineStore.d.ts +24 -0
- package/lib/typescript/plugins/timeline/timelineStore.d.ts.map +1 -0
- package/package.json +131 -0
- package/src/core/DebugBubble.tsx +149 -0
- package/src/core/DebugOverlay.tsx +211 -0
- package/src/core/DebuggerProvider.tsx +211 -0
- package/src/core/GestureDetector.tsx +95 -0
- package/src/core/PluginRegistry.ts +118 -0
- package/src/core/theme.ts +41 -0
- package/src/core/types.ts +339 -0
- package/src/core/useDebugger.ts +24 -0
- package/src/core/utils.ts +221 -0
- package/src/index.ts +67 -0
- package/src/plugins/console/ConsoleInterceptor.ts +110 -0
- package/src/plugins/console/ConsoleViewerPlugin.tsx +241 -0
- package/src/plugins/crashReporter/CrashReporterPlugin.tsx +316 -0
- package/src/plugins/customActions/CustomActionsPlugin.tsx +199 -0
- package/src/plugins/customActions/actionStore.ts +49 -0
- package/src/plugins/deepLinkTester/DeepLinkTesterPlugin.tsx +213 -0
- package/src/plugins/deviceInfo/DeviceInfoPlugin.tsx +153 -0
- package/src/plugins/featureFlags/FeatureFlagsPlugin.tsx +338 -0
- package/src/plugins/featureFlags/flagStore.ts +118 -0
- package/src/plugins/navigationInspector/NavigationInspectorPlugin.tsx +170 -0
- package/src/plugins/navigationInspector/navigationStore.ts +70 -0
- package/src/plugins/network/NetworkInspectorPlugin.tsx +598 -0
- package/src/plugins/network/NetworkInterceptor.ts +344 -0
- package/src/plugins/performance/PerformanceMonitorPlugin.tsx +194 -0
- package/src/plugins/remoteConfig/RemoteConfigPlugin.tsx +205 -0
- package/src/plugins/remoteConfig/remoteConfigStore.ts +48 -0
- package/src/plugins/stateInspector/StateInspectorPlugin.tsx +342 -0
- package/src/plugins/stateInspector/stateAdapterRegistry.ts +66 -0
- package/src/plugins/storageBrowser/StorageBrowserPlugin.tsx +410 -0
- package/src/plugins/storageBrowser/storageAdapterRegistry.ts +47 -0
- package/src/plugins/timeline/TimelinePlugin.tsx +242 -0
- package/src/plugins/timeline/timelineStore.ts +65 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
ScrollView,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
Alert,
|
|
9
|
+
ActivityIndicator,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import type { PluginComponentProps, CustomAction, DebuggerPlugin } from '../../core/types';
|
|
12
|
+
import { actionStore } from './actionStore';
|
|
13
|
+
|
|
14
|
+
const CustomActionsPanel: React.FC<PluginComponentProps> = ({ theme }) => {
|
|
15
|
+
const [actions, setActions] = useState<CustomAction[]>([]);
|
|
16
|
+
const [runningAction, setRunningAction] = useState<string | null>(null);
|
|
17
|
+
const [lastResult, setLastResult] = useState<{
|
|
18
|
+
id: string;
|
|
19
|
+
success: boolean;
|
|
20
|
+
message?: string;
|
|
21
|
+
} | null>(null);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const unsub = actionStore.subscribe(setActions);
|
|
25
|
+
return unsub;
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
const handleRun = useCallback(async (action: CustomAction) => {
|
|
29
|
+
const execute = async () => {
|
|
30
|
+
setRunningAction(action.id);
|
|
31
|
+
setLastResult(null);
|
|
32
|
+
try {
|
|
33
|
+
await action.handler();
|
|
34
|
+
setLastResult({ id: action.id, success: true, message: 'Completed' });
|
|
35
|
+
} catch (err) {
|
|
36
|
+
setLastResult({
|
|
37
|
+
id: action.id,
|
|
38
|
+
success: false,
|
|
39
|
+
message: err instanceof Error ? err.message : 'Failed',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
setRunningAction(null);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if (action.destructive) {
|
|
46
|
+
Alert.alert('⚠️ Confirm', `Run "${action.name}"? This action is marked as destructive.`, [
|
|
47
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
48
|
+
{ text: 'Run', style: 'destructive', onPress: execute },
|
|
49
|
+
]);
|
|
50
|
+
} else {
|
|
51
|
+
execute();
|
|
52
|
+
}
|
|
53
|
+
}, []);
|
|
54
|
+
|
|
55
|
+
const groups = React.useMemo(() => {
|
|
56
|
+
const groupMap = new Map<string, CustomAction[]>();
|
|
57
|
+
for (const action of actions) {
|
|
58
|
+
const group = action.group || 'General';
|
|
59
|
+
const list = groupMap.get(group) || [];
|
|
60
|
+
list.push(action);
|
|
61
|
+
groupMap.set(group, list);
|
|
62
|
+
}
|
|
63
|
+
return groupMap;
|
|
64
|
+
}, [actions]);
|
|
65
|
+
|
|
66
|
+
if (actions.length === 0) {
|
|
67
|
+
return (
|
|
68
|
+
<View style={styles.emptyContainer}>
|
|
69
|
+
<Text style={styles.emptyIcon}>🎯</Text>
|
|
70
|
+
<Text style={[styles.emptyTitle, { color: theme.text }]}>No Custom Actions</Text>
|
|
71
|
+
<Text style={[styles.emptyDesc, { color: theme.textMuted }]}>
|
|
72
|
+
Register actions:{'\n\n'}
|
|
73
|
+
<Text style={{ fontFamily: 'monospace', color: theme.codeText }}>
|
|
74
|
+
{
|
|
75
|
+
'registerAction({\n id: "clear-cache",\n name: "Clear Cache",\n icon: "🗑️",\n handler: () => clearCache(),\n})'
|
|
76
|
+
}
|
|
77
|
+
</Text>
|
|
78
|
+
</Text>
|
|
79
|
+
</View>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<ScrollView style={styles.container}>
|
|
85
|
+
{/* Result Banner */}
|
|
86
|
+
{lastResult && (
|
|
87
|
+
<View
|
|
88
|
+
style={[
|
|
89
|
+
styles.resultBanner,
|
|
90
|
+
{ backgroundColor: lastResult.success ? `${theme.success}20` : `${theme.error}20` },
|
|
91
|
+
]}
|
|
92
|
+
>
|
|
93
|
+
<Text
|
|
94
|
+
style={[styles.resultText, { color: lastResult.success ? theme.success : theme.error }]}
|
|
95
|
+
>
|
|
96
|
+
{lastResult.success ? '✅' : '❌'} {lastResult.message}
|
|
97
|
+
</Text>
|
|
98
|
+
</View>
|
|
99
|
+
)}
|
|
100
|
+
|
|
101
|
+
{/* Action Groups */}
|
|
102
|
+
{Array.from(groups.entries()).map(([groupName, groupActions]) => (
|
|
103
|
+
<View key={groupName} style={styles.group}>
|
|
104
|
+
<Text style={[styles.groupTitle, { color: theme.textSecondary }]}>{groupName}</Text>
|
|
105
|
+
<View style={styles.grid}>
|
|
106
|
+
{groupActions.map((action) => (
|
|
107
|
+
<TouchableOpacity
|
|
108
|
+
key={action.id}
|
|
109
|
+
style={[
|
|
110
|
+
styles.actionCard,
|
|
111
|
+
{
|
|
112
|
+
backgroundColor: theme.surface,
|
|
113
|
+
borderColor: action.destructive ? theme.error : theme.border,
|
|
114
|
+
},
|
|
115
|
+
]}
|
|
116
|
+
onPress={() => handleRun(action)}
|
|
117
|
+
activeOpacity={0.7}
|
|
118
|
+
disabled={!!runningAction}
|
|
119
|
+
>
|
|
120
|
+
{runningAction === action.id ? (
|
|
121
|
+
<ActivityIndicator color={theme.accent} size="small" />
|
|
122
|
+
) : (
|
|
123
|
+
<>
|
|
124
|
+
<Text style={styles.actionIcon}>{action.icon || '⚡'}</Text>
|
|
125
|
+
<Text
|
|
126
|
+
style={[
|
|
127
|
+
styles.actionName,
|
|
128
|
+
{ color: action.destructive ? theme.error : theme.text },
|
|
129
|
+
]}
|
|
130
|
+
numberOfLines={1}
|
|
131
|
+
>
|
|
132
|
+
{action.name}
|
|
133
|
+
</Text>
|
|
134
|
+
{action.description && (
|
|
135
|
+
<Text
|
|
136
|
+
style={[styles.actionDesc, { color: theme.textMuted }]}
|
|
137
|
+
numberOfLines={2}
|
|
138
|
+
>
|
|
139
|
+
{action.description}
|
|
140
|
+
</Text>
|
|
141
|
+
)}
|
|
142
|
+
</>
|
|
143
|
+
)}
|
|
144
|
+
</TouchableOpacity>
|
|
145
|
+
))}
|
|
146
|
+
</View>
|
|
147
|
+
</View>
|
|
148
|
+
))}
|
|
149
|
+
<View style={{ height: 40 }} />
|
|
150
|
+
</ScrollView>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const styles = StyleSheet.create({
|
|
155
|
+
container: { flex: 1 },
|
|
156
|
+
emptyContainer: {
|
|
157
|
+
flex: 1,
|
|
158
|
+
alignItems: 'center',
|
|
159
|
+
justifyContent: 'center',
|
|
160
|
+
paddingHorizontal: 32,
|
|
161
|
+
},
|
|
162
|
+
emptyIcon: { fontSize: 48, marginBottom: 16 },
|
|
163
|
+
emptyTitle: { fontSize: 16, fontWeight: '700', marginBottom: 8 },
|
|
164
|
+
emptyDesc: { fontSize: 13, textAlign: 'center', lineHeight: 20 },
|
|
165
|
+
resultBanner: { padding: 10, marginHorizontal: 12, marginVertical: 8, borderRadius: 8 },
|
|
166
|
+
resultText: { fontSize: 13, fontWeight: '600', textAlign: 'center' },
|
|
167
|
+
group: { paddingHorizontal: 12, marginBottom: 16 },
|
|
168
|
+
groupTitle: {
|
|
169
|
+
fontSize: 11,
|
|
170
|
+
fontWeight: '700',
|
|
171
|
+
textTransform: 'uppercase',
|
|
172
|
+
letterSpacing: 1,
|
|
173
|
+
marginBottom: 8,
|
|
174
|
+
marginTop: 12,
|
|
175
|
+
},
|
|
176
|
+
grid: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 },
|
|
177
|
+
actionCard: {
|
|
178
|
+
width: '47%',
|
|
179
|
+
padding: 16,
|
|
180
|
+
borderRadius: 12,
|
|
181
|
+
borderWidth: 1,
|
|
182
|
+
alignItems: 'center',
|
|
183
|
+
justifyContent: 'center',
|
|
184
|
+
minHeight: 90,
|
|
185
|
+
},
|
|
186
|
+
actionIcon: { fontSize: 24, marginBottom: 8 },
|
|
187
|
+
actionName: { fontSize: 13, fontWeight: '700', textAlign: 'center' },
|
|
188
|
+
actionDesc: { fontSize: 10, textAlign: 'center', marginTop: 4 },
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
export function createCustomActionsPlugin(): DebuggerPlugin {
|
|
192
|
+
return {
|
|
193
|
+
id: 'custom-actions',
|
|
194
|
+
name: 'Actions',
|
|
195
|
+
icon: '🎯',
|
|
196
|
+
component: CustomActionsPanel,
|
|
197
|
+
order: 110,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CustomAction } from '../../core/types';
|
|
2
|
+
|
|
3
|
+
type ActionListener = (actions: CustomAction[]) => void;
|
|
4
|
+
|
|
5
|
+
class ActionStoreClass {
|
|
6
|
+
private actions: Map<string, CustomAction> = new Map();
|
|
7
|
+
private listeners: Set<ActionListener> = new Set();
|
|
8
|
+
|
|
9
|
+
register(action: CustomAction): void {
|
|
10
|
+
this.actions.set(action.id, action);
|
|
11
|
+
this.notify();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
remove(id: string): void {
|
|
15
|
+
this.actions.delete(id);
|
|
16
|
+
this.notify();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getAll(): CustomAction[] {
|
|
20
|
+
return Array.from(this.actions.values());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
subscribe(listener: ActionListener): () => void {
|
|
24
|
+
this.listeners.add(listener);
|
|
25
|
+
listener(this.getAll());
|
|
26
|
+
return () => this.listeners.delete(listener);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private notify(): void {
|
|
30
|
+
const snapshot = this.getAll();
|
|
31
|
+
for (const l of this.listeners) {
|
|
32
|
+
try {
|
|
33
|
+
l(snapshot);
|
|
34
|
+
} catch {
|
|
35
|
+
/* ignore */
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const actionStore = new ActionStoreClass();
|
|
42
|
+
|
|
43
|
+
export function registerAction(action: CustomAction): void {
|
|
44
|
+
actionStore.register(action);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function removeAction(id: string): void {
|
|
48
|
+
actionStore.remove(id);
|
|
49
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import React, { useState, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
TextInput,
|
|
7
|
+
ScrollView,
|
|
8
|
+
StyleSheet,
|
|
9
|
+
Linking,
|
|
10
|
+
Alert,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
import type { PluginComponentProps, DebuggerPlugin } from '../../core/types';
|
|
13
|
+
|
|
14
|
+
const DeepLinkTesterPanel: React.FC<PluginComponentProps> = ({ theme }) => {
|
|
15
|
+
const [url, setUrl] = useState('');
|
|
16
|
+
const [history, setHistory] = useState<{ url: string; timestamp: number; success: boolean }[]>(
|
|
17
|
+
[],
|
|
18
|
+
);
|
|
19
|
+
const [presets, setPresets] = useState<string[]>([]);
|
|
20
|
+
|
|
21
|
+
const handleOpen = useCallback(async () => {
|
|
22
|
+
if (!url.trim()) return;
|
|
23
|
+
try {
|
|
24
|
+
const canOpen = await Linking.canOpenURL(url);
|
|
25
|
+
if (canOpen) {
|
|
26
|
+
await Linking.openURL(url);
|
|
27
|
+
setHistory((prev) => [{ url, timestamp: Date.now(), success: true }, ...prev].slice(0, 20));
|
|
28
|
+
} else {
|
|
29
|
+
Alert.alert('Error', `Cannot open URL: ${url}`);
|
|
30
|
+
setHistory((prev) =>
|
|
31
|
+
[{ url, timestamp: Date.now(), success: false }, ...prev].slice(0, 20),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
} catch (err) {
|
|
35
|
+
Alert.alert('Error', err instanceof Error ? err.message : 'Failed');
|
|
36
|
+
setHistory((prev) => [{ url, timestamp: Date.now(), success: false }, ...prev].slice(0, 20));
|
|
37
|
+
}
|
|
38
|
+
}, [url]);
|
|
39
|
+
|
|
40
|
+
const handleSavePreset = useCallback(() => {
|
|
41
|
+
if (!url.trim() || presets.includes(url)) return;
|
|
42
|
+
setPresets((prev) => [url, ...prev]);
|
|
43
|
+
}, [url, presets]);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<ScrollView style={styles.container}>
|
|
47
|
+
{/* URL Input */}
|
|
48
|
+
<View style={[styles.inputSection, { backgroundColor: theme.surface }]}>
|
|
49
|
+
<Text style={[styles.sectionTitle, { color: theme.textSecondary }]}>DEEP LINK URL</Text>
|
|
50
|
+
<TextInput
|
|
51
|
+
style={[
|
|
52
|
+
styles.urlInput,
|
|
53
|
+
{ color: theme.text, backgroundColor: theme.surfaceAlt, borderColor: theme.border },
|
|
54
|
+
]}
|
|
55
|
+
value={url}
|
|
56
|
+
onChangeText={setUrl}
|
|
57
|
+
placeholder="myapp://path/to/screen?param=value"
|
|
58
|
+
placeholderTextColor={theme.textMuted}
|
|
59
|
+
autoCapitalize="none"
|
|
60
|
+
autoCorrect={false}
|
|
61
|
+
/>
|
|
62
|
+
<View style={styles.buttonRow}>
|
|
63
|
+
<TouchableOpacity
|
|
64
|
+
style={[styles.primaryBtn, { backgroundColor: theme.accent }]}
|
|
65
|
+
onPress={handleOpen}
|
|
66
|
+
activeOpacity={0.7}
|
|
67
|
+
>
|
|
68
|
+
<Text style={styles.primaryBtnText}>🔗 Open Link</Text>
|
|
69
|
+
</TouchableOpacity>
|
|
70
|
+
<TouchableOpacity
|
|
71
|
+
style={[styles.secondaryBtn, { borderColor: theme.border }]}
|
|
72
|
+
onPress={handleSavePreset}
|
|
73
|
+
activeOpacity={0.7}
|
|
74
|
+
>
|
|
75
|
+
<Text style={[styles.secondaryBtnText, { color: theme.textSecondary }]}>Save</Text>
|
|
76
|
+
</TouchableOpacity>
|
|
77
|
+
</View>
|
|
78
|
+
</View>
|
|
79
|
+
|
|
80
|
+
{/* Common Schemes */}
|
|
81
|
+
<View style={[styles.section, { backgroundColor: theme.surface }]}>
|
|
82
|
+
<Text style={[styles.sectionTitle, { color: theme.textSecondary }]}>COMMON SCHEMES</Text>
|
|
83
|
+
<View style={styles.schemeGrid}>
|
|
84
|
+
{[
|
|
85
|
+
'tel:+1234567890',
|
|
86
|
+
'mailto:test@example.com',
|
|
87
|
+
'sms:+1234567890',
|
|
88
|
+
'https://example.com',
|
|
89
|
+
'geo:37.7749,-122.4194',
|
|
90
|
+
].map((scheme) => (
|
|
91
|
+
<TouchableOpacity
|
|
92
|
+
key={scheme}
|
|
93
|
+
style={[
|
|
94
|
+
styles.schemeChip,
|
|
95
|
+
{ backgroundColor: theme.surfaceAlt, borderColor: theme.border },
|
|
96
|
+
]}
|
|
97
|
+
onPress={() => setUrl(scheme)}
|
|
98
|
+
activeOpacity={0.7}
|
|
99
|
+
>
|
|
100
|
+
<Text style={[styles.schemeText, { color: theme.text }]} numberOfLines={1}>
|
|
101
|
+
{scheme.length > 25 ? scheme.slice(0, 25) + '…' : scheme}
|
|
102
|
+
</Text>
|
|
103
|
+
</TouchableOpacity>
|
|
104
|
+
))}
|
|
105
|
+
</View>
|
|
106
|
+
</View>
|
|
107
|
+
|
|
108
|
+
{/* Saved Presets */}
|
|
109
|
+
{presets.length > 0 && (
|
|
110
|
+
<View style={[styles.section, { backgroundColor: theme.surface }]}>
|
|
111
|
+
<Text style={[styles.sectionTitle, { color: theme.textSecondary }]}>SAVED PRESETS</Text>
|
|
112
|
+
{presets.map((preset, i) => (
|
|
113
|
+
<TouchableOpacity
|
|
114
|
+
key={i}
|
|
115
|
+
style={[styles.presetRow, { borderBottomColor: theme.border }]}
|
|
116
|
+
onPress={() => setUrl(preset)}
|
|
117
|
+
activeOpacity={0.7}
|
|
118
|
+
>
|
|
119
|
+
<Text style={[styles.presetText, { color: theme.accent }]} numberOfLines={1}>
|
|
120
|
+
{preset}
|
|
121
|
+
</Text>
|
|
122
|
+
</TouchableOpacity>
|
|
123
|
+
))}
|
|
124
|
+
</View>
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
{/* History */}
|
|
128
|
+
{history.length > 0 && (
|
|
129
|
+
<View style={[styles.section, { backgroundColor: theme.surface }]}>
|
|
130
|
+
<Text style={[styles.sectionTitle, { color: theme.textSecondary }]}>HISTORY</Text>
|
|
131
|
+
{history.map((entry, i) => (
|
|
132
|
+
<TouchableOpacity
|
|
133
|
+
key={i}
|
|
134
|
+
style={[styles.historyRow, { borderBottomColor: theme.border }]}
|
|
135
|
+
onPress={() => setUrl(entry.url)}
|
|
136
|
+
activeOpacity={0.7}
|
|
137
|
+
>
|
|
138
|
+
<Text style={[styles.historyStatus]}>{entry.success ? '✅' : '❌'}</Text>
|
|
139
|
+
<Text style={[styles.historyUrl, { color: theme.text }]} numberOfLines={1}>
|
|
140
|
+
{entry.url}
|
|
141
|
+
</Text>
|
|
142
|
+
</TouchableOpacity>
|
|
143
|
+
))}
|
|
144
|
+
</View>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
<View style={{ height: 40 }} />
|
|
148
|
+
</ScrollView>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const styles = StyleSheet.create({
|
|
153
|
+
container: { flex: 1 },
|
|
154
|
+
inputSection: { margin: 12, padding: 12, borderRadius: 12 },
|
|
155
|
+
section: { margin: 12, marginTop: 0, padding: 12, borderRadius: 12 },
|
|
156
|
+
sectionTitle: {
|
|
157
|
+
fontSize: 11,
|
|
158
|
+
fontWeight: '700',
|
|
159
|
+
textTransform: 'uppercase',
|
|
160
|
+
letterSpacing: 1,
|
|
161
|
+
marginBottom: 8,
|
|
162
|
+
},
|
|
163
|
+
urlInput: {
|
|
164
|
+
height: 44,
|
|
165
|
+
borderRadius: 8,
|
|
166
|
+
paddingHorizontal: 12,
|
|
167
|
+
fontSize: 14,
|
|
168
|
+
borderWidth: 1,
|
|
169
|
+
fontFamily: 'monospace',
|
|
170
|
+
},
|
|
171
|
+
buttonRow: { flexDirection: 'row', gap: 8, marginTop: 12 },
|
|
172
|
+
primaryBtn: {
|
|
173
|
+
flex: 1,
|
|
174
|
+
height: 42,
|
|
175
|
+
borderRadius: 8,
|
|
176
|
+
alignItems: 'center',
|
|
177
|
+
justifyContent: 'center',
|
|
178
|
+
},
|
|
179
|
+
primaryBtnText: { color: '#FFF', fontSize: 14, fontWeight: '700' },
|
|
180
|
+
secondaryBtn: {
|
|
181
|
+
paddingHorizontal: 16,
|
|
182
|
+
height: 42,
|
|
183
|
+
borderRadius: 8,
|
|
184
|
+
alignItems: 'center',
|
|
185
|
+
justifyContent: 'center',
|
|
186
|
+
borderWidth: 1,
|
|
187
|
+
},
|
|
188
|
+
secondaryBtnText: { fontSize: 13, fontWeight: '600' },
|
|
189
|
+
schemeGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 6 },
|
|
190
|
+
schemeChip: { paddingHorizontal: 10, paddingVertical: 6, borderRadius: 8, borderWidth: 1 },
|
|
191
|
+
schemeText: { fontSize: 11, fontFamily: 'monospace' },
|
|
192
|
+
presetRow: { paddingVertical: 10, borderBottomWidth: StyleSheet.hairlineWidth },
|
|
193
|
+
presetText: { fontSize: 13, fontFamily: 'monospace' },
|
|
194
|
+
historyRow: {
|
|
195
|
+
flexDirection: 'row',
|
|
196
|
+
alignItems: 'center',
|
|
197
|
+
paddingVertical: 8,
|
|
198
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
199
|
+
gap: 8,
|
|
200
|
+
},
|
|
201
|
+
historyStatus: { fontSize: 12 },
|
|
202
|
+
historyUrl: { fontSize: 12, fontFamily: 'monospace', flex: 1 },
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
export function createDeepLinkTesterPlugin(): DebuggerPlugin {
|
|
206
|
+
return {
|
|
207
|
+
id: 'deep-link-tester',
|
|
208
|
+
name: 'Links',
|
|
209
|
+
icon: '🔗',
|
|
210
|
+
component: DeepLinkTesterPanel,
|
|
211
|
+
order: 120,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import React, { useMemo, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
ScrollView,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
Platform,
|
|
9
|
+
Dimensions,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import type { PluginComponentProps, DebuggerPlugin } from '../../core/types';
|
|
12
|
+
import { copyToClipboard } from '../../core/utils';
|
|
13
|
+
|
|
14
|
+
const SCREEN = Dimensions.get('window');
|
|
15
|
+
|
|
16
|
+
const DeviceInfoPanel: React.FC<PluginComponentProps> = ({ theme }) => {
|
|
17
|
+
const info = useMemo(() => {
|
|
18
|
+
const items: { label: string; value: string; group: string }[] = [];
|
|
19
|
+
|
|
20
|
+
// Platform
|
|
21
|
+
items.push({ label: 'Platform', value: Platform.OS, group: 'Platform' });
|
|
22
|
+
items.push({ label: 'OS Version', value: String(Platform.Version), group: 'Platform' });
|
|
23
|
+
items.push({
|
|
24
|
+
label: 'Hermes',
|
|
25
|
+
value: typeof HermesInternal !== 'undefined' ? '✅ Enabled' : '❌ Disabled',
|
|
26
|
+
group: 'Platform',
|
|
27
|
+
});
|
|
28
|
+
items.push({
|
|
29
|
+
label: '__DEV__',
|
|
30
|
+
value: typeof __DEV__ !== 'undefined' && __DEV__ ? 'true' : 'false',
|
|
31
|
+
group: 'Platform',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Screen
|
|
35
|
+
items.push({
|
|
36
|
+
label: 'Screen',
|
|
37
|
+
value: `${SCREEN.width} × ${SCREEN.height}`,
|
|
38
|
+
group: 'Display',
|
|
39
|
+
});
|
|
40
|
+
items.push({
|
|
41
|
+
label: 'Scale',
|
|
42
|
+
value: String(Dimensions.get('window').scale ?? 'N/A'),
|
|
43
|
+
group: 'Display',
|
|
44
|
+
});
|
|
45
|
+
items.push({
|
|
46
|
+
label: 'Font Scale',
|
|
47
|
+
value: String(Dimensions.get('window').fontScale ?? 'N/A'),
|
|
48
|
+
group: 'Display',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Runtime
|
|
52
|
+
items.push({
|
|
53
|
+
label: 'Locale',
|
|
54
|
+
value:
|
|
55
|
+
Platform.OS === 'ios'
|
|
56
|
+
? (Platform as unknown as { locale?: string }).locale || 'Unknown'
|
|
57
|
+
: 'Check via library',
|
|
58
|
+
group: 'Runtime',
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return items;
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
const handleCopyAll = useCallback(() => {
|
|
65
|
+
const text = info.map((i) => `${i.label}: ${i.value}`).join('\n');
|
|
66
|
+
copyToClipboard(text);
|
|
67
|
+
}, [info]);
|
|
68
|
+
|
|
69
|
+
const groups = useMemo(() => {
|
|
70
|
+
const groupMap = new Map<string, typeof info>();
|
|
71
|
+
for (const item of info) {
|
|
72
|
+
const group = groupMap.get(item.group) || [];
|
|
73
|
+
group.push(item);
|
|
74
|
+
groupMap.set(item.group, group);
|
|
75
|
+
}
|
|
76
|
+
return groupMap;
|
|
77
|
+
}, [info]);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<ScrollView style={styles.container}>
|
|
81
|
+
{/* Copy All */}
|
|
82
|
+
<View style={[styles.header, { backgroundColor: theme.surface }]}>
|
|
83
|
+
<Text style={[styles.headerTitle, { color: theme.text }]}>📱 Device Info</Text>
|
|
84
|
+
<TouchableOpacity onPress={handleCopyAll} activeOpacity={0.7}>
|
|
85
|
+
<Text style={[styles.copyAllBtn, { color: theme.accent }]}>Copy All</Text>
|
|
86
|
+
</TouchableOpacity>
|
|
87
|
+
</View>
|
|
88
|
+
|
|
89
|
+
{/* Info Groups */}
|
|
90
|
+
{Array.from(groups.entries()).map(([groupName, items]) => (
|
|
91
|
+
<View key={groupName} style={[styles.group, { backgroundColor: theme.surface }]}>
|
|
92
|
+
<Text style={[styles.groupTitle, { color: theme.textSecondary }]}>{groupName}</Text>
|
|
93
|
+
{items.map((item) => (
|
|
94
|
+
<TouchableOpacity
|
|
95
|
+
key={item.label}
|
|
96
|
+
style={[styles.infoRow, { borderBottomColor: theme.border }]}
|
|
97
|
+
onLongPress={() => copyToClipboard(item.value)}
|
|
98
|
+
activeOpacity={0.7}
|
|
99
|
+
>
|
|
100
|
+
<Text style={[styles.infoLabel, { color: theme.textSecondary }]}>{item.label}</Text>
|
|
101
|
+
<Text style={[styles.infoValue, { color: theme.text }]} selectable>
|
|
102
|
+
{item.value}
|
|
103
|
+
</Text>
|
|
104
|
+
</TouchableOpacity>
|
|
105
|
+
))}
|
|
106
|
+
</View>
|
|
107
|
+
))}
|
|
108
|
+
</ScrollView>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const styles = StyleSheet.create({
|
|
113
|
+
container: { flex: 1 },
|
|
114
|
+
header: {
|
|
115
|
+
flexDirection: 'row',
|
|
116
|
+
justifyContent: 'space-between',
|
|
117
|
+
alignItems: 'center',
|
|
118
|
+
paddingHorizontal: 12,
|
|
119
|
+
paddingVertical: 12,
|
|
120
|
+
},
|
|
121
|
+
headerTitle: { fontSize: 16, fontWeight: '700' },
|
|
122
|
+
copyAllBtn: { fontSize: 13, fontWeight: '600' },
|
|
123
|
+
group: { marginHorizontal: 12, marginBottom: 12, borderRadius: 12, overflow: 'hidden' },
|
|
124
|
+
groupTitle: {
|
|
125
|
+
fontSize: 12,
|
|
126
|
+
fontWeight: '700',
|
|
127
|
+
textTransform: 'uppercase',
|
|
128
|
+
letterSpacing: 1,
|
|
129
|
+
paddingHorizontal: 12,
|
|
130
|
+
paddingTop: 12,
|
|
131
|
+
paddingBottom: 4,
|
|
132
|
+
},
|
|
133
|
+
infoRow: {
|
|
134
|
+
flexDirection: 'row',
|
|
135
|
+
justifyContent: 'space-between',
|
|
136
|
+
alignItems: 'center',
|
|
137
|
+
paddingHorizontal: 12,
|
|
138
|
+
paddingVertical: 10,
|
|
139
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
140
|
+
},
|
|
141
|
+
infoLabel: { fontSize: 13, flex: 1 },
|
|
142
|
+
infoValue: { fontSize: 13, fontWeight: '600', flex: 1, textAlign: 'right' },
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
export function createDeviceInfoPlugin(): DebuggerPlugin {
|
|
146
|
+
return {
|
|
147
|
+
id: 'device-info',
|
|
148
|
+
name: 'Device',
|
|
149
|
+
icon: '📱',
|
|
150
|
+
component: DeviceInfoPanel,
|
|
151
|
+
order: 80,
|
|
152
|
+
};
|
|
153
|
+
}
|