@riddledc/riddle-proof 0.7.21 → 0.7.22

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.
@@ -38,6 +38,79 @@ function stringValue(value) {
38
38
  function numberValue(value) {
39
39
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
40
40
  }
41
+ function horizontalBoundsOverflowPx(value) {
42
+ if (!isRecord(value)) return 0;
43
+ let max = maxPositiveNumber(
44
+ value.overflow_px,
45
+ value.overflow,
46
+ value.bounds_overflow_px,
47
+ value.horizontal_overflow_px,
48
+ value.left_overflow_px,
49
+ value.right_overflow_px
50
+ );
51
+ for (const offender of boundsOffendersForEvidence(value)) {
52
+ max = Math.max(max, horizontalOffenderOverflowPx(offender));
53
+ }
54
+ return roundPixels(max);
55
+ }
56
+ function horizontalOffenderOverflowPx(value) {
57
+ if (!isRecord(value)) return 0;
58
+ let max = maxPositiveNumber(
59
+ value.overflow,
60
+ value.overflow_px,
61
+ value.bounds_overflow_px,
62
+ value.horizontal_overflow_px,
63
+ value.left_overflow_px,
64
+ value.right_overflow_px,
65
+ value.leftOverflowPx,
66
+ value.rightOverflowPx
67
+ );
68
+ const clipped = isRecord(value.clipped || value.clip || value.clipping) ? value.clipped || value.clip || value.clipping : void 0;
69
+ if (isRecord(clipped)) {
70
+ max = Math.max(max, maxPositiveNumber(
71
+ clipped.left,
72
+ clipped.right,
73
+ clipped.left_px,
74
+ clipped.right_px,
75
+ clipped.leftPx,
76
+ clipped.rightPx
77
+ ));
78
+ }
79
+ const rect = isRecord(value.rect || value.bounds || value.bounding_rect || value.boundingRect) ? value.rect || value.bounds || value.bounding_rect || value.boundingRect : void 0;
80
+ const viewportWidth = numberValue(value.viewport_width ?? value.viewportWidth);
81
+ if (isRecord(rect) && viewportWidth !== void 0) {
82
+ const left = numberValue(rect.left);
83
+ const right = numberValue(rect.right);
84
+ if (left !== void 0 && left < 0) max = Math.max(max, Math.abs(left));
85
+ if (right !== void 0 && right > viewportWidth) max = Math.max(max, right - viewportWidth);
86
+ }
87
+ return roundPixels(max);
88
+ }
89
+ function boundsOffendersForEvidence(value) {
90
+ if (!isRecord(value)) return [];
91
+ const offenders = [
92
+ ...Array.isArray(value.overflow_offenders) ? value.overflow_offenders : [],
93
+ ...Array.isArray(value.overflowOffenders) ? value.overflowOffenders : [],
94
+ ...Array.isArray(value.bounds_offenders) ? value.bounds_offenders : [],
95
+ ...Array.isArray(value.boundsOffenders) ? value.boundsOffenders : [],
96
+ ...Array.isArray(value.clipped_elements) ? value.clipped_elements : [],
97
+ ...Array.isArray(value.clippedElements) ? value.clippedElements : [],
98
+ ...Array.isArray(value.clipping_offenders) ? value.clipping_offenders : [],
99
+ ...Array.isArray(value.clippingOffenders) ? value.clippingOffenders : []
100
+ ];
101
+ return offenders.filter((item) => isRecord(item)).sort((a, b) => horizontalOffenderOverflowPx(b) - horizontalOffenderOverflowPx(a));
102
+ }
103
+ function maxPositiveNumber(...values) {
104
+ let max = 0;
105
+ for (const value of values) {
106
+ const number = numberValue(value);
107
+ if (number !== void 0 && number > max) max = number;
108
+ }
109
+ return max;
110
+ }
111
+ function roundPixels(value) {
112
+ return Math.round(value * 100) / 100;
113
+ }
41
114
  function timeoutSecValue(value) {
42
115
  const number = numberValue(value);
43
116
  return number && number > 0 ? Math.ceil(number) : void 0;
@@ -380,7 +453,7 @@ function assessCheckFromEvidence(check, evidence) {
380
453
  message: "No applicable viewport evidence was captured for overflow check."
381
454
  };
382
455
  }
383
- const failed = applicable.filter((viewport) => (viewport.overflow_px ?? 0) > maxOverflow);
456
+ const failed = applicable.filter((viewport) => horizontalBoundsOverflowPx(viewport) > maxOverflow);
384
457
  return {
385
458
  type: check.type,
386
459
  label: checkLabel(check),
@@ -388,9 +461,11 @@ function assessCheckFromEvidence(check, evidence) {
388
461
  evidence: {
389
462
  max_overflow_px: maxOverflow,
390
463
  overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null),
464
+ bounds_overflow_px: applicable.map((viewport) => horizontalBoundsOverflowPx(viewport)),
465
+ overflow_offender_counts: applicable.map((viewport) => boundsOffendersForEvidence(viewport).length),
391
466
  viewports: applicable.map((viewport) => viewport.name)
392
467
  },
393
- message: failed.length ? `Horizontal overflow exceeded ${maxOverflow}px in ${failed.length} viewport(s).` : void 0
468
+ message: failed.length ? `Horizontal bounds overflow exceeded ${maxOverflow}px in ${failed.length} viewport(s).` : void 0
394
469
  };
395
470
  }
