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.
Files changed (227) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +350 -0
  3. package/lib/commonjs/core/DebugBubble.js +139 -0
  4. package/lib/commonjs/core/DebugBubble.js.map +1 -0
  5. package/lib/commonjs/core/DebugOverlay.js +184 -0
  6. package/lib/commonjs/core/DebugOverlay.js.map +1 -0
  7. package/lib/commonjs/core/DebuggerProvider.js +174 -0
  8. package/lib/commonjs/core/DebuggerProvider.js.map +1 -0
  9. package/lib/commonjs/core/GestureDetector.js +83 -0
  10. package/lib/commonjs/core/GestureDetector.js.map +1 -0
  11. package/lib/commonjs/core/PluginRegistry.js +124 -0
  12. package/lib/commonjs/core/PluginRegistry.js.map +1 -0
  13. package/lib/commonjs/core/theme.js +50 -0
  14. package/lib/commonjs/core/theme.js.map +1 -0
  15. package/lib/commonjs/core/types.js +6 -0
  16. package/lib/commonjs/core/types.js.map +1 -0
  17. package/lib/commonjs/core/useDebugger.js +27 -0
  18. package/lib/commonjs/core/useDebugger.js.map +1 -0
  19. package/lib/commonjs/core/utils.js +239 -0
  20. package/lib/commonjs/core/utils.js.map +1 -0
  21. package/lib/commonjs/index.js +132 -0
  22. package/lib/commonjs/index.js.map +1 -0
  23. package/lib/commonjs/plugins/console/ConsoleInterceptor.js +103 -0
  24. package/lib/commonjs/plugins/console/ConsoleInterceptor.js.map +1 -0
  25. package/lib/commonjs/plugins/console/ConsoleViewerPlugin.js +269 -0
  26. package/lib/commonjs/plugins/console/ConsoleViewerPlugin.js.map +1 -0
  27. package/lib/commonjs/plugins/crashReporter/CrashReporterPlugin.js +363 -0
  28. package/lib/commonjs/plugins/crashReporter/CrashReporterPlugin.js.map +1 -0
  29. package/lib/commonjs/plugins/customActions/CustomActionsPlugin.js +218 -0
  30. package/lib/commonjs/plugins/customActions/CustomActionsPlugin.js.map +1 -0
  31. package/lib/commonjs/plugins/customActions/actionStore.js +46 -0
  32. package/lib/commonjs/plugins/customActions/actionStore.js.map +1 -0
  33. package/lib/commonjs/plugins/deepLinkTester/DeepLinkTesterPlugin.js +268 -0
  34. package/lib/commonjs/plugins/deepLinkTester/DeepLinkTesterPlugin.js.map +1 -0
  35. package/lib/commonjs/plugins/deviceInfo/DeviceInfoPlugin.js +184 -0
  36. package/lib/commonjs/plugins/deviceInfo/DeviceInfoPlugin.js.map +1 -0
  37. package/lib/commonjs/plugins/featureFlags/FeatureFlagsPlugin.js +381 -0
  38. package/lib/commonjs/plugins/featureFlags/FeatureFlagsPlugin.js.map +1 -0
  39. package/lib/commonjs/plugins/featureFlags/flagStore.js +125 -0
  40. package/lib/commonjs/plugins/featureFlags/flagStore.js.map +1 -0
  41. package/lib/commonjs/plugins/navigationInspector/NavigationInspectorPlugin.js +250 -0
  42. package/lib/commonjs/plugins/navigationInspector/NavigationInspectorPlugin.js.map +1 -0
  43. package/lib/commonjs/plugins/navigationInspector/navigationStore.js +65 -0
  44. package/lib/commonjs/plugins/navigationInspector/navigationStore.js.map +1 -0
  45. package/lib/commonjs/plugins/network/NetworkInspectorPlugin.js +709 -0
  46. package/lib/commonjs/plugins/network/NetworkInspectorPlugin.js.map +1 -0
  47. package/lib/commonjs/plugins/network/NetworkInterceptor.js +305 -0
  48. package/lib/commonjs/plugins/network/NetworkInterceptor.js.map +1 -0
  49. package/lib/commonjs/plugins/performance/PerformanceMonitorPlugin.js +261 -0
  50. package/lib/commonjs/plugins/performance/PerformanceMonitorPlugin.js.map +1 -0
  51. package/lib/commonjs/plugins/remoteConfig/RemoteConfigPlugin.js +265 -0
  52. package/lib/commonjs/plugins/remoteConfig/RemoteConfigPlugin.js.map +1 -0
  53. package/lib/commonjs/plugins/remoteConfig/remoteConfigStore.js +50 -0
  54. package/lib/commonjs/plugins/remoteConfig/remoteConfigStore.js.map +1 -0
  55. package/lib/commonjs/plugins/stateInspector/StateInspectorPlugin.js +378 -0
  56. package/lib/commonjs/plugins/stateInspector/StateInspectorPlugin.js.map +1 -0
  57. package/lib/commonjs/plugins/stateInspector/stateAdapterRegistry.js +62 -0
  58. package/lib/commonjs/plugins/stateInspector/stateAdapterRegistry.js.map +1 -0
  59. package/lib/commonjs/plugins/storageBrowser/StorageBrowserPlugin.js +496 -0
  60. package/lib/commonjs/plugins/storageBrowser/StorageBrowserPlugin.js.map +1 -0
  61. package/lib/commonjs/plugins/storageBrowser/storageAdapterRegistry.js +44 -0
  62. package/lib/commonjs/plugins/storageBrowser/storageAdapterRegistry.js.map +1 -0
  63. package/lib/commonjs/plugins/timeline/TimelinePlugin.js +294 -0
  64. package/lib/commonjs/plugins/timeline/TimelinePlugin.js.map +1 -0
  65. package/lib/commonjs/plugins/timeline/timelineStore.js +61 -0
  66. package/lib/commonjs/plugins/timeline/timelineStore.js.map +1 -0
  67. package/lib/module/core/DebugBubble.js +131 -0
  68. package/lib/module/core/DebugBubble.js.map +1 -0
  69. package/lib/module/core/DebugOverlay.js +176 -0
  70. package/lib/module/core/DebugOverlay.js.map +1 -0
  71. package/lib/module/core/DebuggerProvider.js +167 -0
  72. package/lib/module/core/DebuggerProvider.js.map +1 -0
  73. package/lib/module/core/GestureDetector.js +75 -0
  74. package/lib/module/core/GestureDetector.js.map +1 -0
  75. package/lib/module/core/PluginRegistry.js +116 -0
  76. package/lib/module/core/PluginRegistry.js.map +1 -0
  77. package/lib/module/core/theme.js +43 -0
  78. package/lib/module/core/theme.js.map +1 -0
  79. package/lib/module/core/types.js +2 -0
  80. package/lib/module/core/types.js.map +1 -0
  81. package/lib/module/core/useDebugger.js +21 -0
  82. package/lib/module/core/useDebugger.js.map +1 -0
  83. package/lib/module/core/utils.js +219 -0
  84. package/lib/module/core/utils.js.map +1 -0
  85. package/lib/module/index.js +44 -0
  86. package/lib/module/index.js.map +1 -0
  87. package/lib/module/plugins/console/ConsoleInterceptor.js +97 -0
  88. package/lib/module/plugins/console/ConsoleInterceptor.js.map +1 -0
  89. package/lib/module/plugins/console/ConsoleViewerPlugin.js +262 -0
  90. package/lib/module/plugins/console/ConsoleViewerPlugin.js.map +1 -0
  91. package/lib/module/plugins/crashReporter/CrashReporterPlugin.js +357 -0
  92. package/lib/module/plugins/crashReporter/CrashReporterPlugin.js.map +1 -0
  93. package/lib/module/plugins/customActions/CustomActionsPlugin.js +211 -0
  94. package/lib/module/plugins/customActions/CustomActionsPlugin.js.map +1 -0
  95. package/lib/module/plugins/customActions/actionStore.js +38 -0
  96. package/lib/module/plugins/customActions/actionStore.js.map +1 -0
  97. package/lib/module/plugins/deepLinkTester/DeepLinkTesterPlugin.js +261 -0
  98. package/lib/module/plugins/deepLinkTester/DeepLinkTesterPlugin.js.map +1 -0
  99. package/lib/module/plugins/deviceInfo/DeviceInfoPlugin.js +177 -0
  100. package/lib/module/plugins/deviceInfo/DeviceInfoPlugin.js.map +1 -0
  101. package/lib/module/plugins/featureFlags/FeatureFlagsPlugin.js +374 -0
  102. package/lib/module/plugins/featureFlags/FeatureFlagsPlugin.js.map +1 -0
  103. package/lib/module/plugins/featureFlags/flagStore.js +117 -0
  104. package/lib/module/plugins/featureFlags/flagStore.js.map +1 -0
  105. package/lib/module/plugins/navigationInspector/NavigationInspectorPlugin.js +243 -0
  106. package/lib/module/plugins/navigationInspector/NavigationInspectorPlugin.js.map +1 -0
  107. package/lib/module/plugins/navigationInspector/navigationStore.js +58 -0
  108. package/lib/module/plugins/navigationInspector/navigationStore.js.map +1 -0
  109. package/lib/module/plugins/network/NetworkInspectorPlugin.js +703 -0
  110. package/lib/module/plugins/network/NetworkInspectorPlugin.js.map +1 -0
  111. package/lib/module/plugins/network/NetworkInterceptor.js +299 -0
  112. package/lib/module/plugins/network/NetworkInterceptor.js.map +1 -0
  113. package/lib/module/plugins/performance/PerformanceMonitorPlugin.js +254 -0
  114. package/lib/module/plugins/performance/PerformanceMonitorPlugin.js.map +1 -0
  115. package/lib/module/plugins/remoteConfig/RemoteConfigPlugin.js +258 -0
  116. package/lib/module/plugins/remoteConfig/RemoteConfigPlugin.js.map +1 -0
  117. package/lib/module/plugins/remoteConfig/remoteConfigStore.js +43 -0
  118. package/lib/module/plugins/remoteConfig/remoteConfigStore.js.map +1 -0
  119. package/lib/module/plugins/stateInspector/StateInspectorPlugin.js +372 -0
  120. package/lib/module/plugins/stateInspector/StateInspectorPlugin.js.map +1 -0
  121. package/lib/module/plugins/stateInspector/stateAdapterRegistry.js +54 -0
  122. package/lib/module/plugins/stateInspector/stateAdapterRegistry.js.map +1 -0
  123. package/lib/module/plugins/storageBrowser/StorageBrowserPlugin.js +489 -0
  124. package/lib/module/plugins/storageBrowser/StorageBrowserPlugin.js.map +1 -0
  125. package/lib/module/plugins/storageBrowser/storageAdapterRegistry.js +36 -0
  126. package/lib/module/plugins/storageBrowser/storageAdapterRegistry.js.map +1 -0
  127. package/lib/module/plugins/timeline/TimelinePlugin.js +287 -0
  128. package/lib/module/plugins/timeline/TimelinePlugin.js.map +1 -0
  129. package/lib/module/plugins/timeline/timelineStore.js +54 -0
  130. package/lib/module/plugins/timeline/timelineStore.js.map +1 -0
  131. package/lib/typescript/core/DebugBubble.d.ts +22 -0
  132. package/lib/typescript/core/DebugBubble.d.ts.map +1 -0
  133. package/lib/typescript/core/DebugOverlay.d.ts +18 -0
  134. package/lib/typescript/core/DebugOverlay.d.ts.map +1 -0
  135. package/lib/typescript/core/DebuggerProvider.d.ts +25 -0
  136. package/lib/typescript/core/DebuggerProvider.d.ts.map +1 -0
  137. package/lib/typescript/core/GestureDetector.d.ts +20 -0
  138. package/lib/typescript/core/GestureDetector.d.ts.map +1 -0
  139. package/lib/typescript/core/PluginRegistry.d.ts +41 -0
  140. package/lib/typescript/core/PluginRegistry.d.ts.map +1 -0
  141. package/lib/typescript/core/theme.d.ts +11 -0
  142. package/lib/typescript/core/theme.d.ts.map +1 -0
  143. package/lib/typescript/core/types.d.ts +269 -0
  144. package/lib/typescript/core/types.d.ts.map +1 -0
  145. package/lib/typescript/core/useDebugger.d.ts +14 -0
  146. package/lib/typescript/core/useDebugger.d.ts.map +1 -0
  147. package/lib/typescript/core/utils.d.ts +46 -0
  148. package/lib/typescript/core/utils.d.ts.map +1 -0
  149. package/lib/typescript/index.d.ts +23 -0
  150. package/lib/typescript/index.d.ts.map +1 -0
  151. package/lib/typescript/plugins/console/ConsoleInterceptor.d.ts +32 -0
  152. package/lib/typescript/plugins/console/ConsoleInterceptor.d.ts.map +1 -0
  153. package/lib/typescript/plugins/console/ConsoleViewerPlugin.d.ts +3 -0
  154. package/lib/typescript/plugins/console/ConsoleViewerPlugin.d.ts.map +1 -0
  155. package/lib/typescript/plugins/crashReporter/CrashReporterPlugin.d.ts +3 -0
  156. package/lib/typescript/plugins/crashReporter/CrashReporterPlugin.d.ts.map +1 -0
  157. package/lib/typescript/plugins/customActions/CustomActionsPlugin.d.ts +3 -0
  158. package/lib/typescript/plugins/customActions/CustomActionsPlugin.d.ts.map +1 -0
  159. package/lib/typescript/plugins/customActions/actionStore.d.ts +16 -0
  160. package/lib/typescript/plugins/customActions/actionStore.d.ts.map +1 -0
  161. package/lib/typescript/plugins/deepLinkTester/DeepLinkTesterPlugin.d.ts +3 -0
  162. package/lib/typescript/plugins/deepLinkTester/DeepLinkTesterPlugin.d.ts.map +1 -0
  163. package/lib/typescript/plugins/deviceInfo/DeviceInfoPlugin.d.ts +3 -0
  164. package/lib/typescript/plugins/deviceInfo/DeviceInfoPlugin.d.ts.map +1 -0
  165. package/lib/typescript/plugins/featureFlags/FeatureFlagsPlugin.d.ts +3 -0
  166. package/lib/typescript/plugins/featureFlags/FeatureFlagsPlugin.d.ts.map +1 -0
  167. package/lib/typescript/plugins/featureFlags/flagStore.d.ts +45 -0
  168. package/lib/typescript/plugins/featureFlags/flagStore.d.ts.map +1 -0
  169. package/lib/typescript/plugins/navigationInspector/NavigationInspectorPlugin.d.ts +3 -0
  170. package/lib/typescript/plugins/navigationInspector/NavigationInspectorPlugin.d.ts.map +1 -0
  171. package/lib/typescript/plugins/navigationInspector/navigationStore.d.ts +30 -0
  172. package/lib/typescript/plugins/navigationInspector/navigationStore.d.ts.map +1 -0
  173. package/lib/typescript/plugins/network/NetworkInspectorPlugin.d.ts +3 -0
  174. package/lib/typescript/plugins/network/NetworkInspectorPlugin.d.ts.map +1 -0
  175. package/lib/typescript/plugins/network/NetworkInterceptor.d.ts +42 -0
  176. package/lib/typescript/plugins/network/NetworkInterceptor.d.ts.map +1 -0
  177. package/lib/typescript/plugins/performance/PerformanceMonitorPlugin.d.ts +3 -0
  178. package/lib/typescript/plugins/performance/PerformanceMonitorPlugin.d.ts.map +1 -0
  179. package/lib/typescript/plugins/remoteConfig/RemoteConfigPlugin.d.ts +3 -0
  180. package/lib/typescript/plugins/remoteConfig/RemoteConfigPlugin.d.ts.map +1 -0
  181. package/lib/typescript/plugins/remoteConfig/remoteConfigStore.d.ts +20 -0
  182. package/lib/typescript/plugins/remoteConfig/remoteConfigStore.d.ts.map +1 -0
  183. package/lib/typescript/plugins/stateInspector/StateInspectorPlugin.d.ts +3 -0
  184. package/lib/typescript/plugins/stateInspector/StateInspectorPlugin.d.ts.map +1 -0
  185. package/lib/typescript/plugins/stateInspector/stateAdapterRegistry.d.ts +34 -0
  186. package/lib/typescript/plugins/stateInspector/stateAdapterRegistry.d.ts.map +1 -0
  187. package/lib/typescript/plugins/storageBrowser/StorageBrowserPlugin.d.ts +3 -0
  188. package/lib/typescript/plugins/storageBrowser/StorageBrowserPlugin.d.ts.map +1 -0
  189. package/lib/typescript/plugins/storageBrowser/storageAdapterRegistry.d.ts +16 -0
  190. package/lib/typescript/plugins/storageBrowser/storageAdapterRegistry.d.ts.map +1 -0
  191. package/lib/typescript/plugins/timeline/TimelinePlugin.d.ts +3 -0
  192. package/lib/typescript/plugins/timeline/TimelinePlugin.d.ts.map +1 -0
  193. package/lib/typescript/plugins/timeline/timelineStore.d.ts +24 -0
  194. package/lib/typescript/plugins/timeline/timelineStore.d.ts.map +1 -0
  195. package/package.json +131 -0
  196. package/src/core/DebugBubble.tsx +149 -0
  197. package/src/core/DebugOverlay.tsx +211 -0
  198. package/src/core/DebuggerProvider.tsx +211 -0
  199. package/src/core/GestureDetector.tsx +95 -0
  200. package/src/core/PluginRegistry.ts +118 -0
  201. package/src/core/theme.ts +41 -0
  202. package/src/core/types.ts +339 -0
  203. package/src/core/useDebugger.ts +24 -0
  204. package/src/core/utils.ts +221 -0
  205. package/src/index.ts +67 -0
  206. package/src/plugins/console/ConsoleInterceptor.ts +110 -0
  207. package/src/plugins/console/ConsoleViewerPlugin.tsx +241 -0
  208. package/src/plugins/crashReporter/CrashReporterPlugin.tsx +316 -0
  209. package/src/plugins/customActions/CustomActionsPlugin.tsx +199 -0
  210. package/src/plugins/customActions/actionStore.ts +49 -0
  211. package/src/plugins/deepLinkTester/DeepLinkTesterPlugin.tsx +213 -0
  212. package/src/plugins/deviceInfo/DeviceInfoPlugin.tsx +153 -0
  213. package/src/plugins/featureFlags/FeatureFlagsPlugin.tsx +338 -0
  214. package/src/plugins/featureFlags/flagStore.ts +118 -0
  215. package/src/plugins/navigationInspector/NavigationInspectorPlugin.tsx +170 -0
  216. package/src/plugins/navigationInspector/navigationStore.ts +70 -0
  217. package/src/plugins/network/NetworkInspectorPlugin.tsx +598 -0
  218. package/src/plugins/network/NetworkInterceptor.ts +344 -0
  219. package/src/plugins/performance/PerformanceMonitorPlugin.tsx +194 -0
  220. package/src/plugins/remoteConfig/RemoteConfigPlugin.tsx +205 -0
  221. package/src/plugins/remoteConfig/remoteConfigStore.ts +48 -0
  222. package/src/plugins/stateInspector/StateInspectorPlugin.tsx +342 -0
  223. package/src/plugins/stateInspector/stateAdapterRegistry.ts +66 -0
  224. package/src/plugins/storageBrowser/StorageBrowserPlugin.tsx +410 -0
  225. package/src/plugins/storageBrowser/storageAdapterRegistry.ts +47 -0
  226. package/src/plugins/timeline/TimelinePlugin.tsx +242 -0
  227. 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
+ }