bee-ladybug 0.6.0 → 0.6.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bee-ladybug",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Universal telemetry and visual inspection overlay for Canvas, DOM, Python and AI flows.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -219,9 +219,9 @@ export class CSSAdapter {
219
219
  const findings = [];
220
220
  pushNegativeMargins(el, findings);
221
221
  pushInvisible(el, findings);
222
- pushBlockers(el, findings);
222
+ pushBlockers(el, findings, true);
223
223
  pushImage(el, findings);
224
- pushStacking(el, findings);
224
+ pushStacking(el, findings, true);
225
225
  pushImportant(el, built.rules, findings);
226
226
  for (let j = 0; j < findings.length; j++) {
227
227
  rows.push({ el, selector: cssPath(el), ...findings[j] });
@@ -755,7 +755,7 @@ function pushInvisible(el, findings) {
755
755
  }
756
756
  }
757
757
 
758
- function pushBlockers(el, findings) {
758
+ function pushBlockers(el, findings, skipNoise = false) {
759
759
  if (!el.getBoundingClientRect || typeof document.elementFromPoint !== 'function') return;
760
760
  const rect = el.getBoundingClientRect();
761
761
  const x = rect.left + rect.width / 2;
@@ -768,6 +768,7 @@ function pushBlockers(el, findings) {
768
768
  const hit = document.elementFromPoint(x, y);
769
769
  if (!hit || isBeeChrome(hit) || hit === el || el.contains(hit)) return;
770
770
  if (pseudo && (hit === document.documentElement || hit === document.body)) return;
771
+ if (skipNoise && isReportNoise(hit)) return;
771
772
  findings.push(issue(`click blocked by ${cssPath(hit)}`, ISSUE_RANK.blocker, 40));
772
773
  }
773
774
 
@@ -950,7 +951,7 @@ function willChangeHint(value) {
950
951
  return hit.join(', ');
951
952
  }
952
953
 
953
- function pushStacking(el, findings) {
954
+ function pushStacking(el, findings, skipNoise = false) {
954
955
  if (!el || typeof getComputedStyle !== 'function' || !el.parentElement) return;
955
956
  const own = getComputedStyle(el);
956
957
  const ownZ = own.zIndex || own.getPropertyValue?.('z-index') || 'auto';
@@ -958,6 +959,7 @@ function pushStacking(el, findings) {
958
959
  let node = el.parentElement;
959
960
  while (node && node.nodeType === 1) {
960
961
  if (isBeeChrome(node)) return;
962
+ if (skipNoise && isReportNoise(node)) return;
961
963
  const found = stackingReasons(getComputedStyle(node));
962
964
  const ancestor = cssPath(node);
963
965
  if (found.bugs.length) {
@@ -190,7 +190,7 @@ export class CanvasAdapter {
190
190
  severity: box.hit ? 'warn' : 'ok',
191
191
  selector: box.name,
192
192
  rule: 'hitbox',
193
- value: `${box.x},${box.y} ${box.w}x${box.h}${box.hit ? ' colliding' : ''}`,
193
+ value: `${round1(box.x)},${round1(box.y)} ${round1(box.w)}x${round1(box.h)}${box.hit ? ' colliding' : ''}`,
194
194
  source: this.source
195
195
  }));
196
196
  const fps = this.#fpsItem();
@@ -10,7 +10,7 @@ import { UIOverlay } from './UIOverlay.js';
10
10
  import { buildAssistantSnapshot, normalizeAssistant } from './AiAssistant.js';
11
11
  import { exportReport as writeReport } from './ReportExporter.js';
12
12
 
13
- export const BEE_LADYBUG_VERSION = '0.6.0';
13
+ export const BEE_LADYBUG_VERSION = '0.6.1';
14
14
 
15
15
  export const CORE_DEFAULTS = Object.freeze({
16
16
  toggleKey: 'F2',
@@ -74,11 +74,19 @@ export function isReportNoise(node) {
74
74
  while (current && typeof current === 'object') {
75
75
  if (seen.has(current)) break;
76
76
  seen.add(current);
77
- if (current.id === 'wpadminbar') return true;
78
- if (hasAbClass(current.className)) return true;
77
+ if (current.id === 'wpadminbar' || elementId(current) === 'wpadminbar') return true;
78
+ if (hasAbClass(classText(current))) return true;
79
79
  if (current.tagName === 'BEE-LADYBUG-OVERLAY') return true;
80
80
  if (hasBeeAttribute(current)) return true;
81
- current = current.parentElement || null;
81
+ if (typeof current.closest === 'function') {
82
+ try {
83
+ const bar = current.closest('#wpadminbar');
84
+ if (bar) return true;
85
+ } catch {
86
+ /* closest is optional */
87
+ }
88
+ }
89
+ current = current.parentElement || (current.parentNode && current.parentNode.nodeType === 1 ? current.parentNode : null);
82
90
  }
83
91
 
84
92
  const style = readStyle(el);
@@ -103,7 +111,7 @@ function buildReport(core) {
103
111
  const series = readSeries(core);
104
112
  const latest = readLatest(core);
105
113
  const adapters = readAdapters(core);
106
- const summary = countFindings(adapters);
114
+ const summary = countFindings(adapters, logs);
107
115
 
108
116
  return {
109
117
  version: state.version,
@@ -219,7 +227,7 @@ function readAdapters(core) {
219
227
  const list = result && Array.isArray(result.items) ? result.items : [];
220
228
  sections.push({
221
229
  name,
222
- items: list.filter((row) => row && typeof row === 'object').map((row) => makeItem(row, name))
230
+ items: groupIdentical(list.filter((row) => row && typeof row === 'object').map((row) => makeItem(row, name)))
223
231
  });
224
232
  }
225
233
  return sections;
@@ -240,14 +248,45 @@ function makeItem(row, fallbackSource = '') {
240
248
  };
241
249
  }
242
250
 
251
+ /**
252
+ * Identical selector + rule + value collapse to one row. The count is part of the value.
253
+ * @param {ReportItem[]} items
254
+ * @returns {ReportItem[]}
255
+ */
256
+ function groupIdentical(items) {
257
+ const order = [];
258
+ const map = new Map();
259
+ for (let i = 0; i < items.length; i++) {
260
+ const item = items[i];
261
+ const key = `${item.selector}\0${item.rule}\0${item.value}`;
262
+ const prev = map.get(key);
263
+ if (!prev) {
264
+ const row = { ...item, count: 1 };
265
+ map.set(key, row);
266
+ order.push(row);
267
+ continue;
268
+ }
269
+ prev.count += 1;
270
+ }
271
+ return order.map((row) => ({
272
+ severity: row.severity,
273
+ selector: row.selector,
274
+ rule: row.rule,
275
+ value: row.count > 1 ? `${row.value} ×${row.count}` : row.value,
276
+ source: row.source
277
+ }));
278
+ }
279
+
243
280
  /**
244
281
  * @param {Array<{ items: ReportItem[] }>} adapters
282
+ * @param {Array<{ type: string, level: string | null }>} logs
245
283
  */
246
- function countFindings(adapters) {
284
+ function countFindings(adapters, logs) {
247
285
  const summary = { error: 0, warn: 0, ok: 0 };
248
286
  for (const section of adapters) {
249
287
  for (const item of section.items) summary[item.severity] += 1;
250
288
  }
289
+ for (let i = 0; i < logs.length; i++) summary[logSeverity(logs[i])] += 1;
251
290
  return summary;
252
291
  }
253
292
 
@@ -267,7 +306,7 @@ function normalizeFormat(format) {
267
306
  function normalizeSeverity(value) {
268
307
  const text = String(value || '').toLowerCase();
269
308
  if (text === 'error') return 'error';
270
- if (text === 'warn' || text === 'warning') return 'warn';
309
+ if (text === 'warn' || text === 'warning' || text === 'warm') return 'warn';
271
310
  return 'ok';
272
311
  }
273
312
 
@@ -332,8 +371,10 @@ function csvCell(value) {
332
371
  * @returns {ReportSeverity}
333
372
  */
334
373
  function logSeverity(log) {
335
- if (log.level === 'error' || log.type === 'error') return 'error';
336
- if (log.level === 'warn' || log.type === 'warn') return 'warn';
374
+ const level = String(log.level || '').toLowerCase();
375
+ if (level === 'warn' || level === 'warning' || level === 'warm') return 'warn';
376
+ if (level === 'error' || log.type === 'error') return 'error';
377
+ if (log.type === 'warn') return 'warn';
337
378
  return 'ok';
338
379
  }
339
380
 
@@ -490,11 +531,22 @@ function saveIfMounted(core, report, ext, mime, text) {
490
531
  if (typeof document === 'undefined' || !document.body) return;
491
532
  if (typeof URL === 'undefined' || typeof URL.createObjectURL !== 'function' || typeof Blob === 'undefined') return;
492
533
 
493
- const blob = new Blob([text], { type: `${mime};charset=utf-8` });
534
+ const filename = `report-${report.fileStamp}.${ext}`;
535
+ const type = `${mime};charset=utf-8`;
536
+ let blob;
537
+ try {
538
+ blob = typeof File === 'function'
539
+ ? new File([text], filename, { type })
540
+ : new Blob([text], { type });
541
+ } catch {
542
+ if (typeof Blob !== 'function') return;
543
+ blob = new Blob([text], { type });
544
+ }
494
545
  const url = URL.createObjectURL(blob);
495
546
  const link = document.createElement('a');
496
547
  link.href = url;
497
- link.download = `report-${report.fileStamp}.${ext}`;
548
+ link.download = filename;
549
+ link.setAttribute('download', filename);
498
550
  link.rel = 'noopener';
499
551
  document.body.appendChild(link);
500
552
  link.click();
@@ -580,6 +632,34 @@ function escapeHtml(value) {
580
632
  .replace(/"/g, '&quot;');
581
633
  }
582
634
 
635
+ function classText(node) {
636
+ if (!node || typeof node !== 'object') return '';
637
+ const parts = [];
638
+ if (typeof node.className === 'string' && node.className) parts.push(node.className);
639
+ else if (node.className && typeof node.className === 'object' && 'baseVal' in node.className && node.className.baseVal) {
640
+ parts.push(String(node.className.baseVal));
641
+ }
642
+ if (node.classList && typeof node.classList.length === 'number') {
643
+ try {
644
+ parts.push([...node.classList].join(' '));
645
+ } catch {
646
+ /* classList may not be iterable */
647
+ }
648
+ }
649
+ if (typeof node.getAttribute === 'function') {
650
+ const attr = node.getAttribute('class');
651
+ if (attr) parts.push(attr);
652
+ }
653
+ return parts.join(' ');
654
+ }
655
+
656
+ function elementId(node) {
657
+ if (!node || typeof node !== 'object') return '';
658
+ if (typeof node.id === 'string' && node.id) return node.id;
659
+ if (typeof node.getAttribute === 'function') return node.getAttribute('id') || '';
660
+ return '';
661
+ }
662
+
583
663
  /**
584
664
  * @param {unknown} className
585
665
  */