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
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const state = window.RNDebug.state;
|
|
4
|
-
const $ = window.RNDebug.$;
|
|
5
|
-
const esc = window.RNDebug.esc;
|
|
6
|
-
const ts = window.RNDebug.ts;
|
|
7
|
-
|
|
8
|
-
// ─── Object Tree Renderer (Chrome DevTools-like) ─────────────────────────────
|
|
9
|
-
// Builds interactive, collapsible DOM nodes for objects/arrays.
|
|
10
|
-
|
|
11
|
-
function objPreview(val, maxLen) {
|
|
12
|
-
maxLen = maxLen || 80;
|
|
13
|
-
if (val === null) return 'null';
|
|
14
|
-
if (val === undefined) return 'undefined';
|
|
15
|
-
if (Array.isArray(val)) {
|
|
16
|
-
if (val.length === 0) return '[]';
|
|
17
|
-
const items = [];
|
|
18
|
-
let len = 2; // [ ]
|
|
19
|
-
for (let i = 0; i < val.length && len < maxLen; i++) {
|
|
20
|
-
const s = primitivePreview(val[i]);
|
|
21
|
-
len += s.length + 2;
|
|
22
|
-
items.push(s);
|
|
23
|
-
}
|
|
24
|
-
const suffix = items.length < val.length ? ', ...' : '';
|
|
25
|
-
return `(${val.length}) [${items.join(', ')}${suffix}]`;
|
|
26
|
-
}
|
|
27
|
-
if (typeof val === 'object') {
|
|
28
|
-
const keys = Object.keys(val);
|
|
29
|
-
if (keys.length === 0) return '{}';
|
|
30
|
-
const items = [];
|
|
31
|
-
let len = 2;
|
|
32
|
-
for (let i = 0; i < keys.length && len < maxLen; i++) {
|
|
33
|
-
const s = `${keys[i]}: ${primitivePreview(val[keys[i]])}`;
|
|
34
|
-
len += s.length + 2;
|
|
35
|
-
items.push(s);
|
|
36
|
-
}
|
|
37
|
-
const suffix = items.length < keys.length ? ', ...' : '';
|
|
38
|
-
return `{${items.join(', ')}${suffix}}`;
|
|
39
|
-
}
|
|
40
|
-
return primitivePreview(val);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function primitivePreview(val) {
|
|
44
|
-
if (val === null) return 'null';
|
|
45
|
-
if (val === undefined) return 'undefined';
|
|
46
|
-
if (typeof val === 'string') return val.length > 50 ? `"${val.slice(0,50)}..."` : `"${val}"`;
|
|
47
|
-
if (typeof val === 'number' || typeof val === 'boolean') return String(val);
|
|
48
|
-
if (Array.isArray(val)) return `Array(${val.length})`;
|
|
49
|
-
if (typeof val === 'object') return `{...}`;
|
|
50
|
-
return String(val);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function createTreeNode(key, val, startCollapsed) {
|
|
54
|
-
const isArray = Array.isArray(val);
|
|
55
|
-
const isObj = val !== null && typeof val === 'object';
|
|
56
|
-
|
|
57
|
-
if (!isObj) {
|
|
58
|
-
// Primitive leaf
|
|
59
|
-
const row = document.createElement('div');
|
|
60
|
-
row.className = 'ov-leaf';
|
|
61
|
-
if (key !== null) {
|
|
62
|
-
const k = document.createElement('span');
|
|
63
|
-
k.className = 'ov-key';
|
|
64
|
-
k.textContent = isNaN(key) ? `${key}: ` : `${key}: `;
|
|
65
|
-
row.appendChild(k);
|
|
66
|
-
}
|
|
67
|
-
row.appendChild(createPrimitiveSpan(val));
|
|
68
|
-
return row;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Collapsible object/array
|
|
72
|
-
const container = document.createElement('div');
|
|
73
|
-
container.className = 'ov-node';
|
|
74
|
-
|
|
75
|
-
const header = document.createElement('div');
|
|
76
|
-
header.className = 'ov-header';
|
|
77
|
-
|
|
78
|
-
const arrow = document.createElement('span');
|
|
79
|
-
arrow.className = 'ov-arrow';
|
|
80
|
-
arrow.textContent = '\u25B6'; // ▶
|
|
81
|
-
header.appendChild(arrow);
|
|
82
|
-
|
|
83
|
-
if (key !== null) {
|
|
84
|
-
const k = document.createElement('span');
|
|
85
|
-
k.className = 'ov-key';
|
|
86
|
-
k.textContent = `${key}: `;
|
|
87
|
-
header.appendChild(k);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const preview = document.createElement('span');
|
|
91
|
-
preview.className = 'ov-preview';
|
|
92
|
-
preview.textContent = objPreview(val);
|
|
93
|
-
header.appendChild(preview);
|
|
94
|
-
|
|
95
|
-
container.appendChild(header);
|
|
96
|
-
|
|
97
|
-
const children = document.createElement('div');
|
|
98
|
-
children.className = 'ov-children';
|
|
99
|
-
children.style.display = 'none';
|
|
100
|
-
|
|
101
|
-
let populated = false;
|
|
102
|
-
|
|
103
|
-
function populateChildren() {
|
|
104
|
-
if (populated) return;
|
|
105
|
-
populated = true;
|
|
106
|
-
const entries = isArray ? val.map((v, i) => [i, v]) : Object.entries(val);
|
|
107
|
-
entries.forEach(([k, v]) => {
|
|
108
|
-
children.appendChild(createTreeNode(k, v, true));
|
|
109
|
-
});
|
|
110
|
-
// For arrays show length, for objects show prototype hint
|
|
111
|
-
if (isArray) {
|
|
112
|
-
const lenNode = document.createElement('div');
|
|
113
|
-
lenNode.className = 'ov-leaf ov-meta';
|
|
114
|
-
lenNode.textContent = `length: ${val.length}`;
|
|
115
|
-
children.appendChild(lenNode);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
let expanded = !startCollapsed;
|
|
120
|
-
if (expanded) {
|
|
121
|
-
populateChildren();
|
|
122
|
-
children.style.display = 'block';
|
|
123
|
-
arrow.textContent = '\u25BC'; // ▼
|
|
124
|
-
arrow.classList.add('expanded');
|
|
125
|
-
preview.style.display = 'none';
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
header.addEventListener('click', (e) => {
|
|
129
|
-
e.stopPropagation();
|
|
130
|
-
expanded = !expanded;
|
|
131
|
-
if (expanded) {
|
|
132
|
-
populateChildren();
|
|
133
|
-
children.style.display = 'block';
|
|
134
|
-
arrow.textContent = '\u25BC';
|
|
135
|
-
arrow.classList.add('expanded');
|
|
136
|
-
preview.style.display = 'none';
|
|
137
|
-
} else {
|
|
138
|
-
children.style.display = 'none';
|
|
139
|
-
arrow.textContent = '\u25B6';
|
|
140
|
-
arrow.classList.remove('expanded');
|
|
141
|
-
preview.style.display = '';
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
container.appendChild(children);
|
|
146
|
-
return container;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function createPrimitiveSpan(val) {
|
|
150
|
-
const s = document.createElement('span');
|
|
151
|
-
if (val === null) { s.className = 'ov-null'; s.textContent = 'null'; }
|
|
152
|
-
else if (val === undefined) { s.className = 'ov-undef'; s.textContent = 'undefined'; }
|
|
153
|
-
else if (typeof val === 'string') { s.className = 'ov-str'; s.textContent = `"${val}"`; }
|
|
154
|
-
else if (typeof val === 'number') { s.className = 'ov-num'; s.textContent = String(val); }
|
|
155
|
-
else if (typeof val === 'boolean') { s.className = 'ov-bool'; s.textContent = String(val); }
|
|
156
|
-
else { s.textContent = String(val); }
|
|
157
|
-
return s;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Parse a structured arg from the SDK (or fall back to raw message string)
|
|
161
|
-
function renderConsoleArg(arg) {
|
|
162
|
-
if (!arg || typeof arg !== 'object' || !arg.t) {
|
|
163
|
-
// Backward compat: raw string
|
|
164
|
-
const s = document.createElement('span');
|
|
165
|
-
s.className = 'ov-str';
|
|
166
|
-
s.textContent = String(arg);
|
|
167
|
-
return s;
|
|
168
|
-
}
|
|
169
|
-
const { t, v } = arg;
|
|
170
|
-
if (t === 'string') {
|
|
171
|
-
const s = document.createElement('span');
|
|
172
|
-
s.className = 'log-text';
|
|
173
|
-
s.textContent = v;
|
|
174
|
-
return s;
|
|
175
|
-
}
|
|
176
|
-
if (t === 'number') { return createPrimitiveSpan(v); }
|
|
177
|
-
if (t === 'boolean') { return createPrimitiveSpan(v); }
|
|
178
|
-
if (t === 'null') { return createPrimitiveSpan(null); }
|
|
179
|
-
if (t === 'undefined') { return createPrimitiveSpan(undefined); }
|
|
180
|
-
if (t === 'object' || t === 'array') {
|
|
181
|
-
return createTreeNode(null, v, false);
|
|
182
|
-
}
|
|
183
|
-
const s = document.createElement('span');
|
|
184
|
-
s.textContent = String(v);
|
|
185
|
-
return s;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Build the body of a console log row. If structured args exist, render each;
|
|
189
|
-
// otherwise fall back to the flat message string and try to detect JSON in it.
|
|
190
|
-
function buildLogBody(logEntry) {
|
|
191
|
-
const container = document.createElement('div');
|
|
192
|
-
container.className = 'log-body';
|
|
193
|
-
|
|
194
|
-
if (logEntry.args && Array.isArray(logEntry.args) && logEntry.args.length > 0) {
|
|
195
|
-
// Structured args from updated SDK
|
|
196
|
-
logEntry.args.forEach((arg, i) => {
|
|
197
|
-
if (i > 0) container.appendChild(document.createTextNode(' '));
|
|
198
|
-
container.appendChild(renderConsoleArg(arg));
|
|
199
|
-
});
|
|
200
|
-
} else if (logEntry.message != null) {
|
|
201
|
-
// Legacy / flat message — try to parse JSON objects out of it
|
|
202
|
-
const msg = String(logEntry.message);
|
|
203
|
-
// Try parsing the whole message as JSON
|
|
204
|
-
try {
|
|
205
|
-
const parsed = JSON.parse(msg);
|
|
206
|
-
if (typeof parsed === 'object' && parsed !== null) {
|
|
207
|
-
container.appendChild(createTreeNode(null, parsed, false));
|
|
208
|
-
return container;
|
|
209
|
-
}
|
|
210
|
-
} catch {}
|
|
211
|
-
|
|
212
|
-
// Otherwise render as text, but look for embedded JSON blocks
|
|
213
|
-
// If it looks like it contains JSON, try to pretty-render inline
|
|
214
|
-
const jsonRe = /(\{[\s\S]*\}|\[[\s\S]*\])/;
|
|
215
|
-
const match = msg.match(jsonRe);
|
|
216
|
-
if (match && match[0].length > 2) {
|
|
217
|
-
try {
|
|
218
|
-
const parsed = JSON.parse(match[0]);
|
|
219
|
-
// There's text before/after
|
|
220
|
-
const before = msg.slice(0, match.index);
|
|
221
|
-
const after = msg.slice(match.index + match[0].length);
|
|
222
|
-
if (before) container.appendChild(document.createTextNode(before));
|
|
223
|
-
container.appendChild(createTreeNode(null, parsed, false));
|
|
224
|
-
if (after) container.appendChild(document.createTextNode(after));
|
|
225
|
-
return container;
|
|
226
|
-
} catch {}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Plain text
|
|
230
|
-
const span = document.createElement('span');
|
|
231
|
-
span.className = 'log-text';
|
|
232
|
-
span.textContent = msg;
|
|
233
|
-
container.appendChild(span);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return container;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// ─── Export via window.RNDebug ────────────────────────────────────────────────
|
|
240
|
-
window.RNDebug.objPreview = objPreview;
|
|
241
|
-
window.RNDebug.primitivePreview = primitivePreview;
|
|
242
|
-
window.RNDebug.createTreeNode = createTreeNode;
|
|
243
|
-
window.RNDebug.createPrimitiveSpan = createPrimitiveSpan;
|
|
244
|
-
window.RNDebug.renderConsoleArg = renderConsoleArg;
|
|
245
|
-
window.RNDebug.buildLogBody = buildLogBody;
|
package/src/renderer/index.html
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta http-equiv="Content-Security-Policy" content="default-src 'self' http://localhost:* ws://localhost:*; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com;">
|
|
6
|
-
<title>RN Debugger</title>
|
|
7
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
-
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600&family=Syne:wght@700;800&display=swap" rel="stylesheet">
|
|
9
|
-
|
|
10
|
-
<!-- Styles (modular) -->
|
|
11
|
-
<link rel="stylesheet" href="styles/main.css">
|
|
12
|
-
<link rel="stylesheet" href="styles/components.css">
|
|
13
|
-
<link rel="stylesheet" href="styles/console.css">
|
|
14
|
-
<link rel="stylesheet" href="styles/network.css">
|
|
15
|
-
<link rel="stylesheet" href="styles/sources.css">
|
|
16
|
-
<link rel="stylesheet" href="styles/performance.css">
|
|
17
|
-
<link rel="stylesheet" href="styles/redux.css">
|
|
18
|
-
<link rel="stylesheet" href="styles/storage.css">
|
|
19
|
-
<link rel="stylesheet" href="styles/settings.css">
|
|
20
|
-
<link rel="stylesheet" href="styles/theme-light.css">
|
|
21
|
-
</head>
|
|
22
|
-
<body>
|
|
23
|
-
<div id="app">
|
|
24
|
-
|
|
25
|
-
<!-- ── TITLE BAR (logo + device status + actions in one line) ── -->
|
|
26
|
-
<header id="titlebar">
|
|
27
|
-
<div class="titlebar-drag"></div>
|
|
28
|
-
<div class="logo">RN<span>debug</span></div>
|
|
29
|
-
<span class="title-sep">—</span>
|
|
30
|
-
<div id="deviceStatus" class="device-status waiting">
|
|
31
|
-
<span class="device-dot"></span>
|
|
32
|
-
<span id="deviceText">Waiting for device...</span>
|
|
33
|
-
</div>
|
|
34
|
-
<div class="titlebar-actions">
|
|
35
|
-
<input id="filterInput" class="filter-input" placeholder="⌘F Filter…" />
|
|
36
|
-
<button class="tb-btn" id="btnClear" title="Clear active tab (⌘K clears all)">Clear</button>
|
|
37
|
-
<button class="tb-btn primary" id="btnCDP" title="Open JS Debugger (⌘D)">JS Debugger ↗</button>
|
|
38
|
-
</div>
|
|
39
|
-
</header>
|
|
40
|
-
|
|
41
|
-
<!-- ── SIDEBAR ── -->
|
|
42
|
-
<nav id="sidebar">
|
|
43
|
-
<button class="nav-btn active" data-panel="console" title="Console">
|
|
44
|
-
<svg viewBox="0 0 20 20"><path d="M3 5h14M3 10h8M3 15h10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
|
45
|
-
<span>Console</span>
|
|
46
|
-
</button>
|
|
47
|
-
<button class="nav-btn" data-panel="sources" title="Sources">
|
|
48
|
-
<svg viewBox="0 0 20 20"><path d="M7 3l-4 7 4 7M13 3l4 7-4 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none"/></svg>
|
|
49
|
-
<span>Sources</span>
|
|
50
|
-
</button>
|
|
51
|
-
<button class="nav-btn" data-panel="network" title="Network">
|
|
52
|
-
<svg viewBox="0 0 20 20"><circle cx="10" cy="10" r="7" stroke="currentColor" stroke-width="1.5" fill="none"/><path d="M10 3c-2 2.5-2 11.5 0 14M10 3c2 2.5 2 11.5 0 14M3 10h14" stroke="currentColor" stroke-width="1.5"/></svg>
|
|
53
|
-
<span>Network</span>
|
|
54
|
-
</button>
|
|
55
|
-
<button class="nav-btn" data-panel="performance" title="Performance">
|
|
56
|
-
<svg viewBox="0 0 20 20"><polyline points="2,16 6,10 10,13 14,5 18,8" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
57
|
-
<span>Perf</span>
|
|
58
|
-
</button>
|
|
59
|
-
<button class="nav-btn" data-panel="memory" title="Memory">
|
|
60
|
-
<svg viewBox="0 0 20 20"><rect x="3" y="8" width="3" height="8" rx="0.5" stroke="currentColor" stroke-width="1.2" fill="none"/><rect x="8.5" y="4" width="3" height="12" rx="0.5" stroke="currentColor" stroke-width="1.2" fill="none"/><rect x="14" y="6" width="3" height="10" rx="0.5" stroke="currentColor" stroke-width="1.2" fill="none"/></svg>
|
|
61
|
-
<span>Memory</span>
|
|
62
|
-
</button>
|
|
63
|
-
<button class="nav-btn" data-panel="redux" title="Redux">
|
|
64
|
-
<svg viewBox="0 0 20 20"><rect x="3" y="3" width="6" height="6" rx="1" stroke="currentColor" stroke-width="1.5" fill="none"/><rect x="11" y="3" width="6" height="6" rx="1" stroke="currentColor" stroke-width="1.5" fill="none"/><rect x="7" y="11" width="6" height="6" rx="1" stroke="currentColor" stroke-width="1.5" fill="none"/></svg>
|
|
65
|
-
<span>Redux</span>
|
|
66
|
-
</button>
|
|
67
|
-
<button class="nav-btn" data-panel="storage" title="Application">
|
|
68
|
-
<svg viewBox="0 0 20 20"><ellipse cx="10" cy="6" rx="7" ry="3" stroke="currentColor" stroke-width="1.5" fill="none"/><path d="M3 6v4c0 1.66 3.13 3 7 3s7-1.34 7-3V6" stroke="currentColor" stroke-width="1.5" fill="none"/><path d="M3 10v4c0 1.66 3.13 3 7 3s7-1.34 7-3v-4" stroke="currentColor" stroke-width="1.5" fill="none"/></svg>
|
|
69
|
-
<span>App</span>
|
|
70
|
-
</button>
|
|
71
|
-
<button class="nav-btn" data-panel="react" title="React Tree">
|
|
72
|
-
<svg viewBox="0 0 20 20"><circle cx="10" cy="10" r="2" fill="currentColor"/><ellipse cx="10" cy="10" rx="8" ry="3.5" stroke="currentColor" stroke-width="1.5" fill="none"/><ellipse cx="10" cy="10" rx="8" ry="3.5" stroke="currentColor" stroke-width="1.5" fill="none" transform="rotate(60 10 10)"/><ellipse cx="10" cy="10" rx="8" ry="3.5" stroke="currentColor" stroke-width="1.5" fill="none" transform="rotate(120 10 10)"/></svg>
|
|
73
|
-
<span>React</span>
|
|
74
|
-
</button>
|
|
75
|
-
<div class="nav-spacer"></div>
|
|
76
|
-
<button class="nav-btn" data-panel="settings" title="Settings">
|
|
77
|
-
<svg viewBox="0 0 20 20"><path d="M10 13a3 3 0 100-6 3 3 0 000 6z" stroke="currentColor" stroke-width="1.5" fill="none"/><path d="M17.4 11c.2-.6.2-1.4 0-2l1.3-1-1.7-3-1.6.5c-.5-.4-1-.7-1.6-.9L13.4 3H10.6l-.4 1.6c-.6.2-1.1.5-1.6.9L7 5 5.3 8l1.3 1c-.2.6-.2 1.4 0 2L5.3 12 7 15l1.6-.5c.5.4 1 .7 1.6.9l.4 1.6h2.8l.4-1.6c.6-.2 1.1-.5 1.6-.9l1.6.5 1.7-3-1.3-1z" stroke="currentColor" stroke-width="1.5" fill="none"/></svg>
|
|
78
|
-
<span>Settings</span>
|
|
79
|
-
</button>
|
|
80
|
-
</nav>
|
|
81
|
-
|
|
82
|
-
<!-- ── PANELS ── -->
|
|
83
|
-
<main id="content">
|
|
84
|
-
<div id="panel-console" class="panel active"></div>
|
|
85
|
-
<div id="panel-sources" class="panel"></div>
|
|
86
|
-
<div id="panel-network" class="panel"></div>
|
|
87
|
-
<div id="panel-performance" class="panel"></div>
|
|
88
|
-
<div id="panel-memory" class="panel"></div>
|
|
89
|
-
<div id="panel-redux" class="panel"></div>
|
|
90
|
-
<div id="panel-storage" class="panel"></div>
|
|
91
|
-
<div id="panel-react" class="panel"></div>
|
|
92
|
-
<div id="panel-settings" class="panel"></div>
|
|
93
|
-
</main>
|
|
94
|
-
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
<!-- Scripts (loaded in order — each depends on the previous) -->
|
|
98
|
-
<script src="state.js"></script>
|
|
99
|
-
<script src="components/object-tree.js"></script>
|
|
100
|
-
<script src="panels/console.js"></script>
|
|
101
|
-
<script src="panels/network.js"></script>
|
|
102
|
-
<script src="panels/sources.js"></script>
|
|
103
|
-
<script src="panels/performance.js"></script>
|
|
104
|
-
<script src="panels/memory.js"></script>
|
|
105
|
-
<script src="panels/redux.js"></script>
|
|
106
|
-
<script src="panels/storage.js"></script>
|
|
107
|
-
<script src="panels/react.js"></script>
|
|
108
|
-
<script src="panels/settings.js"></script>
|
|
109
|
-
<script src="app.js"></script>
|
|
110
|
-
</body>
|
|
111
|
-
</html>
|
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const state = window.RNDebug.state;
|
|
4
|
-
const $ = window.RNDebug.$;
|
|
5
|
-
const esc = window.RNDebug.esc;
|
|
6
|
-
const ts = window.RNDebug.ts;
|
|
7
|
-
const extractCallerShort = window.RNDebug.extractCallerShort;
|
|
8
|
-
const showContextMenu = window.RNDebug.showContextMenu;
|
|
9
|
-
const buildLogBody = window.RNDebug.buildLogBody;
|
|
10
|
-
|
|
11
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
-
// CONSOLE PANEL
|
|
13
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
-
function initConsolePanel() {
|
|
15
|
-
const panel = $('panel-console');
|
|
16
|
-
panel.innerHTML = `
|
|
17
|
-
<div class="panel-toolbar">
|
|
18
|
-
<span class="panel-label">Console</span>
|
|
19
|
-
<span class="badge" id="cBadge">0</span>
|
|
20
|
-
<div class="tab-row" style="margin-left:12px">
|
|
21
|
-
<button class="tab active" onclick="setConsoleLevel('all',this)">All</button>
|
|
22
|
-
<button class="tab" onclick="setConsoleLevel('log',this)">Log</button>
|
|
23
|
-
<button class="tab" onclick="setConsoleLevel('info',this)">Info</button>
|
|
24
|
-
<button class="tab" onclick="setConsoleLevel('warn',this)">Warn</button>
|
|
25
|
-
<button class="tab" onclick="setConsoleLevel('error',this)">Error</button>
|
|
26
|
-
</div>
|
|
27
|
-
</div>
|
|
28
|
-
<div class="scroll-area" id="consoleList">
|
|
29
|
-
<div class="empty-state" id="consoleEmpty">
|
|
30
|
-
<div class="icon">⬛</div>
|
|
31
|
-
<div class="label">No logs yet</div>
|
|
32
|
-
<div class="hint">Add RNDebugSDK.js to your app</div>
|
|
33
|
-
</div>
|
|
34
|
-
</div>`;
|
|
35
|
-
}
|
|
36
|
-
window.setConsoleLevel = (level, btn) => {
|
|
37
|
-
state.console.levelFilter = level;
|
|
38
|
-
document.querySelectorAll('#panel-console .tab').forEach(b => b.classList.remove('active'));
|
|
39
|
-
btn.classList.add('active');
|
|
40
|
-
renderConsole();
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// Console is fed via IPC (network-event handled in IPC section above)
|
|
44
|
-
|
|
45
|
-
// ─── Batched console append (fixes re-render performance) ────────────────────
|
|
46
|
-
let _consolePending = [];
|
|
47
|
-
let _consoleRAF = null;
|
|
48
|
-
|
|
49
|
-
function addConsoleLog(event) {
|
|
50
|
-
state.console.logs.push(event);
|
|
51
|
-
_consolePending.push(event);
|
|
52
|
-
// Batch DOM updates via rAF — only one paint per frame
|
|
53
|
-
if (!_consoleRAF) {
|
|
54
|
-
_consoleRAF = requestAnimationFrame(flushConsoleBatch);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function flushConsoleBatch() {
|
|
59
|
-
_consoleRAF = null;
|
|
60
|
-
const batch = _consolePending;
|
|
61
|
-
_consolePending = [];
|
|
62
|
-
if (!batch.length) return;
|
|
63
|
-
|
|
64
|
-
$('cBadge').textContent = state.console.logs.length;
|
|
65
|
-
|
|
66
|
-
const list = $('consoleList');
|
|
67
|
-
const empty = $('consoleEmpty');
|
|
68
|
-
if (!list) return;
|
|
69
|
-
|
|
70
|
-
const { levelFilter } = state.console;
|
|
71
|
-
const frag = document.createDocumentFragment();
|
|
72
|
-
let added = 0;
|
|
73
|
-
|
|
74
|
-
batch.forEach(l => {
|
|
75
|
-
if (levelFilter !== 'all' && l.level !== levelFilter) return;
|
|
76
|
-
if (state.filter && !l.message?.toLowerCase().includes(state.filter)) return;
|
|
77
|
-
frag.appendChild(buildLogRow(l));
|
|
78
|
-
added++;
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
if (added > 0) {
|
|
82
|
-
empty.style.display = 'none';
|
|
83
|
-
list.appendChild(frag);
|
|
84
|
-
list.scrollTop = list.scrollHeight;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
window.electronAPI?.on('console-event', addConsoleLog);
|
|
89
|
-
|
|
90
|
-
function buildLogRow(l) {
|
|
91
|
-
const div = document.createElement('div');
|
|
92
|
-
div.className = `log-row ${l.level}`;
|
|
93
|
-
|
|
94
|
-
const timeSpan = document.createElement('span');
|
|
95
|
-
timeSpan.className = 'log-time';
|
|
96
|
-
timeSpan.textContent = ts(l.ts);
|
|
97
|
-
div.appendChild(timeSpan);
|
|
98
|
-
|
|
99
|
-
const lvlSpan = document.createElement('span');
|
|
100
|
-
lvlSpan.className = `lvl-badge lvl-${l.level}`;
|
|
101
|
-
lvlSpan.textContent = l.level;
|
|
102
|
-
div.appendChild(lvlSpan);
|
|
103
|
-
|
|
104
|
-
// Body wrapper with preview (collapsed) and full (expanded)
|
|
105
|
-
const bodyWrap = document.createElement('div');
|
|
106
|
-
bodyWrap.className = 'log-body-wrap';
|
|
107
|
-
|
|
108
|
-
// Single-line preview with caller at end
|
|
109
|
-
const preview = document.createElement('div');
|
|
110
|
-
preview.className = 'log-preview';
|
|
111
|
-
const msgText = (l.message || '').replace(/\n/g, ' ').slice(0, 200);
|
|
112
|
-
const previewText = document.createElement('span');
|
|
113
|
-
previewText.textContent = msgText + ((l.message || '').length > 200 ? '...' : '');
|
|
114
|
-
preview.appendChild(previewText);
|
|
115
|
-
if (l.caller) {
|
|
116
|
-
// Extract short filename:line from caller like "at Component (file.js:42:10)"
|
|
117
|
-
const callerShort = extractCallerShort(l.caller);
|
|
118
|
-
if (callerShort) {
|
|
119
|
-
const callerTag = document.createElement('span');
|
|
120
|
-
callerTag.className = 'log-caller-inline';
|
|
121
|
-
callerTag.textContent = callerShort;
|
|
122
|
-
preview.appendChild(callerTag);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
bodyWrap.appendChild(preview);
|
|
126
|
-
|
|
127
|
-
// Full content (hidden by default)
|
|
128
|
-
const full = document.createElement('div');
|
|
129
|
-
full.className = 'log-full';
|
|
130
|
-
full.style.display = 'none';
|
|
131
|
-
full.appendChild(buildLogBody(l));
|
|
132
|
-
if (l.caller) {
|
|
133
|
-
const callerSpan = document.createElement('span');
|
|
134
|
-
callerSpan.className = 'log-caller';
|
|
135
|
-
callerSpan.textContent = l.caller;
|
|
136
|
-
full.appendChild(callerSpan);
|
|
137
|
-
}
|
|
138
|
-
bodyWrap.appendChild(full);
|
|
139
|
-
|
|
140
|
-
// Expand/collapse arrow
|
|
141
|
-
const arrow = document.createElement('span');
|
|
142
|
-
arrow.className = 'log-arrow';
|
|
143
|
-
arrow.textContent = '\u25B6';
|
|
144
|
-
bodyWrap.prepend(arrow);
|
|
145
|
-
|
|
146
|
-
let expanded = false;
|
|
147
|
-
// Only toggle on click, NOT on text selection drag
|
|
148
|
-
let _mouseDownPos = null;
|
|
149
|
-
bodyWrap.addEventListener('mousedown', (e) => {
|
|
150
|
-
_mouseDownPos = { x: e.clientX, y: e.clientY };
|
|
151
|
-
});
|
|
152
|
-
bodyWrap.addEventListener('click', (e) => {
|
|
153
|
-
// Don't toggle if user is selecting text (dragged mouse)
|
|
154
|
-
if (_mouseDownPos) {
|
|
155
|
-
const dx = Math.abs(e.clientX - _mouseDownPos.x);
|
|
156
|
-
const dy = Math.abs(e.clientY - _mouseDownPos.y);
|
|
157
|
-
if (dx > 3 || dy > 3) return; // user dragged to select
|
|
158
|
-
}
|
|
159
|
-
// Don't toggle if there's an active text selection
|
|
160
|
-
const sel = window.getSelection();
|
|
161
|
-
if (sel && sel.toString().length > 0) return;
|
|
162
|
-
// Don't toggle if clicking inside object tree expander
|
|
163
|
-
if (e.target.closest('.ov-header')) return;
|
|
164
|
-
expanded = !expanded;
|
|
165
|
-
if (expanded) {
|
|
166
|
-
preview.style.display = 'none';
|
|
167
|
-
full.style.display = 'block';
|
|
168
|
-
arrow.textContent = '\u25BC';
|
|
169
|
-
arrow.classList.add('expanded');
|
|
170
|
-
} else {
|
|
171
|
-
preview.style.display = '';
|
|
172
|
-
full.style.display = 'none';
|
|
173
|
-
arrow.textContent = '\u25B6';
|
|
174
|
-
arrow.classList.remove('expanded');
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// Right-click → copy options
|
|
179
|
-
div.addEventListener('contextmenu', (e) => {
|
|
180
|
-
e.preventDefault();
|
|
181
|
-
const items = [];
|
|
182
|
-
|
|
183
|
-
// Copy selected text
|
|
184
|
-
const sel = window.getSelection();
|
|
185
|
-
if (sel && sel.toString().length > 0) {
|
|
186
|
-
items.push({ label: 'Copy Selection', action: () => navigator.clipboard.writeText(sel.toString()) });
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Copy full log message
|
|
190
|
-
items.push({ label: 'Copy Message', action: () => {
|
|
191
|
-
navigator.clipboard.writeText(l.message || '');
|
|
192
|
-
}});
|
|
193
|
-
|
|
194
|
-
// Copy as JSON (if structured args exist)
|
|
195
|
-
if (l.args && l.args.length > 0) {
|
|
196
|
-
items.push({ label: 'Copy as JSON', action: () => {
|
|
197
|
-
const json = l.args.map(a => {
|
|
198
|
-
if (a.t === 'object' || a.t === 'array') return JSON.stringify(a.v, null, 2);
|
|
199
|
-
return String(a.v);
|
|
200
|
-
}).join(' ');
|
|
201
|
-
navigator.clipboard.writeText(json);
|
|
202
|
-
}});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Copy caller location
|
|
206
|
-
if (l.caller) {
|
|
207
|
-
items.push({ label: 'Copy Caller', action: () => navigator.clipboard.writeText(l.caller) });
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
showContextMenu(e, items);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
div.appendChild(bodyWrap);
|
|
214
|
-
return div;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Full re-render — only used on filter/level change, NOT on every incoming log
|
|
218
|
-
function renderConsole() {
|
|
219
|
-
const list = $('consoleList');
|
|
220
|
-
const empty = $('consoleEmpty');
|
|
221
|
-
if (!list) return;
|
|
222
|
-
|
|
223
|
-
const { levelFilter } = state.console;
|
|
224
|
-
const visible = state.console.logs.filter(l => {
|
|
225
|
-
if (levelFilter !== 'all' && l.level !== levelFilter) return false;
|
|
226
|
-
if (state.filter && !l.message?.toLowerCase().includes(state.filter)) return false;
|
|
227
|
-
return true;
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
list.querySelectorAll('.log-row').forEach(e => e.remove());
|
|
231
|
-
empty.style.display = visible.length ? 'none' : 'flex';
|
|
232
|
-
|
|
233
|
-
const frag = document.createDocumentFragment();
|
|
234
|
-
visible.forEach(l => frag.appendChild(buildLogRow(l)));
|
|
235
|
-
list.appendChild(frag);
|
|
236
|
-
list.scrollTop = list.scrollHeight;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// ─── Export via window.RNDebug ────────────────────────────────────────────────
|
|
240
|
-
window.RNDebug.initConsolePanel = initConsolePanel;
|
|
241
|
-
window.RNDebug.setConsoleLevel = window.setConsoleLevel;
|
|
242
|
-
window.RNDebug.addConsoleLog = addConsoleLog;
|
|
243
|
-
window.RNDebug.flushConsoleBatch = flushConsoleBatch;
|
|
244
|
-
window.RNDebug.buildLogRow = buildLogRow;
|
|
245
|
-
window.RNDebug.renderConsole = renderConsole;
|
|
246
|
-
// Expose batch state for clearing
|
|
247
|
-
window.RNDebug._getConsolePending = () => _consolePending;
|
|
248
|
-
window.RNDebug._setConsolePending = (val) => { _consolePending = val; };
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const state = window.RNDebug.state;
|
|
4
|
-
const $ = window.RNDebug.$;
|
|
5
|
-
const formatSize = window.RNDebug.formatSize;
|
|
6
|
-
|
|
7
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
8
|
-
// MEMORY PANEL — Heap snapshot summary via Hermes CDP
|
|
9
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
-
function initMemoryPanel() {
|
|
11
|
-
const panel = $('panel-memory');
|
|
12
|
-
panel.innerHTML = `
|
|
13
|
-
<div class="panel-toolbar">
|
|
14
|
-
<span class="panel-label">Memory</span>
|
|
15
|
-
<div class="ml-auto" style="display:flex;gap:6px">
|
|
16
|
-
<button class="tb-btn primary" id="btnHeapSnapshot">Take Heap Snapshot</button>
|
|
17
|
-
</div>
|
|
18
|
-
</div>
|
|
19
|
-
<div class="memory-layout">
|
|
20
|
-
<div class="perf-meters" style="padding:14px">
|
|
21
|
-
<div class="perf-meter">
|
|
22
|
-
<div class="perf-meter-label">JS Heap Used</div>
|
|
23
|
-
<div class="perf-meter-value" id="memHeapUsed">—</div>
|
|
24
|
-
</div>
|
|
25
|
-
<div class="perf-meter">
|
|
26
|
-
<div class="perf-meter-label">JS Heap Total</div>
|
|
27
|
-
<div class="perf-meter-value" id="memHeapTotal">—</div>
|
|
28
|
-
</div>
|
|
29
|
-
<div class="perf-meter">
|
|
30
|
-
<div class="perf-meter-label">Native Memory</div>
|
|
31
|
-
<div class="perf-meter-value" id="memNative">—</div>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
<div class="scroll-area" id="memoryContent">
|
|
35
|
-
<div class="empty-state" id="memoryEmpty">
|
|
36
|
-
<div class="icon" style="font-size:28px;opacity:.2">🧠</div>
|
|
37
|
-
<div class="label">No memory data</div>
|
|
38
|
-
<div class="hint">Click "Take Heap Snapshot" to capture memory usage</div>
|
|
39
|
-
<div class="hint">Requires Hermes CDP connection (press Cmd+D first)</div>
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
</div>`;
|
|
43
|
-
|
|
44
|
-
$('btnHeapSnapshot').addEventListener('click', () => {
|
|
45
|
-
// Request heap snapshot via CDP - this opens the DevTools window
|
|
46
|
-
// which has built-in Memory profiler
|
|
47
|
-
window.electronAPI?.openCDPTarget(null);
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Handle memory events from SDK
|
|
52
|
-
function handleMemoryEvent(event) {
|
|
53
|
-
if (event.heapUsed != null) $('memHeapUsed').textContent = formatSize(event.heapUsed);
|
|
54
|
-
if (event.heapTotal != null) $('memHeapTotal').textContent = formatSize(event.heapTotal);
|
|
55
|
-
if (event.native != null) $('memNative').textContent = formatSize(event.native);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ─── Export via window.RNDebug ────────────────────────────────────────────────
|
|
59
|
-
window.RNDebug.initMemoryPanel = initMemoryPanel;
|
|
60
|
-
window.RNDebug.handleMemoryEvent = handleMemoryEvent;
|