396
471
  if (check.type === "no_fatal_console_errors") {
@@ -612,6 +687,77 @@ function textMatches(sample, check) {
612
687
  }
613
688
  return String(sample || "").includes(check.text || "");
614
689
  }
690
+ function numberValue(value) {
691
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
692
+ }
693
+ function maxPositiveNumber() {
694
+ let max = 0;
695
+ for (const value of arguments) {
696
+ const number = numberValue(value);
697
+ if (number !== undefined && number > max) max = number;
698
+ }
699
+ return max;
700
+ }
701
+ function roundPixels(value) {
702
+ return Math.round(value * 100) / 100;
703
+ }
704
+ function isRecord(value) {
705
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
706
+ }
707
+ function boundsOffendersForEvidence(value) {
708
+ if (!isRecord(value)) return [];
709
+ const offenders = []
710
+ .concat(Array.isArray(value.overflow_offenders) ? value.overflow_offenders : [])
711
+ .concat(Array.isArray(value.overflowOffenders) ? value.overflowOffenders : [])
712
+ .concat(Array.isArray(value.bounds_offenders) ? value.bounds_offenders : [])
713
+ .concat(Array.isArray(value.boundsOffenders) ? value.boundsOffenders : [])
714
+ .concat(Array.isArray(value.clipped_elements) ? value.clipped_elements : [])
715
+ .concat(Array.isArray(value.clippedElements) ? value.clippedElements : [])
716
+ .concat(Array.isArray(value.clipping_offenders) ? value.clipping_offenders : [])
717
+ .concat(Array.isArray(value.clippingOffenders) ? value.clippingOffenders : []);
718
+ return offenders.filter(isRecord).sort((a, b) => horizontalOffenderOverflowPx(b) - horizontalOffenderOverflowPx(a));
719
+ }
720
+ function horizontalOffenderOverflowPx(value) {
721
+ if (!isRecord(value)) return 0;
722
+ let max = maxPositiveNumber(
723
+ value.overflow,
724
+ value.overflow_px,
725
+ value.bounds_overflow_px,
726
+ value.horizontal_overflow_px,
727
+ value.left_overflow_px,
728
+ value.right_overflow_px,
729
+ value.leftOverflowPx,
730
+ value.rightOverflowPx
731
+ );
732
+ const clipped = value.clipped || value.clip || value.clipping;
733
+ if (isRecord(clipped)) {
734
+ max = Math.max(max, maxPositiveNumber(clipped.left, clipped.right, clipped.left_px, clipped.right_px, clipped.leftPx, clipped.rightPx));
735
+ }
736
+ const rect = value.rect || value.bounds || value.bounding_rect || value.boundingRect;
737
+ const viewportWidth = numberValue(value.viewport_width != null ? value.viewport_width : value.viewportWidth);
738
+ if (isRecord(rect) && viewportWidth !== undefined) {
739
+ const left = numberValue(rect.left);
740
+ const right = numberValue(rect.right);
741
+ if (left !== undefined && left < 0) max = Math.max(max, Math.abs(left));
742
+ if (right !== undefined && right > viewportWidth) max = Math.max(max, right - viewportWidth);
743
+ }
744
+ return roundPixels(max);
745
+ }
746
+ function horizontalBoundsOverflowPx(value) {
747
+ if (!isRecord(value)) return 0;
748
+ let max = maxPositiveNumber(
749
+ value.overflow_px,
750
+ value.overflow,
751
+ value.bounds_overflow_px,
752
+ value.horizontal_overflow_px,
753
+ value.left_overflow_px,
754
+ value.right_overflow_px
755
+ );
756
+ for (const offender of boundsOffendersForEvidence(value)) {
757
+ max = Math.max(max, horizontalOffenderOverflowPx(offender));
758
+ }
759
+ return roundPixels(max);
760
+ }
615
761
  function assessProfile(profile, evidence) {
616
762
  const checks = [];
617
763
  const viewports = evidence.viewports || [];
@@ -729,13 +875,19 @@ function assessProfile(profile, evidence) {
729
875
  });
