react-native-debug-toolkit 3.1.2 → 3.1.3
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/node/daemon/src/console/console.html +230 -153
- package/package.json +1 -1
|
@@ -179,12 +179,6 @@ header h1 span{color:var(--text3);font-weight:400}
|
|
|
179
179
|
font-size:12px;color:var(--text2);display:flex;align-items:center;gap:6px;
|
|
180
180
|
font-family:var(--font-mono);cursor:pointer;
|
|
181
181
|
}
|
|
182
|
-
.toolbar input[type=number]{
|
|
183
|
-
width:56px;padding:4px 8px;
|
|
184
|
-
background:var(--surface);border:1px solid var(--border2);border-radius:4px;
|
|
185
|
-
color:var(--text);font-size:11px;font-family:var(--font-mono);
|
|
186
|
-
}
|
|
187
|
-
.toolbar input[type=number]:focus{outline:none;border-color:var(--cyan)}
|
|
188
182
|
.toggle{
|
|
189
183
|
position:relative;width:32px;height:18px;
|
|
190
184
|
background:var(--surface3);border-radius:9px;cursor:pointer;
|
|
@@ -227,6 +221,26 @@ header h1 span{color:var(--text3);font-weight:400}
|
|
|
227
221
|
background:var(--surface2);border:1px solid var(--border2);
|
|
228
222
|
padding:1px 4px;border-radius:2px;letter-spacing:.02em;
|
|
229
223
|
}
|
|
224
|
+
.pager{
|
|
225
|
+
display:flex;align-items:center;justify-content:flex-end;gap:8px;
|
|
226
|
+
flex-wrap:wrap;color:var(--text3);font-family:var(--font-mono);font-size:11px;
|
|
227
|
+
}
|
|
228
|
+
.toolbar .pager{margin-left:auto}
|
|
229
|
+
.pager-bottom{margin-top:14px}
|
|
230
|
+
.pager-info{white-space:nowrap}
|
|
231
|
+
.page-btn{
|
|
232
|
+
padding:4px 9px;border:1px solid var(--border2);border-radius:4px;
|
|
233
|
+
background:var(--surface);color:var(--text2);font-family:var(--font-mono);
|
|
234
|
+
font-size:11px;cursor:pointer;
|
|
235
|
+
}
|
|
236
|
+
.page-btn:hover:not(:disabled){color:var(--cyan);border-color:var(--cyan-mid)}
|
|
237
|
+
.page-btn:disabled{opacity:.4;cursor:not-allowed}
|
|
238
|
+
.live-notice{
|
|
239
|
+
display:none;padding:4px 9px;border:1px solid var(--cyan-mid);border-radius:4px;
|
|
240
|
+
background:var(--cyan-dim);color:var(--cyan);font-family:var(--font-mono);
|
|
241
|
+
font-size:11px;cursor:pointer;
|
|
242
|
+
}
|
|
243
|
+
.live-notice.visible{display:inline-flex}
|
|
230
244
|
|
|
231
245
|
/* Curl help - collapsible */
|
|
232
246
|
.curl-panel{
|
|
@@ -540,6 +554,10 @@ mark{
|
|
|
540
554
|
var authToken = null;
|
|
541
555
|
var searchTerm = '';
|
|
542
556
|
var focusedIndex = -1;
|
|
557
|
+
var PAGE_SIZE = 200;
|
|
558
|
+
var currentPage = 1;
|
|
559
|
+
var pendingLiveCount = 0;
|
|
560
|
+
var liveSequence = 0;
|
|
543
561
|
|
|
544
562
|
try {
|
|
545
563
|
var params = new URLSearchParams(location.search);
|
|
@@ -960,7 +978,7 @@ mark{
|
|
|
960
978
|
var lc = deviceLog.logCount || {};
|
|
961
979
|
var deviceText = formatDevice(deviceLog.device);
|
|
962
980
|
var ipText = formatIp(deviceLog.source);
|
|
963
|
-
html += '<div class="device-card" data-device-id="' + escapeHtml(deviceLog.deviceId) + '" style="animation-delay:' + (i * 40) + 'ms"
|
|
981
|
+
html += '<div class="device-card" data-device-id="' + escapeHtml(deviceLog.deviceId) + '" style="animation-delay:' + (i * 40) + 'ms">';
|
|
964
982
|
html += '<div><div class="device-title">' + escapeHtml(deviceText) + '</div>';
|
|
965
983
|
html += '<div class="device-subtitle">IP ' + escapeHtml(ipText) + '</div></div>';
|
|
966
984
|
html += '<div class="device-meta-group">';
|
|
@@ -985,6 +1003,7 @@ mark{
|
|
|
985
1003
|
expandedRows = {};
|
|
986
1004
|
searchTerm = '';
|
|
987
1005
|
focusedIndex = -1;
|
|
1006
|
+
currentPage = 1;
|
|
988
1007
|
window._currentFilterType = '';
|
|
989
1008
|
window._failedOnly = false;
|
|
990
1009
|
statusEl.textContent = 'loading...';
|
|
@@ -1000,7 +1019,7 @@ mark{
|
|
|
1000
1019
|
var html = '';
|
|
1001
1020
|
|
|
1002
1021
|
// Back link
|
|
1003
|
-
html += '<a href="#" class="back-link"
|
|
1022
|
+
html += '<a href="#" class="back-link">';
|
|
1004
1023
|
html += '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M19 12H5"/><path d="M12 19l-7-7 7-7"/></svg>';
|
|
1005
1024
|
html += 'All devices</a>';
|
|
1006
1025
|
|
|
@@ -1038,9 +1057,9 @@ mark{
|
|
|
1038
1057
|
|
|
1039
1058
|
html += renderCurlPanel('Curl this device', [
|
|
1040
1059
|
curlCommand('/devices/' + encodeURIComponent(deviceId)),
|
|
1041
|
-
curlCommand('/devices/' + encodeURIComponent(deviceId) + '/logs?limit=
|
|
1060
|
+
curlCommand('/devices/' + encodeURIComponent(deviceId) + '/logs?limit=200'),
|
|
1042
1061
|
curlCommand('/devices/' + encodeURIComponent(deviceId) + '/logs?type=network&failedOnly=true&limit=50'),
|
|
1043
|
-
curlCommand('/devices/' + encodeURIComponent(deviceId) + '/logs?type=console&limit=
|
|
1062
|
+
curlCommand('/devices/' + encodeURIComponent(deviceId) + '/logs?type=console&limit=200'),
|
|
1044
1063
|
]);
|
|
1045
1064
|
|
|
1046
1065
|
// Tabs
|
|
@@ -1060,11 +1079,13 @@ mark{
|
|
|
1060
1079
|
html += '<button class="search-clear" id="searchClear" onclick="clearSearch()">×</button>';
|
|
1061
1080
|
html += '</div>';
|
|
1062
1081
|
html += '<label>Failed only <div class="toggle" id="failedToggle" onclick="toggleFailed()"></div></label>';
|
|
1063
|
-
html += '<
|
|
1082
|
+
html += '<button class="live-notice" id="liveNotice" onclick="showLiveUpdates()">0 new logs</button>';
|
|
1083
|
+
html += '<div class="pager" id="pagerTop"></div>';
|
|
1064
1084
|
html += '</div>';
|
|
1065
1085
|
|
|
1066
1086
|
// Log container
|
|
1067
1087
|
html += '<div id="logsContainer"></div>';
|
|
1088
|
+
html += '<div class="pager pager-bottom" id="pagerBottom"></div>';
|
|
1068
1089
|
|
|
1069
1090
|
// Actions
|
|
1070
1091
|
html += '<div class="actions">';
|
|
@@ -1086,15 +1107,14 @@ mark{
|
|
|
1086
1107
|
applyFilters();
|
|
1087
1108
|
});
|
|
1088
1109
|
|
|
1089
|
-
|
|
1090
|
-
renderLogs(logs, '', 50, false);
|
|
1110
|
+
renderLogs(logs, '', false);
|
|
1091
1111
|
}).catch(function(err) {
|
|
1092
1112
|
statusEl.textContent = '';
|
|
1093
1113
|
app.innerHTML = '<div class="empty" style="color:var(--red)">Failed to load: ' + escapeHtml(err.message) + '</div>';
|
|
1094
1114
|
});
|
|
1095
1115
|
}
|
|
1096
1116
|
|
|
1097
|
-
function
|
|
1117
|
+
function collectLogEntries(logs, type, failedOnly) {
|
|
1098
1118
|
var entries = [];
|
|
1099
1119
|
if (type && logs[type]) {
|
|
1100
1120
|
entries = Array.isArray(logs[type])
|
|
@@ -1126,8 +1146,63 @@ mark{
|
|
|
1126
1146
|
.sort(function(a, b) {
|
|
1127
1147
|
var byTime = readTimestamp(b.entry) - readTimestamp(a.entry);
|
|
1128
1148
|
return byTime || b.order - a.order;
|
|
1129
|
-
})
|
|
1130
|
-
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
return entries;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
function renderPagination(total, page, pageSize) {
|
|
1155
|
+
var totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
1156
|
+
var start = total === 0 ? 0 : ((page - 1) * pageSize) + 1;
|
|
1157
|
+
var end = Math.min(page * pageSize, total);
|
|
1158
|
+
var html = '<span class="pager-info">' + start + '-' + end + ' / ' + total + ' · ' + pageSize + ' per page</span>';
|
|
1159
|
+
html += '<button class="page-btn" onclick="goToPage(' + (page - 1) + ')"' + (page <= 1 ? ' disabled' : '') + '>Prev</button>';
|
|
1160
|
+
html += '<span class="pager-info">' + page + ' / ' + totalPages + '</span>';
|
|
1161
|
+
html += '<button class="page-btn" onclick="goToPage(' + (page + 1) + ')"' + (page >= totalPages ? ' disabled' : '') + '>Next</button>';
|
|
1162
|
+
|
|
1163
|
+
['pagerTop', 'pagerBottom'].forEach(function(id) {
|
|
1164
|
+
var el = document.getElementById(id);
|
|
1165
|
+
if (el) el.innerHTML = html;
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
function renderLogEntryHtml(entry, type, rowId, index, isExpanded) {
|
|
1170
|
+
var lt = type || getLogType(entry);
|
|
1171
|
+
var typeClass = toKeyPart(lt);
|
|
1172
|
+
var ts = readTimestamp(entry);
|
|
1173
|
+
var html = '<div class="log-entry' + (isExpanded ? ' expanded' : '') + '" id="entry-' + rowId + '" data-index="' + index + '" data-sort="' + ts + '">';
|
|
1174
|
+
html += '<div class="log-row" onclick="toggleRow(\'' + rowId + '\')">';
|
|
1175
|
+
html += '<div class="log-type log-type-' + typeClass + '">' + escapeHtml(labelForType(lt)) + '</div>';
|
|
1176
|
+
html += '<div class="log-summary-col">';
|
|
1177
|
+
html += '<div class="log-summary">' + matchSearch(summarize(entry)) + '</div>';
|
|
1178
|
+
if (ts) {
|
|
1179
|
+
html += '<div class="log-timestamp">' + formatTimeShort(new Date(ts).toISOString()) + '</div>';
|
|
1180
|
+
}
|
|
1181
|
+
html += '</div>';
|
|
1182
|
+
html += '<div class="log-status">' + statusBadge(entry) + '</div>';
|
|
1183
|
+
html += '<div class="log-copy" onclick="event.stopPropagation();copyEntryJSON(\'' + rowId + '\')"><button class="copy-btn" title="Copy entry JSON">⎘</button></div>';
|
|
1184
|
+
html += '<div class="log-expand">' + (isExpanded ? '▶' : '▶') + '</div>';
|
|
1185
|
+
html += '</div>';
|
|
1186
|
+
html += '<div class="log-detail' + (isExpanded ? '' : '') + '" id="detail-' + rowId + '">';
|
|
1187
|
+
html += '<div class="log-detail-inner"><div class="detail-sections">';
|
|
1188
|
+
html += renderLogDetails(entry, lt);
|
|
1189
|
+
html += '<div class="entry-footer">';
|
|
1190
|
+
html += '<button class="btn btn-sm" onclick="event.stopPropagation();copyEntryJSON(\'' + rowId + '\')">⎘ Copy JSON</button>';
|
|
1191
|
+
html += '</div>';
|
|
1192
|
+
html += '</div></div></div>';
|
|
1193
|
+
html += '</div>';
|
|
1194
|
+
return html;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function renderLogs(logs, type, failedOnly) {
|
|
1198
|
+
var allEntries = collectLogEntries(logs, type, failedOnly);
|
|
1199
|
+
var totalPages = Math.max(1, Math.ceil(allEntries.length / PAGE_SIZE));
|
|
1200
|
+
if (currentPage > totalPages) currentPage = totalPages;
|
|
1201
|
+
if (currentPage < 1) currentPage = 1;
|
|
1202
|
+
|
|
1203
|
+
var startIndex = (currentPage - 1) * PAGE_SIZE;
|
|
1204
|
+
var entries = allEntries.slice(startIndex, startIndex + PAGE_SIZE);
|
|
1205
|
+
renderPagination(allEntries.length, currentPage, PAGE_SIZE);
|
|
1131
1206
|
|
|
1132
1207
|
var container = document.getElementById('logsContainer');
|
|
1133
1208
|
if (!entries.length) {
|
|
@@ -1140,37 +1215,9 @@ mark{
|
|
|
1140
1215
|
focusedIndex = -1;
|
|
1141
1216
|
var html = '<div class="log-list">';
|
|
1142
1217
|
entries.forEach(function(item, i) {
|
|
1143
|
-
var
|
|
1144
|
-
var rowId = getLogEntryKey(entry, item.type,
|
|
1145
|
-
|
|
1146
|
-
var typeClass = toKeyPart(lt);
|
|
1147
|
-
var isExpanded = expandedRows[rowId];
|
|
1148
|
-
var ts = readTimestamp(entry);
|
|
1149
|
-
|
|
1150
|
-
html += '<div class="log-entry' + (isExpanded ? ' expanded' : '') + '" id="entry-' + rowId + '" data-index="' + i + '">';
|
|
1151
|
-
html += '<div class="log-row" onclick="toggleRow(\'' + rowId + '\')">';
|
|
1152
|
-
html += '<div class="log-type log-type-' + typeClass + '">' + escapeHtml(labelForType(lt)) + '</div>';
|
|
1153
|
-
html += '<div class="log-summary-col">';
|
|
1154
|
-
html += '<div class="log-summary">' + matchSearch(summarize(entry)) + '</div>';
|
|
1155
|
-
if (ts) {
|
|
1156
|
-
html += '<div class="log-timestamp">' + formatTimeShort(new Date(ts).toISOString()) + '</div>';
|
|
1157
|
-
}
|
|
1158
|
-
html += '</div>';
|
|
1159
|
-
html += '<div class="log-status">' + statusBadge(entry) + '</div>';
|
|
1160
|
-
html += '<div class="log-copy" onclick="event.stopPropagation();copyEntryJSON(\'' + rowId + '\')"><button class="copy-btn" title="Copy entry JSON">⎘</button></div>';
|
|
1161
|
-
html += '<div class="log-expand">' + (isExpanded ? '▶' : '▶') + '</div>';
|
|
1162
|
-
html += '</div>';
|
|
1163
|
-
|
|
1164
|
-
// Detail panel
|
|
1165
|
-
html += '<div class="log-detail' + (isExpanded ? '' : '') + '" id="detail-' + rowId + '">';
|
|
1166
|
-
html += '<div class="log-detail-inner"><div class="detail-sections">';
|
|
1167
|
-
html += renderLogDetails(entry, lt);
|
|
1168
|
-
html += '<div class="entry-footer">';
|
|
1169
|
-
html += '<button class="btn btn-sm" onclick="event.stopPropagation();copyEntryJSON(\'' + rowId + '\')">⎘ Copy JSON</button>';
|
|
1170
|
-
html += '</div>';
|
|
1171
|
-
html += '</div></div></div>';
|
|
1172
|
-
|
|
1173
|
-
html += '</div>';
|
|
1218
|
+
var absoluteIndex = startIndex + i;
|
|
1219
|
+
var rowId = getLogEntryKey(item.entry, item.type, absoluteIndex);
|
|
1220
|
+
html += renderLogEntryHtml(item.entry, item.type, rowId, i, expandedRows[rowId]);
|
|
1174
1221
|
});
|
|
1175
1222
|
html += '</div>';
|
|
1176
1223
|
container.innerHTML = html;
|
|
@@ -1182,7 +1229,7 @@ mark{
|
|
|
1182
1229
|
return {
|
|
1183
1230
|
type: window._currentFilterType || '',
|
|
1184
1231
|
failedOnly: window._failedOnly || false,
|
|
1185
|
-
|
|
1232
|
+
page: currentPage,
|
|
1186
1233
|
};
|
|
1187
1234
|
}
|
|
1188
1235
|
|
|
@@ -1190,7 +1237,27 @@ mark{
|
|
|
1190
1237
|
if (!currentDevice) return;
|
|
1191
1238
|
var logs = currentDevice.report ? currentDevice.report.logs : {};
|
|
1192
1239
|
var options = readVisibleLogOptions();
|
|
1193
|
-
renderLogs(logs, options.type, options.
|
|
1240
|
+
renderLogs(logs, options.type, options.failedOnly);
|
|
1241
|
+
updateLiveNotice();
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
function updateCurrentPagination() {
|
|
1245
|
+
if (!currentDevice) return;
|
|
1246
|
+
var logs = currentDevice.report ? currentDevice.report.logs : {};
|
|
1247
|
+
var options = readVisibleLogOptions();
|
|
1248
|
+
var total = collectLogEntries(logs, options.type, options.failedOnly).length;
|
|
1249
|
+
renderPagination(total, currentPage, PAGE_SIZE);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
function updateLiveNotice() {
|
|
1253
|
+
var notice = document.getElementById('liveNotice');
|
|
1254
|
+
if (!notice) return;
|
|
1255
|
+
if (pendingLiveCount > 0) {
|
|
1256
|
+
notice.textContent = pendingLiveCount + ' new logs';
|
|
1257
|
+
notice.classList.add('visible');
|
|
1258
|
+
} else {
|
|
1259
|
+
notice.classList.remove('visible');
|
|
1260
|
+
}
|
|
1194
1261
|
}
|
|
1195
1262
|
|
|
1196
1263
|
function refreshCurrentDevice() {
|
|
@@ -1207,6 +1274,7 @@ mark{
|
|
|
1207
1274
|
currentDevice.logCount = data.logCount;
|
|
1208
1275
|
currentDevice.receivedAt = data.receivedAt;
|
|
1209
1276
|
currentDevice.lastSeenAt = data.lastSeenAt;
|
|
1277
|
+
pendingLiveCount = 0;
|
|
1210
1278
|
rerenderVisibleLogs();
|
|
1211
1279
|
updateTabCounts();
|
|
1212
1280
|
}).catch(function(err) {
|
|
@@ -1238,6 +1306,24 @@ mark{
|
|
|
1238
1306
|
window.applyFilters = function() {
|
|
1239
1307
|
if (!currentDevice) return;
|
|
1240
1308
|
expandedRows = {};
|
|
1309
|
+
currentPage = 1;
|
|
1310
|
+
pendingLiveCount = 0;
|
|
1311
|
+
rerenderVisibleLogs();
|
|
1312
|
+
};
|
|
1313
|
+
|
|
1314
|
+
window.goToPage = function(page) {
|
|
1315
|
+
if (!currentDevice) return;
|
|
1316
|
+
currentPage = Math.max(1, Math.floor(page));
|
|
1317
|
+
expandedRows = {};
|
|
1318
|
+
if (currentPage === 1) pendingLiveCount = 0;
|
|
1319
|
+
rerenderVisibleLogs();
|
|
1320
|
+
};
|
|
1321
|
+
|
|
1322
|
+
window.showLiveUpdates = function() {
|
|
1323
|
+
if (!currentDevice) return;
|
|
1324
|
+
currentPage = 1;
|
|
1325
|
+
expandedRows = {};
|
|
1326
|
+
pendingLiveCount = 0;
|
|
1241
1327
|
rerenderVisibleLogs();
|
|
1242
1328
|
};
|
|
1243
1329
|
|
|
@@ -1294,33 +1380,11 @@ mark{
|
|
|
1294
1380
|
var el = document.getElementById('entry-' + rowId);
|
|
1295
1381
|
if (!el) return;
|
|
1296
1382
|
|
|
1297
|
-
// Re-derive the entry from current logs
|
|
1298
1383
|
var logs = currentDevice.report ? currentDevice.report.logs : {};
|
|
1299
|
-
var entries = [];
|
|
1300
|
-
Object.entries(logs).forEach(function(logGroup) {
|
|
1301
|
-
var logType = logGroup[0];
|
|
1302
|
-
var value = logGroup[1];
|
|
1303
|
-
if (Array.isArray(value)) {
|
|
1304
|
-
value.forEach(function(entry, index) {
|
|
1305
|
-
entries.push({ type: logType, entry: entry, order: entries.length });
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
});
|
|
1309
|
-
|
|
1310
1384
|
var options = readVisibleLogOptions();
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
if (options.failedOnly) {
|
|
1315
|
-
entries = entries.filter(function(item) { return isFailedEntry(item.entry); });
|
|
1316
|
-
}
|
|
1317
|
-
if (searchTerm) {
|
|
1318
|
-
entries = entries.filter(function(item) { return entryMatchesSearch(item.entry); });
|
|
1319
|
-
}
|
|
1320
|
-
entries = entries.slice().sort(function(a, b) {
|
|
1321
|
-
var byTime = readTimestamp(b.entry) - readTimestamp(a.entry);
|
|
1322
|
-
return byTime || b.order - a.order;
|
|
1323
|
-
}).slice(0, options.limit);
|
|
1385
|
+
var startIndex = (options.page - 1) * PAGE_SIZE;
|
|
1386
|
+
var entries = collectLogEntries(logs, options.type, options.failedOnly)
|
|
1387
|
+
.slice(startIndex, startIndex + PAGE_SIZE);
|
|
1324
1388
|
|
|
1325
1389
|
var idx = parseInt(el.getAttribute('data-index'), 10);
|
|
1326
1390
|
if (isNaN(idx) || idx < 0 || idx >= entries.length) return;
|
|
@@ -1417,6 +1481,25 @@ mark{
|
|
|
1417
1481
|
items[focusedIndex].click();
|
|
1418
1482
|
}
|
|
1419
1483
|
|
|
1484
|
+
function openDeviceDetail(deviceId) {
|
|
1485
|
+
if (!deviceId) return;
|
|
1486
|
+
var nextHash = 'device/' + encodeURIComponent(deviceId);
|
|
1487
|
+
if (location.hash === '#' + nextHash) {
|
|
1488
|
+
route();
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
location.hash = nextHash;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
document.addEventListener('click', function(e) {
|
|
1495
|
+
var target = e.target;
|
|
1496
|
+
if (!target || !target.closest) return;
|
|
1497
|
+
var card = target.closest('.device-card[data-device-id]');
|
|
1498
|
+
if (!card) return;
|
|
1499
|
+
e.preventDefault();
|
|
1500
|
+
openDeviceDetail(card.getAttribute('data-device-id'));
|
|
1501
|
+
});
|
|
1502
|
+
|
|
1420
1503
|
// --- Routing ---
|
|
1421
1504
|
|
|
1422
1505
|
window._currentFilterType = '';
|
|
@@ -1450,13 +1533,85 @@ mark{
|
|
|
1450
1533
|
return html;
|
|
1451
1534
|
}
|
|
1452
1535
|
|
|
1536
|
+
function findDeviceCard(deviceId) {
|
|
1537
|
+
var cards = document.querySelectorAll('.device-card[data-device-id]');
|
|
1538
|
+
for (var i = 0; i < cards.length; i += 1) {
|
|
1539
|
+
if (cards[i].getAttribute('data-device-id') === deviceId) return cards[i];
|
|
1540
|
+
}
|
|
1541
|
+
return null;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
function updateVisibleIndexes(list) {
|
|
1545
|
+
Array.from(list.querySelectorAll('.log-entry')).forEach(function(entry, index) {
|
|
1546
|
+
entry.setAttribute('data-index', index);
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
function visibleDeltaItems(deltaLogs) {
|
|
1551
|
+
var options = readVisibleLogOptions();
|
|
1552
|
+
var items = [];
|
|
1553
|
+
Object.entries(deltaLogs || {}).forEach(function(logGroup) {
|
|
1554
|
+
var type = logGroup[0];
|
|
1555
|
+
var entries = logGroup[1];
|
|
1556
|
+
if (!Array.isArray(entries)) return;
|
|
1557
|
+
entries.forEach(function(entry) {
|
|
1558
|
+
if (options.type && type !== options.type) return;
|
|
1559
|
+
if (options.failedOnly && !isFailedEntry(entry)) return;
|
|
1560
|
+
if (searchTerm && !entryMatchesSearch(entry)) return;
|
|
1561
|
+
items.push({ type: type, entry: entry, order: items.length });
|
|
1562
|
+
});
|
|
1563
|
+
});
|
|
1564
|
+
return items.sort(function(a, b) {
|
|
1565
|
+
var byTime = readTimestamp(b.entry) - readTimestamp(a.entry);
|
|
1566
|
+
return byTime || b.order - a.order;
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
function appendDeltaLogs(deltaLogs) {
|
|
1571
|
+
var items = visibleDeltaItems(deltaLogs);
|
|
1572
|
+
updateCurrentPagination();
|
|
1573
|
+
if (!items.length) {
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
if (currentPage !== 1) {
|
|
1578
|
+
pendingLiveCount += items.length;
|
|
1579
|
+
updateLiveNotice();
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
pendingLiveCount = 0;
|
|
1584
|
+
updateLiveNotice();
|
|
1585
|
+
|
|
1586
|
+
var container = document.getElementById('logsContainer');
|
|
1587
|
+
var list = container ? container.querySelector('.log-list') : null;
|
|
1588
|
+
if (!list) {
|
|
1589
|
+
rerenderVisibleLogs();
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
var html = '';
|
|
1594
|
+
items.forEach(function(item) {
|
|
1595
|
+
var rowId = getLogEntryKey(item.entry, item.type, 'live-' + (liveSequence += 1));
|
|
1596
|
+
html += renderLogEntryHtml(item.entry, item.type, rowId, 0, false);
|
|
1597
|
+
});
|
|
1598
|
+
list.insertAdjacentHTML('afterbegin', html);
|
|
1599
|
+
|
|
1600
|
+
while (list.querySelectorAll('.log-entry').length > PAGE_SIZE) {
|
|
1601
|
+
var last = list.lastElementChild;
|
|
1602
|
+
if (!last || last.classList.contains('expanded')) break;
|
|
1603
|
+
list.removeChild(last);
|
|
1604
|
+
}
|
|
1605
|
+
updateVisibleIndexes(list);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1453
1608
|
function appendDeviceCard(payload) {
|
|
1454
1609
|
var grid = document.querySelector('.device-grid');
|
|
1455
1610
|
if (!grid) { renderList(); return; }
|
|
1456
1611
|
|
|
1457
1612
|
var deviceId = payload.deviceId;
|
|
1458
1613
|
if (!deviceId) return;
|
|
1459
|
-
var existing =
|
|
1614
|
+
var existing = findDeviceCard(deviceId);
|
|
1460
1615
|
if (existing) {
|
|
1461
1616
|
var tags = existing.querySelector('.device-tags');
|
|
1462
1617
|
if (tags) tags.innerHTML = renderDeviceTags(payload.logCount || {});
|
|
@@ -1469,7 +1624,6 @@ mark{
|
|
|
1469
1624
|
var card = document.createElement('div');
|
|
1470
1625
|
card.className = 'device-card';
|
|
1471
1626
|
card.setAttribute('data-device-id', deviceId);
|
|
1472
|
-
card.setAttribute('onclick', "location.hash='device/" + encodeURIComponent(deviceId) + "'");
|
|
1473
1627
|
var html = '<div><div class="device-title">' + escapeHtml(deviceText) + '</div>';
|
|
1474
1628
|
html += '<div class="device-subtitle">IP ' + escapeHtml(ipText) + '</div></div>';
|
|
1475
1629
|
html += '<div class="device-meta-group">';
|
|
@@ -1488,83 +1642,6 @@ mark{
|
|
|
1488
1642
|
}
|
|
1489
1643
|
}
|
|
1490
1644
|
|
|
1491
|
-
function buildLogEntryHtml(entry, rowId, type, index) {
|
|
1492
|
-
var lt = type || getLogType(entry);
|
|
1493
|
-
var typeClass = toKeyPart(lt);
|
|
1494
|
-
var ts = readTimestamp(entry);
|
|
1495
|
-
var html = '<div class="log-row">';
|
|
1496
|
-
html += '<div class="log-type log-type-' + typeClass + '">' + escapeHtml(labelForType(lt)) + '</div>';
|
|
1497
|
-
html += '<div class="log-summary-col">';
|
|
1498
|
-
html += '<div class="log-summary">' + matchSearch(summarize(entry)) + '</div>';
|
|
1499
|
-
if (ts) {
|
|
1500
|
-
html += '<div class="log-timestamp">' + formatTimeShort(new Date(ts).toISOString()) + '</div>';
|
|
1501
|
-
}
|
|
1502
|
-
html += '</div>';
|
|
1503
|
-
html += '<div class="log-status">' + statusBadge(entry) + '</div>';
|
|
1504
|
-
html += '<div class="log-copy" onclick="event.stopPropagation();copyEntryJSON(\'' + rowId + '\')"><button class="copy-btn" title="Copy entry JSON">⎘</button></div>';
|
|
1505
|
-
html += '<div class="log-expand">▶</div>';
|
|
1506
|
-
html += '</div>';
|
|
1507
|
-
html += '<div class="log-detail" id="detail-' + rowId + '">';
|
|
1508
|
-
html += '<div class="log-detail-inner"><div class="detail-sections">';
|
|
1509
|
-
html += renderLogDetails(entry, lt);
|
|
1510
|
-
html += '<div class="entry-footer">';
|
|
1511
|
-
html += '<button class="btn btn-sm" onclick="event.stopPropagation();copyEntryJSON(\'' + rowId + '\')">⎘ Copy JSON</button>';
|
|
1512
|
-
html += '</div>';
|
|
1513
|
-
html += '</div></div></div>';
|
|
1514
|
-
return html;
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
function appendDeltaLogs(deltaLogs) {
|
|
1518
|
-
var container = document.getElementById('logsContainer');
|
|
1519
|
-
if (!container) return;
|
|
1520
|
-
|
|
1521
|
-
var list = container.querySelector('.log-list');
|
|
1522
|
-
if (!list) {
|
|
1523
|
-
applyFilters();
|
|
1524
|
-
return;
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
var type = window._currentFilterType || '';
|
|
1528
|
-
var failedOnly = window._failedOnly || false;
|
|
1529
|
-
var limit = document.getElementById('limitInput') ? parseInt(document.getElementById('limitInput').value, 10) || 50 : 50;
|
|
1530
|
-
var allNewEntries = [];
|
|
1531
|
-
|
|
1532
|
-
Object.entries(deltaLogs).forEach(function(entry) {
|
|
1533
|
-
var t = entry[0];
|
|
1534
|
-
var entries = entry[1];
|
|
1535
|
-
if (!Array.isArray(entries)) return;
|
|
1536
|
-
entries.forEach(function(e) {
|
|
1537
|
-
if (failedOnly && !isFailedEntry(e)) return;
|
|
1538
|
-
if (type && t !== type) return;
|
|
1539
|
-
if (searchTerm && !entryMatchesSearch(e)) return;
|
|
1540
|
-
allNewEntries.push({ type: t, entry: e });
|
|
1541
|
-
});
|
|
1542
|
-
});
|
|
1543
|
-
|
|
1544
|
-
var count = list.querySelectorAll('.log-entry').length;
|
|
1545
|
-
allNewEntries.forEach(function(item) {
|
|
1546
|
-
var entry = item.entry;
|
|
1547
|
-
var rowId = getLogEntryKey(entry, item.type, count++);
|
|
1548
|
-
var div = document.createElement('div');
|
|
1549
|
-
div.className = 'log-entry';
|
|
1550
|
-
div.id = 'entry-' + rowId;
|
|
1551
|
-
div.setAttribute('data-index', count - 1);
|
|
1552
|
-
div.setAttribute('onclick', '');
|
|
1553
|
-
div.querySelector('.log-row').setAttribute('onclick', "toggleRow('" + rowId + "')");
|
|
1554
|
-
div.innerHTML = buildLogEntryHtml(entry, rowId, item.type, count - 1);
|
|
1555
|
-
list.prepend(div);
|
|
1556
|
-
});
|
|
1557
|
-
|
|
1558
|
-
// Trim oldest rows from bottom if over limit, skip expanded entries.
|
|
1559
|
-
var entries = list.querySelectorAll('.log-entry');
|
|
1560
|
-
while (entries.length > limit) {
|
|
1561
|
-
var last = entries[entries.length - 1];
|
|
1562
|
-
if (last && last.classList.contains('expanded')) break;
|
|
1563
|
-
list.removeChild(last);
|
|
1564
|
-
entries = list.querySelectorAll('.log-entry');
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
1645
|
function updateTabCounts() {
|
|
1569
1646
|
if (!currentDevice) return;
|
|
1570
1647
|
var logs = currentDevice.report ? currentDevice.report.logs : {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-debug-toolkit",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.3",
|
|
4
4
|
"description": "A local-first React Native debugging bridge with in-app logs, desktop daemon, Web Console, HTTP API, and MCP support",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|