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,144 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const state = window.RNDebug.state;
|
|
4
|
-
const $ = window.RNDebug.$;
|
|
5
|
-
|
|
6
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
-
// PERFORMANCE PANEL — FPS, render timing, JS thread
|
|
8
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
-
const perfState = { fps: [], jsThread: [], uiThread: [], recording: false, data: [] };
|
|
10
|
-
|
|
11
|
-
function initPerformancePanel() {
|
|
12
|
-
const panel = $('panel-performance');
|
|
13
|
-
panel.innerHTML = `
|
|
14
|
-
<div class="panel-toolbar">
|
|
15
|
-
<span class="panel-label">Performance</span>
|
|
16
|
-
<div class="ml-auto" style="display:flex;gap:6px">
|
|
17
|
-
<button class="tb-btn" id="btnPerfRecord">Record</button>
|
|
18
|
-
<button class="tb-btn" id="btnPerfClear">Clear</button>
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
<div class="perf-layout">
|
|
22
|
-
<div class="perf-meters">
|
|
23
|
-
<div class="perf-meter">
|
|
24
|
-
<div class="perf-meter-label">FPS</div>
|
|
25
|
-
<div class="perf-meter-value" id="perfFPS">—</div>
|
|
26
|
-
<canvas class="perf-canvas" id="perfFPSCanvas" width="200" height="60"></canvas>
|
|
27
|
-
</div>
|
|
28
|
-
<div class="perf-meter">
|
|
29
|
-
<div class="perf-meter-label">JS Thread</div>
|
|
30
|
-
<div class="perf-meter-value" id="perfJS">—</div>
|
|
31
|
-
<canvas class="perf-canvas" id="perfJSCanvas" width="200" height="60"></canvas>
|
|
32
|
-
</div>
|
|
33
|
-
<div class="perf-meter">
|
|
34
|
-
<div class="perf-meter-label">UI Thread</div>
|
|
35
|
-
<div class="perf-meter-value" id="perfUI">—</div>
|
|
36
|
-
<canvas class="perf-canvas" id="perfUICanvas" width="200" height="60"></canvas>
|
|
37
|
-
</div>
|
|
38
|
-
</div>
|
|
39
|
-
<div class="scroll-area perf-timeline" id="perfTimeline">
|
|
40
|
-
<div class="empty-state" id="perfEmpty">
|
|
41
|
-
<div class="icon" style="font-size:28px;opacity:.2">📊</div>
|
|
42
|
-
<div class="label">No performance data</div>
|
|
43
|
-
<div class="hint">Click "Record" to start capturing performance metrics</div>
|
|
44
|
-
<div class="hint">The SDK sends FPS + thread usage automatically when connected</div>
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
</div>`;
|
|
48
|
-
|
|
49
|
-
$('btnPerfRecord').addEventListener('click', () => {
|
|
50
|
-
perfState.recording = !perfState.recording;
|
|
51
|
-
$('btnPerfRecord').textContent = perfState.recording ? 'Stop' : 'Record';
|
|
52
|
-
$('btnPerfRecord').classList.toggle('primary', perfState.recording);
|
|
53
|
-
if (perfState.recording) {
|
|
54
|
-
// Tell SDK to start sending perf data
|
|
55
|
-
window.electronAPI?.setNetworkCapture(true); // reuse channel
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
$('btnPerfClear').addEventListener('click', () => {
|
|
60
|
-
perfState.fps = [];
|
|
61
|
-
perfState.jsThread = [];
|
|
62
|
-
perfState.uiThread = [];
|
|
63
|
-
perfState.data = [];
|
|
64
|
-
$('perfFPS').textContent = '—';
|
|
65
|
-
$('perfJS').textContent = '—';
|
|
66
|
-
$('perfUI').textContent = '—';
|
|
67
|
-
clearPerfCanvas('perfFPSCanvas');
|
|
68
|
-
clearPerfCanvas('perfJSCanvas');
|
|
69
|
-
clearPerfCanvas('perfUICanvas');
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function clearPerfCanvas(id) {
|
|
74
|
-
const canvas = $(id);
|
|
75
|
-
if (!canvas) return;
|
|
76
|
-
const ctx = canvas.getContext('2d');
|
|
77
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function drawPerfGraph(canvasId, data, maxVal, color) {
|
|
81
|
-
const canvas = $(canvasId);
|
|
82
|
-
if (!canvas || !data.length) return;
|
|
83
|
-
const ctx = canvas.getContext('2d');
|
|
84
|
-
const w = canvas.width, h = canvas.height;
|
|
85
|
-
ctx.clearRect(0, 0, w, h);
|
|
86
|
-
|
|
87
|
-
// Grid lines
|
|
88
|
-
ctx.strokeStyle = 'rgba(255,255,255,0.05)';
|
|
89
|
-
ctx.lineWidth = 1;
|
|
90
|
-
for (let y = 0; y < h; y += h/4) {
|
|
91
|
-
ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(w, y); ctx.stroke();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Data line
|
|
95
|
-
ctx.strokeStyle = color;
|
|
96
|
-
ctx.lineWidth = 1.5;
|
|
97
|
-
ctx.beginPath();
|
|
98
|
-
const step = w / Math.max(data.length - 1, 1);
|
|
99
|
-
data.forEach((v, i) => {
|
|
100
|
-
const x = i * step;
|
|
101
|
-
const y = h - (v / maxVal) * h;
|
|
102
|
-
if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
|
|
103
|
-
});
|
|
104
|
-
ctx.stroke();
|
|
105
|
-
|
|
106
|
-
// Fill under
|
|
107
|
-
ctx.lineTo(w, h);
|
|
108
|
-
ctx.lineTo(0, h);
|
|
109
|
-
ctx.closePath();
|
|
110
|
-
ctx.fillStyle = color.replace('1)', '0.1)');
|
|
111
|
-
ctx.fill();
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Handle performance events from SDK (always updates meters, graphs only when recording)
|
|
115
|
-
function handlePerfEvent(event) {
|
|
116
|
-
if (event.fps != null) {
|
|
117
|
-
perfState.fps.push(event.fps);
|
|
118
|
-
if (perfState.fps.length > 100) perfState.fps.shift();
|
|
119
|
-
const fpsEl = $('perfFPS');
|
|
120
|
-
if (fpsEl) fpsEl.textContent = event.fps + ' fps';
|
|
121
|
-
drawPerfGraph('perfFPSCanvas', perfState.fps, 60, 'rgba(61,214,140,1)');
|
|
122
|
-
}
|
|
123
|
-
if (event.jsThread != null) {
|
|
124
|
-
perfState.jsThread.push(event.jsThread);
|
|
125
|
-
if (perfState.jsThread.length > 100) perfState.jsThread.shift();
|
|
126
|
-
const jsEl = $('perfJS');
|
|
127
|
-
if (jsEl) jsEl.textContent = event.jsThread.toFixed(1) + 'ms';
|
|
128
|
-
drawPerfGraph('perfJSCanvas', perfState.jsThread, 32, 'rgba(79,172,255,1)');
|
|
129
|
-
}
|
|
130
|
-
if (event.uiThread != null) {
|
|
131
|
-
perfState.uiThread.push(event.uiThread);
|
|
132
|
-
if (perfState.uiThread.length > 100) perfState.uiThread.shift();
|
|
133
|
-
const uiEl = $('perfUI');
|
|
134
|
-
if (uiEl) uiEl.textContent = event.uiThread.toFixed(1) + 'ms';
|
|
135
|
-
drawPerfGraph('perfUICanvas', perfState.uiThread, 32, 'rgba(155,127,255,1)');
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// ─── Export via window.RNDebug ────────────────────────────────────────────────
|
|
140
|
-
window.RNDebug.perfState = perfState;
|
|
141
|
-
window.RNDebug.initPerformancePanel = initPerformancePanel;
|
|
142
|
-
window.RNDebug.clearPerfCanvas = clearPerfCanvas;
|
|
143
|
-
window.RNDebug.drawPerfGraph = drawPerfGraph;
|
|
144
|
-
window.RNDebug.handlePerfEvent = handlePerfEvent;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const state = window.RNDebug.state;
|
|
4
|
-
const $ = window.RNDebug.$;
|
|
5
|
-
|
|
6
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
-
// REACT TREE PANEL
|
|
8
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
-
function initReactPanel() {
|
|
10
|
-
const panel = $('panel-react');
|
|
11
|
-
panel.innerHTML = `
|
|
12
|
-
<div class="panel-toolbar">
|
|
13
|
-
<span class="panel-label">React Tree</span>
|
|
14
|
-
</div>
|
|
15
|
-
<div class="react-panel-inner">
|
|
16
|
-
<div class="react-connect-hint" id="reactHint">
|
|
17
|
-
<div class="icon" style="font-size:40px;opacity:.2">⚛️</div>
|
|
18
|
-
<div class="label">React DevTools</div>
|
|
19
|
-
<div class="hint">Launches as a separate window connected to your app</div>
|
|
20
|
-
<div class="hint">React Native auto-connects on port <code>8097</code> in dev mode</div>
|
|
21
|
-
<button class="btn-launch" id="btnReactDT">Open React DevTools ↗</button>
|
|
22
|
-
</div>
|
|
23
|
-
</div>`;
|
|
24
|
-
|
|
25
|
-
$('btnReactDT').addEventListener('click', () => {
|
|
26
|
-
window.electronAPI?.openReactDevTools();
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// ─── Export via window.RNDebug ────────────────────────────────────────────────
|
|
31
|
-
window.RNDebug.initReactPanel = initReactPanel;
|
|
@@ -1,159 +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 renderJSON = window.RNDebug.renderJSON;
|
|
8
|
-
|
|
9
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
-
// REDUX PANEL
|
|
11
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
-
function initReduxPanel() {
|
|
13
|
-
const panel = $('panel-redux');
|
|
14
|
-
panel.innerHTML = `
|
|
15
|
-
<div class="panel-toolbar">
|
|
16
|
-
<span class="panel-label">Redux</span>
|
|
17
|
-
<span class="badge" id="rBadge">0</span>
|
|
18
|
-
<div class="tab-row ml-auto">
|
|
19
|
-
<button class="tab" onclick="reduxJumpTo(-1)">⏮ Reset</button>
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
22
|
-
<div class="redux-layout">
|
|
23
|
-
<div class="redux-actions">
|
|
24
|
-
<div class="panel-toolbar" style="height:32px">
|
|
25
|
-
<span style="font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:1px">Actions</span>
|
|
26
|
-
</div>
|
|
27
|
-
<div class="scroll-area redux-actions-list" id="reduxActionList">
|
|
28
|
-
<div class="empty-state" id="reduxEmpty">
|
|
29
|
-
<div class="icon">🔲</div>
|
|
30
|
-
<div class="label">No actions dispatched</div>
|
|
31
|
-
<div class="hint">Connect Redux store to RNDebugPlugin</div>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
<div class="time-travel-bar">
|
|
35
|
-
<button class="tt-btn" onclick="reduxJumpTo(state.redux.selected-1)">◀ Prev</button>
|
|
36
|
-
<button class="tt-btn" onclick="reduxJumpTo(state.redux.selected+1)">Next ▶</button>
|
|
37
|
-
<span class="tt-label" id="ttLabel">— / —</span>
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|
|
40
|
-
<div class="redux-state-view">
|
|
41
|
-
<div class="redux-state-tabs">
|
|
42
|
-
<button class="tab active" onclick="setReduxTab('state',this)">State</button>
|
|
43
|
-
<button class="tab" onclick="setReduxTab('diff',this)">Diff</button>
|
|
44
|
-
<button class="tab" onclick="setReduxTab('action',this)">Action</button>
|
|
45
|
-
</div>
|
|
46
|
-
<div class="redux-state-body" id="reduxStateBody">
|
|
47
|
-
<span style="color:var(--text-dim)">Select an action to inspect state</span>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
</div>`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
window.setReduxTab = (tab, btn) => {
|
|
54
|
-
state.redux.stateTab = tab;
|
|
55
|
-
document.querySelectorAll('#panel-redux .redux-state-tabs .tab').forEach(b=>b.classList.remove('active'));
|
|
56
|
-
btn.classList.add('active');
|
|
57
|
-
renderReduxState();
|
|
58
|
-
};
|
|
59
|
-
window.reduxJumpTo = idx => {
|
|
60
|
-
const { actions } = state.redux;
|
|
61
|
-
if (!actions.length) return;
|
|
62
|
-
idx = Math.max(0, Math.min(actions.length - 1, idx));
|
|
63
|
-
state.redux.selected = idx;
|
|
64
|
-
renderReduxActions();
|
|
65
|
-
renderReduxState();
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
function handleReduxEvent(event) {
|
|
69
|
-
if (event.type !== 'redux') return;
|
|
70
|
-
const { action, nextState } = event;
|
|
71
|
-
const idx = state.redux.actions.length;
|
|
72
|
-
state.redux.actions.push({ type: action?.type || '?', payload: action, ts: event.ts, index: idx });
|
|
73
|
-
state.redux.states.push(nextState);
|
|
74
|
-
state.redux.selected = idx;
|
|
75
|
-
$('rBadge').textContent = state.redux.actions.length;
|
|
76
|
-
renderRedux();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function renderRedux() {
|
|
80
|
-
renderReduxActions();
|
|
81
|
-
renderReduxState();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function renderReduxActions() {
|
|
85
|
-
const list = $('reduxActionList');
|
|
86
|
-
const empty = $('reduxEmpty');
|
|
87
|
-
if (!list) return;
|
|
88
|
-
|
|
89
|
-
const { actions, selected } = state.redux;
|
|
90
|
-
empty.style.display = actions.length ? 'none' : 'flex';
|
|
91
|
-
list.querySelectorAll('.action-row').forEach(e => e.remove());
|
|
92
|
-
|
|
93
|
-
const frag = document.createDocumentFragment();
|
|
94
|
-
actions.forEach((a, i) => {
|
|
95
|
-
const div = document.createElement('div');
|
|
96
|
-
div.className = 'action-row entry' + (i === selected ? ' selected' : '');
|
|
97
|
-
div.innerHTML = `
|
|
98
|
-
<span class="action-index">#${i}</span>
|
|
99
|
-
<span class="action-type">${esc(a.type)}</span>
|
|
100
|
-
<span class="action-time-badge">${ts(a.ts)}</span>`;
|
|
101
|
-
div.onclick = () => { state.redux.selected = i; renderReduxActions(); renderReduxState(); };
|
|
102
|
-
frag.appendChild(div);
|
|
103
|
-
});
|
|
104
|
-
list.appendChild(frag);
|
|
105
|
-
|
|
106
|
-
// scroll selected into view
|
|
107
|
-
const rows = list.querySelectorAll('.action-row');
|
|
108
|
-
rows[selected]?.scrollIntoView({ block: 'nearest' });
|
|
109
|
-
|
|
110
|
-
const ttLabel = $('ttLabel');
|
|
111
|
-
if (ttLabel) ttLabel.textContent = `${selected + 1} / ${actions.length}`;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function renderReduxState() {
|
|
115
|
-
const body = $('reduxStateBody');
|
|
116
|
-
if (!body) return;
|
|
117
|
-
|
|
118
|
-
const { selected, states, actions, stateTab } = state.redux;
|
|
119
|
-
if (selected < 0 || !states.length) {
|
|
120
|
-
body.innerHTML = '<span style="color:var(--text-dim)">Select an action to inspect state</span>';
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (stateTab === 'state') {
|
|
125
|
-
body.innerHTML = renderJSON(states[selected]);
|
|
126
|
-
} else if (stateTab === 'action') {
|
|
127
|
-
// Show previous, current, next actions
|
|
128
|
-
let html = '';
|
|
129
|
-
if (selected > 0) {
|
|
130
|
-
html += `<div class="redux-action-ctx"><span class="redux-ctx-label prev">Previous</span><span class="redux-ctx-type">${esc(actions[selected-1]?.type)}</span></div>`;
|
|
131
|
-
html += `<div class="redux-action-detail prev">${renderJSON(actions[selected-1]?.payload)}</div>`;
|
|
132
|
-
}
|
|
133
|
-
html += `<div class="redux-action-ctx"><span class="redux-ctx-label current">Current</span><span class="redux-ctx-type">${esc(actions[selected]?.type)}</span></div>`;
|
|
134
|
-
html += `<div class="redux-action-detail current">${renderJSON(actions[selected]?.payload)}</div>`;
|
|
135
|
-
if (selected < actions.length - 1) {
|
|
136
|
-
html += `<div class="redux-action-ctx"><span class="redux-ctx-label next">Next</span><span class="redux-ctx-type">${esc(actions[selected+1]?.type)}</span></div>`;
|
|
137
|
-
html += `<div class="redux-action-detail next">${renderJSON(actions[selected+1]?.payload)}</div>`;
|
|
138
|
-
}
|
|
139
|
-
body.innerHTML = html;
|
|
140
|
-
} else if (stateTab === 'diff') {
|
|
141
|
-
if (selected === 0) { body.innerHTML = '<span style="color:var(--text-dim)">No previous state to diff</span>'; return; }
|
|
142
|
-
const prev = JSON.stringify(states[selected-1], null, 2).split('\n');
|
|
143
|
-
const curr = JSON.stringify(states[selected], null, 2).split('\n');
|
|
144
|
-
const diffLines = curr.map((line, i) => {
|
|
145
|
-
if (prev[i] !== line) return `<span style="color:var(--green);background:rgba(61,214,140,.06);display:block">${esc(line)}</span>`;
|
|
146
|
-
return `<span style="color:var(--text-dim)">${esc(line)}</span>`;
|
|
147
|
-
});
|
|
148
|
-
body.innerHTML = diffLines.join('');
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ─── Export via window.RNDebug ────────────────────────────────────────────────
|
|
153
|
-
window.RNDebug.initReduxPanel = initReduxPanel;
|
|
154
|
-
window.RNDebug.setReduxTab = window.setReduxTab;
|
|
155
|
-
window.RNDebug.reduxJumpTo = window.reduxJumpTo;
|
|
156
|
-
window.RNDebug.handleReduxEvent = handleReduxEvent;
|
|
157
|
-
window.RNDebug.renderRedux = renderRedux;
|
|
158
|
-
window.RNDebug.renderReduxActions = renderReduxActions;
|
|
159
|
-
window.RNDebug.renderReduxState = renderReduxState;
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const state = window.RNDebug.state;
|
|
4
|
-
const $ = window.RNDebug.$;
|
|
5
|
-
|
|
6
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
-
// SETTINGS PANEL
|
|
8
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
-
function getStoredTheme() {
|
|
10
|
-
try { return localStorage.getItem('rn-debug-theme') || 'dark'; } catch { return 'dark'; }
|
|
11
|
-
}
|
|
12
|
-
function setStoredTheme(t) {
|
|
13
|
-
try { localStorage.setItem('rn-debug-theme', t); } catch {}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function applyTheme(theme) {
|
|
17
|
-
document.documentElement.setAttribute('data-theme', theme);
|
|
18
|
-
// Tell main process so it can update nativeTheme + window backgroundColor
|
|
19
|
-
window.electronAPI?.setTheme(theme);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function initSettingsPanel() {
|
|
23
|
-
const panel = $('panel-settings');
|
|
24
|
-
const current = getStoredTheme();
|
|
25
|
-
panel.innerHTML = `
|
|
26
|
-
<div class="panel-toolbar">
|
|
27
|
-
<span class="panel-label">Settings</span>
|
|
28
|
-
</div>
|
|
29
|
-
<div class="scroll-area">
|
|
30
|
-
<div class="settings-content">
|
|
31
|
-
<div class="settings-section">
|
|
32
|
-
<div class="settings-section-title">Appearance</div>
|
|
33
|
-
<div class="settings-row">
|
|
34
|
-
<div>
|
|
35
|
-
<div class="settings-label">Theme</div>
|
|
36
|
-
<div class="settings-hint">Switch between dark and light mode</div>
|
|
37
|
-
</div>
|
|
38
|
-
<div class="theme-switcher" id="themeSwitcher">
|
|
39
|
-
<button class="theme-option${current==='dark' ?' active':''}" data-theme="dark">Dark</button>
|
|
40
|
-
<button class="theme-option${current==='light' ?' active':''}" data-theme="light">Light</button>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
<div class="settings-section">
|
|
45
|
-
<div class="settings-section-title">Connection</div>
|
|
46
|
-
<div class="settings-row">
|
|
47
|
-
<div>
|
|
48
|
-
<div class="settings-label">Bridge Ports</div>
|
|
49
|
-
<div class="settings-hint">Redux :9090 · Storage :9091 · Network :9092 · React DT :8097</div>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
<div class="settings-row">
|
|
53
|
-
<div>
|
|
54
|
-
<div class="settings-label">Metro Bundler</div>
|
|
55
|
-
<div class="settings-hint">CDP target discovery on :8081</div>
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
<div class="settings-section">
|
|
60
|
-
<div class="settings-section-title">Keyboard Shortcuts</div>
|
|
61
|
-
<div class="settings-row">
|
|
62
|
-
<div class="settings-label">Clear All</div>
|
|
63
|
-
<div class="settings-hint" style="font-size:11px;color:var(--text-mid)">⌘K</div>
|
|
64
|
-
</div>
|
|
65
|
-
<div class="settings-row">
|
|
66
|
-
<div class="settings-label">Open JS Debugger</div>
|
|
67
|
-
<div class="settings-hint" style="font-size:11px;color:var(--text-mid)">⌘D</div>
|
|
68
|
-
</div>
|
|
69
|
-
<div class="settings-row">
|
|
70
|
-
<div class="settings-label">Open React DevTools</div>
|
|
71
|
-
<div class="settings-hint" style="font-size:11px;color:var(--text-mid)">⌘R</div>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
</div>`;
|
|
76
|
-
|
|
77
|
-
// Theme switcher clicks
|
|
78
|
-
$('themeSwitcher').addEventListener('click', (e) => {
|
|
79
|
-
const btn = e.target.closest('.theme-option');
|
|
80
|
-
if (!btn) return;
|
|
81
|
-
const theme = btn.dataset.theme;
|
|
82
|
-
document.querySelectorAll('#themeSwitcher .theme-option').forEach(b => b.classList.remove('active'));
|
|
83
|
-
btn.classList.add('active');
|
|
84
|
-
setStoredTheme(theme);
|
|
85
|
-
applyTheme(theme);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ─── Export via window.RNDebug ────────────────────────────────────────────────
|
|
90
|
-
window.RNDebug.getStoredTheme = getStoredTheme;
|
|
91
|
-
window.RNDebug.setStoredTheme = setStoredTheme;
|
|
92
|
-
window.RNDebug.applyTheme = applyTheme;
|
|
93
|
-
window.RNDebug.initSettingsPanel = initSettingsPanel;
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const state = window.RNDebug.state;
|
|
4
|
-
const $ = window.RNDebug.$;
|
|
5
|
-
const esc = window.RNDebug.esc;
|
|
6
|
-
const syntaxHighlight = window.RNDebug.syntaxHighlight;
|
|
7
|
-
|
|
8
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
-
// SOURCES PANEL — CDP-based file browser + breakpoints
|
|
10
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
-
function initSourcesPanel() {
|
|
12
|
-
const panel = $('panel-sources');
|
|
13
|
-
panel.innerHTML = `
|
|
14
|
-
<div class="panel-toolbar">
|
|
15
|
-
<span class="panel-label">Sources</span>
|
|
16
|
-
<div class="ml-auto" style="display:flex;gap:6px">
|
|
17
|
-
<button class="tb-btn" id="btnOpenSourcesExt" title="Open in separate DevTools window">Breakpoints ↗</button>
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
<div class="sources-layout">
|
|
21
|
-
<div class="sources-sidebar" id="sourcesSidebar">
|
|
22
|
-
<div class="panel-toolbar" style="height:32px">
|
|
23
|
-
<input id="sourcesSearch" class="net-search-input" style="width:100%" placeholder="Search files..." />
|
|
24
|
-
</div>
|
|
25
|
-
<div class="scroll-area sources-file-list" id="sourcesFileList">
|
|
26
|
-
<div class="empty-state" id="sourcesEmpty">
|
|
27
|
-
<div class="icon" style="font-size:28px;opacity:.2"></></div>
|
|
28
|
-
<div class="label">Waiting for Metro...</div>
|
|
29
|
-
<div class="hint">Source files will load when Metro is running</div>
|
|
30
|
-
</div>
|
|
31
|
-
</div>
|
|
32
|
-
</div>
|
|
33
|
-
<div class="sources-editor" id="sourcesEditor">
|
|
34
|
-
<div class="panel-toolbar" style="height:32px">
|
|
35
|
-
<span id="sourcesFileName" style="font-size:10px;color:var(--accent)"></span>
|
|
36
|
-
<span id="sourcesLineInfo" style="font-size:10px;color:var(--text-dim);margin-left:auto"></span>
|
|
37
|
-
</div>
|
|
38
|
-
<div class="scroll-area sources-code" id="sourcesCode">
|
|
39
|
-
<span style="color:var(--text-dim);padding:20px;display:block">Select a file to view its source</span>
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
</div>`;
|
|
43
|
-
|
|
44
|
-
// Open breakpoints in a separate DevTools CDP window (for breakpoints/stepping)
|
|
45
|
-
$('btnOpenSourcesExt').addEventListener('click', () => {
|
|
46
|
-
window.electronAPI?.openCDPTarget(null);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// Search filter for file list
|
|
50
|
-
$('sourcesSearch').addEventListener('input', (e) => {
|
|
51
|
-
const term = e.target.value.toLowerCase().trim();
|
|
52
|
-
document.querySelectorAll('#sourcesFileList .source-file-row').forEach(row => {
|
|
53
|
-
const name = row.dataset.file || '';
|
|
54
|
-
row.style.display = (!term || name.toLowerCase().includes(term)) ? '' : 'none';
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// Fetch the source map / bundle modules list from Metro
|
|
59
|
-
fetchSourceFileList();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function fetchSourceFileList() {
|
|
63
|
-
// Metro serves source maps at http://localhost:8081/index.map
|
|
64
|
-
// We fetch the bundle source to extract module file paths
|
|
65
|
-
fetch('http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=true&runModule=false')
|
|
66
|
-
.then(r => r.text())
|
|
67
|
-
.then(text => {
|
|
68
|
-
// Extract file paths from __d(function(...), id, deps, "path") patterns
|
|
69
|
-
const fileSet = new Set();
|
|
70
|
-
const re = /__d\(function[^)]*\)[\s\S]*?,\s*\d+\s*,\s*\[[\d,\s]*\]\s*,\s*"([^"]+)"/g;
|
|
71
|
-
let m;
|
|
72
|
-
while ((m = re.exec(text)) !== null) {
|
|
73
|
-
fileSet.add(m[1]);
|
|
74
|
-
}
|
|
75
|
-
// Fallback: try to get paths from source map
|
|
76
|
-
if (fileSet.size === 0) {
|
|
77
|
-
// Try source map approach
|
|
78
|
-
fetch('http://localhost:8081/index.map?platform=ios&dev=true')
|
|
79
|
-
.then(r => r.json())
|
|
80
|
-
.then(map => {
|
|
81
|
-
if (map.sources) {
|
|
82
|
-
map.sources.forEach(s => fileSet.add(s));
|
|
83
|
-
renderSourceFileList(Array.from(fileSet));
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
.catch(() => {});
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
renderSourceFileList(Array.from(fileSet));
|
|
90
|
-
})
|
|
91
|
-
.catch(() => {
|
|
92
|
-
// Metro not running or bundle not ready yet — retry after 5s
|
|
93
|
-
setTimeout(fetchSourceFileList, 5000);
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function renderSourceFileList(files) {
|
|
98
|
-
const list = $('sourcesFileList');
|
|
99
|
-
const empty = $('sourcesEmpty');
|
|
100
|
-
if (!list) return;
|
|
101
|
-
if (!files.length) return;
|
|
102
|
-
if (empty) empty.style.display = 'none';
|
|
103
|
-
list.querySelectorAll('.source-file-row').forEach(e => e.remove());
|
|
104
|
-
|
|
105
|
-
// Sort: project files first, then node_modules
|
|
106
|
-
const sorted = files.sort((a, b) => {
|
|
107
|
-
const aIsNM = a.includes('node_modules');
|
|
108
|
-
const bIsNM = b.includes('node_modules');
|
|
109
|
-
if (aIsNM !== bIsNM) return aIsNM ? 1 : -1;
|
|
110
|
-
return a.localeCompare(b);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const frag = document.createDocumentFragment();
|
|
114
|
-
sorted.forEach(filepath => {
|
|
115
|
-
const row = document.createElement('div');
|
|
116
|
-
row.className = 'source-file-row';
|
|
117
|
-
row.dataset.file = filepath;
|
|
118
|
-
const filename = filepath.split('/').pop();
|
|
119
|
-
const dir = filepath.replace(filename, '');
|
|
120
|
-
const isNM = filepath.includes('node_modules');
|
|
121
|
-
row.innerHTML = `<span style="color:${isNM ? 'var(--text-dim)' : 'var(--accent)'};font-size:11px">${esc(filename)}</span>
|
|
122
|
-
<span style="color:var(--text-dim);font-size:9px;display:block;margin-top:1px;opacity:0.7">${esc(dir)}</span>`;
|
|
123
|
-
row.addEventListener('click', () => loadSourceFile(filepath));
|
|
124
|
-
frag.appendChild(row);
|
|
125
|
-
});
|
|
126
|
-
list.appendChild(frag);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function loadSourceFile(filepath) {
|
|
130
|
-
const codeEl = $('sourcesCode');
|
|
131
|
-
const nameEl = $('sourcesFileName');
|
|
132
|
-
const lineEl = $('sourcesLineInfo');
|
|
133
|
-
if (!codeEl) return;
|
|
134
|
-
if (nameEl) nameEl.textContent = filepath;
|
|
135
|
-
codeEl.innerHTML = '<span style="color:var(--text-dim)">Loading...</span>';
|
|
136
|
-
|
|
137
|
-
// Fetch the individual module source from Metro
|
|
138
|
-
// Metro serves individual files at their path relative to project root
|
|
139
|
-
const url = `http://localhost:8081/${filepath}?platform=ios&dev=true`;
|
|
140
|
-
fetch(url)
|
|
141
|
-
.then(r => r.text())
|
|
142
|
-
.then(source => {
|
|
143
|
-
// Render with line numbers
|
|
144
|
-
const lines = source.split('\n');
|
|
145
|
-
if (lineEl) lineEl.textContent = `${lines.length} lines`;
|
|
146
|
-
codeEl.innerHTML = '';
|
|
147
|
-
const pre = document.createElement('pre');
|
|
148
|
-
pre.className = 'source-pre';
|
|
149
|
-
lines.forEach((line, i) => {
|
|
150
|
-
const lineDiv = document.createElement('div');
|
|
151
|
-
lineDiv.className = 'source-line';
|
|
152
|
-
lineDiv.innerHTML = `<span class="source-line-num">${i + 1}</span><span class="source-line-code">${syntaxHighlight(esc(line))}</span>`;
|
|
153
|
-
pre.appendChild(lineDiv);
|
|
154
|
-
});
|
|
155
|
-
codeEl.appendChild(pre);
|
|
156
|
-
})
|
|
157
|
-
.catch(() => {
|
|
158
|
-
codeEl.innerHTML = `<span style="color:var(--text-dim);padding:20px;display:block">Could not load source for: ${esc(filepath)}</span>`;
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Called from cdp-targets IPC handler (no longer opens external window)
|
|
163
|
-
|
|
164
|
-
// Called from cdp-targets IPC handler (shared, no duplicate registration)
|
|
165
|
-
function updateSourcesPanel(targets) {
|
|
166
|
-
const list = $('sourcesFileList');
|
|
167
|
-
const empty = $('sourcesEmpty');
|
|
168
|
-
if (!list) return;
|
|
169
|
-
if (!targets?.length) { if (empty) empty.style.display = 'flex'; return; }
|
|
170
|
-
if (empty) empty.style.display = 'none';
|
|
171
|
-
list.querySelectorAll('.source-file-row').forEach(e => e.remove());
|
|
172
|
-
targets.forEach(t => {
|
|
173
|
-
const row = document.createElement('div');
|
|
174
|
-
row.className = 'source-file-row';
|
|
175
|
-
row.innerHTML = `<span style="color:var(--accent);font-size:11px">${esc(t.title || 'Unknown')}</span>
|
|
176
|
-
<span style="color:var(--text-dim);font-size:9px;display:block;margin-top:2px">${esc(t.description || '')}</span>`;
|
|
177
|
-
row.addEventListener('click', () => {
|
|
178
|
-
window.electronAPI?.openCDPTarget(t.webSocketDebuggerUrl);
|
|
179
|
-
});
|
|
180
|
-
list.appendChild(row);
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ─── Export via window.RNDebug ────────────────────────────────────────────────
|
|
185
|
-
window.RNDebug.initSourcesPanel = initSourcesPanel;
|
|
186
|
-
window.RNDebug.fetchSourceFileList = fetchSourceFileList;
|
|
187
|
-
window.RNDebug.renderSourceFileList = renderSourceFileList;
|
|
188
|
-
window.RNDebug.loadSourceFile = loadSourceFile;
|
|
189
|
-
window.RNDebug.updateSourcesPanel = updateSourcesPanel;
|