730
876
  continue;
731
877
  }
732
- const failed = applicable.filter((viewport) => (viewport.overflow_px || 0) > maxOverflow);
878
+ const failed = applicable.filter((viewport) => horizontalBoundsOverflowPx(viewport) > maxOverflow);
733
879
  checks.push({
734
880
  type: check.type,
735
881
  label: check.label || check.type,
736
882
  status: failed.length ? "failed" : "passed",
737
- evidence: { max_overflow_px: maxOverflow, overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null), viewports: applicable.map((viewport) => viewport.name) },
738
- message: failed.length ? "Horizontal overflow exceeded " + maxOverflow + "px in " + failed.length + " viewport(s)." : undefined,
883
+ evidence: {
884
+ max_overflow_px: maxOverflow,
885
+ overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null),
886
+ bounds_overflow_px: applicable.map((viewport) => horizontalBoundsOverflowPx(viewport)),
887
+ overflow_offender_counts: applicable.map((viewport) => boundsOffendersForEvidence(viewport).length),
888
+ viewports: applicable.map((viewport) => viewport.name)
889
+ },
890
+ message: failed.length ? "Horizontal bounds overflow exceeded " + maxOverflow + "px in " + failed.length + " viewport(s)." : undefined,
739
891
  });
740
892
  continue;
741
893
  }
