react-native-debug-toolkit 3.3.4 → 3.5.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 (258) hide show
  1. package/README.md +48 -46
  2. package/README.zh-CN.md +48 -46
  3. package/android/src/main/java/com/reactnativedebugtoolkit/DebugToolkitDevConnectModule.java +0 -187
  4. package/bin/debug-toolkit.js +0 -16
  5. package/ios/DebugToolkitDevConnect.h +0 -12
  6. package/ios/DebugToolkitDevConnect.mm +0 -321
  7. package/lib/commonjs/constants/logLevels.js +19 -0
  8. package/lib/commonjs/constants/logLevels.js.map +1 -0
  9. package/lib/commonjs/core/initialize.js +36 -18
  10. package/lib/commonjs/core/initialize.js.map +1 -1
  11. package/lib/commonjs/features/console/ConsoleLogTab.js +4 -15
  12. package/lib/commonjs/features/console/ConsoleLogTab.js.map +1 -1
  13. package/lib/commonjs/features/console/index.js +15 -8
  14. package/lib/commonjs/features/console/index.js.map +1 -1
  15. package/lib/commonjs/features/devConnect/DevConnectTab.js +18 -470
  16. package/lib/commonjs/features/devConnect/DevConnectTab.js.map +1 -1
  17. package/lib/commonjs/features/devConnect/devConnectPreferences.js +0 -12
  18. package/lib/commonjs/features/devConnect/devConnectPreferences.js.map +1 -1
  19. package/lib/commonjs/features/devConnect/devConnectUtils.js +2 -57
  20. package/lib/commonjs/features/devConnect/devConnectUtils.js.map +1 -1
  21. package/lib/commonjs/features/devConnect/index.js +1 -23
  22. package/lib/commonjs/features/devConnect/index.js.map +1 -1
  23. package/lib/commonjs/features/devConnect/nativeDevConnect.js +1 -103
  24. package/lib/commonjs/features/devConnect/nativeDevConnect.js.map +1 -1
  25. package/lib/commonjs/features/network/NetworkLogTab.js +91 -93
  26. package/lib/commonjs/features/network/NetworkLogTab.js.map +1 -1
  27. package/lib/commonjs/features/network/index.js +7 -4
  28. package/lib/commonjs/features/network/index.js.map +1 -1
  29. package/lib/commonjs/features/sessionHistory/SessionHistoryTab.js +1044 -0
  30. package/lib/commonjs/features/sessionHistory/SessionHistoryTab.js.map +1 -0
  31. package/lib/commonjs/features/sessionHistory/index.js +103 -0
  32. package/lib/commonjs/features/sessionHistory/index.js.map +1 -0
  33. package/lib/commonjs/features/track/index.js +4 -3
  34. package/lib/commonjs/features/track/index.js.map +1 -1
  35. package/lib/commonjs/index.js +27 -0
  36. package/lib/commonjs/index.js.map +1 -1
  37. package/lib/commonjs/ui/DebugView.js +3 -0
  38. package/lib/commonjs/ui/DebugView.js.map +1 -1
  39. package/lib/commonjs/ui/panel/DebugPanel.js +67 -34
  40. package/lib/commonjs/ui/panel/DebugPanel.js.map +1 -1
  41. package/lib/commonjs/ui/panel/FeatureIntroCard.js +131 -0
  42. package/lib/commonjs/ui/panel/FeatureIntroCard.js.map +1 -0
  43. package/lib/commonjs/ui/panel/FeatureRail.js +163 -0
  44. package/lib/commonjs/ui/panel/FeatureRail.js.map +1 -0
  45. package/lib/commonjs/ui/panel/FloatPanelView.js +169 -32
  46. package/lib/commonjs/ui/panel/FloatPanelView.js.map +1 -1
  47. package/lib/commonjs/ui/panel/buildFeatureSummary.js +207 -0
  48. package/lib/commonjs/ui/panel/buildFeatureSummary.js.map +1 -0
  49. package/lib/commonjs/ui/panel/filterFeatureSnapshot.js +43 -0
  50. package/lib/commonjs/ui/panel/filterFeatureSnapshot.js.map +1 -0
  51. package/lib/commonjs/ui/panel/tabPersistence.js +17 -0
  52. package/lib/commonjs/ui/panel/tabPersistence.js.map +1 -0
  53. package/lib/commonjs/ui/theme/colors.js +6 -0
  54. package/lib/commonjs/ui/theme/colors.js.map +1 -1
  55. package/lib/commonjs/utils/DaemonClient.js +30 -8
  56. package/lib/commonjs/utils/DaemonClient.js.map +1 -1
  57. package/lib/commonjs/utils/SessionManager.js +132 -0
  58. package/lib/commonjs/utils/SessionManager.js.map +1 -0
  59. package/lib/commonjs/utils/StorageAdapter.js +104 -0
  60. package/lib/commonjs/utils/StorageAdapter.js.map +1 -0
  61. package/lib/commonjs/utils/createChannelFeature.js +22 -5
  62. package/lib/commonjs/utils/createChannelFeature.js.map +1 -1
  63. package/lib/commonjs/utils/createDebugTab.js +21 -0
  64. package/lib/commonjs/utils/createDebugTab.js.map +1 -0
  65. package/lib/commonjs/utils/createPersistedObservableStore.js +14 -8
  66. package/lib/commonjs/utils/createPersistedObservableStore.js.map +1 -1
  67. package/lib/commonjs/utils/debugPreferences.js +28 -6
  68. package/lib/commonjs/utils/debugPreferences.js.map +1 -1
  69. package/lib/commonjs/utils/deviceReport.js +5 -1
  70. package/lib/commonjs/utils/deviceReport.js.map +1 -1
  71. package/lib/commonjs/utils/logRuntime.js +32 -0
  72. package/lib/commonjs/utils/logRuntime.js.map +1 -0
  73. package/lib/module/constants/logLevels.js +15 -0
  74. package/lib/module/constants/logLevels.js.map +1 -0
  75. package/lib/module/core/initialize.js +36 -18
  76. package/lib/module/core/initialize.js.map +1 -1
  77. package/lib/module/features/console/ConsoleLogTab.js +1 -12
  78. package/lib/module/features/console/ConsoleLogTab.js.map +1 -1
  79. package/lib/module/features/console/index.js +15 -8
  80. package/lib/module/features/console/index.js.map +1 -1
  81. package/lib/module/features/devConnect/DevConnectTab.js +21 -473
  82. package/lib/module/features/devConnect/DevConnectTab.js.map +1 -1
  83. package/lib/module/features/devConnect/devConnectPreferences.js +1 -12
  84. package/lib/module/features/devConnect/devConnectPreferences.js.map +1 -1
  85. package/lib/module/features/devConnect/devConnectUtils.js +1 -53
  86. package/lib/module/features/devConnect/devConnectUtils.js.map +1 -1
  87. package/lib/module/features/devConnect/index.js +5 -9
  88. package/lib/module/features/devConnect/index.js.map +1 -1
  89. package/lib/module/features/devConnect/nativeDevConnect.js +1 -100
  90. package/lib/module/features/devConnect/nativeDevConnect.js.map +1 -1
  91. package/lib/module/features/network/NetworkLogTab.js +91 -93
  92. package/lib/module/features/network/NetworkLogTab.js.map +1 -1
  93. package/lib/module/features/network/index.js +7 -4
  94. package/lib/module/features/network/index.js.map +1 -1
  95. package/lib/module/features/sessionHistory/SessionHistoryTab.js +1039 -0
  96. package/lib/module/features/sessionHistory/SessionHistoryTab.js.map +1 -0
  97. package/lib/module/features/sessionHistory/index.js +99 -0
  98. package/lib/module/features/sessionHistory/index.js.map +1 -0
  99. package/lib/module/features/track/index.js +4 -3
  100. package/lib/module/features/track/index.js.map +1 -1
  101. package/lib/module/index.js +4 -0
  102. package/lib/module/index.js.map +1 -1
  103. package/lib/module/ui/DebugView.js +3 -0
  104. package/lib/module/ui/DebugView.js.map +1 -1
  105. package/lib/module/ui/panel/DebugPanel.js +67 -34
  106. package/lib/module/ui/panel/DebugPanel.js.map +1 -1
  107. package/lib/module/ui/panel/FeatureIntroCard.js +126 -0
  108. package/lib/module/ui/panel/FeatureIntroCard.js.map +1 -0
  109. package/lib/module/ui/panel/FeatureRail.js +158 -0
  110. package/lib/module/ui/panel/FeatureRail.js.map +1 -0
  111. package/lib/module/ui/panel/FloatPanelView.js +170 -33
  112. package/lib/module/ui/panel/FloatPanelView.js.map +1 -1
  113. package/lib/module/ui/panel/buildFeatureSummary.js +203 -0
  114. package/lib/module/ui/panel/buildFeatureSummary.js.map +1 -0
  115. package/lib/module/ui/panel/filterFeatureSnapshot.js +39 -0
  116. package/lib/module/ui/panel/filterFeatureSnapshot.js.map +1 -0
  117. package/lib/module/ui/panel/tabPersistence.js +13 -0
  118. package/lib/module/ui/panel/tabPersistence.js.map +1 -0
  119. package/lib/module/ui/theme/colors.js +6 -0
  120. package/lib/module/ui/theme/colors.js.map +1 -1
  121. package/lib/module/utils/DaemonClient.js +30 -8
  122. package/lib/module/utils/DaemonClient.js.map +1 -1
  123. package/lib/module/utils/SessionManager.js +127 -0
  124. package/lib/module/utils/SessionManager.js.map +1 -0
  125. package/lib/module/utils/StorageAdapter.js +96 -0
  126. package/lib/module/utils/StorageAdapter.js.map +1 -0
  127. package/lib/module/utils/createChannelFeature.js +22 -5
  128. package/lib/module/utils/createChannelFeature.js.map +1 -1
  129. package/lib/module/utils/createDebugTab.js +17 -0
  130. package/lib/module/utils/createDebugTab.js.map +1 -0
  131. package/lib/module/utils/createPersistedObservableStore.js +14 -8
  132. package/lib/module/utils/createPersistedObservableStore.js.map +1 -1
  133. package/lib/module/utils/debugPreferences.js +27 -6
  134. package/lib/module/utils/debugPreferences.js.map +1 -1
  135. package/lib/module/utils/deviceReport.js +4 -1
  136. package/lib/module/utils/deviceReport.js.map +1 -1
  137. package/lib/module/utils/logRuntime.js +26 -0
  138. package/lib/module/utils/logRuntime.js.map +1 -0
  139. package/lib/typescript/src/constants/logLevels.d.ts +3 -0
  140. package/lib/typescript/src/constants/logLevels.d.ts.map +1 -0
  141. package/lib/typescript/src/core/initialize.d.ts +6 -0
  142. package/lib/typescript/src/core/initialize.d.ts.map +1 -1
  143. package/lib/typescript/src/features/console/ConsoleLogTab.d.ts.map +1 -1
  144. package/lib/typescript/src/features/console/index.d.ts +2 -1
  145. package/lib/typescript/src/features/console/index.d.ts.map +1 -1
  146. package/lib/typescript/src/features/devConnect/DevConnectTab.d.ts.map +1 -1
  147. package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts +0 -2
  148. package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts.map +1 -1
  149. package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts +0 -20
  150. package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts.map +1 -1
  151. package/lib/typescript/src/features/devConnect/index.d.ts +2 -2
  152. package/lib/typescript/src/features/devConnect/index.d.ts.map +1 -1
  153. package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts +0 -25
  154. package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts.map +1 -1
  155. package/lib/typescript/src/features/devConnect/types.d.ts +1 -3
  156. package/lib/typescript/src/features/devConnect/types.d.ts.map +1 -1
  157. package/lib/typescript/src/features/network/NetworkLogTab.d.ts.map +1 -1
  158. package/lib/typescript/src/features/network/index.d.ts +2 -1
  159. package/lib/typescript/src/features/network/index.d.ts.map +1 -1
  160. package/lib/typescript/src/features/sessionHistory/SessionHistoryTab.d.ts +20 -0
  161. package/lib/typescript/src/features/sessionHistory/SessionHistoryTab.d.ts.map +1 -0
  162. package/lib/typescript/src/features/sessionHistory/index.d.ts +4 -0
  163. package/lib/typescript/src/features/sessionHistory/index.d.ts.map +1 -0
  164. package/lib/typescript/src/features/track/index.d.ts +2 -1
  165. package/lib/typescript/src/features/track/index.d.ts.map +1 -1
  166. package/lib/typescript/src/index.d.ts +6 -0
  167. package/lib/typescript/src/index.d.ts.map +1 -1
  168. package/lib/typescript/src/types/feature.d.ts +1 -1
  169. package/lib/typescript/src/types/feature.d.ts.map +1 -1
  170. package/lib/typescript/src/types/index.d.ts +2 -0
  171. package/lib/typescript/src/types/index.d.ts.map +1 -1
  172. package/lib/typescript/src/ui/DebugView.d.ts +4 -2
  173. package/lib/typescript/src/ui/DebugView.d.ts.map +1 -1
  174. package/lib/typescript/src/ui/panel/DebugPanel.d.ts +3 -1
  175. package/lib/typescript/src/ui/panel/DebugPanel.d.ts.map +1 -1
  176. package/lib/typescript/src/ui/panel/FeatureIntroCard.d.ts +11 -0
  177. package/lib/typescript/src/ui/panel/FeatureIntroCard.d.ts.map +1 -0
  178. package/lib/typescript/src/ui/panel/FeatureRail.d.ts +16 -0
  179. package/lib/typescript/src/ui/panel/FeatureRail.d.ts.map +1 -0
  180. package/lib/typescript/src/ui/panel/FloatPanelView.d.ts.map +1 -1
  181. package/lib/typescript/src/ui/panel/buildFeatureSummary.d.ts +13 -0
  182. package/lib/typescript/src/ui/panel/buildFeatureSummary.d.ts.map +1 -0
  183. package/lib/typescript/src/ui/panel/filterFeatureSnapshot.d.ts +3 -0
  184. package/lib/typescript/src/ui/panel/filterFeatureSnapshot.d.ts.map +1 -0
  185. package/lib/typescript/src/ui/panel/tabPersistence.d.ts +3 -0
  186. package/lib/typescript/src/ui/panel/tabPersistence.d.ts.map +1 -0
  187. package/lib/typescript/src/ui/theme/colors.d.ts +5 -0
  188. package/lib/typescript/src/ui/theme/colors.d.ts.map +1 -1
  189. package/lib/typescript/src/utils/DaemonClient.d.ts +7 -1
  190. package/lib/typescript/src/utils/DaemonClient.d.ts.map +1 -1
  191. package/lib/typescript/src/utils/SessionManager.d.ts +30 -0
  192. package/lib/typescript/src/utils/SessionManager.d.ts.map +1 -0
  193. package/lib/typescript/src/utils/StorageAdapter.d.ts +38 -0
  194. package/lib/typescript/src/utils/StorageAdapter.d.ts.map +1 -0
  195. package/lib/typescript/src/utils/createChannelFeature.d.ts +2 -0
  196. package/lib/typescript/src/utils/createChannelFeature.d.ts.map +1 -1
  197. package/lib/typescript/src/utils/createDebugTab.d.ts +18 -0
  198. package/lib/typescript/src/utils/createDebugTab.d.ts.map +1 -0
  199. package/lib/typescript/src/utils/createPersistedObservableStore.d.ts +4 -1
  200. package/lib/typescript/src/utils/createPersistedObservableStore.d.ts.map +1 -1
  201. package/lib/typescript/src/utils/debugPreferences.d.ts +1 -4
  202. package/lib/typescript/src/utils/debugPreferences.d.ts.map +1 -1
  203. package/lib/typescript/src/utils/deviceReport.d.ts +1 -0
  204. package/lib/typescript/src/utils/deviceReport.d.ts.map +1 -1
  205. package/lib/typescript/src/utils/logRuntime.d.ts +13 -0
  206. package/lib/typescript/src/utils/logRuntime.d.ts.map +1 -0
  207. package/package.json +10 -6
  208. package/src/constants/logLevels.ts +13 -0
  209. package/src/core/initialize.ts +61 -21
  210. package/src/features/console/ConsoleLogTab.tsx +1 -14
  211. package/src/features/console/index.ts +18 -8
  212. package/src/features/devConnect/DevConnectTab.tsx +17 -381
  213. package/src/features/devConnect/devConnectPreferences.ts +0 -15
  214. package/src/features/devConnect/devConnectUtils.ts +1 -81
  215. package/src/features/devConnect/index.ts +2 -9
  216. package/src/features/devConnect/nativeDevConnect.ts +1 -136
  217. package/src/features/devConnect/types.ts +1 -3
  218. package/src/features/network/NetworkLogTab.tsx +61 -71
  219. package/src/features/network/index.ts +12 -3
  220. package/src/features/sessionHistory/SessionHistoryTab.tsx +691 -0
  221. package/src/features/sessionHistory/index.ts +102 -0
  222. package/src/features/track/index.ts +10 -3
  223. package/src/index.ts +13 -0
  224. package/src/types/feature.ts +2 -1
  225. package/src/types/index.ts +10 -0
  226. package/src/ui/DebugView.tsx +6 -1
  227. package/src/ui/panel/DebugPanel.tsx +60 -30
  228. package/src/ui/panel/FeatureIntroCard.tsx +127 -0
  229. package/src/ui/panel/FeatureRail.tsx +165 -0
  230. package/src/ui/panel/FloatPanelView.tsx +176 -25
  231. package/src/ui/panel/buildFeatureSummary.ts +288 -0
  232. package/src/ui/panel/filterFeatureSnapshot.ts +51 -0
  233. package/src/ui/panel/tabPersistence.ts +22 -0
  234. package/src/ui/theme/colors.ts +7 -0
  235. package/src/utils/DaemonClient.ts +33 -5
  236. package/src/utils/SessionManager.ts +174 -0
  237. package/src/utils/StorageAdapter.ts +135 -0
  238. package/src/utils/createChannelFeature.ts +28 -6
  239. package/src/utils/createDebugTab.ts +32 -0
  240. package/src/utils/createPersistedObservableStore.ts +18 -10
  241. package/src/utils/debugPreferences.ts +38 -8
  242. package/src/utils/deviceReport.ts +5 -1
  243. package/src/utils/logRuntime.ts +39 -0
  244. package/app.plugin.js +0 -51
  245. package/dev-client.js +0 -3
  246. package/lib/commonjs/ui/panel/FeatureTabBar.js +0 -182
  247. package/lib/commonjs/ui/panel/FeatureTabBar.js.map +0 -1
  248. package/lib/module/ui/panel/FeatureTabBar.js +0 -177
  249. package/lib/module/ui/panel/FeatureTabBar.js.map +0 -1
  250. package/lib/typescript/src/ui/panel/FeatureTabBar.d.ts +0 -13
  251. package/lib/typescript/src/ui/panel/FeatureTabBar.d.ts.map +0 -1
  252. package/scripts/bundle/android.js +0 -101
  253. package/scripts/bundle/cli.js +0 -57
  254. package/scripts/bundle/doctor.js +0 -38
  255. package/scripts/bundle/ios.js +0 -179
  256. package/scripts/bundle/setup.js +0 -39
  257. package/scripts/debug-bundle.gradle +0 -147
  258. package/src/ui/panel/FeatureTabBar.tsx +0 -204
