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.
@@ -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" onclick="location.hash=\'device/' + encodeURIComponent(deviceLog.deviceId) + '\'">';
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" onclick="location.hash=\'\';return false">';
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=100'),
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=100'),
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()">&times;</button>';
1061
1080
  html += '</div>';
1062
1081
  html += '<label>Failed only <div class="toggle" id="failedToggle" onclick="toggleFailed()"></div></label>';
1063
- html += '<label>Limit <input type="number" id="limitInput" value="50" min="1" max="500"></label>';
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
- document.getElementById('limitInput').addEventListener('change', applyFilters);
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 renderLogs(logs, type, limit, failedOnly) {
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
- .slice(0, limit);
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">&#9112;</button></div>';
1184
+ html += '<div class="log-expand">' + (isExpanded ? '&#9654;' : '&#9654;') + '</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 + '\')">&#9112; 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 entry = item.entry;
1144
- var rowId = getLogEntryKey(entry, item.type, i);
1145
- var lt = item.type || getLogType(entry);
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">&#9112;</button></div>';
1161
- html += '<div class="log-expand">' + (isExpanded ? '&#9654;' : '&#9654;') + '</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 + '\')">&#9112; 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
- limit: document.getElementById('limitInput') ? parseInt(document.getElementById('limitInput').value, 10) || 50 : 50,
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.limit, options.failedOnly);
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
- if (options.type) {
1312
- entries = entries.filter(function(item) { return item.type === options.type; });
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 = grid.querySelector('[data-device-id="' + CSS.escape(deviceId) + '"]');
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">&#9112;</button></div>';
1505
- html += '<div class="log-expand">&#9654;</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 + '\')">&#9112; 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.2",
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",