@@ -965,14 +1117,55 @@ async function captureViewport(viewport) {
965
1117
  const body = document.body;
966
1118
  const documentElement = document.documentElement;
967
1119
  const text = (body ? body.innerText : "").replace(/\s+/g, " ").trim();
1120
+ const clientWidth = documentElement ? documentElement.clientWidth : window.innerWidth;
1121
+ const scrollWidth = documentElement ? documentElement.scrollWidth : 0;
1122
+ const viewportWidth = clientWidth || window.innerWidth;
1123
+ const overflowOffenders = [];
1124
+ for (const element of Array.from(body ? body.querySelectorAll("*") : [])) {
1125
+ const rect = element.getBoundingClientRect();
1126
+ if (!rect || rect.width < 1 || rect.height < 1) continue;
1127
+ const style = window.getComputedStyle(element);
1128
+ if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") continue;
1129
+ const leftOverflow = Math.max(0, -rect.left);
1130
+ const rightOverflow = Math.max(0, rect.right - viewportWidth);
1131
+ const overflow = Math.max(leftOverflow, rightOverflow);
1132
+ if (overflow <= 0.5) continue;
1133
+ const tag = element.tagName ? element.tagName.toLowerCase() : "element";
1134
+ const id = element.id ? "#" + element.id : "";
1135
+ const className = typeof element.className === "string"
1136
+ ? element.className.trim().split(/\s+/).filter(Boolean).slice(0, 2).join(".")
1137
+ : "";
1138
+ overflowOffenders.push({
1139
+ selector: tag + id + (className ? "." + className : ""),
1140
+ tag,
1141
+ text: (element.textContent || "").replace(/\s+/g, " ").trim().slice(0, 120),
1142
+ overflow,
1143
+ left_overflow_px: leftOverflow,
1144
+ right_overflow_px: rightOverflow,
1145
+ viewport_width: viewportWidth,
1146
+ rect: {
1147
+ left: rect.left,
1148
+ right: rect.right,
1149
+ width: rect.width,
1150
+ },
1151
+ });
1152
+ }
1153
+ overflowOffenders.sort((a, b) => b.overflow - a.overflow);
1154
+ const boundsOverflowPx = Math.max(
1155
+ 0,
1156
+ scrollWidth - (clientWidth || viewportWidth),
1157
+ ...overflowOffenders.map((offender) => offender.overflow),
1158
+ );
968
1159
  return {
969
1160
  url: location.href,
970
1161
  pathname: location.pathname,
971
1162
  title: document.title,
972
1163
  body_text_length: text.length,
973
1164
  body_text_sample: text.slice(0, 8000),
974
- scroll_width: documentElement ? documentElement.scrollWidth : 0,
975
- client_width: documentElement ? documentElement.clientWidth : window.innerWidth,
1165
+ scroll_width: scrollWidth,
1166
+ client_width: clientWidth,
1167
+ bounds_overflow_px: Math.round(boundsOverflowPx * 100) / 100,
1168
+ overflow_offenders: overflowOffenders.slice(0, 10),
976
1169
  };
977
1170
  }).catch((error) => ({
978
1171
  url: page.url(),
@@ -982,6 +1175,8 @@ async function captureViewport(viewport) {
982
1175
  body_text_sample: "",
983
1176
  scroll_width: 0,
984
1177
  client_width: viewport.width,
1178
+ bounds_overflow_px: 0,
1179
+ overflow_offenders: [],
985
1180
  evaluation_error: String(error && error.message ? error.message : error).slice(0, 1000),
986
1181
  }));
987
1182
  const selectors = {};
@@ -1020,6 +1215,8 @@ async function captureViewport(viewport) {
1020
1215
  scroll_width: dom.scroll_width,
1021
1216
  client_width: dom.client_width,
1022
1217
  overflow_px: Math.max(0, (dom.scroll_width || 0) - (dom.client_width || viewport.width)),
1218
+ bounds_overflow_px: dom.bounds_overflow_px,
1219
+ overflow_offenders: dom.overflow_offenders || [],
1023
1220
  selectors,
1024
1221
  text_matches,
1025
1222
  setup_action_results: setupActionResults,
@@ -1051,6 +1248,8 @@ function buildProfileEvidence(currentViewports) {
1051
1248
  routes: currentViewports.map((viewport) => viewport.route),
1052
1249
  titles: currentViewports.map((viewport) => viewport.title),
1053
1250
  overflow_px: currentViewports.map((viewport) => viewport.overflow_px),
1251
+ bounds_overflow_px: currentViewports.map((viewport) => viewport.bounds_overflow_px),
1252
+ overflow_offender_counts: currentViewports.map((viewport) => (viewport.overflow_offenders || []).length),
1054
1253
  },
1055
1254
  };
1056
1255
  }
