reactoradar 1.5.8 → 1.5.9
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 +187 -34
- package/package.json +1 -1
- package/styles.css +7 -0
package/app.js
CHANGED
|
@@ -67,7 +67,7 @@ function renderJSON(val) {
|
|
|
67
67
|
try {
|
|
68
68
|
const str = typeof val === 'string' ? val : JSON.stringify(val, null, 2);
|
|
69
69
|
return syntaxHighlight(esc(str));
|
|
70
|
-
} catch { return esc(String(val)); }
|
|
70
|
+
} catch { return esc(typeof val === 'object' ? JSON.stringify(val) : String(val)); }
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function tryURL(url) { try { return new URL(url); } catch { return null; } }
|
|
@@ -239,7 +239,7 @@ if (window.electronAPI) {
|
|
|
239
239
|
window.electronAPI.on('clear-all-ui', clearAll);
|
|
240
240
|
|
|
241
241
|
// Cmd+F — focus the search input for the active panel
|
|
242
|
-
|
|
242
|
+
function _handleFind() {
|
|
243
243
|
const searchMap = {
|
|
244
244
|
console: 'consoleSearch',
|
|
245
245
|
network: 'netSearchInput',
|
|
@@ -257,6 +257,14 @@ if (window.electronAPI) {
|
|
|
257
257
|
const bar = $('consoleFindBar');
|
|
258
258
|
if (bar) { bar.style.display = 'flex'; $('consoleFindInput')?.focus(); }
|
|
259
259
|
}
|
|
260
|
+
}
|
|
261
|
+
window.electronAPI.on('focus-search', _handleFind);
|
|
262
|
+
// Direct keyboard fallback — Electron menu accelerators can miss in some contexts
|
|
263
|
+
document.addEventListener('keydown', (e) => {
|
|
264
|
+
if ((e.metaKey || e.ctrlKey) && e.key === 'f') {
|
|
265
|
+
e.preventDefault();
|
|
266
|
+
_handleFind();
|
|
267
|
+
}
|
|
260
268
|
});
|
|
261
269
|
|
|
262
270
|
window.electronAPI.on('app-version', (version) => {
|
|
@@ -688,6 +696,14 @@ function createTreeNode(key, val, startCollapsed) {
|
|
|
688
696
|
return container;
|
|
689
697
|
}
|
|
690
698
|
|
|
699
|
+
function _safeStr(val) {
|
|
700
|
+
if (val === null) return 'null';
|
|
701
|
+
if (val === undefined) return 'undefined';
|
|
702
|
+
if (typeof val === 'string') return val;
|
|
703
|
+
if (typeof val === 'number' || typeof val === 'boolean') return String(val);
|
|
704
|
+
try { return JSON.stringify(val, null, 2); } catch { return String(val); }
|
|
705
|
+
}
|
|
706
|
+
|
|
691
707
|
function createPrimitiveSpan(val) {
|
|
692
708
|
const s = document.createElement('span');
|
|
693
709
|
if (val === null) { s.className = 'ov-null'; s.textContent = 'null'; }
|
|
@@ -695,7 +711,7 @@ function createPrimitiveSpan(val) {
|
|
|
695
711
|
else if (typeof val === 'string') { s.className = 'ov-str'; s.textContent = `"${val}"`; }
|
|
696
712
|
else if (typeof val === 'number') { s.className = 'ov-num'; s.textContent = String(val); }
|
|
697
713
|
else if (typeof val === 'boolean') { s.className = 'ov-bool'; s.textContent = String(val); }
|
|
698
|
-
else { s.textContent =
|
|
714
|
+
else { s.textContent = _safeStr(val); }
|
|
699
715
|
return s;
|
|
700
716
|
}
|
|
701
717
|
|
|
@@ -705,7 +721,7 @@ function renderConsoleArg(arg) {
|
|
|
705
721
|
// Backward compat: raw string
|
|
706
722
|
const s = document.createElement('span');
|
|
707
723
|
s.className = 'ov-str';
|
|
708
|
-
s.textContent =
|
|
724
|
+
s.textContent = _safeStr(arg);
|
|
709
725
|
return s;
|
|
710
726
|
}
|
|
711
727
|
const { t, v } = arg;
|
|
@@ -723,7 +739,7 @@ function renderConsoleArg(arg) {
|
|
|
723
739
|
return createTreeNode(null, v, false);
|
|
724
740
|
}
|
|
725
741
|
const s = document.createElement('span');
|
|
726
|
-
s.textContent =
|
|
742
|
+
s.textContent = _safeStr(v);
|
|
727
743
|
return s;
|
|
728
744
|
}
|
|
729
745
|
|
|
@@ -1972,6 +1988,98 @@ function _findLeafChanges(oldVal, newVal, basePath, maxDepth) {
|
|
|
1972
1988
|
return changes;
|
|
1973
1989
|
}
|
|
1974
1990
|
|
|
1991
|
+
// Create a tree node with changed paths highlighted in a different color
|
|
1992
|
+
function _createHighlightedTree(key, val, changedPaths, currentPath, isOld) {
|
|
1993
|
+
const isArray = Array.isArray(val);
|
|
1994
|
+
const isObj = val !== null && typeof val === 'object';
|
|
1995
|
+
const myPath = key !== null ? (currentPath ? `${currentPath}.${key}` : String(key)) : currentPath;
|
|
1996
|
+
const isChanged = changedPaths.has(myPath);
|
|
1997
|
+
|
|
1998
|
+
if (!isObj) {
|
|
1999
|
+
// Leaf value
|
|
2000
|
+
const row = document.createElement('div');
|
|
2001
|
+
row.className = 'ov-leaf' + (isChanged ? ' rdx-highlight' : '');
|
|
2002
|
+
if (isChanged) row.style.cssText = isOld
|
|
2003
|
+
? 'background:rgba(255,94,114,.12);border-radius:3px;padding:1px 4px;'
|
|
2004
|
+
: 'background:rgba(61,214,140,.12);border-radius:3px;padding:1px 4px;';
|
|
2005
|
+
if (key !== null) {
|
|
2006
|
+
const k = document.createElement('span');
|
|
2007
|
+
k.className = 'ov-key';
|
|
2008
|
+
k.style.color = isChanged ? (isOld ? 'var(--red)' : 'var(--green)') : '';
|
|
2009
|
+
k.textContent = `${key}: `;
|
|
2010
|
+
row.appendChild(k);
|
|
2011
|
+
}
|
|
2012
|
+
const v = document.createElement('span');
|
|
2013
|
+
v.className = 'ov-prim';
|
|
2014
|
+
if (isChanged) v.style.fontWeight = '700';
|
|
2015
|
+
if (val === null) { v.textContent = 'null'; v.style.color = isChanged ? (isOld ? 'var(--red)' : 'var(--green)') : 'var(--text-dim)'; }
|
|
2016
|
+
else if (typeof val === 'string') { v.textContent = `"${val}"`; v.style.color = isChanged ? (isOld ? 'var(--red)' : 'var(--green)') : 'var(--green)'; }
|
|
2017
|
+
else if (typeof val === 'number') { v.textContent = String(val); v.style.color = isChanged ? (isOld ? 'var(--red)' : 'var(--green)') : 'var(--accent2)'; }
|
|
2018
|
+
else if (typeof val === 'boolean') { v.textContent = String(val); v.style.color = isChanged ? (isOld ? 'var(--red)' : 'var(--green)') : 'var(--accent2)'; }
|
|
2019
|
+
else { v.textContent = _safeStr(val); }
|
|
2020
|
+
row.appendChild(v);
|
|
2021
|
+
return row;
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
// Object/Array — check if any descendants changed
|
|
2025
|
+
const hasChangedDescendant = [...changedPaths].some(p => p === myPath || p.startsWith(myPath ? myPath + '.' : ''));
|
|
2026
|
+
const container = document.createElement('div');
|
|
2027
|
+
container.className = 'ov-node';
|
|
2028
|
+
|
|
2029
|
+
const header = document.createElement('div');
|
|
2030
|
+
header.className = 'ov-header';
|
|
2031
|
+
|
|
2032
|
+
const arrow = document.createElement('span');
|
|
2033
|
+
arrow.className = 'ov-arrow';
|
|
2034
|
+
arrow.textContent = '\u25B6';
|
|
2035
|
+
header.appendChild(arrow);
|
|
2036
|
+
|
|
2037
|
+
if (key !== null) {
|
|
2038
|
+
const k = document.createElement('span');
|
|
2039
|
+
k.className = 'ov-key';
|
|
2040
|
+
if (hasChangedDescendant) k.style.color = isOld ? 'var(--red)' : 'var(--green)';
|
|
2041
|
+
k.textContent = `${key}: `;
|
|
2042
|
+
header.appendChild(k);
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
const preview = document.createElement('span');
|
|
2046
|
+
preview.className = 'ov-preview';
|
|
2047
|
+
preview.textContent = isArray ? `Array(${val.length})` : `{${Object.keys(val).length} keys}`;
|
|
2048
|
+
header.appendChild(preview);
|
|
2049
|
+
|
|
2050
|
+
container.appendChild(header);
|
|
2051
|
+
|
|
2052
|
+
const children = document.createElement('div');
|
|
2053
|
+
children.className = 'ov-children';
|
|
2054
|
+
// Auto-expand if this node has changed descendants, otherwise collapse
|
|
2055
|
+
children.style.display = hasChangedDescendant ? 'block' : 'none';
|
|
2056
|
+
if (hasChangedDescendant) { arrow.textContent = '\u25BC'; arrow.classList.add('open'); }
|
|
2057
|
+
|
|
2058
|
+
let populated = false;
|
|
2059
|
+
function populate() {
|
|
2060
|
+
if (populated) return;
|
|
2061
|
+
populated = true;
|
|
2062
|
+
const entries = isArray ? val.map((v, i) => [i, v]) : Object.entries(val);
|
|
2063
|
+
entries.forEach(([k, v]) => {
|
|
2064
|
+
children.appendChild(_createHighlightedTree(k, v, changedPaths, myPath, isOld));
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
// Populate immediately if expanded, otherwise lazy
|
|
2069
|
+
if (hasChangedDescendant) populate();
|
|
2070
|
+
|
|
2071
|
+
header.addEventListener('click', (e) => {
|
|
2072
|
+
e.stopPropagation();
|
|
2073
|
+
const open = children.style.display !== 'none';
|
|
2074
|
+
children.style.display = open ? 'none' : 'block';
|
|
2075
|
+
arrow.textContent = open ? '\u25B6' : '\u25BC';
|
|
2076
|
+
if (!open) populate();
|
|
2077
|
+
});
|
|
2078
|
+
|
|
2079
|
+
container.appendChild(children);
|
|
2080
|
+
return container;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
1975
2083
|
function handleReduxEvent(event) {
|
|
1976
2084
|
if (event.type !== 'redux') return;
|
|
1977
2085
|
const { action, nextState } = event;
|
|
@@ -2004,6 +2112,29 @@ function handleReduxEvent(event) {
|
|
|
2004
2112
|
}
|
|
2005
2113
|
}
|
|
2006
2114
|
|
|
2115
|
+
// Assign a consistent color to each Redux action category (e.g. ANALYTICS, CART, USER)
|
|
2116
|
+
const _reduxCatColors = {};
|
|
2117
|
+
const _reduxColorPalette = [
|
|
2118
|
+
'var(--accent)', // blue
|
|
2119
|
+
'var(--green)', // green
|
|
2120
|
+
'var(--orange)', // orange
|
|
2121
|
+
'var(--accent2)', // purple
|
|
2122
|
+
'#e06c75', // coral
|
|
2123
|
+
'#56b6c2', // teal
|
|
2124
|
+
'#c678dd', // magenta
|
|
2125
|
+
'#d19a66', // gold
|
|
2126
|
+
'#98c379', // lime
|
|
2127
|
+
'#e5c07b', // yellow
|
|
2128
|
+
];
|
|
2129
|
+
let _reduxColorIdx = 0;
|
|
2130
|
+
function _reduxCategoryColor(category) {
|
|
2131
|
+
if (!_reduxCatColors[category]) {
|
|
2132
|
+
_reduxCatColors[category] = _reduxColorPalette[_reduxColorIdx % _reduxColorPalette.length];
|
|
2133
|
+
_reduxColorIdx++;
|
|
2134
|
+
}
|
|
2135
|
+
return _reduxCatColors[category];
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2007
2138
|
function renderRedux() {
|
|
2008
2139
|
const content = $('reduxContent');
|
|
2009
2140
|
const empty = $('reduxEmpty');
|
|
@@ -2031,12 +2162,32 @@ function renderRedux() {
|
|
|
2031
2162
|
const header = document.createElement('div');
|
|
2032
2163
|
header.className = 'rdx-entry-header';
|
|
2033
2164
|
const changesBadge = a.changedKeys?.length ? `<span class="rdx-changes">${a.changedKeys.length} changed</span>` : '';
|
|
2034
|
-
|
|
2165
|
+
// Color-code action type by category prefix (e.g. ANALYTICS/, CART/, USER/)
|
|
2166
|
+
const typeParts = a.type.split('/');
|
|
2167
|
+
let typeHtml;
|
|
2168
|
+
if (typeParts.length >= 2) {
|
|
2169
|
+
const catColor = _reduxCategoryColor(typeParts[0]);
|
|
2170
|
+
typeHtml = `<span class="rdx-type-cat" style="color:${catColor}">${esc(typeParts[0])}/</span><span class="rdx-type-name">${esc(typeParts.slice(1).join('/'))}</span>`;
|
|
2171
|
+
} else {
|
|
2172
|
+
typeHtml = `<span class="rdx-type">${esc(a.type)}</span>`;
|
|
2173
|
+
}
|
|
2174
|
+
header.innerHTML = `<span class="rdx-index">#${a.index}</span>${typeHtml}${changesBadge}<span class="rdx-time">${ts(a.ts)}</span>`;
|
|
2035
2175
|
// Toggle: click to expand, click again to collapse
|
|
2036
2176
|
header.addEventListener('click', () => {
|
|
2037
2177
|
state.redux.selected = isSelected ? -1 : a.index;
|
|
2038
2178
|
renderRedux();
|
|
2039
2179
|
});
|
|
2180
|
+
// Right-click to copy action type
|
|
2181
|
+
header.addEventListener('contextmenu', (e) => {
|
|
2182
|
+
e.preventDefault();
|
|
2183
|
+
e.stopPropagation();
|
|
2184
|
+
showContextMenu(e, [
|
|
2185
|
+
{ label: 'Copy Action Type', action: () => navigator.clipboard.writeText(a.type) },
|
|
2186
|
+
{ label: 'Copy Action Payload', action: () => navigator.clipboard.writeText(JSON.stringify(a.payload, null, 2)) },
|
|
2187
|
+
]);
|
|
2188
|
+
});
|
|
2189
|
+
// Allow text selection on the action type
|
|
2190
|
+
header.style.userSelect = 'text';
|
|
2040
2191
|
entry.appendChild(header);
|
|
2041
2192
|
|
|
2042
2193
|
// Expanded detail — only for explicitly selected action
|
|
@@ -2062,18 +2213,15 @@ function renderRedux() {
|
|
|
2062
2213
|
detail.appendChild(createTreeNode(null, a.payload, false));
|
|
2063
2214
|
}
|
|
2064
2215
|
|
|
2065
|
-
// Store changes — show
|
|
2216
|
+
// Store changes — show full Previous and Current state for each changed key
|
|
2217
|
+
// with changed sub-keys highlighted
|
|
2066
2218
|
const prevS = a.index > 0 ? states[a.index - 1] : null;
|
|
2067
2219
|
const currS = states[a.index];
|
|
2068
2220
|
if (currS && typeof currS === 'object' && a.changedKeys?.length > 0) {
|
|
2069
|
-
const sLabel = document.createElement('div');
|
|
2070
|
-
sLabel.className = 'redux-section-title';
|
|
2071
|
-
sLabel.textContent = 'Store Changes';
|
|
2072
|
-
detail.appendChild(sLabel);
|
|
2073
|
-
|
|
2074
2221
|
a.changedKeys.forEach(key => {
|
|
2075
2222
|
const keyWrap = document.createElement('div');
|
|
2076
2223
|
keyWrap.className = 'rdx-store-diff';
|
|
2224
|
+
|
|
2077
2225
|
const kLabel = document.createElement('div');
|
|
2078
2226
|
kLabel.className = 'rdx-store-key-label';
|
|
2079
2227
|
kLabel.textContent = key;
|
|
@@ -2082,29 +2230,34 @@ function renderRedux() {
|
|
|
2082
2230
|
const oldVal = prevS ? prevS[key] : undefined;
|
|
2083
2231
|
const newVal = currS[key];
|
|
2084
2232
|
|
|
2085
|
-
//
|
|
2086
|
-
const
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
keyWrap.appendChild(
|
|
2233
|
+
// Find which sub-keys changed (for highlighting)
|
|
2234
|
+
const changedPaths = new Set();
|
|
2235
|
+
_findLeafChanges(oldVal, newVal, '').forEach(c => changedPaths.add(c.path));
|
|
2236
|
+
|
|
2237
|
+
// Previous state
|
|
2238
|
+
if (oldVal !== undefined) {
|
|
2239
|
+
const prevLabel = document.createElement('div');
|
|
2240
|
+
prevLabel.className = 'rdx-state-label prev';
|
|
2241
|
+
prevLabel.textContent = '- Previous';
|
|
2242
|
+
keyWrap.appendChild(prevLabel);
|
|
2243
|
+
const prevTree = document.createElement('div');
|
|
2244
|
+
prevTree.className = 'rdx-state-tree prev';
|
|
2245
|
+
prevTree.appendChild(_createHighlightedTree(null, oldVal, changedPaths, '', true));
|
|
2246
|
+
keyWrap.appendChild(prevTree);
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
// Current state
|
|
2250
|
+
if (newVal !== undefined) {
|
|
2251
|
+
const currLabel = document.createElement('div');
|
|
2252
|
+
currLabel.className = 'rdx-state-label curr';
|
|
2253
|
+
currLabel.textContent = '+ Current';
|
|
2254
|
+
keyWrap.appendChild(currLabel);
|
|
2255
|
+
const currTree = document.createElement('div');
|
|
2256
|
+
currTree.className = 'rdx-state-tree curr';
|
|
2257
|
+
currTree.appendChild(_createHighlightedTree(null, newVal, changedPaths, '', false));
|
|
2258
|
+
keyWrap.appendChild(currTree);
|
|
2107
2259
|
}
|
|
2260
|
+
|
|
2108
2261
|
detail.appendChild(keyWrap);
|
|
2109
2262
|
});
|
|
2110
2263
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reactoradar",
|
|
3
3
|
"productName": "ReactoRadar",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.9",
|
|
5
5
|
"description": "macOS debugger for React Native — Console, Sources, Network, Performance, Memory, Redux, AsyncStorage, React tree. Supports RN 0.74+ with Hermes and New Architecture.",
|
|
6
6
|
"main": "main.js",
|
|
7
7
|
"bin": {
|
package/styles.css
CHANGED
|
@@ -968,6 +968,13 @@ body {
|
|
|
968
968
|
.rdx-diff-old { color: var(--red); text-decoration: line-through; opacity: 0.8; word-break: break-all; }
|
|
969
969
|
.rdx-diff-arrow { color: var(--text-dim); font-size: 10px; flex-shrink: 0; }
|
|
970
970
|
.rdx-diff-new { color: var(--green); font-weight: 600; word-break: break-all; }
|
|
971
|
+
.rdx-state-label { font-size: 10px; font-weight: 700; padding: 4px 8px 2px; margin-top: 4px; letter-spacing: 0.5px; }
|
|
972
|
+
.rdx-state-label.prev { color: var(--red); }
|
|
973
|
+
.rdx-state-label.curr { color: var(--green); }
|
|
974
|
+
.rdx-state-tree { padding: 2px 0 4px 4px; border-left: 2px solid var(--border); margin-left: 4px; }
|
|
975
|
+
.rdx-state-tree.prev { border-left-color: rgba(255,94,114,.3); }
|
|
976
|
+
.rdx-state-tree.curr { border-left-color: rgba(61,214,140,.3); }
|
|
977
|
+
.rdx-highlight { font-weight: 600; }
|
|
971
978
|
.rdx-diff-sign { font-weight: 700; font-size: 11px; flex-shrink: 0; width: 14px; text-align: center; }
|
|
972
979
|
.rdx-diff-row.removed .rdx-diff-sign { color: var(--red); }
|
|
973
980
|
.rdx-diff-row.added .rdx-diff-sign { color: var(--green); }
|