@@ -2,15 +2,21 @@ import React, { Component, useCallback, useEffect, useRef, useState } from 'reac
2
2
  import {
3
3
  View,
4
4
  Text,
5
+ TextInput,
5
6
  StyleSheet,
6
7
  Animated,
7
8
  } from 'react-native';
8
9
  import type { AnyDebugFeature } from '../../types';
10
+ import { Colors } from '../theme/colors';
9
11
  import { getPreference, setPreference, KEYS } from '../../utils/debugPreferences';
10
12
  import { FloatIcon } from '../floating/FloatIcon';
11
13
  import { DebugPanel } from './DebugPanel';
12
- import { FeatureTabBar } from './FeatureTabBar';
13
- import type { TabItem } from './FeatureTabBar';
14
+ import { FeatureRail } from './FeatureRail';
15
+ import type { RailItem } from './FeatureRail';
16
+ import { FeatureIntroCard } from './FeatureIntroCard';
17
+ import { buildFeatureSummary } from './buildFeatureSummary';
18
+ import { filterFeatureSnapshot } from './filterFeatureSnapshot';
19
+ import { resolveStoredTabIndex } from './tabPersistence';
14
20
  import { useTabAnimation } from './useTabAnimation';
15
21
 
16
22
  // ─── Error Boundary ────────────────────────────────────