package/dist/cli.cjs CHANGED
@@ -6818,6 +6818,79 @@ function stringValue2(value) {
6818
6818
  function numberValue(value) {
6819
6819
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
6820
6820
  }
6821
+ function horizontalBoundsOverflowPx(value) {
6822
+ if (!isRecord(value)) return 0;
6823
+ let max = maxPositiveNumber(
6824
+ value.overflow_px,
6825
+ value.overflow,
6826
+ value.bounds_overflow_px,
6827
+ value.horizontal_overflow_px,
6828
+ value.left_overflow_px,
6829
+ value.right_overflow_px
6830
+ );
6831
+ for (const offender of boundsOffendersForEvidence(value)) {
6832
+ max = Math.max(max, horizontalOffenderOverflowPx(offender));
6833
+ }
6834
+ return roundPixels(max);
6835
+ }
6836
+ function horizontalOffenderOverflowPx(value) {
6837
+ if (!isRecord(value)) return 0;
6838
+ let max = maxPositiveNumber(
6839
+ value.overflow,
6840
+ value.overflow_px,
6841
+ value.bounds_overflow_px,
6842
+ value.horizontal_overflow_px,
6843
+ value.left_overflow_px,
6844
+ value.right_overflow_px,
6845
+ value.leftOverflowPx,
6846
+ value.rightOverflowPx
6847
+ );
6848
+ const clipped = isRecord(value.clipped || value.clip || value.clipping) ? value.clipped || value.clip || value.clipping : void 0;
6849
+ if (isRecord(clipped)) {
6850
+ max = Math.max(max, maxPositiveNumber(
6851
+ clipped.left,
6852
+ clipped.right,
6853
+ clipped.left_px,
6854
+ clipped.right_px,
6855
+ clipped.leftPx,
6856
+ clipped.rightPx
6857
+ ));
6858
+ }
6859
+ const rect = isRecord(value.rect || value.bounds || value.bounding_rect || value.boundingRect) ? value.rect || value.bounds || value.bounding_rect || value.boundingRect : void 0;
6860
+ const viewportWidth = numberValue(value.viewport_width ?? value.viewportWidth);
6861
+ if (isRecord(rect) && viewportWidth !== void 0) {
6862
+ const left = numberValue(rect.left);
6863
+ const right = numberValue(rect.right);
6864
+ if (left !== void 0 && left < 0) max = Math.max(max, Math.abs(left));
6865
+ if (right !== void 0 && right > viewportWidth) max = Math.max(max, right - viewportWidth);
6866
+ }
6867
+ return roundPixels(max);
6868
+ }
6869
+ function boundsOffendersForEvidence(value) {
6870
+ if (!isRecord(value)) return [];
6871
+ const offenders = [
6872
+ ...Array.isArray(value.overflow_offenders) ? value.overflow_offenders : [],
6873
+ ...Array.isArray(value.overflowOffenders) ? value.overflowOffenders : [],
6874
+ ...Array.isArray(value.bounds_offenders) ? value.bounds_offenders : [],
6875
+ ...Array.isArray(value.boundsOffenders) ? value.boundsOffenders : [],
6876
+ ...Array.isArray(value.clipped_elements) ? value.clipped_elements : [],
6877
+ ...Array.isArray(value.clippedElements) ? value.clippedElements : [],
6878
+ ...Array.isArray(value.clipping_offenders) ? value.clipping_offenders : [],
6879
+ ...Array.isArray(value.clippingOffenders) ? value.clippingOffenders : []
6880
+ ];
6881
+ return offenders.filter((item) => isRecord(item)).sort((a, b) => horizontalOffenderOverflowPx(b) - horizontalOffenderOverflowPx(a));
6882
+ }
6883
+ function maxPositiveNumber(...values) {
6884
+ let max = 0;
6885
+ for (const value of values) {
6886
+ const number = numberValue(value);
6887
+ if (number !== void 0 && number > max) max = number;
6888
+ }
6889
+ return max;
6890
+ }
6891
+ function roundPixels(value) {
6892
+ return Math.round(value * 100) / 100;
6893
+ }
6821
6894
  function timeoutSecValue(value) {
6822
6895
  const number = numberValue(value);
6823
6896
  return number && number > 0 ? Math.ceil(number) : void 0;
@@ -7160,7 +7233,7 @@ function assessCheckFromEvidence(check, evidence) {
7160
7233
  message: "No applicable viewport evidence was captured for overflow check."
7161
7234
  };
7162
7235
  }
7163
- const failed = applicable.filter((viewport) => (viewport.overflow_px ?? 0) > maxOverflow);
7236
+ const failed = applicable.filter((viewport) => horizontalBoundsOverflowPx(viewport) > maxOverflow);
7164
7237
  return {
7165
7238
  type: check.type,
7166
7239
  label: checkLabel(check),
@@ -7168,9 +7241,11 @@ function assessCheckFromEvidence(check, evidence) {
7168
7241
  evidence: {
7169
7242
  max_overflow_px: maxOverflow,
7170
7243
  overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null),
7244
+ bounds_overflow_px: applicable.map((viewport) => horizontalBoundsOverflowPx(viewport)),
7245
+ overflow_offender_counts: applicable.map((viewport) => boundsOffendersForEvidence(viewport).length),
7171
7246
  viewports: applicable.map((viewport) => viewport.name)
7172
7247
  },
7173
- message: failed.length ? `Horizontal overflow exceeded ${maxOverflow}px in ${failed.length} viewport(s).` : void 0
7248
+ message: failed.length ? `Horizontal bounds overflow exceeded ${maxOverflow}px in ${failed.length} viewport(s).` : void 0
7174
7249
  };
7175
7250
  }
