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.
@@ -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;
@@ -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;