@@ -39,6 +45,57 @@ class DebugErrorBoundary extends Component<
39
45
  }
40
46
  }
41
47
 
48
+ // ─── Snapshot helpers ──────────────────────────────────
49
+
50
+ function snapshotCount(feature: AnyDebugFeature): number | undefined {
51
+ try {
52
+ const snap = feature.getSnapshot();
53
+ if (Array.isArray(snap)) return snap.length;
54
+ if (snap && typeof snap === 'object') {
55
+ const obj = snap as Record<string, unknown>;
56
+ if (Array.isArray(obj.items)) return obj.items.length;
57
+ if (Array.isArray(obj.logs)) return obj.logs.length;
58
+ if (Array.isArray(obj.entries)) return obj.entries.length;
59
+ if (Array.isArray(obj.environments)) return obj.environments.length;
60
+ }
61
+ } catch { /* ignore */ }
62
+ return undefined;
63
+ }
64
+
65
+ interface PanelConnectionStatus {
66
+ label: string;
67
+ color: string;
68
+ }
69
+
70
+ interface DevConnectSnapshot {
71
+ isSimulator?: boolean;
72
+ computerHost?: string;
73
+ daemonPort?: string;
74
+ streaming?: boolean;
75
+ }
76
+
77
+ function buildPanelConnectionStatus(features: AnyDebugFeature[]): PanelConnectionStatus {
78
+ const devConnect = features.find((f) => f.name === 'devConnect');
79
+ if (!devConnect) {
80
+ return { label: 'offline desktop sync unavailable', color: Colors.textLight };
81
+ }
82
+
83
+ try {
84
+ const snap = (devConnect.getSnapshot() ?? {}) as DevConnectSnapshot;
85
+ const host = snap.isSimulator ? 'localhost' : snap.computerHost?.trim();
86
+ const port = snap.daemonPort?.trim();
87
+ const target = host && port ? `${host}:${port}` : host || (port ? `port ${port}` : 'not configured');
88
+ return {
89
+ label: `${snap.streaming ? 'live' : 'offline'} ${target}`,
90
+ color: snap.streaming ? Colors.success : Colors.textLight,
91
+ };
92
+ } catch {
93
+ return { label: 'offline desktop sync unavailable', color: Colors.textLight };
94
+ }
95
+ }
96
+
97
+ // ─── Main Component ────────────────────────────────────
98
+
42
99
  interface FloatPanelViewProps {
43
100
  features: AnyDebugFeature[];
44
101
  panelOpen: boolean;
@@ -49,29 +106,40 @@ interface FloatPanelViewProps {
49
106
 
50
107
  export function FloatPanelView({ features, panelOpen, onOpenPanel, onClosePanel, onClearAll }: FloatPanelViewProps) {
51
108
  const [activeTab, setActiveTab] = useState(0);
109
+ const [searchQuery, setSearchQuery] = useState('');
110
+ const [filterBad, setFilterBad] = useState(false);
52
111
  const tabLoaded = useRef(false);
53
112
 
54
113
  // Restore last tab on mount
55
114
  useEffect(() => {
115
+ if (tabLoaded.current || features.length === 0) return;
56
116
  let mounted = true;
57
117
  getPreference(KEYS.lastTab).then((val) => {
58
- if (!mounted || !val) return;
59
- const idx = parseInt(val, 10);
60
- if (!isNaN(idx) && idx >= 0) {
61
- setActiveTab(idx);
62
- tabLoaded.current = true;
118
+ if (!mounted || tabLoaded.current) return;
119
+ const idx = resolveStoredTabIndex(features, val);
120
+ setActiveTab(idx);
121
+ const featureName = features[idx]?.name;
122
+ if (featureName && featureName !== val) {
123
+ setPreference(KEYS.lastTab, featureName);
63
124
  }
125
+ tabLoaded.current = true;
64
126
  });
65
127
  return () => { mounted = false; };
66
- }, []);
128
+ }, [features]);
67
129
 
68
130
  const { contentOpacity, contentTranslateX, panHandlers, switchTab } = useTabAnimation({
69
131
  activeTab,
70
132
  tabCount: features.length,
71
133
  onTabChange: useCallback((index: number) => {
134
+ tabLoaded.current = true;
135
+ setSearchQuery('');
136
+ setFilterBad(false);
72
137
  setActiveTab(index);
73
- setPreference(KEYS.lastTab, String(index));
74
- }, []),
138
+ const featureName = features[index]?.name;
139
+ if (featureName) {
140
+ setPreference(KEYS.lastTab, featureName);
141
+ }
142
+ }, [features]),
75
143
  });