7176
7251
  if (check.type === "no_fatal_console_errors") {
@@ -7376,6 +7451,77 @@ function textMatches(sample, check) {
7376
7451
  }
7377
7452
  return String(sample || "").includes(check.text || "");
7378
7453
  }
7454
+ function numberValue(value) {
7455
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
7456
+ }
7457
+ function maxPositiveNumber() {
7458
+ let max = 0;
7459
+ for (const value of arguments) {
7460
+ const number = numberValue(value);
7461
+ if (number !== undefined && number > max) max = number;
7462
+ }
7463
+ return max;
7464
+ }
7465
+ function roundPixels(value) {
7466
+ return Math.round(value * 100) / 100;
7467
+ }
7468
+ function isRecord(value) {
7469
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
7470
+ }
7471
+ function boundsOffendersForEvidence(value) {
7472
+ if (!isRecord(value)) return [];
7473
+ const offenders = []
7474
+ .concat(Array.isArray(value.overflow_offenders) ? value.overflow_offenders : [])
7475
+ .concat(Array.isArray(value.overflowOffenders) ? value.overflowOffenders : [])
7476
+ .concat(Array.isArray(value.bounds_offenders) ? value.bounds_offenders : [])
7477
+ .concat(Array.isArray(value.boundsOffenders) ? value.boundsOffenders : [])
7478
+ .concat(Array.isArray(value.clipped_elements) ? value.clipped_elements : [])
7479
+ .concat(Array.isArray(value.clippedElements) ? value.clippedElements : [])
7480
+ .concat(Array.isArray(value.clipping_offenders) ? value.clipping_offenders : [])
7481
+ .concat(Array.isArray(value.clippingOffenders) ? value.clippingOffenders : []);
7482
+ return offenders.filter(isRecord).sort((a, b) => horizontalOffenderOverflowPx(b) - horizontalOffenderOverflowPx(a));
7483
+ }
7484
+ function horizontalOffenderOverflowPx(value) {
7485
+ if (!isRecord(value)) return 0;
7486
+ let max = maxPositiveNumber(
7487
+ value.overflow,
7488
+ value.overflow_px,
7489
+ value.bounds_overflow_px,
7490
+ value.horizontal_overflow_px,
7491
+ value.left_overflow_px,
7492
+ value.right_overflow_px,
7493
+ value.leftOverflowPx,
7494
+ value.rightOverflowPx
7495
+ );
7496
+ const clipped = value.clipped || value.clip || value.clipping;
7497
+ if (isRecord(clipped)) {
7498
+ max = Math.max(max, maxPositiveNumber(clipped.left, clipped.right, clipped.left_px, clipped.right_px, clipped.leftPx, clipped.rightPx));
7499
+ }
7500
+ const rect = value.rect || value.bounds || value.bounding_rect || value.boundingRect;
7501
+ const viewportWidth = numberValue(value.viewport_width != null ? value.viewport_width : value.viewportWidth);
7502
+ if (isRecord(rect) && viewportWidth !== undefined) {
7503
+ const left = numberValue(rect.left);
7504
+ const right = numberValue(rect.right);
7505
+ if (left !== undefined && left < 0) max = Math.max(max, Math.abs(left));
7506
+ if (right !== undefined && right > viewportWidth) max = Math.max(max, right - viewportWidth);
7507
+ }
7508
+ return roundPixels(max);
7509
+ }
7510
+ function horizontalBoundsOverflowPx(value) {
7511
+ if (!isRecord(value)) return 0;
7512
+ let max = maxPositiveNumber(
7513
+ value.overflow_px,
7514
+ value.overflow,
7515
+ value.bounds_overflow_px,
7516
+ value.horizontal_overflow_px,
7517
+ value.left_overflow_px,
7518
+ value.right_overflow_px
7519
+ );
7520
+ for (const offender of boundsOffendersForEvidence(value)) {
7521
+ max = Math.max(max, horizontalOffenderOverflowPx(offender));
7522
+ }
7523
+ return roundPixels(max);
7524
+ }
7379
7525
  function assessProfile(profile, evidence) {
7380
7526
  const checks = [];
7381
7527
  const viewports = evidence.viewports || [];
@@ -7493,13 +7639,19 @@ function assessProfile(profile, evidence) {
7493
7639
  });
