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 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 — state, $, esc, ts, clearAll, freeMemory (no panel dependencies)
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
- ## Critical Rule: Do NOT Break Other Panels
61
+ ## Current Working State (v1.6.10)
60
62
 
61
- **Every panel is currently in a single `app.js` file.** Changes to one panel can silently
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
- 1. **Shared state** — Is the variable you're touching read/written by other panels?
65
- 2. **Shared functions** — Is the function you're modifying called from other panels?
66
- 3. **IPC listeners** The preload `on()` method calls `removeAllListeners(channel)`.
67
- Registering the same channel twice **kills the first listener**.
68
- 4. **Array sync** `state.redux.actions` and `state.redux.states` MUST have the same length.
69
- Never empty one without emptying the other.
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
- ## PanelsOwnership & State
136
+ ## Critical Rules DO NOT VIOLATE
74
137
 
75
- ### Console Panel
76
- - **Init:** `initConsolePanel()` (app.js)
77
- - **State owned:**
78
- - `state.console.logs` array of log entries
79
- - `state.console.levelFilters``{log, info, warn, error, debug}` booleans
80
- - `state.console.searchFilter` string
81
- - `state.console.showRedux` — boolean (show redux actions in console)
82
- - `_consolePending` — batch queue for rAF rendering
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
- ### Network Panel
91
- - **Init:** `initNetworkPanel()` (app.js)
92
- - **State owned:**
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
- ### GA4 Events Panel
117
- - **Init:** `initGA4Panel()` (app.js)
118
- - **State owned:**
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
- ## Shared UtilitiesDO NOT MODIFY without checking all callers
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
- | `clearAll()` | IPC `clear-all-ui`, memory warning |
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
- | `switchPanel(panel)` | Navigation, tab visibility, toasts |
189
- | `updateDeviceBanner(service, on)` | IPC connection handlers |
190
- | `showToast(msg, type, panel)` | Network |
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
- ## IPC Channel Registry
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
- ## Main Process (main.js) — Structure
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
- ### WebSocket Bridges
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` (second `before-quit` handler inside `setupIPC`)
265
+ 5. Kill `_nativeLogProcess`
264
266
 
265
267
  ---
266
268
 
267
269
  ## Rules for Making Changes
268
270
 
269
- ### 1. Adding a new panel
270
- - Add init function: `initXxxPanel()`
271
- - Add to `TAB_CONFIG` array
272
- - Add to init sequence at bottom of app.js
273
- - Add state to `clearAll()` and `freeMemory()` if the panel stores data
274
- - Add case to `clearActiveTab()` if the panel has clearable data
275
-
276
- ### 2. Adding a new IPC channel
277
- - Add `ipcMain.on/handle` in `main.js` `setupIPC()`
278
- - Add channel name to `preload.js` allowed array (line 10-14)
279
- - Add `window.electronAPI.on()` in app.js or expose send method in preload
280
- - **Test:** Verify the listener fires by adding a `console.log` in the handler
281
-
282
- ### 3. Modifying shared state
283
- - Check all panels that read/write the field (see tables above)
284
- - **Redux arrays:** `actions` and `states` must stay in sync
285
- - **rAF IDs:** Cancel with `cancelAnimationFrame()` before clearing data
286
- - **freeMemory():** Only trim, never wipe arrays that other panels index into
287
-
288
- ### 4. Modifying clearAll() or freeMemory()
289
- - `clearAll()` wipes everything + re-renders — used for explicit user action (Cmd+K)
290
- - `freeMemory()` trims heavy data without clearing UI — used on disconnect/quit
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
- ### 5. Modifying the object tree renderer
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
- ### 6. CSS variable naming
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
- ## SDK Integration (in user's React Native app)
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
- The SDK (`RNDebugSDK.js`) has two parts:
311
- 1. **Auto-connect** WebSocket connections to ports 9090/9091/9092 open on import
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
- `npx reactoradar setup` handles:
315
- - Copying SDK to `src/debug/RNDebugSDK.js`
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
- If Redux is not working:
321
- 1. Check if `reduxMiddleware` or `reduxEnhancer` is wired into the store
322
- 2. Connection to port 9090 ("RN app connected") does NOT mean events are flowing
323
- 3. Events only flow when `store.dispatch()` goes through the middleware/enhancer
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
- ## Testing Checklist (after any change)
328
-
329
- - [ ] Console: logs appear, level filters work, search works, Cmd+K clears
330
- - [ ] Network: requests appear, detail panel opens, HAR export works
331
- - [ ] Redux: actions appear, state diff shows, Cmd+K clears
332
- - [ ] GA4: events appear, summary works, color toggle works
333
- - [ ] Storage: keys appear, values render, search works
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 nativeList2 = $('nativeLogList');
240
- if (nativeList2) nativeList2.innerHTML = '';
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 perfFPS2 = $('perfFPS'); if (perfFPS2) perfFPS2.textContent = '—';
247
- const perfJS2 = $('perfJS'); if (perfJS2) perfJS2.textContent = '—';
248
- const perfUI2 = $('perfUI'); if (perfUI2) perfUI2.textContent = '—';
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 memHU2 = $('memHeapUsed'); if (memHU2) memHU2.textContent = '—';
254
- const memHT2 = $('memHeapTotal'); if (memHT2) memHT2.textContent = '—';
255
- const memN2 = $('memNative'); if (memN2) memN2.textContent = '—';
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
- // Trim Redux history (keep actions and states in sync — they must have same length)
285
- if (state.redux.actions.length > 50) {
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);