reactoradar 1.6.7 → 1.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +253 -230
- package/app.js +18 -19
- package/bin/setup.js +77 -15
- package/init.js +19 -7
- package/package.json +1 -1
- package/panels/console.js +7 -6
- package/panels/ga4.js +4 -1
- package/panels/native.js +4 -0
- package/panels/network.js +5 -1
- package/panels/performance.js +9 -2
- package/panels/react.js +2 -0
- package/panels/redux.js +4 -1
- package/panels/settings.js +15 -56
- package/panels/sources.js +7 -0
- package/panels/storage.js +7 -1
- package/sdk/RNDebugSDK.js +114 -40
package/AGENTS.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# ReactoRadar — Architecture & Coding Rules
|
|
2
2
|
|
|
3
|
-
> **Read this file before making ANY code changes.**
|
|
3
|
+
> **Read this file COMPLETELY before making ANY code changes.**
|
|
4
4
|
> This document defines the architecture, panel ownership, state contracts, and rules
|
|
5
5
|
> that must be followed to avoid breaking existing functionality.
|
|
6
|
+
>
|
|
7
|
+
> **NEVER release without passing the Pre-Release Checklist at the bottom.**
|
|
6
8
|
|
|
7
9
|
---
|
|
8
10
|
|
|
@@ -27,7 +29,7 @@
|
|
|
27
29
|
| `panels/performance.js` | Performance + Memory panels — `perfState`, `handlePerfEvent`, `handleMemoryEvent`, `initMemoryPanel` |
|
|
28
30
|
| `panels/native.js` | Native Logs panel — `_nativeState`, `initNativeLogsPanel`, `_appendNativeLog` |
|
|
29
31
|
| `panels/react.js` | React Tree panel — just a connect button |
|
|
30
|
-
| `panels/sources.js` | Sources panel — Metro source file browser |
|
|
32
|
+
| `panels/sources.js` | Sources panel — Metro source file browser (NOT initialized by default — no `panel-sources` in HTML) |
|
|
31
33
|
| `init.js` | **Boot script** (loaded LAST) — IPC wiring, button handlers, memory monitor, settings apply, panel init calls |
|
|
32
34
|
|
|
33
35
|
### Other
|
|
@@ -40,7 +42,7 @@
|
|
|
40
42
|
|
|
41
43
|
### Script Load Order (critical)
|
|
42
44
|
```
|
|
43
|
-
1. app.js
|
|
45
|
+
1. app.js — state, $, esc, ts, clearAll, freeMemory (no panel dependencies)
|
|
44
46
|
2. panels/settings.js — TAB_CONFIG, isTabEnabled, localStorage helpers (used by all panels)
|
|
45
47
|
3. panels/console.js — createTreeNode, showContextMenu, addConsoleLog (used by other panels)
|
|
46
48
|
4. panels/network.js — depends on: console.js (showToast, showContextMenu, createTreeNode)
|
|
@@ -56,120 +58,119 @@
|
|
|
56
58
|
|
|
57
59
|
---
|
|
58
60
|
|
|
59
|
-
##
|
|
61
|
+
## Current Working State (v1.6.10)
|
|
60
62
|
|
|
61
|
-
**
|
|
62
|
-
break another. Before making any change, check:
|
|
63
|
+
> **This section documents exactly what works. Any change MUST preserve all of this.**
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
### Panel Status
|
|
66
|
+
|
|
67
|
+
| Panel | Status | Init Function | File | DOM ID |
|
|
68
|
+
|-------|--------|---------------|------|--------|
|
|
69
|
+
| Console | WORKING | `initConsolePanel()` | `panels/console.js` | `panel-console` |
|
|
70
|
+
| Network | WORKING | `initNetworkPanel()` | `panels/network.js` | `panel-network` |
|
|
71
|
+
| Redux | WORKING | `initReduxPanel()` | `panels/redux.js` | `panel-redux` |
|
|
72
|
+
| GA4 Events | WORKING | `initGA4Panel()` | `panels/ga4.js` | `panel-ga4` |
|
|
73
|
+
| AsyncStorage | WORKING | `initStoragePanel()` | `panels/storage.js` | `panel-storage` |
|
|
74
|
+
| Performance | WORKING | `initPerformancePanel()` | `panels/performance.js` | `panel-performance` |
|
|
75
|
+
| Memory | WORKING | `initMemoryPanel()` | `panels/performance.js` | `panel-memory` |
|
|
76
|
+
| Native Logs | WORKING | `initNativeLogsPanel()` | `panels/native.js` | `panel-native` |
|
|
77
|
+
| React Tree | WORKING | `initReactPanel()` | `panels/react.js` | `panel-react` |
|
|
78
|
+
| Settings | WORKING | `initSettingsPanel()` | `panels/settings.js` | `panel-settings` |
|
|
79
|
+
| Sources | NOT INITIALIZED | `initSourcesPanel()` | `panels/sources.js` | NO DOM element — `panel-sources` does NOT exist in `index.html` |
|
|
80
|
+
|
|
81
|
+
### Init Sequence (in `init.js`)
|
|
82
|
+
```
|
|
83
|
+
initConsolePanel();
|
|
84
|
+
initNetworkPanel();
|
|
85
|
+
initGA4Panel();
|
|
86
|
+
initPerformancePanel();
|
|
87
|
+
initMemoryPanel();
|
|
88
|
+
initReduxPanel();
|
|
89
|
+
initStoragePanel();
|
|
90
|
+
initReactPanel();
|
|
91
|
+
initNativeLogsPanel();
|
|
92
|
+
initSettingsPanel();
|
|
93
|
+
```
|
|
94
|
+
**`initSourcesPanel()` is NOT called — intentional. No DOM element exists for it.**
|
|
95
|
+
|
|
96
|
+
### IPC Channels Currently Registered (in `init.js`)
|
|
97
|
+
All inside `if (window.electronAPI) { }` block:
|
|
98
|
+
```
|
|
99
|
+
ports, cdp-targets, redux-event, network-event, storage-event,
|
|
100
|
+
console-event, ga4-event, perf-event, clear-all-ui,
|
|
101
|
+
device-all-disconnected, redux-connected, network-connected,
|
|
102
|
+
storage-connected, react-dt-status, focus-search, app-version,
|
|
103
|
+
update-available, update-downloaded, trigger-open-cdp, theme-changed
|
|
104
|
+
```
|
|
105
|
+
Native panel registers `native-log` and `native-status` inside `initNativeLogsPanel()`.
|
|
106
|
+
|
|
107
|
+
### SDK Platform Detection
|
|
108
|
+
The SDK (`sdk/RNDebugSDK.js`) auto-detects the platform at runtime:
|
|
109
|
+
- **Android emulator** → `10.0.2.2` (requires `adb reverse` on ports 9090, 9091, 9092, 8097)
|
|
110
|
+
- **iOS simulator** → `127.0.0.1`
|
|
111
|
+
- **Real device** → Set `HOST_OVERRIDE` in SDK to Mac's LAN IP
|
|
112
|
+
- Detection uses `require('react-native').Platform.OS`
|
|
113
|
+
|
|
114
|
+
### Setup Script (`bin/setup.js`) Behavior
|
|
115
|
+
1. Copies `sdk/RNDebugSDK.js` → user's `src/debug/RNDebugSDK.js`
|
|
116
|
+
2. Patches `index.js` to import SDK
|
|
117
|
+
3. Store file detection order:
|
|
118
|
+
- Step 1: Common directories (`src/store/`, `src/redux/`, etc.) with filenames `store.*`, `index.*`
|
|
119
|
+
- Step 2: Root app files (`src/App.tsx`, `src/App.js`, `App.tsx`, `App.js`)
|
|
120
|
+
- Step 3: Deep recursive scan for files with `createStore(` or `configureStore(` call syntax
|
|
121
|
+
4. Auto-patches RTK `configureStore` (adds middleware field)
|
|
122
|
+
5. Auto-patches legacy `createStore` if `const middleware = [...]` pattern found
|
|
123
|
+
6. Falls back to manual instructions if auto-patch fails
|
|
124
|
+
7. Runs `adb reverse` for Android ports
|
|
125
|
+
8. Only sets `HOST_OVERRIDE` for real device LAN IP; emulator/simulator uses auto-detect
|
|
126
|
+
|
|
127
|
+
### Redux Integration Requirements
|
|
128
|
+
- Connection to port 9090 ("RN app connected") does NOT mean events are flowing
|
|
129
|
+
- `reduxMiddleware` or `reduxEnhancer` MUST be wired into the store
|
|
130
|
+
- Events only flow when `store.dispatch()` goes through the middleware/enhancer
|
|
131
|
+
- Thunk/function actions are safely serialized as `{type: "[Function: thunk]"}`
|
|
132
|
+
- State > 1MB is truncated to `{__truncated: true, sizeBytes, keys}`
|
|
70
133
|
|
|
71
134
|
---
|
|
72
135
|
|
|
73
|
-
##
|
|
136
|
+
## Critical Rules — DO NOT VIOLATE
|
|
74
137
|
|
|
75
|
-
###
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
- `_consoleRAF` — pending requestAnimationFrame ID
|
|
84
|
-
- `_lastLogMsg`, `_lastLogRow`, `_lastLogCount` — log grouping state
|
|
85
|
-
- **IPC channels:** `console-event` (line 921)
|
|
86
|
-
- **Key functions:** `addConsoleLog()`, `flushConsoleBatch()`, `renderConsole()`, `buildLogRow()`, `buildLogBody()`
|
|
87
|
-
- **Cross-panel dependency:** Called by Redux (`handleReduxEvent` → `addConsoleLog`)
|
|
88
|
-
- **Constants:** `MAX_CONSOLE_LOGS = 5000`
|
|
138
|
+
### 1. Every init function MUST have a null guard
|
|
139
|
+
```js
|
|
140
|
+
function initXxxPanel() {
|
|
141
|
+
const panel = $('panel-xxx');
|
|
142
|
+
if (!panel) return; // ← REQUIRED — prevents crash if DOM element is missing
|
|
143
|
+
panel.innerHTML = `...`;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
89
146
|
|
|
90
|
-
###
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- `state.network.requests` — `{id: requestObj}` map
|
|
94
|
-
- `state.network.order` — array of request IDs
|
|
95
|
-
- `state.network.selectedId`, `statusFilter`, `typeFilter`, `searchFilter`, `throttle`, `enabled`, `sortCol`, `sortDir`
|
|
96
|
-
- `_netRAF` — pending requestAnimationFrame ID
|
|
97
|
-
- **IPC channels:** `network-event` (line 357)
|
|
98
|
-
- **Key functions:** `handleNetworkEvent()`, `renderNetwork()`, `buildNetRow()`, `selectNetRequest()`, `closeNetDetail()`
|
|
99
|
-
- **Cross-panel dependency:** Calls `addConsoleLog()` for error toasts; calls `showToast()`
|
|
100
|
-
- **Constants:** `NET_COLS` (column definitions)
|
|
101
|
-
|
|
102
|
-
### Redux Panel
|
|
103
|
-
- **Init:** `initReduxPanel()` (app.js)
|
|
104
|
-
- **State owned:**
|
|
105
|
-
- `state.redux.actions` — array of action entries
|
|
106
|
-
- `state.redux.states` — array of full state snapshots (**MUST be same length as actions**)
|
|
107
|
-
- `state.redux.selected` — selected action index (-1 = none)
|
|
108
|
-
- `state.redux.searchFilter`, `sortDir`
|
|
109
|
-
- `_reduxCatColors`, `_reduxColorIdx` — action category color cache
|
|
110
|
-
- **IPC channels:** `redux-event` (line 356)
|
|
111
|
-
- **Key functions:** `handleReduxEvent()`, `renderRedux()`, `_createHighlightedTree()`, `_deepEqual()`, `_findLeafChanges()`
|
|
112
|
-
- **Cross-panel dependency:** Calls `addConsoleLog()` to mirror actions in console
|
|
113
|
-
- **CRITICAL:** `actions` and `states` arrays must always be the same length. Never clear one without the other.
|
|
114
|
-
- **Constants:** `MAX_REDUX_HISTORY = 500`
|
|
147
|
+
### 2. Redux arrays MUST stay in sync
|
|
148
|
+
`state.redux.actions` and `state.redux.states` MUST have the same length.
|
|
149
|
+
Never empty one without emptying the other. In `freeMemory()`, always trim both together.
|
|
115
150
|
|
|
116
|
-
###
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
- `ga4State` — standalone object: `{events, selected, searchFilter, sortDir, _raf}`
|
|
120
|
-
- `_ga4EventColors`, `_ga4ColorIdx` — event color cache
|
|
121
|
-
- **IPC channels:** `ga4-event` (line 360)
|
|
122
|
-
- **Key functions:** `handleGA4Event()`, `renderGA4List()`, `renderGA4Detail()`, `renderGA4Summary()`
|
|
123
|
-
|
|
124
|
-
### Storage Panel
|
|
125
|
-
- **Init:** `initStoragePanel()` (app.js)
|
|
126
|
-
- **State owned:**
|
|
127
|
-
- `state.storage.entries` — `{key: value}` map
|
|
128
|
-
- `state.storage.keys` — ordered key array
|
|
129
|
-
- `state.storage.selected`, `searchFilter`
|
|
130
|
-
- `_storageRAF` — pending requestAnimationFrame ID
|
|
131
|
-
- **IPC channels:** `storage-event` (line 358)
|
|
132
|
-
- **Key functions:** `handleStorageEvent()`, `renderStorage()`, `renderStorageValue()`
|
|
133
|
-
|
|
134
|
-
### Performance Panel
|
|
135
|
-
- **Init:** `initPerformancePanel()` (app.js)
|
|
136
|
-
- **State owned:**
|
|
137
|
-
- `perfState` — standalone object: `{fps, jsThread, uiThread, recording, data}`
|
|
138
|
-
- **IPC channels:** `perf-event` (line 362, shared with Memory)
|
|
139
|
-
- **Key functions:** `handlePerfEvent()`, `drawPerfGraph()`, `clearPerfCanvas()`
|
|
140
|
-
|
|
141
|
-
### Memory Panel
|
|
142
|
-
- **Init:** `initMemoryPanel()` (app.js)
|
|
143
|
-
- **State owned:** None (displays live values from perf events)
|
|
144
|
-
- **IPC channels:** `perf-event` (line 362, shared with Performance)
|
|
145
|
-
- **Key functions:** `handleMemoryEvent()`
|
|
146
|
-
|
|
147
|
-
### Native Logs Panel
|
|
148
|
-
- **Init:** `initNativeLogsPanel()` (app.js)
|
|
149
|
-
- **State owned:**
|
|
150
|
-
- `_nativeState` — standalone object: `{logs, connected, platform, levelFilter, searchFilter}`
|
|
151
|
-
- **IPC channels:** `native-log`, `native-status` (registered inside init, lines 3429, 3440)
|
|
152
|
-
- **Key functions:** `_clearNativeLogs()`, `_appendNativeLog()`, `_renderNativeLogs()`, `_autoDetectNative()`
|
|
153
|
-
- **Constants:** `MAX_NATIVE_LOGS`
|
|
154
|
-
|
|
155
|
-
### Settings Panel
|
|
156
|
-
- **Init:** `initSettingsPanel()` (app.js)
|
|
157
|
-
- **State owned:** All localStorage accessors (theme, font, app name, metro port, tab visibility, tab order, hidden URLs)
|
|
158
|
-
- **Key functions:** `applyTheme()`, `applyFontSize()`, `applyFontFamily()`, `applyAppName()`, `applyTabVisibility()`, `_buildTabVisGrid()`, `_loadVersionHistory()`
|
|
159
|
-
- **Constants:** `TAB_CONFIG`, `FONT_FAMILIES`
|
|
160
|
-
|
|
161
|
-
### Sources Panel
|
|
162
|
-
- **Init:** `initSourcesPanel()` (app.js)
|
|
163
|
-
- **Key functions:** `fetchSourceFileList()`, `renderSourceFileList()`, `buildSourceTreeNode()`, `loadSourceFile()`
|
|
164
|
-
|
|
165
|
-
### React Tree Panel
|
|
166
|
-
- **Init:** `initReactPanel()` (app.js)
|
|
167
|
-
- **Minimal** — just a connect button for React DevTools
|
|
151
|
+
### 3. IPC channels MUST be in preload allowlist
|
|
152
|
+
Every new IPC channel MUST be added to the `allowed` array in `preload.js`.
|
|
153
|
+
If a channel is not in this array, the listener is **silently dropped** — no error, no warning.
|
|
168
154
|
|
|
169
|
-
|
|
155
|
+
### 4. IPC listeners MUST NOT be registered twice
|
|
156
|
+
The preload `on()` method calls `removeAllListeners(channel)` before adding.
|
|
157
|
+
Registering the same channel twice **kills the first listener**.
|
|
158
|
+
ALL IPC listeners go in `init.js` inside the `if (window.electronAPI) {}` block — nowhere else.
|
|
170
159
|
|
|
171
|
-
|
|
160
|
+
### 5. CSS variables — use ONLY defined variables
|
|
161
|
+
Themes define: `--bg`, `--bg2`, `--bg3`, `--bg4` (NOT `--bg1`)
|
|
162
|
+
`--text`, `--text-mid`, `--text-dim`, `--text-bright`
|
|
163
|
+
`--accent`, `--accent2`, `--border`, `--border2`
|
|
164
|
+
`--green`, `--yellow`, `--red`
|
|
165
|
+
**Never use `--bg1`** — it resolves to transparent/empty.
|
|
172
166
|
|
|
167
|
+
### 6. clearAll() and freeMemory() — check ALL panels
|
|
168
|
+
- `clearAll()` wipes everything + re-renders — used for Cmd+K
|
|
169
|
+
- `freeMemory()` trims heavy data without clearing UI — used on disconnect/quit
|
|
170
|
+
- After modifying either, verify ALL panels still render correctly
|
|
171
|
+
- Cancel pending `requestAnimationFrame` IDs before clearing data
|
|
172
|
+
|
|
173
|
+
### 7. Shared functions — check ALL callers before modifying
|
|
173
174
|
| Function | Used By |
|
|
174
175
|
|----------|---------|
|
|
175
176
|
| `$(id)` | Every panel |
|
|
@@ -178,75 +179,76 @@ break another. Before making any change, check:
|
|
|
178
179
|
| `collectEntries(val)` | `objPreview()`, `createTreeNode()` |
|
|
179
180
|
| `objPreview(val)` | `createTreeNode()` |
|
|
180
181
|
| `createTreeNode(key, val, collapsed)` | Console, Network, Redux, Storage, GA4 |
|
|
181
|
-
| `createPrimitiveSpan(val)` | `createTreeNode()`, `renderConsoleArg()` |
|
|
182
182
|
| `showContextMenu(e, items)` | Console, Network, Redux, Storage, GA4, Native |
|
|
183
|
-
| `
|
|
184
|
-
| `freeMemory()` | IPC `device-all-disconnected` (debounced) |
|
|
185
|
-
| `clearActiveTab()` | Keyboard shortcut Cmd+K |
|
|
183
|
+
| `addConsoleLog(entry)` | Console IPC, Redux (`handleReduxEvent`), Network (error toasts) |
|
|
186
184
|
| `isTabEnabled(tabId)` | Redux, GA4, Storage, Native, Performance |
|
|
187
185
|
| `formatSize(bytes)` | Storage, Network, Memory |
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
186
|
+
|
|
187
|
+
### 8. package.json `files` field MUST include all runtime files
|
|
188
|
+
Current required entries:
|
|
189
|
+
```json
|
|
190
|
+
"files": ["main.js", "preload.js", "index.html", "app.js", "init.js", "panels/", "styles.css", "sdk/", "bin/", "assets/", "AGENTS.md"]
|
|
191
|
+
```
|
|
192
|
+
**If you add a new file, add it here or it won't be published to npm.**
|
|
191
193
|
|
|
192
194
|
---
|
|
193
195
|
|
|
194
|
-
##
|
|
195
|
-
|
|
196
|
-
### Renderer listens (app.js → via preload allowlist)
|
|
197
|
-
|
|
198
|
-
| Channel | Handler | Panel |
|
|
199
|
-
|---------|---------|-------|
|
|
200
|
-
| `ports` | Sets `state.ports` | Global |
|
|
201
|
-
| `cdp-targets` | Updates CDP button | Global |
|
|
202
|
-
| `redux-event` | `handleReduxEvent` | Redux |
|
|
203
|
-
| `network-event` | `handleNetworkEvent` | Network |
|
|
204
|
-
| `storage-event` | `handleStorageEvent` | Storage |
|
|
205
|
-
| `console-event` | `addConsoleLog` | Console |
|
|
206
|
-
| `ga4-event` | `handleGA4Event` | GA4 |
|
|
207
|
-
| `perf-event` | `handlePerfEvent` + `handleMemoryEvent` | Perf + Memory |
|
|
208
|
-
| `redux-connected` | `updateDeviceBanner` + cancel disconnect timer | Global |
|
|
209
|
-
| `network-connected` | `updateDeviceBanner` + cancel disconnect timer | Global |
|
|
210
|
-
| `storage-connected` | `updateDeviceBanner` + cancel disconnect timer | Global |
|
|
211
|
-
| `react-dt-status` | `updateDeviceBanner` | Global |
|
|
212
|
-
| `clear-all-ui` | `clearAll()` | Global |
|
|
213
|
-
| `device-all-disconnected` | debounced `freeMemory()` | Global |
|
|
214
|
-
| `app-version` | Sets `state._appVersion`, `state._isPackaged` | Settings |
|
|
215
|
-
| `update-available` | `_applyUpdateBanner()` | Settings |
|
|
216
|
-
| `update-downloaded` | `_applyUpdateBanner()` | Settings |
|
|
217
|
-
| `trigger-open-cdp` | Opens CDP | Global |
|
|
218
|
-
| `theme-changed` | Applies theme | Settings |
|
|
219
|
-
| `focus-search` | Focuses active panel search | Global |
|
|
220
|
-
| `native-log` | Appends native log | Native (registered inside init) |
|
|
221
|
-
| `native-status` | Updates native connection status | Native (registered inside init) |
|
|
222
|
-
|
|
223
|
-
### Preload allowlist (preload.js)
|
|
224
|
-
|
|
225
|
-
**Every new IPC channel MUST be added to the `allowed` array in `preload.js` line 10-14.**
|
|
226
|
-
If a channel is not in this array, the listener is silently dropped — no error, no warning.
|
|
227
|
-
|
|
228
|
-
### Main process sends (main.js)
|
|
229
|
-
|
|
230
|
-
| Channel | Sent from |
|
|
231
|
-
|---------|-----------|
|
|
232
|
-
| `redux-event` | `startBridge` callback for Redux bridge (port 9090) |
|
|
233
|
-
| `network-event` | `startBridge` callback for Network bridge (port 9092) |
|
|
234
|
-
| `storage-event` | `startBridge` callback for Storage bridge (port 9091) |
|
|
235
|
-
| `console-event` | `startBridge` callback for Network bridge (type=console) |
|
|
236
|
-
| `perf-event` | `startBridge` callback for Network bridge (type=perf) |
|
|
237
|
-
| `ga4-event` | `startBridge` callback for Network bridge (type=ga4) |
|
|
238
|
-
| `*-connected` | `startBridge` on WS connect/disconnect |
|
|
239
|
-
| `device-all-disconnected` | `startBridge` when all 3 bridges have 0 clients |
|
|
240
|
-
| `clear-all-ui` | Menu Cmd+K handler |
|
|
241
|
-
| `app-version` | `createMainWindow` on `did-finish-load` |
|
|
242
|
-
| `native-log` | Native log process stdout parser |
|
|
243
|
-
| `native-status` | Native log start/stop/error |
|
|
196
|
+
## Panels — Ownership & State
|
|
244
197
|
|
|
245
|
-
|
|
198
|
+
### Console Panel (`panels/console.js`)
|
|
199
|
+
- **State:** `state.console.logs`, `state.console.levelFilters`, `state.console.searchFilter`, `state.console.showRedux`
|
|
200
|
+
- **Private state:** `_consolePending`, `_consoleRAF`, `_lastLogMsg`, `_lastLogRow`, `_lastLogCount`
|
|
201
|
+
- **IPC:** `console-event` → `addConsoleLog()`
|
|
202
|
+
- **Cross-panel:** Called by Redux (`handleReduxEvent` → `addConsoleLog`)
|
|
203
|
+
- **Constants:** `MAX_CONSOLE_LOGS = 5000`
|
|
246
204
|
|
|
247
|
-
|
|
205
|
+
### Network Panel (`panels/network.js`)
|
|
206
|
+
- **State:** `state.network.requests`, `state.network.order`, `state.network.selectedId`, `statusFilter`, `typeFilter`, `searchFilter`, `sortCol`, `sortDir`
|
|
207
|
+
- **Private state:** `_netRAF`
|
|
208
|
+
- **IPC:** `network-event` → `handleNetworkEvent()`
|
|
209
|
+
- **Cross-panel:** Calls `addConsoleLog()`, `showToast()`, `showContextMenu()`
|
|
248
210
|
|
|
249
|
-
###
|
|
211
|
+
### Redux Panel (`panels/redux.js`)
|
|
212
|
+
- **State:** `state.redux.actions`, `state.redux.states` (MUST be same length), `state.redux.selected`
|
|
213
|
+
- **IPC:** `redux-event` → `handleReduxEvent()`
|
|
214
|
+
- **Cross-panel:** Calls `addConsoleLog()` to mirror actions in console
|
|
215
|
+
- **Constants:** `MAX_REDUX_HISTORY = 500`
|
|
216
|
+
|
|
217
|
+
### GA4 Events Panel (`panels/ga4.js`)
|
|
218
|
+
- **State:** `ga4State` standalone object
|
|
219
|
+
- **IPC:** `ga4-event` → `handleGA4Event()`
|
|
220
|
+
|
|
221
|
+
### AsyncStorage Panel (`panels/storage.js`)
|
|
222
|
+
- **State:** `state.storage.entries`, `state.storage.keys`, `state.storage.selected`
|
|
223
|
+
- **Private state:** `_storageRAF`
|
|
224
|
+
- **IPC:** `storage-event` → `handleStorageEvent()`
|
|
225
|
+
|
|
226
|
+
### Performance Panel (`panels/performance.js`)
|
|
227
|
+
- **State:** `perfState` standalone object
|
|
228
|
+
- **IPC:** `perf-event` → `handlePerfEvent()`
|
|
229
|
+
|
|
230
|
+
### Memory Panel (`panels/performance.js`)
|
|
231
|
+
- **IPC:** `perf-event` → `handleMemoryEvent()` (shared channel with Performance)
|
|
232
|
+
|
|
233
|
+
### Native Logs Panel (`panels/native.js`)
|
|
234
|
+
- **State:** `_nativeState` standalone object
|
|
235
|
+
- **IPC:** `native-log`, `native-status` (registered inside `initNativeLogsPanel`, NOT in init.js)
|
|
236
|
+
- **Constants:** `MAX_NATIVE_LOGS = 2000`
|
|
237
|
+
|
|
238
|
+
### Settings Panel (`panels/settings.js`)
|
|
239
|
+
- **State:** All localStorage accessors
|
|
240
|
+
- **Shared utilities:** `TAB_CONFIG`, `isTabEnabled()`, `applyTheme()`, `getTabVisibility()`, `applyTabVisibility()`
|
|
241
|
+
|
|
242
|
+
### React Tree Panel (`panels/react.js`)
|
|
243
|
+
- Minimal — just a connect button for React DevTools
|
|
244
|
+
|
|
245
|
+
### Sources Panel (`panels/sources.js`)
|
|
246
|
+
- **NOT INITIALIZED** — `panel-sources` does not exist in `index.html`
|
|
247
|
+
- `initSourcesPanel()` has a null guard and returns early
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## WebSocket Bridges (main.js)
|
|
250
252
|
|
|
251
253
|
| Port | Name | Client Set | Events Carried |
|
|
252
254
|
|------|------|------------|----------------|
|
|
@@ -260,82 +262,103 @@ If a channel is not in this array, the listener is silently dropped — no error
|
|
|
260
262
|
2. Destroy `devtoolsWindow`
|
|
261
263
|
3. Close `reactDTServer` + clients
|
|
262
264
|
4. Close all bridge servers + clients
|
|
263
|
-
5. Kill `_nativeLogProcess`
|
|
265
|
+
5. Kill `_nativeLogProcess`
|
|
264
266
|
|
|
265
267
|
---
|
|
266
268
|
|
|
267
269
|
## Rules for Making Changes
|
|
268
270
|
|
|
269
|
-
###
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
- After modifying either, verify ALL panels still render correctly
|
|
292
|
-
- Test: Cmd+K clears all panels, device disconnect doesn't break subsequent events
|
|
271
|
+
### Adding a new panel
|
|
272
|
+
1. Create `panels/newpanel.js` with `function initNewPanel() { const panel = $('panel-new'); if (!panel) return; ... }`
|
|
273
|
+
2. Add `<div id="panel-new" class="panel"></div>` to `index.html`
|
|
274
|
+
3. Add `<script src="panels/newpanel.js"></script>` to `index.html` BEFORE `init.js`
|
|
275
|
+
4. Add `initNewPanel();` to `init.js` init sequence
|
|
276
|
+
5. Add to `TAB_CONFIG` array in `panels/settings.js`
|
|
277
|
+
6. Add state to `clearAll()` and `freeMemory()` in `app.js`
|
|
278
|
+
7. Add case to `clearActiveTab()` in `app.js`
|
|
279
|
+
|
|
280
|
+
### Adding a new IPC channel
|
|
281
|
+
1. Add `ipcMain.on/handle` in `main.js` `setupIPC()`
|
|
282
|
+
2. Add channel name to `preload.js` allowed array
|
|
283
|
+
3. Add `window.electronAPI.on()` in `init.js` inside the `if (window.electronAPI)` block
|
|
284
|
+
4. **Test:** Verify the listener fires
|
|
285
|
+
|
|
286
|
+
### Modifying the SDK
|
|
287
|
+
1. Test on BOTH Android emulator AND iOS simulator
|
|
288
|
+
2. Verify `adb reverse` works for Android
|
|
289
|
+
3. Test hot reload — ensure no timer/WebSocket leaks
|
|
290
|
+
4. Test with Redux Thunk actions (function dispatches)
|
|
291
|
+
5. Test with large Redux state (>1MB)
|
|
292
|
+
6. Test with binary network responses (images, videos)
|
|
293
293
|
|
|
294
|
-
|
|
295
|
-
- `collectEntries()`, `objPreview()`, `createTreeNode()` are used by Console, Network, Redux, Storage, GA4
|
|
296
|
-
- **Any change to these functions affects ALL panels** that render object trees
|
|
297
|
-
- `_createHighlightedTree()` in Redux is a SEPARATE tree renderer — changes to `createTreeNode` do NOT automatically apply to Redux diff trees
|
|
294
|
+
---
|
|
298
295
|
|
|
299
|
-
|
|
300
|
-
- Themes define: `--bg`, `--bg2`, `--bg3`, `--bg4` (NOT `--bg1`)
|
|
301
|
-
- `--text`, `--text-mid`, `--text-dim`, `--text-bright`
|
|
302
|
-
- `--accent`, `--accent2`, `--border`, `--border2`
|
|
303
|
-
- `--green`, `--yellow`, `--red`
|
|
304
|
-
- **Never use undefined variables** — the value resolves to transparent/empty
|
|
296
|
+
## Pre-Release Checklist
|
|
305
297
|
|
|
306
|
-
|
|
298
|
+
> **MANDATORY — Do NOT publish without passing ALL checks.**
|
|
307
299
|
|
|
308
|
-
|
|
300
|
+
### Automated Checks (run these commands)
|
|
301
|
+
```bash
|
|
302
|
+
# 1. Syntax check all files
|
|
303
|
+
for f in app.js init.js main.js preload.js panels/*.js sdk/RNDebugSDK.js bin/setup.js; do
|
|
304
|
+
node -c "$f" || echo "FAIL: $f"
|
|
305
|
+
done
|
|
309
306
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
2. **Manual wiring** — Redux requires adding `reduxMiddleware` or `reduxEnhancer` to the store
|
|
307
|
+
# 2. No duplicate functions
|
|
308
|
+
grep -rhn "^function [a-zA-Z_]" app.js init.js panels/*.js | sed 's/(.*//' | sort | uniq -c | sort -rn | awk '$1 > 1'
|
|
313
309
|
|
|
314
|
-
|
|
315
|
-
-
|
|
316
|
-
- Patching entry file (`index.js`) to import SDK
|
|
317
|
-
- Auto-patching RTK `configureStore` with middleware
|
|
318
|
-
- **Legacy `createStore`**: Only prints manual instructions (does NOT auto-patch)
|
|
310
|
+
# 3. No duplicate top-level variables
|
|
311
|
+
grep -rhn "^const [a-zA-Z_]\|^let [a-zA-Z_]" app.js init.js panels/*.js | sed 's/ =.*//' | sort | uniq -c | sort -rn | awk '$1 > 1'
|
|
319
312
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
313
|
+
# 4. All critical functions exist
|
|
314
|
+
for fn in handleReduxEvent handleNetworkEvent handleStorageEvent handleGA4Event handlePerfEvent handleMemoryEvent clearAll freeMemory updateDeviceBanner initConsolePanel initNetworkPanel initGA4Panel initPerformancePanel initMemoryPanel initReduxPanel initStoragePanel initReactPanel initNativeLogsPanel initSettingsPanel addConsoleLog renderConsole renderNetwork renderRedux renderStorage renderGA4List closeNetDetail clearPerfCanvas showContextMenu createTreeNode isTabEnabled formatSize showToast takeScreenshot collectEntries _applyUpdateBanner _showChangelog; do
|
|
315
|
+
found=$(grep -rln "function $fn\b" app.js init.js panels/*.js 2>/dev/null | head -1)
|
|
316
|
+
[ -z "$found" ] && echo "MISSING: $fn"
|
|
317
|
+
done
|
|
318
|
+
|
|
319
|
+
# 5. npm pack includes all files
|
|
320
|
+
npm pack --dry-run 2>&1 | grep -c "panels/\|init.js\|sdk/\|bin/"
|
|
321
|
+
# Expected: 15+ files
|
|
322
|
+
|
|
323
|
+
# 6. IPC channels all in preload allowlist
|
|
324
|
+
# Every channel registered in init.js must be in preload.js allowed array
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Manual Checks (test in the running app)
|
|
328
|
+
- [ ] **App launches** — `npm start` opens the window, no blank screen
|
|
329
|
+
- [ ] **Console** — logs appear, level filters work, search works, Cmd+K clears
|
|
330
|
+
- [ ] **Network** — requests appear, detail panel opens/closes, search works, Cmd+K clears
|
|
331
|
+
- [ ] **Redux** — actions appear, state diff shows correctly, Cmd+K clears, works after disconnect/reconnect
|
|
332
|
+
- [ ] **GA4** — events appear, summary tab works, color toggle works
|
|
333
|
+
- [ ] **Storage** — keys appear, values render, search works
|
|
334
|
+
- [ ] **Performance** — Record button toggles, graphs render when data arrives
|
|
335
|
+
- [ ] **Memory** — heap values display when device connected
|
|
336
|
+
- [ ] **Native Logs** — Connect button works for Android/iOS, logs stream, Cmd+K clears
|
|
337
|
+
- [ ] **Settings** — theme switch works, font size changes, tab visibility toggles, version history loads
|
|
338
|
+
- [ ] **React Tree** — connect button shows
|
|
339
|
+
- [ ] **Device disconnect** — `freeMemory()` fires after 3s, panels still work after reconnect
|
|
340
|
+
- [ ] **Device reconnect** — cancel disconnect timer, new events flow immediately
|
|
341
|
+
- [ ] **Cmd+K** — clears active tab data (test on each tab)
|
|
342
|
+
- [ ] **All panels render** — switch through every tab, none show blank/white screen
|
|
343
|
+
|
|
344
|
+
### SDK Checks (test in RN app)
|
|
345
|
+
- [ ] **iOS simulator** — SDK connects with `127.0.0.1`, console/network/storage data flows
|
|
346
|
+
- [ ] **Android emulator** — SDK connects with `10.0.2.2` (after `adb reverse`), all data flows
|
|
347
|
+
- [ ] **Redux** — actions appear in Redux tab AND console (if showRedux enabled)
|
|
348
|
+
- [ ] **Hot reload** — reload RN app, SDK reconnects, no duplicate data/timers
|
|
349
|
+
|
|
350
|
+
### Setup Script Checks
|
|
351
|
+
- [ ] `npx reactoradar setup` — copies SDK, patches index.js, detects store file
|
|
352
|
+
- [ ] Redux auto-patch — adds `reduxMiddleware` to middleware array
|
|
353
|
+
- [ ] `npx reactoradar remove` — cleans up SDK and patches
|
|
354
|
+
- [ ] `adb reverse` — ports forwarded for Android
|
|
324
355
|
|
|
325
356
|
---
|
|
326
357
|
|
|
327
|
-
##
|
|
328
|
-
|
|
329
|
-
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
- [ ] Performance: FPS/JS/UI graphs render when recording
|
|
335
|
-
- [ ] Memory: heap values appear
|
|
336
|
-
- [ ] Native Logs: connect to adb/xcrun, logs stream, Cmd+K clears
|
|
337
|
-
- [ ] Settings: theme switch, font size, tab visibility, version history loads
|
|
338
|
-
- [ ] Device disconnect: `freeMemory()` fires after 3s, panels still work after reconnect
|
|
339
|
-
- [ ] Device reconnect: cancel disconnect timer, new events flow immediately
|
|
340
|
-
- [ ] Cmd+K: clears active tab (all tabs including native)
|
|
341
|
-
- [ ] App quit: `devtoolsWindow` closed, bridges closed, no crash
|
|
358
|
+
## Known Limitations
|
|
359
|
+
|
|
360
|
+
1. **Sources panel** — `panel-sources` div does not exist in `index.html`. `initSourcesPanel()` is not called. Panel is dormant.
|
|
361
|
+
2. **`npx reactoradar setup` store detection** — if store is in an unusual file (not `App.tsx`, not `store.*`), setup may not find it
|
|
362
|
+
3. **iOS real device** — requires manually setting `HOST_OVERRIDE` in the SDK
|
|
363
|
+
4. **`adb reverse` drops** — if Android emulator restarts, `adb reverse` must be re-run
|
|
364
|
+
5. **Performance `jsThread` metric** — currently uses `performance.now() % 16.67` which is approximate
|
package/app.js
CHANGED
|
@@ -136,6 +136,8 @@ document.addEventListener('keydown', (e) => {
|
|
|
136
136
|
}
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
+
// Global filter removed — each panel has its own search input
|
|
140
|
+
|
|
139
141
|
// ─── Clear (each panel has its own clear button now) ─────────────────────────
|
|
140
142
|
|
|
141
143
|
function clearActiveTab() {
|
|
@@ -236,28 +238,28 @@ function clearAll() {
|
|
|
236
238
|
if (ga4Detail) ga4Detail.innerHTML = '';
|
|
237
239
|
// Native logs
|
|
238
240
|
_nativeState.logs = [];
|
|
239
|
-
const
|
|
240
|
-
if (
|
|
241
|
+
const nativeList = $('nativeLogList');
|
|
242
|
+
if (nativeList) nativeList.innerHTML = '';
|
|
241
243
|
// Performance
|
|
242
244
|
perfState.fps = [];
|
|
243
245
|
perfState.jsThread = [];
|
|
244
246
|
perfState.uiThread = [];
|
|
245
247
|
perfState.data = [];
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
const
|
|
248
|
+
const perfFPS = $('perfFPS'); if (perfFPS) perfFPS.textContent = '—';
|
|
249
|
+
const perfJS = $('perfJS'); if (perfJS) perfJS.textContent = '—';
|
|
250
|
+
const perfUI = $('perfUI'); if (perfUI) perfUI.textContent = '—';
|
|
249
251
|
clearPerfCanvas('perfFPSCanvas');
|
|
250
252
|
clearPerfCanvas('perfJSCanvas');
|
|
251
253
|
clearPerfCanvas('perfUICanvas');
|
|
252
254
|
// Memory
|
|
253
|
-
const
|
|
254
|
-
const
|
|
255
|
-
const
|
|
255
|
+
const memHU = $('memHeapUsed'); if (memHU) memHU.textContent = '—';
|
|
256
|
+
const memHT = $('memHeapTotal'); if (memHT) memHT.textContent = '—';
|
|
257
|
+
const memN = $('memNative'); if (memN) memN.textContent = '—';
|
|
256
258
|
// Badges
|
|
257
|
-
$('cBadge').textContent = '0';
|
|
258
|
-
$('nBadge').textContent = '0';
|
|
259
|
-
$('rBadge').textContent = '0';
|
|
260
|
-
$('sBadge').textContent = '0';
|
|
259
|
+
const cB = $('cBadge'); if (cB) cB.textContent = '0';
|
|
260
|
+
const nB = $('nBadge'); if (nB) nB.textContent = '0';
|
|
261
|
+
const rB = $('rBadge'); if (rB) rB.textContent = '0';
|
|
262
|
+
const sB = $('sBadge'); if (sB) sB.textContent = '0';
|
|
261
263
|
if ($('ga4Badge')) $('ga4Badge').textContent = '0';
|
|
262
264
|
if ($('nativeBadge')) $('nativeBadge').textContent = '0';
|
|
263
265
|
// Re-render all
|
|
@@ -281,13 +283,8 @@ function freeMemory() {
|
|
|
281
283
|
if (state.console.logs.length > 200) {
|
|
282
284
|
state.console.logs = state.console.logs.slice(-200);
|
|
283
285
|
}
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
state.redux.actions = state.redux.actions.slice(-50);
|
|
287
|
-
state.redux.states = state.redux.states.slice(-50);
|
|
288
|
-
state.redux.actions.forEach((a, i) => a.index = i);
|
|
289
|
-
state.redux.selected = -1;
|
|
290
|
-
}
|
|
286
|
+
// Drop full Redux state snapshots (keep action metadata)
|
|
287
|
+
state.redux.states = [];
|
|
291
288
|
// Drop storage values (keep keys for reference)
|
|
292
289
|
for (const k in state.storage.entries) {
|
|
293
290
|
state.storage.entries[k] = null;
|
|
@@ -327,11 +324,13 @@ function updateDeviceBanner(service, connected) {
|
|
|
327
324
|
}
|
|
328
325
|
}
|
|
329
326
|
|
|
327
|
+
|
|
330
328
|
function takeScreenshot() {
|
|
331
329
|
const btn = $('btnScreenshot');
|
|
332
330
|
if (!btn) return;
|
|
333
331
|
const origText = btn.innerHTML;
|
|
334
332
|
btn.innerHTML = '<span style="opacity:0.6">Saving...</span>';
|
|
333
|
+
// Use Electron's native capturePage — always works, no DOM rendering issues
|
|
335
334
|
window.electronAPI?.captureScreenshot();
|
|
336
335
|
btn.innerHTML = '<span style="color:var(--green)">Saved!</span>';
|
|
337
336
|
setTimeout(() => { btn.innerHTML = origText; }, 2000);
|