7494
7640
  continue;
7495
7641
  }
7496
- const failed = applicable.filter((viewport) => (viewport.overflow_px || 0) > maxOverflow);
7642
+ const failed = applicable.filter((viewport) => horizontalBoundsOverflowPx(viewport) > maxOverflow);
7497
7643
  checks.push({
7498
7644
  type: check.type,
7499
7645
  label: check.label || check.type,
7500
7646
  status: failed.length ? "failed" : "passed",
7501
- evidence: { max_overflow_px: maxOverflow, overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null), viewports: applicable.map((viewport) => viewport.name) },
7502
- message: failed.length ? "Horizontal overflow exceeded " + maxOverflow + "px in " + failed.length + " viewport(s)." : undefined,
7647
+ evidence: {
7648
+ max_overflow_px: maxOverflow,
7649
+ overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null),
7650
+ bounds_overflow_px: applicable.map((viewport) => horizontalBoundsOverflowPx(viewport)),
7651
+ overflow_offender_counts: applicable.map((viewport) => boundsOffendersForEvidence(viewport).length),
7652
+ viewports: applicable.map((viewport) => viewport.name)
7653
+ },
7654
+ message: failed.length ? "Horizontal bounds overflow exceeded " + maxOverflow + "px in " + failed.length + " viewport(s)." : undefined,
7503
7655
  });
7504
7656
  continue;
7505
7657
  }
