reactoradar 1.6.4 → 1.6.6
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/app.js +74 -4236
- package/index.html +12 -0
- package/main.js +86 -9
- package/package.json +1 -1
- package/preload.js +2 -1
- package/styles.css +130 -7
- package/src/main/main.js +0 -396
- package/src/main/preload.js +0 -28
- package/src/renderer/app.js +0 -221
- package/src/renderer/components/object-tree.js +0 -245
- package/src/renderer/index.html +0 -111
- package/src/renderer/panels/console.js +0 -248
- package/src/renderer/panels/memory.js +0 -60
- package/src/renderer/panels/network.js +0 -559
- package/src/renderer/panels/performance.js +0 -144
- package/src/renderer/panels/react.js +0 -31
- package/src/renderer/panels/redux.js +0 -159
- package/src/renderer/panels/settings.js +0 -93
- package/src/renderer/panels/sources.js +0 -189
- package/src/renderer/panels/storage.js +0 -134
- package/src/renderer/state.js +0 -132
- package/src/renderer/styles/components.css +0 -145
- package/src/renderer/styles/console.css +0 -73
- package/src/renderer/styles/main.css +0 -229
- package/src/renderer/styles/network.css +0 -242
- package/src/renderer/styles/performance.css +0 -45
- package/src/renderer/styles/redux.css +0 -77
- package/src/renderer/styles/settings.css +0 -63
- package/src/renderer/styles/sources.css +0 -48
- package/src/renderer/styles/storage.css +0 -28
- package/src/renderer/styles/theme-light.css +0 -57
package/src/main/main.js
DELETED
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { app, BrowserWindow, ipcMain, Menu, shell, nativeTheme } = require('electron');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const http = require('http');
|
|
6
|
-
const { WebSocketServer, WebSocket } = require('ws');
|
|
7
|
-
|
|
8
|
-
// ─── Ports ────────────────────────────────────────────────────────────────────
|
|
9
|
-
const PORTS = {
|
|
10
|
-
METRO: 8081, // Metro bundler (CDP proxy lives here)
|
|
11
|
-
REACT_DT: 8097, // react-devtools-core server port
|
|
12
|
-
REDUX_BRIDGE: 9090, // our custom Redux WS bridge
|
|
13
|
-
STORAGE_BRIDGE:9091, // AsyncStorage WS bridge
|
|
14
|
-
NETWORK_BRIDGE:9092, // Network intercept WS bridge
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// ─── Windows ──────────────────────────────────────────────────────────────────
|
|
18
|
-
let mainWindow = null;
|
|
19
|
-
let devtoolsWindow = null; // hosts the embedded CDP DevTools frontend
|
|
20
|
-
|
|
21
|
-
// ─── State ────────────────────────────────────────────────────────────────────
|
|
22
|
-
let reduxClients = new Set();
|
|
23
|
-
let storageClients = new Set();
|
|
24
|
-
let networkClients = new Set();
|
|
25
|
-
|
|
26
|
-
// ─── App lifecycle ────────────────────────────────────────────────────────────
|
|
27
|
-
app.whenReady().then(async () => {
|
|
28
|
-
// Theme will be set by renderer via IPC once it reads localStorage
|
|
29
|
-
nativeTheme.themeSource = 'dark';
|
|
30
|
-
|
|
31
|
-
await createMainWindow();
|
|
32
|
-
startBridgeServers();
|
|
33
|
-
startReactDevToolsServer();
|
|
34
|
-
setupMetroCDPProxy();
|
|
35
|
-
setupIPC();
|
|
36
|
-
buildMenu();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
app.on('window-all-closed', () => {
|
|
40
|
-
if (process.platform !== 'darwin') app.quit();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
app.on('before-quit', () => {
|
|
44
|
-
// Close all WS servers gracefully
|
|
45
|
-
if (reactDTServer) {
|
|
46
|
-
reactDTServer.close();
|
|
47
|
-
reactDTClients.forEach(ws => ws.close());
|
|
48
|
-
reactDTClients.clear();
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
app.on('activate', () => {
|
|
53
|
-
if (BrowserWindow.getAllWindows().length === 0) createMainWindow();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// ─── Main Window ──────────────────────────────────────────────────────────────
|
|
57
|
-
async function createMainWindow() {
|
|
58
|
-
mainWindow = new BrowserWindow({
|
|
59
|
-
width: 1400,
|
|
60
|
-
height: 900,
|
|
61
|
-
minWidth: 900,
|
|
62
|
-
minHeight: 600,
|
|
63
|
-
titleBarStyle: 'hiddenInset',
|
|
64
|
-
backgroundColor: '#0a0b0e',
|
|
65
|
-
vibrancy: 'under-window',
|
|
66
|
-
visualEffectState: 'active',
|
|
67
|
-
webPreferences: {
|
|
68
|
-
nodeIntegration: false,
|
|
69
|
-
contextIsolation: true,
|
|
70
|
-
preload: path.join(__dirname, 'preload.js'),
|
|
71
|
-
},
|
|
72
|
-
icon: path.join(__dirname, '../../assets/icon.png'),
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
|
|
76
|
-
|
|
77
|
-
// Open the JS Debugger panel (CDP DevTools) in a second window
|
|
78
|
-
mainWindow.webContents.on('did-finish-load', () => {
|
|
79
|
-
mainWindow.webContents.send('ports', PORTS);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ─── CDP DevTools Window (JS breakpoints, Sources, Console) ──────────────────
|
|
84
|
-
let lastKnownTargets = [];
|
|
85
|
-
|
|
86
|
-
function openCDPWindow(target) {
|
|
87
|
-
if (devtoolsWindow && !devtoolsWindow.isDestroyed()) {
|
|
88
|
-
devtoolsWindow.focus();
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Build the frontend URL from Metro's provided devtoolsFrontendUrl
|
|
93
|
-
// Metro /json/list returns: { devtoolsFrontendUrl: "/debugger-frontend/rn_fusebox.html?ws=...", ... }
|
|
94
|
-
let frontendUrl;
|
|
95
|
-
if (target.devtoolsFrontendUrl) {
|
|
96
|
-
// Metro provides the exact path — use it
|
|
97
|
-
frontendUrl = `http://localhost:${PORTS.METRO}${target.devtoolsFrontendUrl}`;
|
|
98
|
-
} else if (target.webSocketDebuggerUrl) {
|
|
99
|
-
// Fallback: construct URL manually with rn_fusebox (RN 0.76+) or rn_inspector (older)
|
|
100
|
-
const wsUrl = target.webSocketDebuggerUrl;
|
|
101
|
-
frontendUrl = `http://localhost:${PORTS.METRO}/debugger-frontend/rn_fusebox.html?ws=${encodeURIComponent(wsUrl)}&sources.hide_add_folder=true`;
|
|
102
|
-
} else {
|
|
103
|
-
console.warn('[CDP] No usable target URL');
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const titleSuffix = target.deviceName ? ` — ${target.deviceName}` : '';
|
|
108
|
-
devtoolsWindow = new BrowserWindow({
|
|
109
|
-
width: 1200,
|
|
110
|
-
height: 800,
|
|
111
|
-
titleBarStyle: 'hiddenInset',
|
|
112
|
-
backgroundColor: '#0a0b0e',
|
|
113
|
-
title: `JS Debugger${titleSuffix}`,
|
|
114
|
-
webPreferences: {
|
|
115
|
-
nodeIntegration: false,
|
|
116
|
-
contextIsolation: false,
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
console.log(`[CDP] Loading DevTools: ${frontendUrl}`);
|
|
121
|
-
devtoolsWindow.loadURL(frontendUrl);
|
|
122
|
-
|
|
123
|
-
devtoolsWindow.on('closed', () => { devtoolsWindow = null; });
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// ─── Metro CDP — fetch targets on demand (no continuous polling) ──────────────
|
|
127
|
-
// Continuous polling causes Metro's dev-middleware WebSocket to crash with
|
|
128
|
-
// "readyState 3 (CLOSED)" when connections are opened/closed rapidly.
|
|
129
|
-
// Instead, we fetch targets only when the user needs them.
|
|
130
|
-
function fetchCDPTargets(callback) {
|
|
131
|
-
http.get(`http://localhost:${PORTS.METRO}/json/list`, (res) => {
|
|
132
|
-
let data = '';
|
|
133
|
-
res.on('data', d => data += d);
|
|
134
|
-
res.on('end', () => {
|
|
135
|
-
try {
|
|
136
|
-
const targets = JSON.parse(data);
|
|
137
|
-
const rnTargets = targets.filter(t =>
|
|
138
|
-
t.type === 'node' || t.devtoolsFrontendUrl
|
|
139
|
-
);
|
|
140
|
-
lastKnownTargets = rnTargets;
|
|
141
|
-
mainWindow?.webContents.send('cdp-targets', rnTargets);
|
|
142
|
-
if (callback) callback(rnTargets);
|
|
143
|
-
} catch (_) {
|
|
144
|
-
if (callback) callback([]);
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
}).on('error', () => {
|
|
148
|
-
lastKnownTargets = [];
|
|
149
|
-
mainWindow?.webContents.send('cdp-targets', []);
|
|
150
|
-
if (callback) callback([]);
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function setupMetroCDPProxy() {
|
|
155
|
-
// Single fetch after app starts (not continuous polling)
|
|
156
|
-
setTimeout(() => fetchCDPTargets(), 3000);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// ─── React DevTools Relay Server (Component Tree + Profiler) ─────────────────
|
|
160
|
-
// React Native automatically connects to ws://localhost:8097 in dev mode.
|
|
161
|
-
// We run a simple WS relay on that port. When a standalone react-devtools
|
|
162
|
-
// window connects (via `npx react-devtools`) or when the RN app connects,
|
|
163
|
-
// we track the connection and relay messages between frontend ↔ backend.
|
|
164
|
-
let reactDTServer = null;
|
|
165
|
-
let reactDTClients = new Set();
|
|
166
|
-
|
|
167
|
-
function startReactDevToolsServer() {
|
|
168
|
-
try {
|
|
169
|
-
reactDTServer = new WebSocketServer({ port: PORTS.REACT_DT });
|
|
170
|
-
reactDTServer.on('error', (err) => {
|
|
171
|
-
console.warn(`[ReactDT] Server error: ${err.message}`);
|
|
172
|
-
if (err.code === 'EADDRINUSE') {
|
|
173
|
-
mainWindow?.webContents.send('react-dt-status', false);
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
reactDTServer.on('connection', (ws) => {
|
|
177
|
-
reactDTClients.add(ws);
|
|
178
|
-
console.log(`[ReactDT] Client connected (total: ${reactDTClients.size})`);
|
|
179
|
-
mainWindow?.webContents.send('react-dt-status', true);
|
|
180
|
-
|
|
181
|
-
// Relay messages between all connected clients (frontend ↔ backend)
|
|
182
|
-
ws.on('message', (data) => {
|
|
183
|
-
reactDTClients.forEach((client) => {
|
|
184
|
-
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
|
185
|
-
client.send(data);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
ws.on('close', () => {
|
|
191
|
-
reactDTClients.delete(ws);
|
|
192
|
-
console.log(`[ReactDT] Client disconnected (total: ${reactDTClients.size})`);
|
|
193
|
-
if (reactDTClients.size === 0) {
|
|
194
|
-
mainWindow?.webContents.send('react-dt-status', false);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
console.log(`[ReactDT] Relay server on :${PORTS.REACT_DT}`);
|
|
199
|
-
} catch (e) {
|
|
200
|
-
console.warn('[ReactDT] Failed to start relay server:', e.message);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// ─── Bridge Servers (Redux, Storage, Network) ─────────────────────────────────
|
|
205
|
-
function startBridgeServers() {
|
|
206
|
-
// Redux Bridge
|
|
207
|
-
startBridge(PORTS.REDUX_BRIDGE, 'redux', reduxClients, (event) => {
|
|
208
|
-
mainWindow?.webContents.send('redux-event', event);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// AsyncStorage Bridge
|
|
212
|
-
startBridge(PORTS.STORAGE_BRIDGE, 'storage', storageClients, (event) => {
|
|
213
|
-
mainWindow?.webContents.send('storage-event', event);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// Network + Console + Perf Bridge (port 9092 carries all types from RNDebugSDK)
|
|
217
|
-
startBridge(PORTS.NETWORK_BRIDGE, 'network', networkClients, (event) => {
|
|
218
|
-
if (event.type === 'control') return;
|
|
219
|
-
if (event.type === 'console') {
|
|
220
|
-
mainWindow?.webContents.send('console-event', event);
|
|
221
|
-
} else if (event.type === 'perf') {
|
|
222
|
-
mainWindow?.webContents.send('perf-event', event);
|
|
223
|
-
} else {
|
|
224
|
-
mainWindow?.webContents.send('network-event', event);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function startBridge(port, name, clients, onEvent) {
|
|
230
|
-
const wss = new WebSocketServer({ port });
|
|
231
|
-
wss.on('connection', (ws) => {
|
|
232
|
-
clients.add(ws);
|
|
233
|
-
console.log(`[${name}] RN app connected`);
|
|
234
|
-
mainWindow?.webContents.send(`${name}-connected`, true);
|
|
235
|
-
|
|
236
|
-
ws.on('message', (raw) => {
|
|
237
|
-
try {
|
|
238
|
-
const event = JSON.parse(raw.toString());
|
|
239
|
-
onEvent(event);
|
|
240
|
-
} catch (_) {}
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
ws.on('close', () => {
|
|
244
|
-
clients.delete(ws);
|
|
245
|
-
if (clients.size === 0) {
|
|
246
|
-
mainWindow?.webContents.send(`${name}-connected`, false);
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
console.log(`[${name}] Bridge on :${port}`);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// ─── IPC from Renderer ────────────────────────────────────────────────────────
|
|
254
|
-
function setupIPC() {
|
|
255
|
-
ipcMain.on('open-cdp-target', (_, wsUrl) => {
|
|
256
|
-
// Always fetch fresh targets, then open
|
|
257
|
-
fetchCDPTargets((targets) => {
|
|
258
|
-
if (wsUrl && targets.length > 0) {
|
|
259
|
-
const target = targets.find(t => t.webSocketDebuggerUrl === wsUrl) || targets[0];
|
|
260
|
-
openCDPWindow(target);
|
|
261
|
-
} else if (targets.length > 0) {
|
|
262
|
-
const target = targets.find(t =>
|
|
263
|
-
t.reactNative?.capabilities?.prefersFuseboxFrontend
|
|
264
|
-
) || targets[0];
|
|
265
|
-
openCDPWindow(target);
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
ipcMain.on('open-react-devtools', () => {
|
|
271
|
-
// Open standalone react-devtools window
|
|
272
|
-
const rdtWin = new BrowserWindow({
|
|
273
|
-
width: 1100,
|
|
274
|
-
height: 700,
|
|
275
|
-
titleBarStyle: 'hiddenInset',
|
|
276
|
-
backgroundColor: '#0a0b0e',
|
|
277
|
-
title: 'React DevTools',
|
|
278
|
-
webPreferences: { nodeIntegration: false, contextIsolation: false },
|
|
279
|
-
});
|
|
280
|
-
// Metro serves the React DevTools frontend at /debugger-ui
|
|
281
|
-
rdtWin.loadURL(`http://localhost:${PORTS.METRO}/debugger-ui`);
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
ipcMain.on('clear-all', () => {
|
|
285
|
-
// Broadcast clear to all bridges
|
|
286
|
-
// (renderer handles local state clearing; no bridge action needed)
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
ipcMain.on('set-network-capture', (_, enabled) => {
|
|
290
|
-
// Broadcast to connected RN apps so they can stop/start intercepting
|
|
291
|
-
networkClients.forEach(ws => {
|
|
292
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
293
|
-
ws.send(JSON.stringify({ type: 'control', action: 'set-network-capture', enabled }));
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
ipcMain.on('set-network-throttle', (_, profile) => {
|
|
299
|
-
// Broadcast throttle config to connected RN apps
|
|
300
|
-
networkClients.forEach(ws => {
|
|
301
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
302
|
-
ws.send(JSON.stringify({ type: 'control', action: 'set-throttle', profile }));
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
ipcMain.on('set-theme', (_, theme) => {
|
|
308
|
-
nativeTheme.themeSource = theme === 'light' ? 'light' : 'dark';
|
|
309
|
-
const bg = theme === 'light' ? '#f5f6f8' : '#0a0b0e';
|
|
310
|
-
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
311
|
-
mainWindow.setBackgroundColor(bg);
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// ─── macOS App Menu ───────────────────────────────────────────────────────────
|
|
317
|
-
function buildMenu() {
|
|
318
|
-
const template = [
|
|
319
|
-
{
|
|
320
|
-
label: app.name,
|
|
321
|
-
submenu: [
|
|
322
|
-
{ role: 'about' },
|
|
323
|
-
{ type: 'separator' },
|
|
324
|
-
{ role: 'hide' }, { role: 'hideOthers' }, { role: 'unhide' },
|
|
325
|
-
{ type: 'separator' },
|
|
326
|
-
{ role: 'quit' },
|
|
327
|
-
],
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
label: 'Debugger',
|
|
331
|
-
submenu: [
|
|
332
|
-
{
|
|
333
|
-
label: 'Open JS Debugger (CDP)',
|
|
334
|
-
accelerator: 'Cmd+D',
|
|
335
|
-
click: () => { mainWindow?.webContents.send('trigger-open-cdp'); },
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
label: 'Open React DevTools',
|
|
339
|
-
accelerator: 'Cmd+R',
|
|
340
|
-
click: () => { ipcMain.emit('open-react-devtools'); },
|
|
341
|
-
},
|
|
342
|
-
{ type: 'separator' },
|
|
343
|
-
{
|
|
344
|
-
label: 'Clear All',
|
|
345
|
-
accelerator: 'Cmd+K',
|
|
346
|
-
click: () => { mainWindow?.webContents.send('clear-all-ui'); },
|
|
347
|
-
},
|
|
348
|
-
{ type: 'separator' },
|
|
349
|
-
{
|
|
350
|
-
label: 'Toggle Dark/Light Mode',
|
|
351
|
-
accelerator: 'Cmd+Shift+T',
|
|
352
|
-
click: () => {
|
|
353
|
-
const next = nativeTheme.themeSource === 'dark' ? 'light' : 'dark';
|
|
354
|
-
nativeTheme.themeSource = next;
|
|
355
|
-
const bg = next === 'light' ? '#f5f6f8' : '#0a0b0e';
|
|
356
|
-
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
357
|
-
mainWindow.setBackgroundColor(bg);
|
|
358
|
-
mainWindow.webContents.send('theme-changed', next);
|
|
359
|
-
}
|
|
360
|
-
},
|
|
361
|
-
},
|
|
362
|
-
],
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
label: 'Edit',
|
|
366
|
-
submenu: [
|
|
367
|
-
{ role: 'undo' },
|
|
368
|
-
{ role: 'redo' },
|
|
369
|
-
{ type: 'separator' },
|
|
370
|
-
{ role: 'cut' },
|
|
371
|
-
{ role: 'copy' },
|
|
372
|
-
{ role: 'paste' },
|
|
373
|
-
{ role: 'selectAll' },
|
|
374
|
-
],
|
|
375
|
-
},
|
|
376
|
-
{
|
|
377
|
-
label: 'View',
|
|
378
|
-
submenu: [
|
|
379
|
-
{ role: 'reload' }, { role: 'forceReload' },
|
|
380
|
-
{ type: 'separator' },
|
|
381
|
-
{ role: 'resetZoom' }, { role: 'zoomIn' }, { role: 'zoomOut' },
|
|
382
|
-
{ type: 'separator' },
|
|
383
|
-
{ role: 'togglefullscreen' },
|
|
384
|
-
],
|
|
385
|
-
},
|
|
386
|
-
{
|
|
387
|
-
label: 'Window',
|
|
388
|
-
submenu: [
|
|
389
|
-
{ role: 'minimize' }, { role: 'zoom' },
|
|
390
|
-
{ type: 'separator' },
|
|
391
|
-
{ role: 'front' },
|
|
392
|
-
],
|
|
393
|
-
},
|
|
394
|
-
];
|
|
395
|
-
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
|
|
396
|
-
}
|
package/src/main/preload.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { contextBridge, ipcRenderer } = require('electron');
|
|
4
|
-
|
|
5
|
-
const registeredChannels = new Set();
|
|
6
|
-
|
|
7
|
-
contextBridge.exposeInMainWorld('electronAPI', {
|
|
8
|
-
// Listen from main (idempotent — only one listener per channel)
|
|
9
|
-
on: (channel, cb) => {
|
|
10
|
-
const allowed = [
|
|
11
|
-
'ports', 'cdp-targets', 'redux-event', 'storage-event', 'network-event',
|
|
12
|
-
'console-event', 'perf-event', 'redux-connected', 'storage-connected', 'network-connected',
|
|
13
|
-
'react-dt-status', 'trigger-open-cdp', 'clear-all-ui', 'theme-changed',
|
|
14
|
-
];
|
|
15
|
-
if (allowed.includes(channel)) {
|
|
16
|
-
ipcRenderer.removeAllListeners(channel);
|
|
17
|
-
registeredChannels.add(channel);
|
|
18
|
-
ipcRenderer.on(channel, (_, ...args) => cb(...args));
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
// Send to main
|
|
22
|
-
openCDPTarget: (wsUrl) => ipcRenderer.send('open-cdp-target', wsUrl),
|
|
23
|
-
openReactDevTools: () => ipcRenderer.send('open-react-devtools'),
|
|
24
|
-
clearAll: () => ipcRenderer.send('clear-all'),
|
|
25
|
-
setTheme: (theme) => ipcRenderer.send('set-theme', theme),
|
|
26
|
-
setNetworkCapture: (enabled) => ipcRenderer.send('set-network-capture', enabled),
|
|
27
|
-
setNetworkThrottle: (profile) => ipcRenderer.send('set-network-throttle', profile),
|
|
28
|
-
});
|
package/src/renderer/app.js
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const state = window.RNDebug.state;
|
|
4
|
-
const $ = window.RNDebug.$;
|
|
5
|
-
|
|
6
|
-
// Pull in all needed functions from window.RNDebug
|
|
7
|
-
const renderConsole = window.RNDebug.renderConsole;
|
|
8
|
-
const renderNetwork = window.RNDebug.renderNetwork;
|
|
9
|
-
const renderStorage = window.RNDebug.renderStorage;
|
|
10
|
-
const renderRedux = window.RNDebug.renderRedux;
|
|
11
|
-
const closeNetDetail = window.RNDebug.closeNetDetail;
|
|
12
|
-
const clearPerfCanvas = window.RNDebug.clearPerfCanvas;
|
|
13
|
-
const handleReduxEvent = window.RNDebug.handleReduxEvent;
|
|
14
|
-
const handleNetworkEvent = window.RNDebug.handleNetworkEvent;
|
|
15
|
-
const handleStorageEvent = window.RNDebug.handleStorageEvent;
|
|
16
|
-
const handlePerfEvent = window.RNDebug.handlePerfEvent;
|
|
17
|
-
const handleMemoryEvent = window.RNDebug.handleMemoryEvent;
|
|
18
|
-
const updateSourcesPanel = window.RNDebug.updateSourcesPanel;
|
|
19
|
-
const setStoredTheme = window.RNDebug.setStoredTheme;
|
|
20
|
-
const getStoredTheme = window.RNDebug.getStoredTheme;
|
|
21
|
-
const applyTheme = window.RNDebug.applyTheme;
|
|
22
|
-
const perfState = window.RNDebug.perfState;
|
|
23
|
-
|
|
24
|
-
const initConsolePanel = window.RNDebug.initConsolePanel;
|
|
25
|
-
const initSourcesPanel = window.RNDebug.initSourcesPanel;
|
|
26
|
-
const initNetworkPanel = window.RNDebug.initNetworkPanel;
|
|
27
|
-
const initPerformancePanel = window.RNDebug.initPerformancePanel;
|
|
28
|
-
const initMemoryPanel = window.RNDebug.initMemoryPanel;
|
|
29
|
-
const initReduxPanel = window.RNDebug.initReduxPanel;
|
|
30
|
-
const initStoragePanel = window.RNDebug.initStoragePanel;
|
|
31
|
-
const initReactPanel = window.RNDebug.initReactPanel;
|
|
32
|
-
const initSettingsPanel = window.RNDebug.initSettingsPanel;
|
|
33
|
-
|
|
34
|
-
// ─── Navigation ───────────────────────────────────────────────────────────────
|
|
35
|
-
document.querySelectorAll('.nav-btn').forEach(btn => {
|
|
36
|
-
btn.addEventListener('click', () => {
|
|
37
|
-
const panel = btn.dataset.panel;
|
|
38
|
-
document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
|
|
39
|
-
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
|
|
40
|
-
btn.classList.add('active');
|
|
41
|
-
$(`panel-${panel}`).classList.add('active');
|
|
42
|
-
state.activePanel = panel;
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// ─── Filter ───────────────────────────────────────────────────────────────────
|
|
47
|
-
$('filterInput').addEventListener('input', e => {
|
|
48
|
-
state.filter = e.target.value.toLowerCase().trim();
|
|
49
|
-
renderConsole();
|
|
50
|
-
renderNetwork();
|
|
51
|
-
renderStorage();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// ─── Clear (active tab only) ──────────────────────────────────────────────────
|
|
55
|
-
$('btnClear').addEventListener('click', clearActiveTab);
|
|
56
|
-
|
|
57
|
-
function clearActiveTab() {
|
|
58
|
-
switch (state.activePanel) {
|
|
59
|
-
case 'console':
|
|
60
|
-
state.console.logs = [];
|
|
61
|
-
window.RNDebug._setConsolePending([]);
|
|
62
|
-
$('cBadge').textContent = '0';
|
|
63
|
-
renderConsole();
|
|
64
|
-
break;
|
|
65
|
-
case 'network':
|
|
66
|
-
state.network.requests = {};
|
|
67
|
-
state.network.order = [];
|
|
68
|
-
state.network.selectedId = null;
|
|
69
|
-
closeNetDetail();
|
|
70
|
-
$('nBadge').textContent = '0';
|
|
71
|
-
renderNetwork();
|
|
72
|
-
break;
|
|
73
|
-
case 'redux':
|
|
74
|
-
state.redux.actions = [];
|
|
75
|
-
state.redux.states = [];
|
|
76
|
-
state.redux.selected = -1;
|
|
77
|
-
$('rBadge').textContent = '0';
|
|
78
|
-
renderRedux();
|
|
79
|
-
break;
|
|
80
|
-
case 'storage':
|
|
81
|
-
state.storage.entries = {};
|
|
82
|
-
state.storage.keys = [];
|
|
83
|
-
state.storage.selected = null;
|
|
84
|
-
$('sBadge').textContent = '0';
|
|
85
|
-
renderStorage();
|
|
86
|
-
break;
|
|
87
|
-
case 'performance':
|
|
88
|
-
perfState.fps = [];
|
|
89
|
-
perfState.jsThread = [];
|
|
90
|
-
perfState.uiThread = [];
|
|
91
|
-
perfState.data = [];
|
|
92
|
-
const perfFPS = $('perfFPS'); if (perfFPS) perfFPS.textContent = '—';
|
|
93
|
-
const perfJS = $('perfJS'); if (perfJS) perfJS.textContent = '—';
|
|
94
|
-
const perfUI = $('perfUI'); if (perfUI) perfUI.textContent = '—';
|
|
95
|
-
clearPerfCanvas('perfFPSCanvas');
|
|
96
|
-
clearPerfCanvas('perfJSCanvas');
|
|
97
|
-
clearPerfCanvas('perfUICanvas');
|
|
98
|
-
break;
|
|
99
|
-
case 'memory':
|
|
100
|
-
const memHU = $('memHeapUsed'); if (memHU) memHU.textContent = '—';
|
|
101
|
-
const memHT = $('memHeapTotal'); if (memHT) memHT.textContent = '—';
|
|
102
|
-
const memN = $('memNative'); if (memN) memN.textContent = '—';
|
|
103
|
-
break;
|
|
104
|
-
default:
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Clear all (used by IPC clear-all-ui from menu Cmd+K)
|
|
110
|
-
function clearAll() {
|
|
111
|
-
state.console.logs = [];
|
|
112
|
-
window.RNDebug._setConsolePending([]);
|
|
113
|
-
state.network.requests = {};
|
|
114
|
-
state.network.order = [];
|
|
115
|
-
state.network.selectedId = null;
|
|
116
|
-
closeNetDetail();
|
|
117
|
-
state.redux.actions = [];
|
|
118
|
-
state.redux.states = [];
|
|
119
|
-
state.redux.selected = -1;
|
|
120
|
-
state.storage.entries = {};
|
|
121
|
-
state.storage.keys = [];
|
|
122
|
-
state.storage.selected = null;
|
|
123
|
-
$('cBadge').textContent = '0';
|
|
124
|
-
$('nBadge').textContent = '0';
|
|
125
|
-
$('rBadge').textContent = '0';
|
|
126
|
-
$('sBadge').textContent = '0';
|
|
127
|
-
renderConsole();
|
|
128
|
-
renderNetwork();
|
|
129
|
-
renderRedux();
|
|
130
|
-
renderStorage();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// ─── CDP Button ───────────────────────────────────────────────────────────────
|
|
134
|
-
$('btnCDP').addEventListener('click', () => {
|
|
135
|
-
// Tell main process to open the CDP DevTools window with the best available target
|
|
136
|
-
window.electronAPI?.openCDPTarget(null); // null = use latest known target
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
140
|
-
// IPC from Main
|
|
141
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
142
|
-
if (window.electronAPI) {
|
|
143
|
-
window.electronAPI.on('ports', ports => { state.ports = ports; });
|
|
144
|
-
|
|
145
|
-
window.electronAPI.on('cdp-targets', targets => {
|
|
146
|
-
const hasCDP = targets?.length > 0;
|
|
147
|
-
$('btnCDP').textContent = hasCDP
|
|
148
|
-
? `JS Debugger (${targets.length}) ↗`
|
|
149
|
-
: 'JS Debugger ↗';
|
|
150
|
-
$('btnCDP').style.opacity = hasCDP ? '1' : '0.5';
|
|
151
|
-
if (hasCDP) {
|
|
152
|
-
$('btnCDP').onclick = () => window.electronAPI.openCDPTarget(targets[0].webSocketDebuggerUrl);
|
|
153
|
-
}
|
|
154
|
-
// Also update Sources panel file list
|
|
155
|
-
updateSourcesPanel(targets);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
window.electronAPI.on('redux-event', handleReduxEvent);
|
|
159
|
-
window.electronAPI.on('network-event', handleNetworkEvent);
|
|
160
|
-
window.electronAPI.on('storage-event', handleStorageEvent);
|
|
161
|
-
|
|
162
|
-
window.electronAPI.on('perf-event', event => {
|
|
163
|
-
handlePerfEvent(event);
|
|
164
|
-
handleMemoryEvent(event);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
window.electronAPI.on('redux-connected', on => { updateDeviceBanner('redux', on); });
|
|
168
|
-
window.electronAPI.on('network-connected', on => { updateDeviceBanner('network', on); });
|
|
169
|
-
window.electronAPI.on('storage-connected', on => { updateDeviceBanner('storage', on); });
|
|
170
|
-
window.electronAPI.on('react-dt-status', on => { updateDeviceBanner('reactDT', on); });
|
|
171
|
-
|
|
172
|
-
window.electronAPI.on('clear-all-ui', clearAll);
|
|
173
|
-
|
|
174
|
-
// Theme toggle from menu shortcut (Cmd+Shift+T)
|
|
175
|
-
window.electronAPI.on('theme-changed', theme => {
|
|
176
|
-
document.documentElement.setAttribute('data-theme', theme);
|
|
177
|
-
setStoredTheme(theme);
|
|
178
|
-
// Update settings panel buttons if visible
|
|
179
|
-
document.querySelectorAll('#themeSwitcher .theme-option')
|
|
180
|
-
.forEach(b => b.classList.toggle('active', b.dataset.theme === theme));
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ─── Device Connection Status (inline in titlebar) ───────────────────────────
|
|
185
|
-
function updateDeviceBanner(service, connected) {
|
|
186
|
-
state.connections[service] = connected;
|
|
187
|
-
const el = $('deviceStatus');
|
|
188
|
-
const text = $('deviceText');
|
|
189
|
-
if (!el || !text) return;
|
|
190
|
-
|
|
191
|
-
const any = state.connections.redux || state.connections.network || state.connections.storage || state.connections.reactDT;
|
|
192
|
-
|
|
193
|
-
if (any) {
|
|
194
|
-
el.className = 'device-status connected';
|
|
195
|
-
text.textContent = 'Device connected';
|
|
196
|
-
} else {
|
|
197
|
-
el.className = 'device-status waiting';
|
|
198
|
-
text.textContent = 'Waiting for device...';
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// ─── Apply saved theme on load ───────────────────────────────────────────────
|
|
203
|
-
applyTheme(getStoredTheme());
|
|
204
|
-
|
|
205
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
206
|
-
// INIT
|
|
207
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
208
|
-
initConsolePanel();
|
|
209
|
-
initSourcesPanel();
|
|
210
|
-
initNetworkPanel();
|
|
211
|
-
initPerformancePanel();
|
|
212
|
-
initMemoryPanel();
|
|
213
|
-
initReduxPanel();
|
|
214
|
-
initStoragePanel();
|
|
215
|
-
initReactPanel();
|
|
216
|
-
initSettingsPanel();
|
|
217
|
-
|
|
218
|
-
// ─── Export via window.RNDebug ────────────────────────────────────────────────
|
|
219
|
-
window.RNDebug.clearActiveTab = clearActiveTab;
|
|
220
|
-
window.RNDebug.clearAll = clearAll;
|
|
221
|
-
window.RNDebug.updateDeviceBanner = updateDeviceBanner;
|