react-native-debug-toolkit 0.6.4 → 2.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 (277) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -212
  3. package/lib/commonjs/components/ClipboardTab.js +92 -0
  4. package/lib/commonjs/components/ClipboardTab.js.map +1 -0
  5. package/lib/commonjs/components/ConsoleLogTab.js +295 -0
  6. package/lib/commonjs/components/ConsoleLogTab.js.map +1 -0
  7. package/lib/commonjs/components/EnvironmentTab.js +288 -0
  8. package/lib/commonjs/components/EnvironmentTab.js.map +1 -0
  9. package/lib/commonjs/components/FloatPanelView.js +797 -0
  10. package/lib/commonjs/components/FloatPanelView.js.map +1 -0
  11. package/lib/commonjs/components/NavigationLogTab.js +131 -0
  12. package/lib/commonjs/components/NavigationLogTab.js.map +1 -0
  13. package/lib/commonjs/components/NetworkLogTab.js +575 -0
  14. package/lib/commonjs/components/NetworkLogTab.js.map +1 -0
  15. package/lib/commonjs/components/ThirdPartyLibsTab.js +97 -0
  16. package/lib/commonjs/components/ThirdPartyLibsTab.js.map +1 -0
  17. package/lib/commonjs/components/TrackLogTab.js +372 -0
  18. package/lib/commonjs/components/TrackLogTab.js.map +1 -0
  19. package/lib/commonjs/components/ZustandLogTab.js +451 -0
  20. package/lib/commonjs/components/ZustandLogTab.js.map +1 -0
  21. package/lib/commonjs/components/shared/CollapsibleSection.js +84 -0
  22. package/lib/commonjs/components/shared/CollapsibleSection.js.map +1 -0
  23. package/lib/commonjs/components/shared/CopyButton.js +64 -0
  24. package/lib/commonjs/components/shared/CopyButton.js.map +1 -0
  25. package/lib/commonjs/components/shared/JsonView.js +157 -0
  26. package/lib/commonjs/components/shared/JsonView.js.map +1 -0
  27. package/lib/commonjs/core/DebugToolkit.js +142 -0
  28. package/lib/commonjs/core/DebugToolkit.js.map +1 -0
  29. package/lib/commonjs/core/DebugToolkitProvider.js +64 -0
  30. package/lib/commonjs/core/DebugToolkitProvider.js.map +1 -0
  31. package/lib/commonjs/features/ClipboardFeature.js +17 -0
  32. package/lib/commonjs/features/ClipboardFeature.js.map +1 -0
  33. package/lib/commonjs/features/ConsoleLogFeature.js +98 -0
  34. package/lib/commonjs/features/ConsoleLogFeature.js.map +1 -0
  35. package/lib/commonjs/features/EnvironmentFeature.js +168 -0
  36. package/lib/commonjs/features/EnvironmentFeature.js.map +1 -0
  37. package/lib/commonjs/features/NavigationLogFeature.js +60 -0
  38. package/lib/commonjs/features/NavigationLogFeature.js.map +1 -0
  39. package/lib/commonjs/features/NetworkFeature.js +328 -0
  40. package/lib/commonjs/features/NetworkFeature.js.map +1 -0
  41. package/lib/commonjs/features/ThirdPartyLibsFeature.js +51 -0
  42. package/lib/commonjs/features/ThirdPartyLibsFeature.js.map +1 -0
  43. package/lib/commonjs/features/TrackFeature.js +55 -0
  44. package/lib/commonjs/features/TrackFeature.js.map +1 -0
  45. package/lib/commonjs/features/ZustandLogFeature.js +76 -0
  46. package/lib/commonjs/features/ZustandLogFeature.js.map +1 -0
  47. package/lib/commonjs/hooks/useNavigationLogger.js +78 -0
  48. package/lib/commonjs/hooks/useNavigationLogger.js.map +1 -0
  49. package/lib/commonjs/hooks/useSlideDetailAnimation.js +53 -0
  50. package/lib/commonjs/hooks/useSlideDetailAnimation.js.map +1 -0
  51. package/lib/commonjs/index.js +165 -0
  52. package/lib/commonjs/index.js.map +1 -0
  53. package/lib/commonjs/initialize.js +161 -0
  54. package/lib/commonjs/initialize.js.map +1 -0
  55. package/lib/commonjs/native/NativeDebugLibs.js +58 -0
  56. package/lib/commonjs/native/NativeDebugLibs.js.map +1 -0
  57. package/lib/commonjs/package.json +1 -0
  58. package/lib/commonjs/types/index.js +6 -0
  59. package/lib/commonjs/types/index.js.map +1 -0
  60. package/lib/commonjs/utils/constants.js +135 -0
  61. package/lib/commonjs/utils/constants.js.map +1 -0
  62. package/lib/commonjs/utils/copyToComputer.js +97 -0
  63. package/lib/commonjs/utils/copyToComputer.js.map +1 -0
  64. package/lib/commonjs/utils/createEventChannel.js +21 -0
  65. package/lib/commonjs/utils/createEventChannel.js.map +1 -0
  66. package/lib/commonjs/utils/createObservableStore.js +38 -0
  67. package/lib/commonjs/utils/createObservableStore.js.map +1 -0
  68. package/lib/commonjs/utils/safeStringify.js +27 -0
  69. package/lib/commonjs/utils/safeStringify.js.map +1 -0
  70. package/lib/module/components/ClipboardTab.js +86 -0
  71. package/lib/module/components/ClipboardTab.js.map +1 -0
  72. package/lib/module/components/ConsoleLogTab.js +290 -0
  73. package/lib/module/components/ConsoleLogTab.js.map +1 -0
  74. package/lib/module/components/EnvironmentTab.js +282 -0
  75. package/lib/module/components/EnvironmentTab.js.map +1 -0
  76. package/lib/module/components/FloatPanelView.js +791 -0
  77. package/lib/module/components/FloatPanelView.js.map +1 -0
  78. package/lib/module/components/NavigationLogTab.js +126 -0
  79. package/lib/module/components/NavigationLogTab.js.map +1 -0
  80. package/lib/module/components/NetworkLogTab.js +570 -0
  81. package/lib/module/components/NetworkLogTab.js.map +1 -0
  82. package/lib/module/components/ThirdPartyLibsTab.js +91 -0
  83. package/lib/module/components/ThirdPartyLibsTab.js.map +1 -0
  84. package/lib/module/components/TrackLogTab.js +367 -0
  85. package/lib/module/components/TrackLogTab.js.map +1 -0
  86. package/lib/module/components/ZustandLogTab.js +446 -0
  87. package/lib/module/components/ZustandLogTab.js.map +1 -0
  88. package/lib/module/components/shared/CollapsibleSection.js +78 -0
  89. package/lib/module/components/shared/CollapsibleSection.js.map +1 -0
  90. package/lib/module/components/shared/CopyButton.js +58 -0
  91. package/lib/module/components/shared/CopyButton.js.map +1 -0
  92. package/lib/module/components/shared/JsonView.js +152 -0
  93. package/lib/module/components/shared/JsonView.js.map +1 -0
  94. package/lib/module/core/DebugToolkit.js +137 -0
  95. package/lib/module/core/DebugToolkit.js.map +1 -0
  96. package/lib/module/core/DebugToolkitProvider.js +58 -0
  97. package/lib/module/core/DebugToolkitProvider.js.map +1 -0
  98. package/lib/module/features/ClipboardFeature.js +12 -0
  99. package/lib/module/features/ClipboardFeature.js.map +1 -0
  100. package/lib/module/features/ConsoleLogFeature.js +93 -0
  101. package/lib/module/features/ConsoleLogFeature.js.map +1 -0
  102. package/lib/module/features/EnvironmentFeature.js +164 -0
  103. package/lib/module/features/EnvironmentFeature.js.map +1 -0
  104. package/lib/module/features/NavigationLogFeature.js +54 -0
  105. package/lib/module/features/NavigationLogFeature.js.map +1 -0
  106. package/lib/module/features/NetworkFeature.js +322 -0
  107. package/lib/module/features/NetworkFeature.js.map +1 -0
  108. package/lib/module/features/ThirdPartyLibsFeature.js +46 -0
  109. package/lib/module/features/ThirdPartyLibsFeature.js.map +1 -0
  110. package/lib/module/features/TrackFeature.js +49 -0
  111. package/lib/module/features/TrackFeature.js.map +1 -0
  112. package/lib/module/features/ZustandLogFeature.js +69 -0
  113. package/lib/module/features/ZustandLogFeature.js.map +1 -0
  114. package/lib/module/hooks/useNavigationLogger.js +74 -0
  115. package/lib/module/hooks/useNavigationLogger.js.map +1 -0
  116. package/lib/module/hooks/useSlideDetailAnimation.js +50 -0
  117. package/lib/module/hooks/useSlideDetailAnimation.js.map +1 -0
  118. package/lib/module/index.js +29 -0
  119. package/lib/module/index.js.map +1 -0
  120. package/lib/module/initialize.js +156 -0
  121. package/lib/module/initialize.js.map +1 -0
  122. package/lib/module/native/NativeDebugLibs.js +54 -0
  123. package/lib/module/native/NativeDebugLibs.js.map +1 -0
  124. package/lib/module/package.json +1 -0
  125. package/lib/module/types/index.js +4 -0
  126. package/lib/module/types/index.js.map +1 -0
  127. package/lib/module/utils/constants.js +130 -0
  128. package/lib/module/utils/constants.js.map +1 -0
  129. package/lib/module/utils/copyToComputer.js +91 -0
  130. package/lib/module/utils/copyToComputer.js.map +1 -0
  131. package/lib/module/utils/createEventChannel.js +17 -0
  132. package/lib/module/utils/createEventChannel.js.map +1 -0
  133. package/lib/module/utils/createObservableStore.js +34 -0
  134. package/lib/module/utils/createObservableStore.js.map +1 -0
  135. package/lib/module/utils/safeStringify.js +23 -0
  136. package/lib/module/utils/safeStringify.js.map +1 -0
  137. package/lib/typescript/src/components/ClipboardTab.d.ts +4 -0
  138. package/lib/typescript/src/components/ClipboardTab.d.ts.map +1 -0
  139. package/lib/typescript/src/components/ConsoleLogTab.d.ts +4 -0
  140. package/lib/typescript/src/components/ConsoleLogTab.d.ts.map +1 -0
  141. package/lib/typescript/src/components/EnvironmentTab.d.ts +4 -0
  142. package/lib/typescript/src/components/EnvironmentTab.d.ts.map +1 -0
  143. package/lib/typescript/src/components/FloatPanelView.d.ts +64 -0
  144. package/lib/typescript/src/components/FloatPanelView.d.ts.map +1 -0
  145. package/lib/typescript/src/components/NavigationLogTab.d.ts +4 -0
  146. package/lib/typescript/src/components/NavigationLogTab.d.ts.map +1 -0
  147. package/lib/typescript/src/components/NetworkLogTab.d.ts +4 -0
  148. package/lib/typescript/src/components/NetworkLogTab.d.ts.map +1 -0
  149. package/lib/typescript/src/components/ThirdPartyLibsTab.d.ts +4 -0
  150. package/lib/typescript/src/components/ThirdPartyLibsTab.d.ts.map +1 -0
  151. package/lib/typescript/src/components/TrackLogTab.d.ts +4 -0
  152. package/lib/typescript/src/components/TrackLogTab.d.ts.map +1 -0
  153. package/lib/typescript/src/components/ZustandLogTab.d.ts +4 -0
  154. package/lib/typescript/src/components/ZustandLogTab.d.ts.map +1 -0
  155. package/lib/typescript/src/components/shared/CollapsibleSection.d.ts +9 -0
  156. package/lib/typescript/src/components/shared/CollapsibleSection.d.ts.map +1 -0
  157. package/lib/typescript/src/components/shared/CopyButton.d.ts +12 -0
  158. package/lib/typescript/src/components/shared/CopyButton.d.ts.map +1 -0
  159. package/lib/typescript/src/components/shared/JsonView.d.ts +6 -0
  160. package/lib/typescript/src/components/shared/JsonView.d.ts.map +1 -0
  161. package/lib/typescript/src/core/DebugToolkit.d.ts +27 -0
  162. package/lib/typescript/src/core/DebugToolkit.d.ts.map +1 -0
  163. package/lib/typescript/src/core/DebugToolkitProvider.d.ts +18 -0
  164. package/lib/typescript/src/core/DebugToolkitProvider.d.ts.map +1 -0
  165. package/lib/typescript/src/features/ClipboardFeature.d.ts +3 -0
  166. package/lib/typescript/src/features/ClipboardFeature.d.ts.map +1 -0
  167. package/lib/typescript/src/features/ConsoleLogFeature.d.ts +7 -0
  168. package/lib/typescript/src/features/ConsoleLogFeature.d.ts.map +1 -0
  169. package/lib/typescript/src/features/EnvironmentFeature.d.ts +8 -0
  170. package/lib/typescript/src/features/EnvironmentFeature.d.ts.map +1 -0
  171. package/lib/typescript/src/features/NavigationLogFeature.d.ts +8 -0
  172. package/lib/typescript/src/features/NavigationLogFeature.d.ts.map +1 -0
  173. package/lib/typescript/src/features/NetworkFeature.d.ts +28 -0
  174. package/lib/typescript/src/features/NetworkFeature.d.ts.map +1 -0
  175. package/lib/typescript/src/features/ThirdPartyLibsFeature.d.ts +3 -0
  176. package/lib/typescript/src/features/ThirdPartyLibsFeature.d.ts.map +1 -0
  177. package/lib/typescript/src/features/TrackFeature.d.ts +12 -0
  178. package/lib/typescript/src/features/TrackFeature.d.ts.map +1 -0
  179. package/lib/typescript/src/features/ZustandLogFeature.d.ts +29 -0
  180. package/lib/typescript/src/features/ZustandLogFeature.d.ts.map +1 -0
  181. package/lib/typescript/src/hooks/useNavigationLogger.d.ts +20 -0
  182. package/lib/typescript/src/hooks/useNavigationLogger.d.ts.map +1 -0
  183. package/lib/typescript/src/hooks/useSlideDetailAnimation.d.ts +11 -0
  184. package/lib/typescript/src/hooks/useSlideDetailAnimation.d.ts.map +1 -0
  185. package/lib/typescript/src/index.d.ts +26 -0
  186. package/lib/typescript/src/index.d.ts.map +1 -0
  187. package/lib/typescript/src/initialize.d.ts +51 -0
  188. package/lib/typescript/src/initialize.d.ts.map +1 -0
  189. package/lib/typescript/src/native/NativeDebugLibs.d.ts +11 -0
  190. package/lib/typescript/src/native/NativeDebugLibs.d.ts.map +1 -0
  191. package/lib/typescript/src/types/index.d.ts +112 -0
  192. package/lib/typescript/src/types/index.d.ts.map +1 -0
  193. package/lib/typescript/src/utils/constants.d.ts +96 -0
  194. package/lib/typescript/src/utils/constants.d.ts.map +1 -0
  195. package/lib/typescript/src/utils/copyToComputer.d.ts +30 -0
  196. package/lib/typescript/src/utils/copyToComputer.d.ts.map +1 -0
  197. package/lib/typescript/src/utils/createEventChannel.d.ts +7 -0
  198. package/lib/typescript/src/utils/createEventChannel.d.ts.map +1 -0
  199. package/lib/typescript/src/utils/createObservableStore.d.ts +9 -0
  200. package/lib/typescript/src/utils/createObservableStore.d.ts.map +1 -0
  201. package/lib/typescript/src/utils/safeStringify.d.ts +6 -0
  202. package/lib/typescript/src/utils/safeStringify.d.ts.map +1 -0
  203. package/package.json +63 -24
  204. package/src/components/ClipboardTab.tsx +81 -0
  205. package/src/components/ConsoleLogTab.tsx +209 -0
  206. package/src/components/EnvironmentTab.tsx +276 -0
  207. package/src/components/FloatPanelView.tsx +714 -0
  208. package/src/components/NavigationLogTab.tsx +66 -0
  209. package/src/components/NetworkLogTab.tsx +411 -0
  210. package/src/components/ThirdPartyLibsTab.tsx +63 -0
  211. package/src/components/TrackLogTab.tsx +245 -0
  212. package/src/components/ZustandLogTab.tsx +305 -0
  213. package/src/components/shared/CollapsibleSection.tsx +78 -0
  214. package/src/components/shared/CopyButton.tsx +68 -0
  215. package/src/components/shared/JsonView.tsx +125 -0
  216. package/src/core/DebugToolkit.tsx +174 -0
  217. package/src/core/DebugToolkitProvider.tsx +89 -0
  218. package/src/features/ClipboardFeature.ts +11 -0
  219. package/src/features/ConsoleLogFeature.ts +118 -0
  220. package/src/features/EnvironmentFeature.ts +194 -0
  221. package/src/features/NavigationLogFeature.ts +74 -0
  222. package/src/features/NetworkFeature.ts +488 -0
  223. package/src/features/ThirdPartyLibsFeature.ts +42 -0
  224. package/src/features/TrackFeature.ts +69 -0
  225. package/src/features/ZustandLogFeature.ts +127 -0
  226. package/src/hooks/useNavigationLogger.ts +107 -0
  227. package/src/hooks/useSlideDetailAnimation.ts +45 -0
  228. package/src/index.ts +52 -0
  229. package/src/initialize.ts +214 -0
  230. package/src/native/NativeDebugLibs.ts +74 -0
  231. package/src/types/index.ts +138 -0
  232. package/src/utils/constants.ts +91 -0
  233. package/src/utils/copyToComputer.ts +104 -0
  234. package/src/utils/createEventChannel.ts +22 -0
  235. package/src/utils/createObservableStore.ts +42 -0
  236. package/src/utils/safeStringify.ts +25 -0
  237. package/.cursor/rules/react-native.mdc +0 -41
  238. package/README.zh-CN.md +0 -230
  239. package/index.js +0 -41
  240. package/ios/BuildTypeModule.h +0 -9
  241. package/ios/BuildTypeModule.m +0 -42
  242. package/ios/RNDebugLibs.h +0 -10
  243. package/ios/RNDebugLibs.m +0 -79
  244. package/lib/DebugToolKit.js +0 -126
  245. package/lib/EnvironmentManager.ts +0 -80
  246. package/lib/NativeDebugLibs.js +0 -67
  247. package/lib/features/ConsoleLogFeature.js +0 -70
  248. package/lib/features/NavigationLogFeature.js +0 -45
  249. package/lib/features/NetworkFeature.js +0 -389
  250. package/lib/features/PerformanceFeature.js +0 -390
  251. package/lib/features/ThirdPartyLibsFeature.js +0 -63
  252. package/lib/features/TrackFeature.js +0 -94
  253. package/lib/features/ZustandLogFeature.js +0 -44
  254. package/lib/hooks/useNavigationLogger.js +0 -92
  255. package/lib/index.js +0 -114
  256. package/lib/navigation/NavigationLogger.js +0 -1
  257. package/lib/types/TrackTypes.ts +0 -92
  258. package/lib/utils/DebugConst.js +0 -67
  259. package/lib/utils/StorageUtils.js +0 -80
  260. package/lib/views/ConsoleLogDetails.js +0 -314
  261. package/lib/views/FloatPanelView.js +0 -697
  262. package/lib/views/HttpLogDetails.js +0 -648
  263. package/lib/views/NavigationLogDetails.js +0 -302
  264. package/lib/views/RestartModal.js +0 -75
  265. package/lib/views/SubViewConsoleLogs.js +0 -209
  266. package/lib/views/SubViewEnvironment.js +0 -73
  267. package/lib/views/SubViewHTTPLogs.js +0 -235
  268. package/lib/views/SubViewNavigationLogs.js +0 -199
  269. package/lib/views/SubViewPerformance.js +0 -515
  270. package/lib/views/SubViewThirdPartyLibs.js +0 -239
  271. package/lib/views/SubViewTrackLogs.js +0 -318
  272. package/lib/views/SubViewZustandLogs.js +0 -279
  273. package/lib/views/TabView.js +0 -66
  274. package/lib/views/TrackLogDetails.js +0 -481
  275. package/lib/views/ZustandLogDetails.js +0 -355
  276. package/react-native-debug-toolkit.podspec +0 -25
  277. package/react-native.config.js +0 -18
@@ -0,0 +1,245 @@
1
+ import React, { useState } from 'react';
2
+ import { View, Text, FlatList, TouchableOpacity, StyleSheet, Animated, ScrollView } from 'react-native';
3
+ import { Colors } from '../utils/constants';
4
+ import { safeStringify } from '../utils/safeStringify';
5
+ import { CollapsibleSection } from './shared/CollapsibleSection';
6
+ import { JsonView } from './shared/JsonView';
7
+ import { CopyButton } from './shared/CopyButton';
8
+ import { useSlideDetailAnimation } from '../hooks/useSlideDetailAnimation';
9
+ import type { DebugFeatureRenderProps, TrackLogEntry } from '../types';
10
+
11
+ export const TrackLogTab: React.FC<DebugFeatureRenderProps<TrackLogEntry>> = React.memo(({
12
+ data,
13
+ }) => {
14
+ const [selected, setSelected] = useState<TrackLogEntry | null>(null);
15
+ const { detailTranslateX, listTranslateX, listOpacity } = useSlideDetailAnimation(selected);
16
+
17
+ const renderItem = ({ item }: { item: TrackLogEntry }) => (
18
+ <TouchableOpacity style={s.card} onPress={() => setSelected(item)} activeOpacity={0.6}>
19
+ <View style={s.cardRow}>
20
+ <View style={s.eventIcon}>
21
+ <Text style={s.eventIconText}>●</Text>
22
+ </View>
23
+ <View style={s.cardContent}>
24
+ <View style={s.cardHeader}>
25
+ <Text style={s.eventName}>{item.eventName}</Text>
26
+ <Text style={s.time}>{new Date(item.timestamp).toLocaleTimeString()}</Text>
27
+ </View>
28
+ <View style={s.previewRow}>
29
+ {Object.entries(item)
30
+ .filter(([k]) => k !== 'id' && k !== 'eventName' && k !== 'timestamp')
31
+ .slice(0, 2)
32
+ .map(([key, value]) => (
33
+ <View key={key} style={s.previewChip}>
34
+ <Text style={s.previewText} numberOfLines={1}>
35
+ <Text style={s.previewKey}>{key}</Text> {String(value ?? '').slice(0, 25)}
36
+ </Text>
37
+ </View>
38
+ ))}
39
+ </View>
40
+ </View>
41
+ </View>
42
+ </TouchableOpacity>
43
+ );
44
+
45
+ const extraProps = selected
46
+ ? Object.entries(selected).filter(
47
+ ([k, v]) => k !== 'id' && k !== 'eventName' && k !== 'timestamp' && v !== undefined && v !== null,
48
+ )
49
+ : [];
50
+
51
+ return (
52
+ <View style={s.container}>
53
+ {/* List */}
54
+ <Animated.View style={[s.listWrap, selected ? { opacity: listOpacity, transform: [{ translateX: listTranslateX }] } : null]}>
55
+ {data.length === 0 ? (
56
+ <View style={s.emptyContainer}>
57
+ <Text style={s.emptyIcon}>~</Text>
58
+ <Text style={s.empty}>No track events</Text>
59
+ </View>
60
+ ) : (
61
+ <FlatList
62
+ data={[...data].reverse()}
63
+ renderItem={renderItem}
64
+ keyExtractor={(item) => item.id}
65
+ contentContainerStyle={s.listContent}
66
+ initialNumToRender={20}
67
+ maxToRenderPerBatch={10}
68
+ windowSize={5}
69
+ removeClippedSubviews={true}
70
+ />
71
+ )}
72
+ </Animated.View>
73
+
74
+ {/* Detail (push navigation) */}
75
+ {selected && (
76
+ <Animated.View style={[s.detailOverlay, { transform: [{ translateX: detailTranslateX }] }]}>
77
+ <View style={s.detailWrap}>
78
+ <View style={s.detailHeader}>
79
+ <TouchableOpacity onPress={() => setSelected(null)} style={s.backBtn} activeOpacity={0.6}>
80
+ <Text style={s.backIcon}>‹</Text>
81
+ <Text style={s.backText}>Back</Text>
82
+ </TouchableOpacity>
83
+ <View style={s.eventBadge}>
84
+ <Text style={s.eventBadgeText}>{selected.eventName}</Text>
85
+ </View>
86
+ </View>
87
+ <ScrollView style={s.detailBody} contentContainerStyle={s.detailBodyContent}>
88
+ {/* Properties */}
89
+ <CollapsibleSection title="Properties" initiallyExpanded>
90
+ <View style={s.sectionWithCopy}>
91
+ <CopyButton text={safeStringify(Object.fromEntries(extraProps), 2)} label="Track Properties" />
92
+ <View style={s.propsGrid}>
93
+ {extraProps.map(([key, value]) => (
94
+ <View key={key} style={s.propRow}>
95
+ <Text style={s.propKey}>{key}</Text>
96
+ <Text style={s.propValue} selectable>
97
+ {typeof value === 'object' ? safeStringify(value) : String(value ?? '')}
98
+ </Text>
99
+ </View>
100
+ ))}
101
+ </View>
102
+ </View>
103
+ </CollapsibleSection>
104
+
105
+ {/* Full JSON */}
106
+ <CollapsibleSection title="Full Event Data">
107
+ <View style={s.sectionWithCopy}>
108
+ <CopyButton text={safeStringify(selected, 2)} label="Track Event" />
109
+ <JsonView data={selected} maxHeight={300} />
110
+ </View>
111
+ </CollapsibleSection>
112
+
113
+ {/* Timing */}
114
+ <View style={s.timingCard}>
115
+ <Text style={s.timingLabel}>Time</Text>
116
+ <Text style={s.timingValue}>
117
+ {new Date(selected.timestamp).toLocaleString()}
118
+ </Text>
119
+ </View>
120
+ </ScrollView>
121
+ </View>
122
+ </Animated.View>
123
+ )}
124
+ </View>
125
+ );
126
+ });
127
+
128
+ const s = StyleSheet.create({
129
+ container: { flex: 1, backgroundColor: Colors.background },
130
+ listWrap: {
131
+ position: 'absolute',
132
+ top: 0,
133
+ left: 0,
134
+ right: 0,
135
+ bottom: 0,
136
+ },
137
+ listContent: { padding: 12 },
138
+
139
+ // Empty
140
+ emptyContainer: { flex: 1, alignItems: 'center', justifyContent: 'center' },
141
+ emptyIcon: { fontSize: 40, color: Colors.textLight, marginBottom: 4 },
142
+ empty: { textAlign: 'center', color: Colors.textLight, fontSize: 14 },
143
+
144
+ // Card
145
+ card: {
146
+ backgroundColor: Colors.surface,
147
+ borderRadius: 12,
148
+ marginBottom: 8,
149
+ overflow: 'hidden',
150
+ },
151
+ cardRow: { flexDirection: 'row', padding: 14, alignItems: 'flex-start' },
152
+ eventIcon: {
153
+ width: 28,
154
+ height: 28,
155
+ borderRadius: 8,
156
+ backgroundColor: 'rgba(175,82,222,0.1)',
157
+ alignItems: 'center',
158
+ justifyContent: 'center',
159
+ marginRight: 12,
160
+ marginTop: 1,
161
+ },
162
+ eventIconText: { color: Colors.purple, fontSize: 10 },
163
+ cardContent: { flex: 1 },
164
+ cardHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 },
165
+ eventName: { fontSize: 15, fontWeight: '600', color: Colors.text },
166
+ time: { fontSize: 12, color: Colors.textSecondary },
167
+ previewRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 4 },
168
+ previewChip: {
169
+ backgroundColor: Colors.background,
170
+ borderRadius: 6,
171
+ paddingHorizontal: 8,
172
+ paddingVertical: 3,
173
+ },
174
+ previewText: { fontSize: 12, color: Colors.textSecondary, lineHeight: 16 },
175
+ previewKey: { fontWeight: '600', color: Colors.text },
176
+
177
+ // Detail (push navigation)
178
+ detailOverlay: {
179
+ position: 'absolute',
180
+ top: 0,
181
+ left: 0,
182
+ right: 0,
183
+ bottom: 0,
184
+ backgroundColor: Colors.background,
185
+ },
186
+ detailWrap: { flex: 1, backgroundColor: Colors.background },
187
+ detailHeader: {
188
+ flexDirection: 'row',
189
+ alignItems: 'center',
190
+ paddingHorizontal: 8,
191
+ paddingVertical: 10,
192
+ backgroundColor: Colors.surface,
193
+ borderBottomWidth: StyleSheet.hairlineWidth,
194
+ borderBottomColor: Colors.border,
195
+ gap: 8,
196
+ },
197
+ backBtn: {
198
+ flexDirection: 'row',
199
+ alignItems: 'center',
200
+ paddingHorizontal: 6,
201
+ paddingVertical: 4,
202
+ borderRadius: 8,
203
+ },
204
+ backIcon: {
205
+ fontSize: 24,
206
+ fontWeight: '300',
207
+ color: Colors.primary,
208
+ marginTop: -2,
209
+ marginRight: 2,
210
+ },
211
+ backText: {
212
+ fontSize: 16,
213
+ color: Colors.primary,
214
+ fontWeight: '500',
215
+ },
216
+ eventBadge: {
217
+ backgroundColor: Colors.purple,
218
+ paddingHorizontal: 12,
219
+ paddingVertical: 5,
220
+ borderRadius: 8,
221
+ },
222
+ eventBadgeText: { color: '#FFF', fontSize: 14, fontWeight: '700' },
223
+ detailBody: { flex: 1 },
224
+ detailBodyContent: { padding: 12, paddingBottom: 40 },
225
+ sectionWithCopy: { gap: 8 },
226
+
227
+ // Properties
228
+ propsGrid: { gap: 12 },
229
+ propRow: {},
230
+ propKey: { fontSize: 13, fontWeight: '600', color: Colors.textSecondary, marginBottom: 3 },
231
+ propValue: { fontSize: 14, color: Colors.text, lineHeight: 20 },
232
+
233
+ // Timing
234
+ timingCard: {
235
+ backgroundColor: Colors.surface,
236
+ borderRadius: 12,
237
+ padding: 14,
238
+ flexDirection: 'row',
239
+ justifyContent: 'space-between',
240
+ alignItems: 'center',
241
+ marginTop: 4,
242
+ },
243
+ timingLabel: { fontSize: 13, color: Colors.textSecondary, fontWeight: '500' },
244
+ timingValue: { fontSize: 13, color: Colors.text },
245
+ });
@@ -0,0 +1,305 @@
1
+ import React, { useState } from 'react';
2
+ import { View, Text, FlatList, TouchableOpacity, StyleSheet, Animated, ScrollView } from 'react-native';
3
+ import { Colors } from '../utils/constants';
4
+ import { safeStringify } from '../utils/safeStringify';
5
+ import { CollapsibleSection } from './shared/CollapsibleSection';
6
+ import { JsonView } from './shared/JsonView';
7
+ import { CopyButton } from './shared/CopyButton';
8
+ import { useSlideDetailAnimation } from '../hooks/useSlideDetailAnimation';
9
+ import type { DebugFeatureRenderProps, ZustandLogEntry } from '../types';
10
+
11
+ const getActionColor = (action: string): string => {
12
+ if (action.includes('add') || action.includes('create')) return '#34C759';
13
+ if (action.includes('remove') || action.includes('delete')) return '#FF3B30';
14
+ if (action.includes('update') || action.includes('set')) return '#007AFF';
15
+ return '#AF52DE';
16
+ };
17
+
18
+ const getActionBgColor = (action: string): string => {
19
+ if (action.includes('add') || action.includes('create')) return 'rgba(52,199,89,0.1)';
20
+ if (action.includes('remove') || action.includes('delete')) return 'rgba(255,59,48,0.1)';
21
+ if (action.includes('update') || action.includes('set')) return 'rgba(0,122,255,0.1)';
22
+ return 'rgba(175,82,222,0.1)';
23
+ };
24
+
25
+ export const ZustandLogTab: React.FC<DebugFeatureRenderProps<ZustandLogEntry>> = React.memo(({
26
+ data,
27
+ }) => {
28
+ const [selected, setSelected] = useState<ZustandLogEntry | null>(null);
29
+ const { detailTranslateX, listTranslateX, listOpacity } = useSlideDetailAnimation(selected);
30
+
31
+ const renderItem = ({ item }: { item: ZustandLogEntry }) => (
32
+ <TouchableOpacity style={s.card} onPress={() => setSelected(item)} activeOpacity={0.6}>
33
+ <View style={s.cardRow}>
34
+ <View style={[s.actionIcon, { backgroundColor: getActionBgColor(item.action) }]}>
35
+ <View style={[s.actionDot, { backgroundColor: getActionColor(item.action) }]} />
36
+ </View>
37
+ <View style={s.cardContent}>
38
+ <View style={s.cardMeta}>
39
+ <Text style={s.action}>{item.action}</Text>
40
+ {item.storeName && (
41
+ <View style={s.storeBadge}>
42
+ <Text style={s.storeBadgeText}>{item.storeName}</Text>
43
+ </View>
44
+ )}
45
+ </View>
46
+ <Text style={s.time}>{new Date(item.timestamp).toLocaleTimeString()}</Text>
47
+ </View>
48
+ {item.actionCompleteTime != null && (
49
+ <View style={s.durationBadge}>
50
+ <Text style={s.durationText}>{item.actionCompleteTime}ms</Text>
51
+ </View>
52
+ )}
53
+ </View>
54
+ </TouchableOpacity>
55
+ );
56
+
57
+ const findChanges = (prev: unknown, next: unknown): string[] => {
58
+ if (typeof prev !== 'object' || typeof next !== 'object' || !prev || !next) return [];
59
+ const allKeys = new Set([...Object.keys(prev as object), ...Object.keys(next as object)]);
60
+ const changed: string[] = [];
61
+ allKeys.forEach((key) => {
62
+ const pv = (prev as Record<string, unknown>)[key];
63
+ const nv = (next as Record<string, unknown>)[key];
64
+ if (safeStringify(pv) !== safeStringify(nv)) changed.push(key);
65
+ });
66
+ return changed;
67
+ };
68
+
69
+ return (
70
+ <View style={s.container}>
71
+ {/* List */}
72
+ <Animated.View style={[s.listWrap, selected ? { opacity: listOpacity, transform: [{ translateX: listTranslateX }] } : null]}>
73
+ {data.length === 0 ? (
74
+ <View style={s.emptyContainer}>
75
+ <Text style={s.emptyIcon}>~</Text>
76
+ <Text style={s.empty}>No Zustand state changes</Text>
77
+ </View>
78
+ ) : (
79
+ <FlatList
80
+ data={[...data].reverse()}
81
+ renderItem={renderItem}
82
+ keyExtractor={(item) => item.id}
83
+ contentContainerStyle={s.listContent}
84
+ initialNumToRender={20}
85
+ maxToRenderPerBatch={10}
86
+ windowSize={5}
87
+ removeClippedSubviews={true}
88
+ />
89
+ )}
90
+ </Animated.View>
91
+
92
+ {/* Detail (push navigation) */}
93
+ {selected && (
94
+ <Animated.View style={[s.detailOverlay, { transform: [{ translateX: detailTranslateX }] }]}>
95
+ <View style={s.detailWrap}>
96
+ <View style={s.detailHeader}>
97
+ <TouchableOpacity onPress={() => setSelected(null)} style={s.backBtn} activeOpacity={0.6}>
98
+ <Text style={s.backIcon}>‹</Text>
99
+ <Text style={s.backText}>Back</Text>
100
+ </TouchableOpacity>
101
+ <View style={s.detailHeaderCenter}>
102
+ <Text style={[s.detailAction, { color: getActionColor(selected.action) }]}>
103
+ {selected.action}
104
+ </Text>
105
+ {selected.storeName && (
106
+ <View style={s.storeBadge}>
107
+ <Text style={s.storeBadgeText}>{selected.storeName}</Text>
108
+ </View>
109
+ )}
110
+ </View>
111
+ </View>
112
+ <ScrollView style={s.detailBody} contentContainerStyle={s.detailBodyContent}>
113
+ {/* Meta */}
114
+ <View style={s.metaCard}>
115
+ <View style={s.metaItem}>
116
+ <Text style={s.metaLabel}>Time</Text>
117
+ <Text style={s.metaValue}>
118
+ {new Date(selected.timestamp).toLocaleString()}
119
+ </Text>
120
+ </View>
121
+ {selected.actionCompleteTime != null && (
122
+ <View style={s.metaDivider} />
123
+ )}
124
+ {selected.actionCompleteTime != null && (
125
+ <View style={s.metaItem}>
126
+ <Text style={s.metaLabel}>Duration</Text>
127
+ <Text style={s.metaValue}>{selected.actionCompleteTime}ms</Text>
128
+ </View>
129
+ )}
130
+ </View>
131
+
132
+ {/* Changed keys */}
133
+ {(() => {
134
+ const changes = findChanges(selected.prevState, selected.nextState);
135
+ if (changes.length === 0) return null;
136
+ return (
137
+ <View style={s.changesCard}>
138
+ <Text style={s.changesTitle}>Changed Keys</Text>
139
+ <View style={s.changesTags}>
140
+ {changes.map((key) => (
141
+ <View key={key} style={s.changeTag}>
142
+ <Text style={s.changeTagText}>{key}</Text>
143
+ </View>
144
+ ))}
145
+ </View>
146
+ </View>
147
+ );
148
+ })()}
149
+
150
+ {/* Previous State */}
151
+ <CollapsibleSection title="Previous State">
152
+ <View style={s.sectionWithCopy}>
153
+ <CopyButton text={safeStringify(selected.prevState, 2)} label="Previous State" />
154
+ <JsonView data={selected.prevState} maxHeight={250} />
155
+ </View>
156
+ </CollapsibleSection>
157
+
158
+ {/* Next State */}
159
+ <CollapsibleSection title="Next State" initiallyExpanded>
160
+ <View style={s.sectionWithCopy}>
161
+ <CopyButton text={safeStringify(selected.nextState, 2)} label="Next State" />
162
+ <JsonView data={selected.nextState} maxHeight={250} />
163
+ </View>
164
+ </CollapsibleSection>
165
+ </ScrollView>
166
+ </View>
167
+ </Animated.View>
168
+ )}
169
+ </View>
170
+ );
171
+ });
172
+
173
+ const s = StyleSheet.create({
174
+ container: { flex: 1, backgroundColor: Colors.background },
175
+ listWrap: {
176
+ position: 'absolute',
177
+ top: 0,
178
+ left: 0,
179
+ right: 0,
180
+ bottom: 0,
181
+ },
182
+ listContent: { padding: 12 },
183
+
184
+ // Empty
185
+ emptyContainer: { flex: 1, alignItems: 'center', justifyContent: 'center' },
186
+ emptyIcon: { fontSize: 40, color: Colors.textLight, marginBottom: 4 },
187
+ empty: { textAlign: 'center', color: Colors.textLight, fontSize: 14 },
188
+
189
+ // Card
190
+ card: {
191
+ backgroundColor: Colors.surface,
192
+ borderRadius: 12,
193
+ marginBottom: 8,
194
+ overflow: 'hidden',
195
+ },
196
+ cardRow: { flexDirection: 'row', padding: 14, alignItems: 'center' },
197
+ actionIcon: {
198
+ width: 28,
199
+ height: 28,
200
+ borderRadius: 8,
201
+ alignItems: 'center',
202
+ justifyContent: 'center',
203
+ marginRight: 12,
204
+ },
205
+ actionDot: { width: 8, height: 8, borderRadius: 4 },
206
+ cardContent: { flex: 1 },
207
+ cardMeta: { flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: 3 },
208
+ action: { fontSize: 15, fontWeight: '600', color: Colors.text },
209
+ storeBadge: {
210
+ backgroundColor: 'rgba(0,122,255,0.08)',
211
+ paddingHorizontal: 8,
212
+ paddingVertical: 2,
213
+ borderRadius: 4,
214
+ },
215
+ storeBadgeText: { fontSize: 11, color: Colors.primary, fontWeight: '600' },
216
+ time: { fontSize: 12, color: Colors.textSecondary },
217
+ durationBadge: {
218
+ backgroundColor: Colors.background,
219
+ borderRadius: 8,
220
+ paddingHorizontal: 8,
221
+ paddingVertical: 4,
222
+ },
223
+ durationText: { fontSize: 12, color: Colors.textSecondary, fontWeight: '500' },
224
+
225
+ // Detail (push navigation)
226
+ detailOverlay: {
227
+ position: 'absolute',
228
+ top: 0,
229
+ left: 0,
230
+ right: 0,
231
+ bottom: 0,
232
+ backgroundColor: Colors.background,
233
+ },
234
+ detailWrap: { flex: 1, backgroundColor: Colors.background },
235
+ detailHeader: {
236
+ flexDirection: 'row',
237
+ alignItems: 'center',
238
+ paddingHorizontal: 8,
239
+ paddingVertical: 10,
240
+ backgroundColor: Colors.surface,
241
+ borderBottomWidth: StyleSheet.hairlineWidth,
242
+ borderBottomColor: Colors.border,
243
+ },
244
+ backBtn: {
245
+ flexDirection: 'row',
246
+ alignItems: 'center',
247
+ paddingHorizontal: 6,
248
+ paddingVertical: 4,
249
+ borderRadius: 8,
250
+ marginRight: 8,
251
+ },
252
+ backIcon: {
253
+ fontSize: 24,
254
+ fontWeight: '300',
255
+ color: Colors.primary,
256
+ marginTop: -2,
257
+ marginRight: 2,
258
+ },
259
+ backText: {
260
+ fontSize: 16,
261
+ color: Colors.primary,
262
+ fontWeight: '500',
263
+ },
264
+ detailHeaderCenter: {
265
+ flexDirection: 'row',
266
+ alignItems: 'center',
267
+ gap: 10,
268
+ flex: 1,
269
+ },
270
+ detailAction: { fontSize: 17, fontWeight: '700' },
271
+ detailBody: { flex: 1 },
272
+ detailBodyContent: { padding: 12, paddingBottom: 40 },
273
+ sectionWithCopy: { gap: 8 },
274
+
275
+ // Meta
276
+ metaCard: {
277
+ backgroundColor: Colors.surface,
278
+ borderRadius: 12,
279
+ padding: 14,
280
+ marginBottom: 8,
281
+ flexDirection: 'row',
282
+ alignItems: 'center',
283
+ },
284
+ metaItem: { flex: 1 },
285
+ metaDivider: { width: 1, height: 28, backgroundColor: Colors.border, marginHorizontal: 12 },
286
+ metaLabel: { fontSize: 11, color: Colors.textSecondary, fontWeight: '600', textTransform: 'uppercase', marginBottom: 2 },
287
+ metaValue: { fontSize: 14, color: Colors.text, fontWeight: '500' },
288
+
289
+ // Changes
290
+ changesCard: {
291
+ backgroundColor: Colors.surface,
292
+ borderRadius: 12,
293
+ padding: 14,
294
+ marginBottom: 8,
295
+ },
296
+ changesTitle: { fontSize: 13, fontWeight: '600', color: Colors.textSecondary, marginBottom: 10 },
297
+ changesTags: { flexDirection: 'row', flexWrap: 'wrap', gap: 6 },
298
+ changeTag: {
299
+ backgroundColor: 'rgba(0,122,255,0.08)',
300
+ paddingHorizontal: 10,
301
+ paddingVertical: 4,
302
+ borderRadius: 6,
303
+ },
304
+ changeTagText: { fontSize: 12, color: Colors.primary, fontWeight: '600' },
305
+ });
@@ -0,0 +1,78 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { View, Text, Pressable, StyleSheet, Animated } from 'react-native';
3
+ import { Colors } from '../../utils/constants';
4
+
5
+ interface Props {
6
+ title: string;
7
+ initiallyExpanded?: boolean;
8
+ children: React.ReactNode;
9
+ }
10
+
11
+ export const CollapsibleSection: React.FC<Props> = ({
12
+ title,
13
+ initiallyExpanded = false,
14
+ children,
15
+ }) => {
16
+ const [expanded, setExpanded] = useState(initiallyExpanded);
17
+ const [rotationAnim] = useState(() => new Animated.Value(initiallyExpanded ? 1 : 0));
18
+
19
+ useEffect(() => {
20
+ Animated.timing(rotationAnim, {
21
+ toValue: expanded ? 1 : 0,
22
+ duration: 200,
23
+ useNativeDriver: true,
24
+ }).start();
25
+ }, [expanded, rotationAnim]);
26
+
27
+ const chevronRotation = rotationAnim.interpolate({
28
+ inputRange: [0, 1],
29
+ outputRange: ['0deg', '90deg'],
30
+ });
31
+
32
+ return (
33
+ <View style={styles.section}>
34
+ <Pressable
35
+ style={styles.header}
36
+ onPress={() => setExpanded(!expanded)}
37
+ >
38
+ <Text style={styles.title}>{title}</Text>
39
+ <Animated.Text style={[styles.chevron, { transform: [{ rotate: chevronRotation }] }]}>
40
+
41
+ </Animated.Text>
42
+ </Pressable>
43
+ {expanded && <View style={styles.body}>{children}</View>}
44
+ </View>
45
+ );
46
+ };
47
+
48
+ const styles = StyleSheet.create({
49
+ section: {
50
+ backgroundColor: Colors.surface,
51
+ borderRadius: 12,
52
+ marginBottom: 8,
53
+ overflow: 'hidden',
54
+ },
55
+ header: {
56
+ flexDirection: 'row',
57
+ justifyContent: 'space-between',
58
+ alignItems: 'center',
59
+ paddingHorizontal: 16,
60
+ paddingVertical: 13,
61
+ },
62
+ title: {
63
+ fontSize: 15,
64
+ fontWeight: '600',
65
+ color: Colors.text,
66
+ },
67
+ chevron: {
68
+ fontSize: 20,
69
+ fontWeight: '500',
70
+ color: Colors.textLight,
71
+ width: 20,
72
+ textAlign: 'center',
73
+ },
74
+ body: {
75
+ paddingHorizontal: 16,
76
+ paddingBottom: 14,
77
+ },
78
+ });
@@ -0,0 +1,68 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { TouchableOpacity, Text, StyleSheet } from 'react-native';
3
+ import { Colors } from '../../utils/constants';
4
+ import { copyToComputer } from '../../utils/copyToComputer';
5
+
6
+ interface CopyButtonProps {
7
+ /** Content to copy */
8
+ text: string;
9
+ /** Label for console.log identification */
10
+ label?: string;
11
+ /** Compact mode for inline use */
12
+ compact?: boolean;
13
+ }
14
+
15
+ export const CopyButton: React.FC<CopyButtonProps> = ({ text, label, compact }) => {
16
+ const [feedback, setFeedback] = useState<'copied' | 'logged' | null>(null);
17
+
18
+ const handleCopy = useCallback(() => {
19
+ try {
20
+ const result = copyToComputer(text, { label });
21
+ setFeedback(result.method === 'clipboard' ? 'copied' : 'logged');
22
+ } catch {
23
+ setFeedback('logged');
24
+ }
25
+ setTimeout(() => setFeedback(null), 2000);
26
+ }, [text, label]);
27
+
28
+ if (!text) return null;
29
+
30
+ const feedbackLabel =
31
+ feedback === 'copied' ? 'Copied' :
32
+ feedback === 'logged' ? 'Logged' :
33
+ null;
34
+
35
+ return (
36
+ <TouchableOpacity
37
+ style={[s.copyBtn, compact && s.copyBtnCompact]}
38
+ onPress={handleCopy}
39
+ activeOpacity={0.7}
40
+ >
41
+ <Text style={[s.copyBtnText, compact && s.copyBtnTextCompact]}>
42
+ {feedbackLabel ?? 'Copy'}
43
+ </Text>
44
+ </TouchableOpacity>
45
+ );
46
+ };
47
+
48
+ const s = StyleSheet.create({
49
+ copyBtn: {
50
+ backgroundColor: Colors.background,
51
+ paddingHorizontal: 12,
52
+ paddingVertical: 5,
53
+ borderRadius: 8,
54
+ alignSelf: 'flex-end',
55
+ },
56
+ copyBtnCompact: {
57
+ paddingHorizontal: 8,
58
+ paddingVertical: 3,
59
+ },
60
+ copyBtnText: {
61
+ fontSize: 12,
62
+ color: Colors.textSecondary,
63
+ fontWeight: '500',
64
+ },
65
+ copyBtnTextCompact: {
66
+ fontSize: 11,
67
+ },
68
+ });