@@ -7729,14 +7881,55 @@ async function captureViewport(viewport) {
7729
7881
  const body = document.body;
7730
7882
  const documentElement = document.documentElement;
7731
7883
  const text = (body ? body.innerText : "").replace(/\s+/g, " ").trim();
7884
+ const clientWidth = documentElement ? documentElement.clientWidth : window.innerWidth;
7885
+ const scrollWidth = documentElement ? documentElement.scrollWidth : 0;
7886
+ const viewportWidth = clientWidth || window.innerWidth;
7887
+ const overflowOffenders = [];
7888
+ for (const element of Array.from(body ? body.querySelectorAll("*") : [])) {
7889
+ const rect = element.getBoundingClientRect();
7890
+ if (!rect || rect.width < 1 || rect.height < 1) continue;
7891
+ const style = window.getComputedStyle(element);
7892
+ if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") continue;
7893
+ const leftOverflow = Math.max(0, -rect.left);
7894
+ const rightOverflow = Math.max(0, rect.right - viewportWidth);
7895
+ const overflow = Math.max(leftOverflow, rightOverflow);
7896
+ if (overflow <= 0.5) continue;
7897
+ const tag = element.tagName ? element.tagName.toLowerCase() : "element";
7898
+ const id = element.id ? "#" + element.id : "";
7899
+ const className = typeof element.className === "string"
7900
+ ? element.className.trim().split(/\s+/).filter(Boolean).slice(0, 2).join(".")
7901
+ : "";
7902
+ overflowOffenders.push({
7903
+ selector: tag + id + (className ? "." + className : ""),
7904
+ tag,
7905
+ text: (element.textContent || "").replace(/\s+/g, " ").trim().slice(0, 120),
7906
+ overflow,
7907
+ left_overflow_px: leftOverflow,
7908
+ right_overflow_px: rightOverflow,
7909
+ viewport_width: viewportWidth,
7910
+ rect: {
7911
+ left: rect.left,
7912
+ right: rect.right,
7913
+ width: rect.width,
7914
+ },
7915
+ });
7916
+ }
7917
+ overflowOffenders.sort((a, b) => b.overflow - a.overflow);
7918
+ const boundsOverflowPx = Math.max(
7919
+ 0,
7920
+ scrollWidth - (clientWidth || viewportWidth),
7921
+ ...overflowOffenders.map((offender) => offender.overflow),
7922
+ );
7732
7923
  return {
7733
7924
  url: location.href,
7734
7925
  pathname: location.pathname,
7735
7926
  title: document.title,
7736
7927
  body_text_length: text.length,
7737
7928
  body_text_sample: text.slice(0, 8000),
7738
- scroll_width: documentElement ? documentElement.scrollWidth : 0,
7739
- client_width: documentElement ? documentElement.clientWidth : window.innerWidth,
7929
+ scroll_width: scrollWidth,
7930
+ client_width: clientWidth,
7931
+ bounds_overflow_px: Math.round(boundsOverflowPx * 100) / 100,
7932
+ overflow_offenders: overflowOffenders.slice(0, 10),
7740
7933
  };
7741
7934
  }).catch((error) => ({
7742
7935
  url: page.url(),
@@ -7746,6 +7939,8 @@ async function captureViewport(viewport) {
7746
7939
  body_text_sample: "",
7747
7940
  scroll_width: 0,
7748
7941
  client_width: viewport.width,
7942
+ bounds_overflow_px: 0,
7943
+ overflow_offenders: [],
7749
7944
  evaluation_error: String(error && error.message ? error.message : error).slice(0, 1000),
7750
7945
  }));
7751
7946
  const selectors = {};
@@ -7784,6 +7979,8 @@ async function captureViewport(viewport) {
7784
7979
  scroll_width: dom.scroll_width,
7785
7980
  client_width: dom.client_width,
7786
7981
  overflow_px: Math.max(0, (dom.scroll_width || 0) - (dom.client_width || viewport.width)),
7982
+ bounds_overflow_px: dom.bounds_overflow_px,
7983
+ overflow_offenders: dom.overflow_offenders || [],
7787
7984
  selectors,
7788
7985
  text_matches,
7789
7986
  setup_action_results: setupActionResults,
@@ -7815,6 +8012,8 @@ function buildProfileEvidence(currentViewports) {
7815
8012
  routes: currentViewports.map((viewport) => viewport.route),
7816
8013
  titles: currentViewports.map((viewport) => viewport.title),
7817
8014
  overflow_px: currentViewports.map((viewport) => viewport.overflow_px),
8015
+ bounds_overflow_px: currentViewports.map((viewport) => viewport.bounds_overflow_px),
8016
+ overflow_offender_counts: currentViewports.map((viewport) => (viewport.overflow_offenders || []).length),
7818
8017
  },
7819
8018
  };
7820
8019
  }
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-TPUR67H5.js";
13
+ } from "./chunk-WTHSVHG5.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport