react-native-debug-toolkit 3.3.8 → 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/README.md +40 -26
  2. package/README.zh-CN.md +52 -38
  3. package/android/src/main/java/com/reactnativedebugtoolkit/DebugToolkitNativeLogsModule.java +146 -0
  4. package/android/src/main/java/com/reactnativedebugtoolkit/ReactNativeDebugToolkitPackage.java +5 -3
  5. package/ios/DebugToolkitNativeLogs.mm +92 -0
  6. package/lib/commonjs/constants/logLevels.js +19 -0
  7. package/lib/commonjs/constants/logLevels.js.map +1 -0
  8. package/lib/commonjs/core/initialize.js +32 -19
  9. package/lib/commonjs/core/initialize.js.map +1 -1
  10. package/lib/commonjs/features/console/ConsoleLogTab.js +4 -15
  11. package/lib/commonjs/features/console/ConsoleLogTab.js.map +1 -1
  12. package/lib/commonjs/features/console/index.js +15 -8
  13. package/lib/commonjs/features/console/index.js.map +1 -1
  14. package/lib/commonjs/features/nativeLogs/NativeLogTab.js +156 -0
  15. package/lib/commonjs/features/nativeLogs/NativeLogTab.js.map +1 -0
  16. package/lib/commonjs/features/nativeLogs/index.js +97 -0
  17. package/lib/commonjs/features/nativeLogs/index.js.map +1 -0
  18. package/lib/commonjs/features/nativeLogs/nativeLogsBridge.js +71 -0
  19. package/lib/commonjs/features/nativeLogs/nativeLogsBridge.js.map +1 -0
  20. package/lib/commonjs/features/network/NetworkLogTab.js +90 -95
  21. package/lib/commonjs/features/network/NetworkLogTab.js.map +1 -1
  22. package/lib/commonjs/features/network/index.js +7 -4
  23. package/lib/commonjs/features/network/index.js.map +1 -1
  24. package/lib/commonjs/features/sessionHistory/SessionHistoryTab.js +1046 -0
  25. package/lib/commonjs/features/sessionHistory/SessionHistoryTab.js.map +1 -0
  26. package/lib/commonjs/features/sessionHistory/index.js +104 -0
  27. package/lib/commonjs/features/sessionHistory/index.js.map +1 -0
  28. package/lib/commonjs/features/track/index.js +4 -3
  29. package/lib/commonjs/features/track/index.js.map +1 -1
  30. package/lib/commonjs/index.js +27 -0
  31. package/lib/commonjs/index.js.map +1 -1
  32. package/lib/commonjs/ui/DebugView.js +2 -0
  33. package/lib/commonjs/ui/DebugView.js.map +1 -1
  34. package/lib/commonjs/ui/panel/DebugPanel.js +67 -34
  35. package/lib/commonjs/ui/panel/DebugPanel.js.map +1 -1
  36. package/lib/commonjs/ui/panel/FeatureIntroCard.js +151 -0
  37. package/lib/commonjs/ui/panel/FeatureIntroCard.js.map +1 -0
  38. package/lib/commonjs/ui/panel/FeatureRail.js +163 -0
  39. package/lib/commonjs/ui/panel/FeatureRail.js.map +1 -0
  40. package/lib/commonjs/ui/panel/FloatPanelView.js +119 -22
  41. package/lib/commonjs/ui/panel/FloatPanelView.js.map +1 -1
  42. package/lib/commonjs/ui/panel/buildFeatureSummary.js +207 -0
  43. package/lib/commonjs/ui/panel/buildFeatureSummary.js.map +1 -0
  44. package/lib/commonjs/ui/panel/filterFeatureSnapshot.js +43 -0
  45. package/lib/commonjs/ui/panel/filterFeatureSnapshot.js.map +1 -0
  46. package/lib/commonjs/ui/theme/colors.js +6 -0
  47. package/lib/commonjs/ui/theme/colors.js.map +1 -1
  48. package/lib/commonjs/utils/DaemonClient.js +30 -8
  49. package/lib/commonjs/utils/DaemonClient.js.map +1 -1
  50. package/lib/commonjs/utils/SessionManager.js +132 -0
  51. package/lib/commonjs/utils/SessionManager.js.map +1 -0
  52. package/lib/commonjs/utils/StorageAdapter.js +104 -0
  53. package/lib/commonjs/utils/StorageAdapter.js.map +1 -0
  54. package/lib/commonjs/utils/createChannelFeature.js +22 -5
  55. package/lib/commonjs/utils/createChannelFeature.js.map +1 -1
  56. package/lib/commonjs/utils/createPersistedObservableStore.js +14 -8
  57. package/lib/commonjs/utils/createPersistedObservableStore.js.map +1 -1
  58. package/lib/commonjs/utils/debugPreferences.js +28 -5
  59. package/lib/commonjs/utils/debugPreferences.js.map +1 -1
  60. package/lib/commonjs/utils/deviceReport.js +5 -1
  61. package/lib/commonjs/utils/deviceReport.js.map +1 -1
  62. package/lib/commonjs/utils/logRuntime.js +32 -0
  63. package/lib/commonjs/utils/logRuntime.js.map +1 -0
  64. package/lib/module/constants/logLevels.js +15 -0
  65. package/lib/module/constants/logLevels.js.map +1 -0
  66. package/lib/module/core/initialize.js +32 -19
  67. package/lib/module/core/initialize.js.map +1 -1
  68. package/lib/module/features/console/ConsoleLogTab.js +1 -12
  69. package/lib/module/features/console/ConsoleLogTab.js.map +1 -1
  70. package/lib/module/features/console/index.js +15 -8
  71. package/lib/module/features/console/index.js.map +1 -1
  72. package/lib/module/features/nativeLogs/NativeLogTab.js +151 -0
  73. package/lib/module/features/nativeLogs/NativeLogTab.js.map +1 -0
  74. package/lib/module/features/nativeLogs/index.js +91 -0
  75. package/lib/module/features/nativeLogs/index.js.map +1 -0
  76. package/lib/module/features/nativeLogs/nativeLogsBridge.js +63 -0
  77. package/lib/module/features/nativeLogs/nativeLogsBridge.js.map +1 -0
  78. package/lib/module/features/network/NetworkLogTab.js +90 -95
  79. package/lib/module/features/network/NetworkLogTab.js.map +1 -1
  80. package/lib/module/features/network/index.js +7 -4
  81. package/lib/module/features/network/index.js.map +1 -1
  82. package/lib/module/features/sessionHistory/SessionHistoryTab.js +1041 -0
  83. package/lib/module/features/sessionHistory/SessionHistoryTab.js.map +1 -0
  84. package/lib/module/features/sessionHistory/index.js +100 -0
  85. package/lib/module/features/sessionHistory/index.js.map +1 -0
  86. package/lib/module/features/track/index.js +4 -3
  87. package/lib/module/features/track/index.js.map +1 -1
  88. package/lib/module/index.js +3 -0
  89. package/lib/module/index.js.map +1 -1
  90. package/lib/module/ui/DebugView.js +2 -0
  91. package/lib/module/ui/DebugView.js.map +1 -1
  92. package/lib/module/ui/panel/DebugPanel.js +67 -34
  93. package/lib/module/ui/panel/DebugPanel.js.map +1 -1
  94. package/lib/module/ui/panel/FeatureIntroCard.js +146 -0
  95. package/lib/module/ui/panel/FeatureIntroCard.js.map +1 -0
  96. package/lib/module/ui/panel/FeatureRail.js +158 -0
  97. package/lib/module/ui/panel/FeatureRail.js.map +1 -0
  98. package/lib/module/ui/panel/FloatPanelView.js +119 -22
  99. package/lib/module/ui/panel/FloatPanelView.js.map +1 -1
  100. package/lib/module/ui/panel/buildFeatureSummary.js +203 -0
  101. package/lib/module/ui/panel/buildFeatureSummary.js.map +1 -0
  102. package/lib/module/ui/panel/filterFeatureSnapshot.js +39 -0
  103. package/lib/module/ui/panel/filterFeatureSnapshot.js.map +1 -0
  104. package/lib/module/ui/theme/colors.js +6 -0
  105. package/lib/module/ui/theme/colors.js.map +1 -1
  106. package/lib/module/utils/DaemonClient.js +30 -8
  107. package/lib/module/utils/DaemonClient.js.map +1 -1
  108. package/lib/module/utils/SessionManager.js +127 -0
  109. package/lib/module/utils/SessionManager.js.map +1 -0
  110. package/lib/module/utils/StorageAdapter.js +96 -0
  111. package/lib/module/utils/StorageAdapter.js.map +1 -0
  112. package/lib/module/utils/createChannelFeature.js +22 -5
  113. package/lib/module/utils/createChannelFeature.js.map +1 -1
  114. package/lib/module/utils/createPersistedObservableStore.js +14 -8
  115. package/lib/module/utils/createPersistedObservableStore.js.map +1 -1
  116. package/lib/module/utils/debugPreferences.js +27 -5
  117. package/lib/module/utils/debugPreferences.js.map +1 -1
  118. package/lib/module/utils/deviceReport.js +4 -1
  119. package/lib/module/utils/deviceReport.js.map +1 -1
  120. package/lib/module/utils/logRuntime.js +26 -0
  121. package/lib/module/utils/logRuntime.js.map +1 -0
  122. package/lib/typescript/src/constants/logLevels.d.ts +3 -0
  123. package/lib/typescript/src/constants/logLevels.d.ts.map +1 -0
  124. package/lib/typescript/src/core/initialize.d.ts +6 -0
  125. package/lib/typescript/src/core/initialize.d.ts.map +1 -1
  126. package/lib/typescript/src/features/console/ConsoleLogTab.d.ts.map +1 -1
  127. package/lib/typescript/src/features/console/index.d.ts +2 -1
  128. package/lib/typescript/src/features/console/index.d.ts.map +1 -1
  129. package/lib/typescript/src/features/nativeLogs/NativeLogTab.d.ts +4 -0
  130. package/lib/typescript/src/features/nativeLogs/NativeLogTab.d.ts.map +1 -0
  131. package/lib/typescript/src/features/nativeLogs/index.d.ts +12 -0
  132. package/lib/typescript/src/features/nativeLogs/index.d.ts.map +1 -0
  133. package/lib/typescript/src/features/nativeLogs/nativeLogsBridge.d.ts +11 -0
  134. package/lib/typescript/src/features/nativeLogs/nativeLogsBridge.d.ts.map +1 -0
  135. package/lib/typescript/src/features/network/NetworkLogTab.d.ts.map +1 -1
  136. package/lib/typescript/src/features/network/index.d.ts +2 -1
  137. package/lib/typescript/src/features/network/index.d.ts.map +1 -1
  138. package/lib/typescript/src/features/sessionHistory/SessionHistoryTab.d.ts +20 -0
  139. package/lib/typescript/src/features/sessionHistory/SessionHistoryTab.d.ts.map +1 -0
  140. package/lib/typescript/src/features/sessionHistory/index.d.ts +4 -0
  141. package/lib/typescript/src/features/sessionHistory/index.d.ts.map +1 -0
  142. package/lib/typescript/src/features/track/index.d.ts +2 -1
  143. package/lib/typescript/src/features/track/index.d.ts.map +1 -1
  144. package/lib/typescript/src/index.d.ts +7 -1
  145. package/lib/typescript/src/index.d.ts.map +1 -1
  146. package/lib/typescript/src/types/feature.d.ts +1 -1
  147. package/lib/typescript/src/types/feature.d.ts.map +1 -1
  148. package/lib/typescript/src/types/index.d.ts +3 -1
  149. package/lib/typescript/src/types/index.d.ts.map +1 -1
  150. package/lib/typescript/src/types/logs.d.ts +15 -0
  151. package/lib/typescript/src/types/logs.d.ts.map +1 -1
  152. package/lib/typescript/src/ui/DebugView.d.ts.map +1 -1
  153. package/lib/typescript/src/ui/panel/DebugPanel.d.ts +3 -1
  154. package/lib/typescript/src/ui/panel/DebugPanel.d.ts.map +1 -1
  155. package/lib/typescript/src/ui/panel/FeatureIntroCard.d.ts +14 -0
  156. package/lib/typescript/src/ui/panel/FeatureIntroCard.d.ts.map +1 -0
  157. package/lib/typescript/src/ui/panel/FeatureRail.d.ts +16 -0
  158. package/lib/typescript/src/ui/panel/FeatureRail.d.ts.map +1 -0
  159. package/lib/typescript/src/ui/panel/FloatPanelView.d.ts.map +1 -1
  160. package/lib/typescript/src/ui/panel/buildFeatureSummary.d.ts +13 -0
  161. package/lib/typescript/src/ui/panel/buildFeatureSummary.d.ts.map +1 -0
  162. package/lib/typescript/src/ui/panel/filterFeatureSnapshot.d.ts +3 -0
  163. package/lib/typescript/src/ui/panel/filterFeatureSnapshot.d.ts.map +1 -0
  164. package/lib/typescript/src/ui/theme/colors.d.ts +5 -0
  165. package/lib/typescript/src/ui/theme/colors.d.ts.map +1 -1
  166. package/lib/typescript/src/utils/DaemonClient.d.ts +7 -1
  167. package/lib/typescript/src/utils/DaemonClient.d.ts.map +1 -1
  168. package/lib/typescript/src/utils/SessionManager.d.ts +30 -0
  169. package/lib/typescript/src/utils/SessionManager.d.ts.map +1 -0
  170. package/lib/typescript/src/utils/StorageAdapter.d.ts +38 -0
  171. package/lib/typescript/src/utils/StorageAdapter.d.ts.map +1 -0
  172. package/lib/typescript/src/utils/createChannelFeature.d.ts +2 -0
  173. package/lib/typescript/src/utils/createChannelFeature.d.ts.map +1 -1
  174. package/lib/typescript/src/utils/createPersistedObservableStore.d.ts +4 -1
  175. package/lib/typescript/src/utils/createPersistedObservableStore.d.ts.map +1 -1
  176. package/lib/typescript/src/utils/debugPreferences.d.ts +1 -3
  177. package/lib/typescript/src/utils/debugPreferences.d.ts.map +1 -1
  178. package/lib/typescript/src/utils/deviceReport.d.ts +1 -0
  179. package/lib/typescript/src/utils/deviceReport.d.ts.map +1 -1
  180. package/lib/typescript/src/utils/logRuntime.d.ts +13 -0
  181. package/lib/typescript/src/utils/logRuntime.d.ts.map +1 -0
  182. package/node/daemon/src/console/console.html +18 -0
  183. package/node/mcp/src/logs.js +1 -1
  184. package/package.json +9 -1
  185. package/src/constants/logLevels.ts +13 -0
  186. package/src/core/initialize.ts +54 -25
  187. package/src/features/console/ConsoleLogTab.tsx +1 -14
  188. package/src/features/console/index.ts +18 -8
  189. package/src/features/nativeLogs/NativeLogTab.tsx +66 -0
  190. package/src/features/nativeLogs/index.ts +94 -0
  191. package/src/features/nativeLogs/nativeLogsBridge.ts +51 -0
  192. package/src/features/network/NetworkLogTab.tsx +60 -71
  193. package/src/features/network/index.ts +12 -3
  194. package/src/features/sessionHistory/SessionHistoryTab.tsx +693 -0
  195. package/src/features/sessionHistory/index.ts +102 -0
  196. package/src/features/track/index.ts +10 -3
  197. package/src/index.ts +16 -0
  198. package/src/types/feature.ts +3 -1
  199. package/src/types/index.ts +13 -0
  200. package/src/types/logs.ts +17 -0
  201. package/src/ui/DebugView.tsx +2 -0
  202. package/src/ui/panel/DebugPanel.tsx +60 -30
  203. package/src/ui/panel/FeatureIntroCard.tsx +147 -0
  204. package/src/ui/panel/FeatureRail.tsx +165 -0
  205. package/src/ui/panel/FloatPanelView.tsx +123 -15
  206. package/src/ui/panel/buildFeatureSummary.ts +288 -0
  207. package/src/ui/panel/filterFeatureSnapshot.ts +51 -0
  208. package/src/ui/theme/colors.ts +7 -0
  209. package/src/utils/DaemonClient.ts +33 -5
  210. package/src/utils/SessionManager.ts +174 -0
  211. package/src/utils/StorageAdapter.ts +135 -0
  212. package/src/utils/createChannelFeature.ts +28 -6
  213. package/src/utils/createPersistedObservableStore.ts +18 -10
  214. package/src/utils/debugPreferences.ts +38 -7
  215. package/src/utils/deviceReport.ts +5 -1
  216. package/src/utils/logRuntime.ts +39 -0
  217. package/lib/commonjs/ui/panel/FeatureTabBar.js +0 -182
  218. package/lib/commonjs/ui/panel/FeatureTabBar.js.map +0 -1
  219. package/lib/module/ui/panel/FeatureTabBar.js +0 -177
  220. package/lib/module/ui/panel/FeatureTabBar.js.map +0 -1
  221. package/lib/typescript/src/ui/panel/FeatureTabBar.d.ts +0 -13
  222. package/lib/typescript/src/ui/panel/FeatureTabBar.d.ts.map +0 -1
  223. package/src/ui/panel/FeatureTabBar.tsx +0 -204
@@ -7,22 +7,9 @@ import { JsonView } from '../../ui/shared/JsonView';
7
7
  import { CopyButton } from '../../ui/shared/CopyButton';
8
8
  import { LogListScreen } from '../../ui/shared/LogListScreen';
9
9
  import { fmt } from '../../utils/copyToComputer';
10
+ import { LEVEL_COLORS, LEVEL_ICONS } from '../../constants/logLevels';
10
11
  import type { ConsoleLogEntry, DebugFeatureRenderProps } from '../../types';
11
12
 
12
- const LEVEL_COLORS: Record<string, string> = {
13
- log: '#8E8E93',
14
- info: '#007AFF',
15
- warn: '#FF9500',
16
- error: '#FF3B30',
17
- };
18
-
19
- const LEVEL_ICONS: Record<string, string> = {
20
- log: '●',
21
- info: 'ℹ',
22
- warn: '⚠',
23
- error: '✕',
24
- };
25
-
26
13
  export const ConsoleLogTab: React.FC<DebugFeatureRenderProps<ConsoleLogEntry[]>> = React.memo(({ snapshot }) => (
27
14
  <LogListScreen
28
15
  data={snapshot}
@@ -1,7 +1,7 @@
1
1
  import { ConsoleLogTab } from './ConsoleLogTab';
2
2
  import type { ConsoleLogEntry, DebugFeature } from '../../types';
3
3
  import { createPersistedObservableStore } from '../../utils/createPersistedObservableStore';
4
- import { KEYS } from '../../utils/debugPreferences';
4
+ import { getDefaultLogRuntime, type LogRuntimeContext } from '../../utils/logRuntime';
5
5
 
6
6
  const LEVELS: ConsoleLogEntry['level'][] = ['log', 'info', 'warn', 'error'];
7
7
 
@@ -13,7 +13,9 @@ const consoleCapture = (() => {
13
13
 
14
14
  function stop(): void {
15
15
  refCount = Math.max(0, refCount - 1);
16
- if (refCount > 0) return;
16
+ if (refCount > 0) {
17
+ return;
18
+ }
17
19
 
18
20
  LEVELS.forEach((level) => {
19
21
  const original = originalMethods[level];
@@ -78,10 +80,14 @@ export interface ConsoleFeatureConfig {
78
80
  maxLogs?: number;
79
81
  }
80
82
 
81
- export const createConsoleLogFeature = (config?: ConsoleFeatureConfig): DebugFeature<ConsoleLogEntry[]> => {
83
+ export const createConsoleLogFeature = (
84
+ config?: ConsoleFeatureConfig,
85
+ runtime: LogRuntimeContext = getDefaultLogRuntime(),
86
+ ): DebugFeature<ConsoleLogEntry[]> => {
82
87
  const maxLogs = config?.maxLogs ?? DEFAULT_MAX_LOGS;
83
88
  const logStore = createPersistedObservableStore<ConsoleLogEntry>({
84
- storageKey: KEYS.consoleLogs,
89
+ storage: runtime.logStorage,
90
+ storageKey: runtime.sessionManager.getLogStorageKey('console_logs'),
85
91
  maxPersist: 50,
86
92
  });
87
93
  let initialized = false;
@@ -92,7 +98,9 @@ export const createConsoleLogFeature = (config?: ConsoleFeatureConfig): DebugFea
92
98
  label: 'Console',
93
99
  renderContent: ConsoleLogTab,
94
100
  setup: () => {
95
- if (initialized) return;
101
+ if (initialized) {
102
+ return;
103
+ }
96
104
 
97
105
  stopCapture = consoleCapture.start((entry) => {
98
106
  logStore.push({ ...entry, id: logStore.nextId() }, maxLogs);
@@ -100,13 +108,15 @@ export const createConsoleLogFeature = (config?: ConsoleFeatureConfig): DebugFea
100
108
  initialized = true;
101
109
  },
102
110
  getSnapshot: () => logStore.getData(),
103
- clear: () => { logStore.clear(); },
111
+ clear: () => { logStore.clearPersisted(); },
104
112
  cleanup: () => {
105
- if (!initialized) return;
113
+ if (!initialized) {
114
+ return;
115
+ }
106
116
 
107
117
  stopCapture?.();
108
118
  stopCapture = null;
109
- logStore.clear();
119
+ logStore.dispose();
110
120
  initialized = false;
111
121
  },
112
122
  subscribe: (listener) => logStore.subscribe(listener),
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import { ScrollView, StyleSheet, Text, View } from 'react-native';
3
+ import { CopyButton } from '../../ui/shared/CopyButton';
4
+ import { LogListScreen } from '../../ui/shared/LogListScreen';
5
+ import { Colors } from '../../ui/theme/colors';
6
+ import { fmt } from '../../utils/copyToComputer';
7
+ import type { DebugFeatureRenderProps, NativeLogEntry } from '../../types';
8
+
9
+ const LEVEL_COLORS: Record<string, string> = {
10
+ trace: '#8E8E93', debug: '#8E8E93', info: '#007AFF',
11
+ warn: '#FF9500', error: '#FF3B30', fatal: '#AF52DE', unknown: '#8E8E93',
12
+ };
13
+
14
+ export const NativeLogTab: React.FC<DebugFeatureRenderProps<NativeLogEntry[]>> = React.memo(({ snapshot }) => (
15
+ <LogListScreen
16
+ data={snapshot}
17
+ emptyText="No native logs"
18
+ renderRow={(item) => (
19
+ <View style={s.row}>
20
+ <View style={[s.level, { backgroundColor: LEVEL_COLORS[item.level] ?? LEVEL_COLORS.unknown }]}>
21
+ <Text style={s.levelText}>{item.level.slice(0, 1).toUpperCase()}</Text>
22
+ </View>
23
+ <View style={s.content}>
24
+ <Text style={s.message} numberOfLines={2}>{item.message}</Text>
25
+ <Text style={s.meta}>
26
+ {[item.platform, item.source, item.tag].filter(Boolean).join(' / ')} · {new Date(item.timestamp).toLocaleTimeString()}
27
+ </Text>
28
+ </View>
29
+ </View>
30
+ )}
31
+ renderDetailHeader={(item) => (
32
+ <>
33
+ <View style={[s.badge, { backgroundColor: LEVEL_COLORS[item.level] ?? LEVEL_COLORS.unknown }]}>
34
+ <Text style={s.badgeText}>{item.level.toUpperCase()}</Text>
35
+ </View>
36
+ <Text style={s.detailTime}>{new Date(item.timestamp).toLocaleString()}</Text>
37
+ </>
38
+ )}
39
+ renderDetailBody={(item) => (
40
+ <ScrollView style={s.detailBody} contentContainerStyle={s.detailContent}>
41
+ <View style={s.copyRow}>
42
+ <CopyButton text={fmt(item)} label="Native Log" />
43
+ </View>
44
+ <Text style={s.messageBlock} selectable>{item.message}</Text>
45
+ <Text style={s.rawBlock} selectable>{fmt(item)}</Text>
46
+ </ScrollView>
47
+ )}
48
+ />
49
+ ));
50
+
51
+ const s = StyleSheet.create({
52
+ row: { flexDirection: 'row', padding: 14, alignItems: 'flex-start' },
53
+ level: { width: 24, height: 24, borderRadius: 12, alignItems: 'center', justifyContent: 'center', marginRight: 12 },
54
+ levelText: { color: '#FFF', fontSize: 11, fontWeight: '700' },
55
+ content: { flex: 1 },
56
+ message: { fontSize: 14, color: Colors.text, lineHeight: 20 },
57
+ meta: { fontSize: 12, color: Colors.textSecondary, marginTop: 4 },
58
+ badge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6 },
59
+ badgeText: { color: '#FFF', fontSize: 12, fontWeight: '700' },
60
+ detailTime: { flex: 1, fontSize: 13, color: Colors.textSecondary, textAlign: 'right' },
61
+ detailBody: { flex: 1 },
62
+ detailContent: { padding: 12, gap: 12 },
63
+ copyRow: { alignItems: 'flex-end' },
64
+ messageBlock: { fontFamily: 'Courier', fontSize: 13, color: Colors.text, lineHeight: 20 },
65
+ rawBlock: { fontFamily: 'Courier', fontSize: 12, color: Colors.textSecondary, lineHeight: 18 },
66
+ });
@@ -0,0 +1,94 @@
1
+ import { NativeLogTab } from './NativeLogTab';
2
+ import type { DebugFeature, DebugFeatureListener, NativeLogEntry } from '../../types';
3
+ import { createPersistedObservableStore } from '../../utils/createPersistedObservableStore';
4
+ import { getDefaultLogRuntime, type LogRuntimeContext } from '../../utils/logRuntime';
5
+ import { drainNativeLogs, startNativeLogCapture, stopNativeLogCapture } from './nativeLogsBridge';
6
+
7
+ const DEFAULT_MAX_LOGS = 200;
8
+ const DEFAULT_MAX_PERSIST = 50;
9
+ const DEFAULT_POLL_INTERVAL_MS = 500;
10
+ const DEFAULT_DRAIN_LIMIT = 100;
11
+
12
+ const LEVEL_RANK: Record<NativeLogEntry['level'], number> = {
13
+ trace: 0, debug: 1, info: 2, warn: 3, error: 4, fatal: 5, unknown: 0,
14
+ };
15
+
16
+ export interface NativeLogsFeatureConfig {
17
+ maxLogs?: number;
18
+ pollIntervalMs?: number;
19
+ minLevel?: NativeLogEntry['level'];
20
+ includeTags?: Array<string | RegExp>;
21
+ excludeTags?: Array<string | RegExp>;
22
+ }
23
+
24
+ function matchesPattern(value: string | undefined, patterns: Array<string | RegExp> | undefined): boolean {
25
+ if (!value || !patterns?.length) return false;
26
+ return patterns.some((p) => p instanceof RegExp ? p.test(value) : value.includes(p));
27
+ }
28
+
29
+ function shouldKeepEntry(entry: Omit<NativeLogEntry, 'id'>, config?: NativeLogsFeatureConfig): boolean {
30
+ if (config?.minLevel && LEVEL_RANK[entry.level] < LEVEL_RANK[config.minLevel]) return false;
31
+ if (config?.includeTags?.length && !matchesPattern(entry.tag, config.includeTags)) return false;
32
+ if (matchesPattern(entry.tag, config?.excludeTags)) return false;
33
+ return true;
34
+ }
35
+
36
+ export const createNativeLogsFeature = (
37
+ config?: NativeLogsFeatureConfig,
38
+ runtime: LogRuntimeContext = getDefaultLogRuntime(),
39
+ ): DebugFeature<NativeLogEntry[]> => {
40
+ const maxLogs = config?.maxLogs ?? DEFAULT_MAX_LOGS;
41
+ const pollIntervalMs = Math.max(100, Math.floor(config?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS));
42
+ const logStore = createPersistedObservableStore<NativeLogEntry>({
43
+ storage: runtime.logStorage,
44
+ storageKey: runtime.sessionManager.getLogStorageKey('native_logs'),
45
+ maxPersist: DEFAULT_MAX_PERSIST,
46
+ });
47
+
48
+ let initialized = false;
49
+ let timer: ReturnType<typeof setInterval> | null = null;
50
+ let draining = false;
51
+
52
+ async function drainOnce(): Promise<void> {
53
+ if (draining) return;
54
+ draining = true;
55
+ try {
56
+ const entries = await drainNativeLogs(DEFAULT_DRAIN_LIMIT);
57
+ entries.filter((e) => shouldKeepEntry(e, config)).forEach((entry) => {
58
+ logStore.push({ ...entry, id: logStore.nextId() }, maxLogs);
59
+ });
60
+ } finally { draining = false; }
61
+ }
62
+
63
+ return {
64
+ name: 'native',
65
+ label: 'Native',
66
+ renderContent: NativeLogTab,
67
+ setup: () => {
68
+ if (initialized) return;
69
+ initialized = true;
70
+ startNativeLogCapture({
71
+ minLevel: config?.minLevel,
72
+ includeTags: config?.includeTags?.filter((p) => typeof p === 'string'),
73
+ excludeTags: config?.excludeTags?.filter((p) => typeof p === 'string'),
74
+ }).catch(() => {});
75
+ timer = setInterval(() => { drainOnce().catch(() => {}); }, pollIntervalMs);
76
+ },
77
+ getSnapshot: () => logStore.getData(),
78
+ clear: () => { logStore.clearPersisted(); },
79
+ cleanup: () => {
80
+ if (!initialized) return;
81
+ if (timer) clearInterval(timer);
82
+ timer = null;
83
+ stopNativeLogCapture().catch(() => {});
84
+ logStore.dispose();
85
+ initialized = false;
86
+ draining = false;
87
+ },
88
+ subscribe: (listener: DebugFeatureListener) => logStore.subscribe(listener),
89
+ };
90
+ };
91
+
92
+ export function _resetNativeLogsForTesting(): void {
93
+ stopNativeLogCapture().catch(() => {});
94
+ }
@@ -0,0 +1,51 @@
1
+ import { NativeModules } from 'react-native';
2
+ import type { NativeLogEntry } from '../../types';
3
+
4
+ interface NativeLogsModule {
5
+ startCapture?: (options?: Record<string, unknown>) => Promise<{ ok: boolean }>;
6
+ stopCapture?: () => Promise<{ ok: boolean }>;
7
+ drainLogs?: (max?: number) => Promise<Array<Omit<NativeLogEntry, 'id'>>>;
8
+ getStatus?: () => Promise<{ available: boolean; capturing: boolean; error?: string }>;
9
+ }
10
+
11
+ function getNativeModule(): NativeLogsModule | null {
12
+ const mod = NativeModules.DebugToolkitNativeLogs as NativeLogsModule | undefined;
13
+ if (!mod || typeof mod.drainLogs !== 'function') return null;
14
+ return mod;
15
+ }
16
+
17
+ export function isNativeLogsAvailable(): boolean {
18
+ return getNativeModule() !== null;
19
+ }
20
+
21
+ export async function startNativeLogCapture(options?: Record<string, unknown>): Promise<boolean> {
22
+ const mod = getNativeModule();
23
+ if (!mod?.startCapture) return false;
24
+ try { const r = await mod.startCapture(options ?? {}); return r?.ok === true; }
25
+ catch { return false; }
26
+ }
27
+
28
+ export async function stopNativeLogCapture(): Promise<boolean> {
29
+ const mod = getNativeModule();
30
+ if (!mod?.stopCapture) return false;
31
+ try { const r = await mod.stopCapture(); return r?.ok === true; }
32
+ catch { return false; }
33
+ }
34
+
35
+ export async function drainNativeLogs(max = 100): Promise<Array<Omit<NativeLogEntry, 'id'>>> {
36
+ const mod = getNativeModule();
37
+ if (!mod?.drainLogs) return [];
38
+ try { const entries = await mod.drainLogs(Math.max(1, Math.floor(max))); return Array.isArray(entries) ? entries : []; }
39
+ catch { return []; }
40
+ }
41
+
42
+ export async function getNativeLogsStatus(): Promise<{ available: boolean; capturing: boolean; error?: string }> {
43
+ const mod = getNativeModule();
44
+ if (!mod?.getStatus) return { available: false, capturing: false };
45
+ try {
46
+ const s = await mod.getStatus();
47
+ return { available: s?.available === true, capturing: s?.capturing === true, error: typeof s?.error === 'string' ? s.error : undefined };
48
+ } catch (error) {
49
+ return { available: true, capturing: false, error: error instanceof Error ? error.message : String(error) };
50
+ }
51
+ }
@@ -1,9 +1,8 @@
1
- import React, { useState } from 'react';
1
+ import React from 'react';
2
2
  import {
3
3
  View,
4
4
  Text,
5
5
  StyleSheet,
6
- TextInput,
7
6
  ScrollView,
8
7
  } from 'react-native';
9
8
  import { Colors, getMethodColor } from '../../ui/theme/colors';
@@ -43,58 +42,37 @@ const buildCurl = (log: NetworkLogEntry): string => {
43
42
  export const NetworkLogTab: React.FC<DebugFeatureRenderProps<NetworkLogEntry[]>> = React.memo(({
44
43
  snapshot,
45
44
  }) => {
46
- const [search, setSearch] = useState('');
47
- const data = snapshot;
48
-
49
- const filtered = search
50
- ? data.filter(
51
- (l) =>
52
- l.request.url.toLowerCase().includes(search.toLowerCase()) ||
53
- l.request.method.toLowerCase().includes(search.toLowerCase()),
54
- )
55
- : data;
56
-
57
- const sorted = [...filtered].sort((a, b) => b.timestamp - a.timestamp);
45
+ const sorted = [...snapshot].sort((a, b) => b.timestamp - a.timestamp);
58
46
 
59
47
  return (
60
48
  <LogListScreen
61
49
  data={sorted}
62
50
  reversed={false}
63
51
  emptyText="No HTTP requests logged"
64
- renderListHeader={() => (
65
- <View style={s.searchContainer}>
66
- <View style={s.searchBar}>
67
- <Text style={s.searchIcon}>⌕</Text>
68
- <TextInput
69
- style={s.search}
70
- placeholder="Search URLs..."
71
- placeholderTextColor={Colors.textLight}
72
- value={search}
73
- onChangeText={setSearch}
74
- />
75
- </View>
76
- </View>
77
- )}
78
52
  renderRow={(item) => {
79
53
  const ok = !item.error && (!item.response || item.response.status < 400);
80
- const sc = ok ? Colors.success : Colors.error;
54
+ const statusColor = ok ? Colors.success : Colors.error;
55
+ const urlParts = formatUrlParts(item.request.url);
56
+
81
57
  return (
82
58
  <View style={s.cardRow}>
83
- <View style={[s.statusIndicator, { backgroundColor: sc }]} />
59
+ <View style={[s.statusIndicator, { backgroundColor: statusColor }]} />
84
60
  <View style={s.cardBody}>
85
- <View style={s.cardMeta}>
86
- <Text style={[s.methodText, { color: getMethodColor(item.request.method) }]}>
87
- {item.request.method}
61
+ <View style={s.primaryRow}>
62
+ <View style={[s.methodChip, { backgroundColor: getMethodColor(item.request.method) }]}>
63
+ <Text style={s.methodChipText}>{item.request.method}</Text>
64
+ </View>
65
+ <Text style={[s.pathText, !ok && { color: Colors.error }]} numberOfLines={2}>
66
+ {urlParts.path}
88
67
  </Text>
89
- <View style={[s.miniPill, { backgroundColor: sc }]}>
90
- <Text style={s.miniPillText}>{item.response?.status ?? 'ERR'}</Text>
68
+ </View>
69
+ <View style={s.metaRow}>
70
+ <View style={[s.statusChip, { backgroundColor: statusColor }]}>
71
+ <Text style={s.statusChipText}>{item.response?.status ?? 'ERR'}</Text>
91
72
  </View>
92
- {item.duration != null && <Text style={s.durationText}>{item.duration}ms</Text>}
73
+ {!!urlParts.host && <Text style={[s.metaText, s.hostText]} numberOfLines={1}>{urlParts.host}</Text>}
74
+ <Text style={s.time}>{new Date(item.timestamp).toLocaleTimeString()}</Text>
93
75
  </View>
94
- <Text style={[s.url, !ok && { color: Colors.error }]} numberOfLines={2}>
95
- {item.request.url}
96
- </Text>
97
- <Text style={s.time}>{new Date(item.timestamp).toLocaleTimeString()}</Text>
98
76
  </View>
99
77
  </View>
100
78
  );
@@ -185,43 +163,55 @@ export const NetworkLogTab: React.FC<DebugFeatureRenderProps<NetworkLogEntry[]>>
185
163
  );
186
164
  });
187
165
 
166
+ function formatUrlParts(url: string): { host: string; path: string } {
167
+ try {
168
+ const parsed = new URL(url);
169
+ return {
170
+ host: parsed.host,
171
+ path: parsed.pathname + parsed.search,
172
+ };
173
+ } catch {
174
+ return { host: '', path: url };
175
+ }
176
+ }
177
+
188
178
  const s = StyleSheet.create({
189
- // Search
190
- searchContainer: {
191
- paddingHorizontal: 12,
192
- paddingTop: 10,
193
- paddingBottom: 6,
194
- backgroundColor: Colors.background,
195
- },
196
- searchBar: {
179
+ cardRow: { flexDirection: 'row', padding: 12 },
180
+ statusIndicator: { width: 3, borderRadius: 2, marginRight: 10 },
181
+ cardBody: { flex: 1, gap: 7 },
182
+ primaryRow: {
197
183
  flexDirection: 'row',
198
184
  alignItems: 'center',
199
- backgroundColor: Colors.surface,
200
- borderRadius: 10,
201
- paddingHorizontal: 10,
202
- height: 38,
185
+ gap: 7,
186
+ minWidth: 0,
187
+ },
188
+ methodChip: {
189
+ paddingHorizontal: 8,
190
+ paddingVertical: 3,
191
+ borderRadius: 6,
203
192
  },
204
- searchIcon: { fontSize: 16, color: Colors.textLight, marginRight: 6 },
205
- search: {
193
+ methodChipText: {
194
+ color: '#FFF',
195
+ fontSize: 10,
196
+ fontWeight: '800',
197
+ },
198
+ pathText: {
206
199
  flex: 1,
207
- fontSize: 14,
200
+ fontSize: 13,
201
+ fontWeight: '700',
208
202
  color: Colors.text,
209
- padding: 0,
210
203
  },
211
-
212
- // Row
213
- cardRow: { flexDirection: 'row', padding: 14 },
214
- statusIndicator: { width: 3, borderRadius: 2, marginRight: 12 },
215
- cardBody: { flex: 1 },
216
- cardMeta: { flexDirection: 'row', alignItems: 'center', marginBottom: 6, gap: 6 },
217
- methodText: { fontSize: 13, fontWeight: '700' },
218
- miniPill: { paddingHorizontal: 7, paddingVertical: 2, borderRadius: 4 },
219
- miniPillText: { color: '#FFF', fontSize: 10, fontWeight: '700' },
220
- durationText: { fontSize: 12, color: Colors.textSecondary, fontWeight: '500' },
221
- url: { fontSize: 13, color: Colors.textSecondary, marginBottom: 4, lineHeight: 18 },
204
+ statusChip: { paddingHorizontal: 7, paddingVertical: 3, borderRadius: 6 },
205
+ statusChipText: { color: '#FFF', fontSize: 10, fontWeight: '800' },
206
+ metaRow: {
207
+ flexDirection: 'row',
208
+ alignItems: 'center',
209
+ gap: 7,
210
+ minWidth: 0,
211
+ },
212
+ metaText: { fontSize: 11, color: Colors.textSecondary, fontWeight: '600' },
213
+ hostText: { flex: 1 },
222
214
  time: { fontSize: 11, color: Colors.textLight },
223
-
224
- // Detail header
225
215
  detailHeaderCenter: {
226
216
  flexDirection: 'row',
227
217
  alignItems: 'center',
@@ -236,8 +226,7 @@ const s = StyleSheet.create({
236
226
  methodBadgeText: { color: '#FFF', fontSize: 12, fontWeight: '700' },
237
227
  statusPill: { paddingHorizontal: 9, paddingVertical: 3, borderRadius: 6 },
238
228
  statusPillText: { color: '#FFF', fontSize: 11, fontWeight: '700' },
239
-
240
- // Detail body
229
+ durationText: { fontSize: 12, color: Colors.textSecondary, fontWeight: '500' },
241
230
  detailBody: { flex: 1 },
242
231
  detailBodyContent: { padding: 12, paddingBottom: 40 },
243
232
  urlCard: {
@@ -3,7 +3,8 @@ import { NetworkLogTab } from './NetworkLogTab';
3
3
  import type { NetworkLogEntry } from '../../types';
4
4
  import { createChannelFeature } from '../../utils/createChannelFeature';
5
5
  import { createEventChannel } from '../../utils/createEventChannel';
6
- import { KEYS } from '../../utils/debugPreferences';
6
+ import { sanitizeDebugLogEntry } from '../../utils/deviceReport';
7
+ import { getDefaultLogRuntime, type LogRuntimeContext } from '../../utils/logRuntime';
7
8
  import {
8
9
  startXMLHttpRequest,
9
10
  resetInterceptors,
@@ -44,7 +45,10 @@ export interface NetworkFeatureConfig {
44
45
  blacklist?: Array<string | RegExp>;
45
46
  }
46
47
 
47
- export const createNetworkFeature = (config?: NetworkFeatureConfig) => {
48
+ export const createNetworkFeature = (
49
+ config?: NetworkFeatureConfig,
50
+ runtime: LogRuntimeContext = getDefaultLogRuntime(),
51
+ ) => {
48
52
  const userBlacklist = config?.blacklist ? [...config.blacklist] : [];
49
53
 
50
54
  return createChannelFeature<NetworkLogPayload, NetworkLogEntry>(
@@ -55,7 +59,12 @@ export const createNetworkFeature = (config?: NetworkFeatureConfig) => {
55
59
  label: 'Network',
56
60
  renderContent: NetworkLogTab,
57
61
  maxLogs: config?.maxLogs,
58
- persist: { storageKey: KEYS.networkLogs, maxPersist: 30 },
62
+ persist: {
63
+ storage: runtime.logStorage,
64
+ storageKey: runtime.sessionManager.getLogStorageKey('network_logs'),
65
+ maxPersist: 30,
66
+ serialize: (entry) => sanitizeDebugLogEntry(entry),
67
+ },
59
68
  beforePush: (payload) => {
60
69
  if (isUrlBlacklisted(payload.request.url, [...userBlacklist, ...daemonEndpointBlacklist])) {
61
70
  return null;