76
144
 
77
145
  // Feature subscription → re-render on data changes
@@ -96,14 +164,41 @@ export function FloatPanelView({ features, panelOpen, onOpenPanel, onClosePanel,
96
164
  // Clamp activeTab if features shrink
97
165
  useEffect(() => {
98
166
  if (features.length > 0 && activeTab >= features.length) {
167
+ tabLoaded.current = true;
99
168
  setActiveTab(0);
100
- setPreference(KEYS.lastTab, '0');
169
+ const featureName = features[0]?.name;
170
+ if (featureName) {
171
+ setPreference(KEYS.lastTab, featureName);
172
+ }
101
173
  }
102
- }, [features.length, activeTab]);
174
+ }, [features, activeTab]);
103
175
 
104
176
  // Badge (first feature that returns one)
105
177
  const envBadge = features.map((f) => f.badge?.()).find((b) => b != null) ?? null;
106
- const tabs: TabItem[] = features.map((f) => ({ label: f.label, id: f.name }));
178
+
179
+ // Rail items with counts
180
+ const railItems: RailItem[] = features.map((f) => {
181
+ const b = f.badge?.();
182
+ return { id: f.name, label: f.label, dotColor: b?.color ?? null, count: snapshotCount(f) };
183
+ });
184
+
185
+ const panelConnectionStatus = buildPanelConnectionStatus(features);
186
+
187
+ const handleClearAll = useCallback(() => {
188
+ setSearchQuery('');
189
+ setFilterBad(false);
190
+ onClearAll();
191
+ }, [onClearAll]);
192
+
193
+ // Active feature + summary
194
+ const activeFeature = features[activeTab];
195
+ const activeSnapshot = activeFeature?.getSnapshot();
196
+ const activeSummary = activeFeature ? buildFeatureSummary(activeFeature, activeSnapshot) : null;
197
+
198
+ // Filtered snapshot — reuse activeSnapshot to avoid double getSnapshot()
199
+ const filteredSnapshot = activeFeature
200
+ ? filterFeatureSnapshot(activeFeature, activeSnapshot, searchQuery, filterBad ? 'bad' : 'all')
201
+ : null;
107
202
 
108
203
  // Render active feature content
109
204
  const renderFeatureContent = () => {
@@ -112,7 +207,7 @@ export function FloatPanelView({ features, panelOpen, onOpenPanel, onClosePanel,
112
207
  }
113
208
  const feature = features[activeTab];
114
209
  if (!feature) return <Text style={styles.emptyText}>Feature not found</Text>;
115
- const snapshot = feature.getSnapshot();
210
+ const snapshot = filteredSnapshot;
116
211
  const TabComponent = feature.renderContent;
117
212
  if (TabComponent) return <TabComponent snapshot={snapshot} feature={feature} />;
118
213
  return (
@@ -122,6 +217,8 @@ export function FloatPanelView({ features, panelOpen, onOpenPanel, onClosePanel,
122
217
  );
123
218
  };
124
219
 
220
+ const showSearch = activeSummary ? (activeSummary.count != null && activeSummary.count > 0) : false;
221
+
125
222
  return (
126
223
  <DebugErrorBoundary onError={onClosePanel}>
127
224
  <View style={styles.container} pointerEvents="box-none">
@@ -129,18 +226,44 @@ export function FloatPanelView({ features, panelOpen, onOpenPanel, onClosePanel,
129
226
  {panelOpen && (
130
227
  <DebugPanel
131
228
  onClose={onClosePanel}
132
- onClearAll={onClearAll}
229
+ onClearAll={handleClearAll}
230
+ syncLabel={panelConnectionStatus.label}
231
+ syncColor={panelConnectionStatus.color}
133
232
  >
134
- <FeatureTabBar tabs={tabs} activeIndex={activeTab} onSelectTab={switchTab} />
135
- <Animated.View
136
- style={[
137
- styles.contentContainer,
138
- { opacity: contentOpacity, transform: [{ translateX: contentTranslateX }] },
139
- ]}
140
- {...panHandlers}
141
- >
142
- {renderFeatureContent()}
143
- </Animated.View>
233
+ <View style={styles.bodyRow}>
234
+ <FeatureRail items={railItems} activeIndex={activeTab} onSelectTab={switchTab} />
235
+ <View style={styles.contentColumn}>
236
+ {activeFeature && activeSummary && (
237
+ <FeatureIntroCard
238
+ title={activeFeature.label}
239
+ summary={activeSummary}
240
+ filterBad={filterBad}
241
+ onFilterBad={setFilterBad}
242
+ />
243
+ )}
244
+ {showSearch && (
245
+ <View style={styles.toolbar}>
246
+ <TextInput
247
+ style={styles.searchInput}
248
+ placeholder="Search..."
249
+ placeholderTextColor={Colors.textLight}
250
+ value={searchQuery}
251
+ onChangeText={setSearchQuery}
252
+ returnKeyType="search"
253
+ />
254
+ </View>
255
+ )}
256
+ <Animated.View
257
+ style={[
258
+ styles.contentContainer,
259
+ { opacity: contentOpacity, transform: [{ translateX: contentTranslateX }] },
260
+ ]}
261
+ {...panHandlers}
262
+ >
263
+ {renderFeatureContent()}
264
+ </Animated.View>
265
+ </View>
266
+ </View>
144
267
  </DebugPanel>
145
268
  )}
146
269
  </View>
@@ -157,6 +280,34 @@ const styles = StyleSheet.create({
157
280
  bottom: 0,
158
281
  zIndex: 999,
159
282
  },
283
+ bodyRow: {
284
+ flex: 1,
285
+ flexDirection: 'row',
286
+ },
287
+ contentColumn: {
288
+ flex: 1,
289
+ },
290
+ toolbar: {
291
+ flexDirection: 'row',
292
+ alignItems: 'center',
293
+ gap: 8,
294
+ paddingHorizontal: 10,
295
+ paddingVertical: 8,
296
+ borderBottomWidth: StyleSheet.hairlineWidth,
297
+ borderBottomColor: Colors.border,
298
+ backgroundColor: Colors.background,
299
+ },
300
+ searchInput: {
301
+ flex: 1,
302
+ height: 34,
303
+ borderWidth: 1,
304
+ borderColor: Colors.border,
305
+ borderRadius: 8,
306
+ backgroundColor: Colors.surface,
307
+ paddingHorizontal: 10,
308
+ fontSize: 13,
309
+ color: Colors.text,
310
+ },
160
311
  contentContainer: { flex: 1 },
161
312
  emptyText: {
162
313
  padding: 20,
@@ -0,0 +1,288 @@
1
+ import type { AnyDebugFeature } from '../../types';
2
+
3
+ export interface FeatureSummary {
4
+ capabilityText: string;
5
+ count?: number;
6
+ badCount?: number;
7
+ latestLabel?: string;
8
+ statusLabel?: string;
9
+ statusColor?: string;
10
+ filterMode?: 'all' | 'bad';
11
+ supportsBadFilter: boolean;
12
+ }
13
+
14
+ export function buildFeatureSummary(
15
+ feature: AnyDebugFeature,
16
+ snapshot: unknown,
17
+ ): FeatureSummary {
18
+ const name = feature.name;
19
+
20
+ if (name === 'network') return buildNetworkSummary(snapshot);
21
+ if (name === 'console') return buildConsoleSummary(snapshot);
22
+ if (name === 'navigation') return buildNavigationSummary(snapshot);
23
+ if (name === 'zustand') return buildZustandSummary(snapshot);
24
+ if (name === 'track') return buildTrackSummary(snapshot);
25
+ if (name === 'clipboard') return buildClipboardSummary(snapshot);
26
+ if (name === 'environment') return buildEnvironmentSummary(snapshot);
27
+ if (name === 'devConnect') return buildDevConnectSummary(snapshot);
28
+ if (name === 'sessionHistory') return buildSessionHistorySummary(snapshot);
29
+ if (name === 'thirdPartyLibs') return buildThirdPartyLibsSummary(snapshot);
30
+ if (name === 'native') return buildNativeSummary(snapshot);
31
+
32
+ return buildUnknownSummary(snapshot);
33
+ }
34
+
35
+ // ─── Per-feature builders ──────────────────────────────
36
+
37
+ interface NetworkItem {
38
+ request?: { method?: string; url?: string };
39
+ response?: { status?: number };
40
+ error?: string;
41
+ }
42
+
43
+ function buildNetworkSummary(snapshot: unknown): FeatureSummary {
44
+ const items = asArray(snapshot);
45
+ const count = items.length;
46
+ let badCount = 0;
47
+ let latestLabel: string | undefined;
48
+
49
+ if (count > 0) {
50
+ for (const item of items) {
51
+ const n = item as NetworkItem;
52
+ if (n.error || (n.response?.status ?? 0) >= 400) badCount++;
53
+ }
54
+ const last = items[items.length - 1] as NetworkItem;
55
+ const method = last.request?.method?.toUpperCase() ?? '';
56
+ const url = last.request?.url ?? '';
57
+ const status = last.response?.status;
58
+ const path = extractPath(url);
59
+ latestLabel = [method, path, status].filter(Boolean).join(' ');
60
+ }
61
+
62
+ return {
63
+ capabilityText: 'HTTP capture, status, duration, request and response body',
64
+ count,
65
+ badCount: badCount > 0 ? badCount : undefined,
66
+ latestLabel,
67
+ supportsBadFilter: count > 0,
68
+ };
69
+ }
70
+
71
+ interface ConsoleItem {
72
+ level?: string;
73
+ data?: unknown[];
74
+ }
75
+
76
+ function buildConsoleSummary(snapshot: unknown): FeatureSummary {
77
+ const items = asArray(snapshot);
78
+ const count = items.length;
79
+ let badCount = 0;
80
+ let latestLabel: string | undefined;
81
+
82
+ if (count > 0) {
83
+ for (const item of items) {
84
+ const lvl = (item as ConsoleItem).level ?? '';
85
+ if (lvl === 'warn' || lvl === 'error' || lvl === 'fatal') badCount++;
86
+ }
87
+ const last = items[items.length - 1] as ConsoleItem;
88
+ latestLabel = formatConsoleMessage(last.data);
89
+ }
90
+
91
+ return {
92
+ capabilityText: 'Console log capture with level filtering',
93
+ count,
94
+ badCount: badCount > 0 ? badCount : undefined,
95
+ latestLabel,
96
+ supportsBadFilter: count > 0,
97
+ };
98
+ }
99
+
100
+ interface NavigationItem {
101
+ from?: string;
102
+ to?: string;
103
+ }
104
+
105
+ function buildNavigationSummary(snapshot: unknown): FeatureSummary {
106
+ const items = asArray(snapshot);
107
+ const count = items.length;
108
+ let latestLabel: string | undefined;
109
+
110
+ if (count > 0) {
111
+ const last = items[items.length - 1] as NavigationItem;
112
+ const from = last.from ?? '?';
113
+ const to = last.to ?? '?';
114
+ latestLabel = `${from} → ${to}`;
115
+ }
116
+
117
+ return {
118
+ capabilityText: 'Screen navigation tracking with route history',
119
+ count,
120
+ latestLabel,
121
+ supportsBadFilter: false,
122
+ };
123
+ }
124
+
125
+ interface ZustandItem {
126
+ action?: string;
127
+ storeName?: string;
128
+ }
129
+
130
+ function buildZustandSummary(snapshot: unknown): FeatureSummary {
131
+ const items = asArray(snapshot);
132
+ const count = items.length;
133
+ let latestLabel: string | undefined;
134
+
135
+ if (count > 0) {
136
+ const last = items[items.length - 1] as ZustandItem;
137
+ const parts = [last.action, last.storeName].filter(Boolean);
138
+ latestLabel = parts.join(' @ ') || undefined;
139
+ }
140
+
141
+ return {
142
+ capabilityText: 'State change tracking for Zustand stores',
143
+ count,
144
+ latestLabel,
145
+ supportsBadFilter: false,
146
+ };
147
+ }
148
+
149
+ interface TrackItem {
150
+ eventName?: string;
151
+ }
152
+
153
+ function buildTrackSummary(snapshot: unknown): FeatureSummary {
154
+ const items = asArray(snapshot);
155
+ const count = items.length;
156
+ let latestLabel: string | undefined;
157
+
158
+ if (count > 0) {
159
+ const last = items[items.length - 1] as TrackItem;
160
+ latestLabel = last.eventName;
161
+ }
162
+
163
+ return {
164
+ capabilityText: 'Analytics event tracking and inspection',
165
+ count,
166
+ latestLabel,
167
+ supportsBadFilter: false,
168
+ };
169
+ }
170
+
171
+ function buildClipboardSummary(_snapshot: unknown): FeatureSummary {
172
+ return {
173
+ capabilityText: 'Clipboard event monitoring',
174
+ supportsBadFilter: false,
175
+ };
176
+ }
177
+
178
+ interface EnvironmentSnap {
179
+ environments?: Array<{ id: string; label: string }>;
180
+ currentEnvironmentId?: string | null;
181
+ }
182
+
183
+ function buildEnvironmentSummary(snapshot: unknown): FeatureSummary {
184
+ const env = (snapshot ?? {}) as EnvironmentSnap;
185
+ const envs = env.environments ?? [];
186
+ const current = envs.find((e) => e.id === env.currentEnvironmentId);
187
+
188
+ return {
189
+ capabilityText: 'Environment configuration and switching',
190
+ count: envs.length || undefined,
191
+ latestLabel: current?.label,
192
+ statusLabel: current?.label,
193
+ supportsBadFilter: false,
194
+ };
195
+ }
196
+
197
+ interface DevConnectSnap {
198
+ streaming?: boolean;
199
+ computerHost?: string;
200
+ daemonPort?: string;
201
+ }
202
+
203
+ function buildDevConnectSummary(snapshot: unknown): FeatureSummary {
204
+ const s = (snapshot ?? {}) as DevConnectSnap;
205
+ const host = [s.computerHost, s.daemonPort].filter(Boolean).join(':');
206
+
207
+ return {
208
+ capabilityText: 'Desktop sync with daemon connection',
209
+ statusLabel: host ? `${s.streaming ? 'live' : 'offline'} ${host}` : s.streaming ? 'live' : undefined,
210
+ statusColor: s.streaming ? '#34C759' : undefined,
211
+ supportsBadFilter: false,
212
+ };
213
+ }
214
+
215
+ interface SessionHistorySnap {
216
+ sessions?: unknown[];
217
+ currentSessionId?: string;
218
+ logCounts?: Record<string, unknown>;
219
+ }
220
+
221
+ function buildSessionHistorySummary(snapshot: unknown): FeatureSummary {
222
+ const s = (snapshot ?? {}) as SessionHistorySnap;
223
+ const count = s.sessions?.length;
224
+ return {
225
+ capabilityText: 'Session log recording and replay',
226
+ count: count || undefined,
227
+ latestLabel: s.currentSessionId,
228
+ supportsBadFilter: false,
229
+ };
230
+ }
231
+
232
+ function buildThirdPartyLibsSummary(snapshot: unknown): FeatureSummary {
233
+ const items = asArray(snapshot);
234
+ return {
235
+ capabilityText: 'Third-party library inspection and management',
236
+ count: items.length || undefined,
237
+ supportsBadFilter: false,
238
+ };
239
+ }
240
+
241
+ function buildNativeSummary(snapshot: unknown): FeatureSummary {
242
+ const items = asArray(snapshot);
243
+ let badCount = 0;
244
+ for (const item of items) {
245
+ const lvl = (item as { level?: string }).level ?? '';
246
+ if (lvl === 'warn' || lvl === 'error' || lvl === 'fatal') badCount++;
247
+ }
248
+ return {
249
+ capabilityText: 'Native log capture',
250
+ count: items.length || undefined,
251
+ badCount: badCount > 0 ? badCount : undefined,
252
+ supportsBadFilter: items.length > 0,
253
+ };
254
+ }
255
+
256
+ function buildUnknownSummary(snapshot: unknown): FeatureSummary {
257
+ const items = asArray(snapshot);
258
+ return {
259
+ capabilityText: `${items.length} item${items.length !== 1 ? 's' : ''} captured`,
260
+ count: items.length || undefined,
261
+ supportsBadFilter: false,
262
+ };
263
+ }
264
+
265
+ // ─── Helpers ───────────────────────────────────────────
266
+
267
+ function asArray(snap: unknown): unknown[] {
268
+ if (Array.isArray(snap)) return snap;
269
+ return [];
270
+ }
271
+
272
+ function extractPath(url: string): string {
273
+ try {
274
+ const u = new URL(url);
275
+ return u.pathname + u.search;
276
+ } catch {
277
+ return url;
278
+ }
279
+ }
280
+
281
+ function formatConsoleMessage(data: unknown[] | undefined): string | undefined {
282
+ if (!data || data.length === 0) return undefined;
283
+ const joined = data
284
+ .map((d) => (typeof d === 'string' ? d : JSON.stringify(d)))
285
+ .join(' ')
286
+ .slice(0, 60);
287
+ return joined || undefined;
288
+ }
@@ -0,0 +1,51 @@
1
+ import type { AnyDebugFeature } from '../../types';
2
+
3
+ export function filterFeatureSnapshot(
4
+ feature: AnyDebugFeature,
5
+ snapshot: unknown,
6
+ query: string,
7
+ filterMode: 'all' | 'bad',
8
+ ): unknown {
9
+ if (!Array.isArray(snapshot)) return snapshot;
10
+
11
+ const name = feature.name;
12
+ const supportsBad =
13
+ name === 'network' || name === 'console' || name === 'native';
14
+
15
+ return (snapshot as unknown[]).filter((item) => {
16
+ if (filterMode === 'bad' && supportsBad) {
17
+ if (!isBad(name, item as Record<string, unknown>)) return false;
18
+ }
19
+ if (query) {
20
+ const hay = extractSearchableText(item).toLowerCase();
21
+ if (!hay.includes(query.toLowerCase())) return false;
22
+ }
23
+ return true;
24
+ });
25
+ }
26
+
27
+ function isBad(
28
+ featureName: string,
29
+ item: Record<string, unknown>,
30
+ ): boolean {
31
+ if (featureName === 'network') {
32
+ const resp = item.response as Record<string, unknown> | undefined;
33
+ return !!(item.error || ((resp?.status as number) ?? 0) >= 400);
34
+ }
35
+ // console, native
36
+ const lvl = (item.level as string) ?? '';
37
+ return lvl === 'warn' || lvl === 'error' || lvl === 'fatal';
38
+ }
39
+
40
+ function extractSearchableText(item: unknown): string {
41
+ if (item == null) return '';
42
+ if (typeof item === 'string') return item;
43
+ if (typeof item === 'number' || typeof item === 'boolean') return String(item);
44
+ if (Array.isArray(item)) return item.map(extractSearchableText).join(' ');
45
+ const parts: string[] = [];
46
+ for (const val of Object.values(item as Record<string, unknown>)) {
47
+ const t = extractSearchableText(val);
48
+ if (t) parts.push(t);
49
+ }
50
+ return parts.join(' ');
51
+ }
@@ -0,0 +1,22 @@
1
+ import type { AnyDebugFeature } from '../../types';
2
+
3
+ export function resolveStoredTabIndex(
4
+ features: AnyDebugFeature[],
5
+ storedTab: string | null,
6
+ ): number {
7
+ if (!storedTab) return 0;
8
+
9
+ const nameIndex = features.findIndex((feature) => feature.name === storedTab);
10
+ if (nameIndex >= 0) return nameIndex;
11
+
12
+ const legacyIndex = Number(storedTab);
13
+ if (
14
+ Number.isInteger(legacyIndex) &&
15
+ legacyIndex >= 0 &&
16
+ legacyIndex < features.length
17
+ ) {
18
+ return legacyIndex;
19
+ }
20
+
21
+ return 0;
22
+ }
@@ -14,6 +14,13 @@ export const Colors = {
14
14
 
15
15
  purple: '#AF52DE',
16
16
 
17
+ // Panel-specific
18
+ railBackground: '#E8EEF6',
19
+ panelDivider: '#CED8E4',
20
+ signalRedBg: '#FFE9E7',
21
+ signalAmberBg: '#FFF1D6',
22
+ signalDefaultBg: '#E7EDF5',
23
+
17
24
  get: '#007AFF',
18
25
  post: '#34C759',
19
26
  put: '#FF9500',