@elementor/editor-audits 4.3.0-beta1 → 4.3.0-beta3

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/dist/index.js CHANGED
@@ -51,10 +51,10 @@ var import_i18n22 = require("@wordpress/i18n");
51
51
  var import_editor_floating_panels2 = require("@elementor/editor-floating-panels");
52
52
 
53
53
  // src/components/audit-panel.tsx
54
- var React22 = __toESM(require("react"));
54
+ var React23 = __toESM(require("react"));
55
55
  var import_editor_elements5 = require("@elementor/editor-elements");
56
56
  var import_editor_floating_panels = require("@elementor/editor-floating-panels");
57
- var import_ui21 = require("@elementor/ui");
57
+ var import_ui22 = require("@elementor/ui");
58
58
  var import_i18n21 = require("@wordpress/i18n");
59
59
 
60
60
  // src/hooks/use-audit-report.ts
@@ -77,24 +77,72 @@ function getWindowConfig() {
77
77
  return config;
78
78
  }
79
79
 
80
+ // src/utils/session-expiration.ts
81
+ var NONCE_INVALID_CODE = "rest_cookie_invalid_nonce";
82
+ var DEFAULT_AJAX_URL = "/wp-admin/admin-ajax.php";
83
+ var SessionExpiredError = class extends Error {
84
+ };
85
+ var nonceRefreshPromise = null;
86
+ function isNonceInvalidError(error) {
87
+ const response = error?.response;
88
+ return response?.status === 403 && response?.data?.code === NONCE_INVALID_CODE;
89
+ }
90
+ async function refreshAuditsNonce() {
91
+ if (nonceRefreshPromise) {
92
+ return nonceRefreshPromise;
93
+ }
94
+ nonceRefreshPromise = requestFreshNonce();
95
+ try {
96
+ return await nonceRefreshPromise;
97
+ } finally {
98
+ nonceRefreshPromise = null;
99
+ }
100
+ }
101
+ async function requestFreshNonce() {
102
+ const ajaxUrl = window.elementorCommon?.ajax?.config?.url ?? DEFAULT_AJAX_URL;
103
+ const url = new URL(ajaxUrl, window.location.origin);
104
+ url.searchParams.set("action", "rest-nonce");
105
+ const response = await fetch(url.toString(), { credentials: "same-origin" });
106
+ if (!response.ok) {
107
+ throw new Error(`Failed to refresh audits nonce: HTTP ${response.status}`);
108
+ }
109
+ const nonce = await response.text();
110
+ if (!nonce || "0" === nonce) {
111
+ throw new SessionExpiredError("Session expired \u2014 received invalid nonce");
112
+ }
113
+ window.elementorAudits = { ...getWindowConfig(), nonce };
114
+ return nonce;
115
+ }
116
+
80
117
  // src/api/page-context-client.ts
81
118
  async function fetchPageContext(documentId, attachmentIds) {
119
+ return requestPageContext(documentId, attachmentIds, true);
120
+ }
121
+ async function requestPageContext(documentId, attachmentIds, allowNonceRetry) {
82
122
  const { restNamespace, nonce } = getWindowConfig();
83
123
  const url = `${restNamespace}/audits/page-context`;
84
- const response = await (0, import_http_client.httpService)().get(url, {
85
- params: {
86
- document_id: documentId,
87
- attachment_ids: attachmentIds
88
- },
89
- headers: { "X-WP-Nonce": nonce }
90
- });
91
- return response.data;
124
+ try {
125
+ const response = await (0, import_http_client.httpService)().get(url, {
126
+ params: {
127
+ document_id: documentId,
128
+ attachment_ids: attachmentIds
129
+ },
130
+ headers: { "X-WP-Nonce": nonce }
131
+ });
132
+ return response.data;
133
+ } catch (error) {
134
+ if (!allowNonceRetry || !isNonceInvalidError(error)) {
135
+ throw error;
136
+ }
137
+ await refreshAuditsNonce();
138
+ return requestPageContext(documentId, attachmentIds, false);
139
+ }
92
140
  }
93
141
 
94
142
  // src/registry.ts
95
143
  var registry = /* @__PURE__ */ new Map();
96
- function registerAudit(audit21) {
97
- registry.set(audit21.id, audit21);
144
+ function registerAudit(audit23) {
145
+ registry.set(audit23.id, audit23);
98
146
  }
99
147
  function getRegisteredAudits() {
100
148
  return Array.from(registry.values());
@@ -130,6 +178,11 @@ var CATEGORY_LABELS = {
130
178
  // src/utils/audit-status-summary.ts
131
179
  var import_i18n2 = require("@wordpress/i18n");
132
180
 
181
+ // src/utils/is-scored-audit.ts
182
+ function isScoredAudit(audit23) {
183
+ return audit23.weight > 0;
184
+ }
185
+
133
186
  // src/utils/sort-failed-audits.ts
134
187
  var SEVERITY_SORTING_RANK = {
135
188
  error: 0,
@@ -160,7 +213,9 @@ function partitionAuditResults(report, options = {}) {
160
213
  switch (run.result.status) {
161
214
  case "fail":
162
215
  failed.push({ ...run, result: run.result });
163
- totalViolations += run.result.violations.length;
216
+ if (isScoredAudit(run.audit)) {
217
+ totalViolations += run.result.violations.length;
218
+ }
164
219
  break;
165
220
  case "pass":
166
221
  passed.push({ ...run, result: run.result });
@@ -181,7 +236,10 @@ function auditStatusDisplayCounts(report) {
181
236
  let pass = 0;
182
237
  let skipped = 0;
183
238
  let totalViolations = 0;
184
- for (const { result } of report.auditResults) {
239
+ for (const { audit: audit23, result } of report.auditResults) {
240
+ if (!isScoredAudit(audit23)) {
241
+ continue;
242
+ }
185
243
  switch (result.status) {
186
244
  case "fail":
187
245
  totalViolations += result.violations.length;
@@ -229,16 +287,16 @@ function computeReport(documentId, results) {
229
287
  const accumulators = Object.fromEntries(
230
288
  ALL_CATEGORIES.map((c) => [c, { totalWeight: 0, passedWeight: 0, total: 0, failed: 0 }])
231
289
  );
232
- for (const { audit: audit21, result } of results) {
290
+ for (const { audit: audit23, result } of results) {
233
291
  if (result.status === "skipped") {
234
292
  continue;
235
293
  }
236
- for (const category of audit21.categories) {
294
+ for (const category of audit23.categories) {
237
295
  const acc = accumulators[category];
238
296
  acc.total++;
239
- acc.totalWeight += audit21.weight;
297
+ acc.totalWeight += audit23.weight;
240
298
  if (result.status === "pass") {
241
- acc.passedWeight += audit21.weight;
299
+ acc.passedWeight += audit23.weight;
242
300
  } else {
243
301
  acc.failed++;
244
302
  }
@@ -427,10 +485,10 @@ async function runPageAudit(documentId) {
427
485
  const ctx = { documentId, elements, pageContext, kit };
428
486
  const registered = getRegisteredAudits();
429
487
  const auditResults = await Promise.all(
430
- registered.map(async (audit21) => {
431
- const { evaluate: _evaluate, ...meta } = audit21;
488
+ registered.map(async (audit23) => {
489
+ const { evaluate: _evaluate, ...meta } = audit23;
432
490
  try {
433
- const result = await audit21.evaluate(ctx);
491
+ const result = await audit23.evaluate(ctx);
434
492
  return { audit: meta, result };
435
493
  } catch (error) {
436
494
  const reason = error instanceof Error ? error.message : "unknown-error";
@@ -460,6 +518,9 @@ var slice = (0, import_store.__createSlice)({
460
518
  state.status = "error";
461
519
  state.error = action.payload;
462
520
  },
521
+ runAborted(state) {
522
+ state.status = state.report ? "ready" : "idle";
523
+ },
463
524
  reportRestored(state, action) {
464
525
  state.status = "ready";
465
526
  state.report = action.payload;
@@ -521,6 +582,10 @@ function useAuditReport() {
521
582
  dispatch(slice.actions.runSucceeded(nextReport));
522
583
  persistReport(documentIdToRun, nextReport);
523
584
  } catch (e) {
585
+ if (e instanceof SessionExpiredError) {
586
+ dispatch(slice.actions.runAborted());
587
+ return;
588
+ }
524
589
  dispatch(slice.actions.runFailed(e instanceof Error ? e.message : "Unknown error"));
525
590
  }
526
591
  };
@@ -694,14 +759,14 @@ function WelcomePage() {
694
759
  }
695
760
 
696
761
  // src/components/report-shell.tsx
697
- var React21 = __toESM(require("react"));
762
+ var React22 = __toESM(require("react"));
698
763
  var import_react5 = require("react");
699
- var import_ui20 = require("@elementor/ui");
764
+ var import_ui21 = require("@elementor/ui");
700
765
  var import_i18n20 = require("@wordpress/i18n");
701
766
 
702
767
  // src/components/pages/all-audits-page.tsx
703
- var React11 = __toESM(require("react"));
704
- var import_ui10 = require("@elementor/ui");
768
+ var React12 = __toESM(require("react"));
769
+ var import_ui11 = require("@elementor/ui");
705
770
  var import_i18n12 = require("@wordpress/i18n");
706
771
 
707
772
  // src/components/status-section.tsx
@@ -712,7 +777,7 @@ var import_ui5 = require("@elementor/ui");
712
777
  var import_i18n7 = require("@wordpress/i18n");
713
778
  function StatusSection({ label, count, color = "default", defaultExpanded = false, children }) {
714
779
  const [expanded, setExpanded] = (0, import_react3.useState)(defaultExpanded);
715
- if (count === 0) {
780
+ if (React5.Children.count(children) === 0) {
716
781
  return null;
717
782
  }
718
783
  return /* @__PURE__ */ React5.createElement(import_ui5.Box, { sx: { paddingBlock: 1 } }, /* @__PURE__ */ React5.createElement(
@@ -753,11 +818,11 @@ function SubpageHeader({ title, onBack, backLabel, icon }) {
753
818
  }
754
819
 
755
820
  // src/components/violation-row.tsx
756
- var React10 = __toESM(require("react"));
821
+ var React11 = __toESM(require("react"));
757
822
  var import_react4 = require("react");
758
823
  var import_editor_elements4 = require("@elementor/editor-elements");
759
824
  var import_icons7 = require("@elementor/icons");
760
- var import_ui9 = require("@elementor/ui");
825
+ var import_ui10 = require("@elementor/ui");
761
826
  var import_i18n11 = require("@wordpress/i18n");
762
827
 
763
828
  // src/hooks/focus-violation.ts
@@ -870,14 +935,26 @@ function SeverityIcon({ severity }) {
870
935
  return /* @__PURE__ */ React8.createElement(Icon, { fontSize: "small", color });
871
936
  }
872
937
 
873
- // src/components/violation-icons.tsx
938
+ // src/components/violation-cta-button.tsx
874
939
  var React9 = __toESM(require("react"));
875
- var import_icons6 = require("@elementor/icons");
876
940
  var import_ui8 = require("@elementor/ui");
941
+ function ViolationCtaButton({ ctaLabel, externalUrl }) {
942
+ const handleClick = (event) => {
943
+ event.stopPropagation();
944
+ event.preventDefault();
945
+ window.open(externalUrl, "_blank", "noopener");
946
+ };
947
+ return /* @__PURE__ */ React9.createElement(import_ui8.Button, { variant: "outlined", color: "secondary", size: "small", onClick: handleClick }, ctaLabel);
948
+ }
949
+
950
+ // src/components/violation-icons.tsx
951
+ var React10 = __toESM(require("react"));
952
+ var import_icons6 = require("@elementor/icons");
953
+ var import_ui9 = require("@elementor/ui");
877
954
  function ViolationIcon({ violation, widgetIcon }) {
878
955
  if (widgetIcon) {
879
- return /* @__PURE__ */ React9.createElement(
880
- import_ui8.Box,
956
+ return /* @__PURE__ */ React10.createElement(
957
+ import_ui9.Box,
881
958
  {
882
959
  component: "i",
883
960
  className: widgetIcon,
@@ -887,29 +964,29 @@ function ViolationIcon({ violation, widgetIcon }) {
887
964
  );
888
965
  }
889
966
  if (violation.targetHint === "page-settings") {
890
- return /* @__PURE__ */ React9.createElement(import_icons6.FileSettingsIcon, { fontSize: "inherit", "aria-hidden": true });
967
+ return /* @__PURE__ */ React10.createElement(import_icons6.FileSettingsIcon, { fontSize: "inherit", "aria-hidden": true });
891
968
  }
892
969
  if (violation.targetHint === "site-settings" || violation.targetHint === "site-identity-settings") {
893
- return /* @__PURE__ */ React9.createElement(import_icons6.SettingsIcon, { fontSize: "inherit", "aria-hidden": true });
970
+ return /* @__PURE__ */ React10.createElement(import_icons6.SettingsIcon, { fontSize: "inherit", "aria-hidden": true });
894
971
  }
895
- return /* @__PURE__ */ React9.createElement(import_icons6.ShieldCheckIcon, { fontSize: "inherit", "aria-hidden": true });
972
+ return /* @__PURE__ */ React10.createElement(import_icons6.ShieldCheckIcon, { fontSize: "inherit", "aria-hidden": true });
896
973
  }
897
974
 
898
975
  // src/components/violation-row.tsx
899
976
  function SkipReasonTooltip({ reason }) {
900
- return /* @__PURE__ */ React10.createElement(import_ui9.Tooltip, { title: reason, placement: "top" }, /* @__PURE__ */ React10.createElement(import_ui9.Box, { "aria-label": reason, component: "span", sx: { display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React10.createElement(import_icons7.HelpIcon, { fontSize: "small", color: "action" })));
977
+ return /* @__PURE__ */ React11.createElement(import_ui10.Tooltip, { title: reason, placement: "top" }, /* @__PURE__ */ React11.createElement(import_ui10.Box, { "aria-label": reason, component: "span", sx: { display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React11.createElement(import_icons7.HelpIcon, { fontSize: "small", color: "action" })));
901
978
  }
902
- function StatusIndicator({ audit: audit21, violations }) {
979
+ function StatusIndicator({ audit: audit23, violations }) {
903
980
  if (violations) {
904
- return /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(import_ui9.Typography, { variant: "caption", color: "text.secondary", fontWeight: "bold" }, violations.length), /* @__PURE__ */ React10.createElement(SeverityIcon, { severity: audit21.severity }));
981
+ return /* @__PURE__ */ React11.createElement(React11.Fragment, null, isScoredAudit(audit23) && /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", color: "text.secondary", fontWeight: "bold" }, violations.length), /* @__PURE__ */ React11.createElement(SeverityIcon, { severity: audit23.severity }));
905
982
  }
906
- return /* @__PURE__ */ React10.createElement(import_icons7.CheckIcon, { fontSize: "small", color: "success" });
983
+ return /* @__PURE__ */ React11.createElement(import_icons7.CheckIcon, { fontSize: "small", color: "success" });
907
984
  }
908
- function ViolationRow({ audit: audit21, skipReason, violations }) {
985
+ function ViolationRow({ audit: audit23, skipReason, violations }) {
909
986
  const [expanded, setExpanded] = (0, import_react4.useState)(false);
910
987
  const toggleExpanded = () => setExpanded((value) => !value);
911
- return /* @__PURE__ */ React10.createElement(import_ui9.Box, { sx: { borderBottom: 1, borderColor: "divider", paddingBlock: 0.5 } }, /* @__PURE__ */ React10.createElement(import_ui9.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React10.createElement(
912
- import_ui9.Box,
988
+ return /* @__PURE__ */ React11.createElement(import_ui10.Box, { sx: { borderBottom: 1, borderColor: "divider", paddingBlock: 0.5 } }, /* @__PURE__ */ React11.createElement(import_ui10.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React11.createElement(
989
+ import_ui10.Box,
913
990
  {
914
991
  sx: {
915
992
  alignItems: "center",
@@ -921,16 +998,16 @@ function ViolationRow({ audit: audit21, skipReason, violations }) {
921
998
  },
922
999
  onClick: toggleExpanded
923
1000
  },
924
- /* @__PURE__ */ React10.createElement(import_ui9.Typography, { variant: "body2", sx: { flex: 1 } }, audit21.title),
925
- !skipReason && /* @__PURE__ */ React10.createElement(StatusIndicator, { audit: audit21, violations })
926
- ), skipReason && /* @__PURE__ */ React10.createElement(SkipReasonTooltip, { reason: skipReason }), /* @__PURE__ */ React10.createElement(
927
- import_ui9.IconButton,
1001
+ /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "body2", sx: { flex: 1 } }, audit23.title),
1002
+ !skipReason && /* @__PURE__ */ React11.createElement(StatusIndicator, { audit: audit23, violations })
1003
+ ), skipReason && /* @__PURE__ */ React11.createElement(SkipReasonTooltip, { reason: skipReason }), /* @__PURE__ */ React11.createElement(
1004
+ import_ui10.IconButton,
928
1005
  {
929
1006
  size: "small",
930
1007
  "aria-label": expanded ? (0, import_i18n11.__)("Collapse", "elementor") : (0, import_i18n11.__)("Expand", "elementor"),
931
1008
  onClick: toggleExpanded
932
1009
  },
933
- /* @__PURE__ */ React10.createElement(
1010
+ /* @__PURE__ */ React11.createElement(
934
1011
  import_icons7.ChevronDownIcon,
935
1012
  {
936
1013
  fontSize: "small",
@@ -940,30 +1017,30 @@ function ViolationRow({ audit: audit21, skipReason, violations }) {
940
1017
  }
941
1018
  }
942
1019
  )
943
- )), /* @__PURE__ */ React10.createElement(import_ui9.Collapse, { in: expanded }, /* @__PURE__ */ React10.createElement(import_ui9.Box, { sx: { display: "flex", flexDirection: "column", gap: 1, paddingBlock: 1 } }, /* @__PURE__ */ React10.createElement(
944
- import_ui9.Alert,
1020
+ )), /* @__PURE__ */ React11.createElement(import_ui10.Collapse, { in: expanded }, /* @__PURE__ */ React11.createElement(import_ui10.Box, { sx: { display: "flex", flexDirection: "column", gap: 1, paddingBlock: 1 } }, /* @__PURE__ */ React11.createElement(
1021
+ import_ui10.Alert,
945
1022
  {
946
1023
  severity: "secondary",
947
1024
  sx: { p: 1 },
948
- icon: /* @__PURE__ */ React10.createElement(import_icons7.AlertCircleIcon, { fontSize: "small", color: "secondary", "aria-hidden": true })
1025
+ icon: /* @__PURE__ */ React11.createElement(import_icons7.AlertCircleIcon, { fontSize: "small", color: "secondary", "aria-hidden": true })
949
1026
  },
950
- /* @__PURE__ */ React10.createElement(import_ui9.AlertTitle, null, /* @__PURE__ */ React10.createElement(import_ui9.Typography, { variant: "caption", component: "p", color: "text.primary", fontWeight: "bold" }, (0, import_i18n11.__)("What's the issue", "elementor"))),
951
- /* @__PURE__ */ React10.createElement(import_ui9.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit21.description)
952
- ), /* @__PURE__ */ React10.createElement(
953
- import_ui9.Alert,
1027
+ /* @__PURE__ */ React11.createElement(import_ui10.AlertTitle, null, /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.primary", fontWeight: "bold" }, (0, import_i18n11.__)("What's the issue", "elementor"))),
1028
+ /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit23.description)
1029
+ ), /* @__PURE__ */ React11.createElement(
1030
+ import_ui10.Alert,
954
1031
  {
955
1032
  severity: "info",
956
1033
  sx: { p: 1 },
957
- icon: /* @__PURE__ */ React10.createElement(import_icons7.BulbIcon, { fontSize: "small", color: "info", "aria-hidden": true })
1034
+ icon: /* @__PURE__ */ React11.createElement(import_icons7.BulbIcon, { fontSize: "small", color: "info", "aria-hidden": true })
958
1035
  },
959
- /* @__PURE__ */ React10.createElement(import_ui9.AlertTitle, null, /* @__PURE__ */ React10.createElement(import_ui9.Typography, { variant: "caption", component: "p", color: "text.primary", fontWeight: "bold" }, (0, import_i18n11.__)("How to resolve", "elementor"))),
960
- /* @__PURE__ */ React10.createElement(import_ui9.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit21.fixHint)
961
- )), violations && violations.length > 0 && /* @__PURE__ */ React10.createElement(import_ui9.Box, { role: "list", sx: { paddingBlockEnd: 1, paddingInlineStart: 2 } }, violations.map((violation, idx) => {
1036
+ /* @__PURE__ */ React11.createElement(import_ui10.AlertTitle, null, /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.primary", fontWeight: "bold" }, (0, import_i18n11.__)("How to resolve", "elementor"))),
1037
+ /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit23.fixHint)
1038
+ )), violations && violations.length > 0 && /* @__PURE__ */ React11.createElement(import_ui10.Box, { role: "list", sx: { paddingBlockEnd: 1, paddingInlineStart: 2 } }, violations.map((violation, idx) => {
962
1039
  const widgetIcon = violation.elementId ? (0, import_editor_elements4.getElementIcon)(violation.elementId) : null;
963
1040
  const elementTitle = violation.elementId ? (0, import_editor_elements4.getElementTitle)(violation.elementId) : null;
964
1041
  const rowLabel = elementTitle ? `${elementTitle} - ${violation.label}` : violation.label;
965
- return /* @__PURE__ */ React10.createElement(
966
- import_ui9.Box,
1042
+ return /* @__PURE__ */ React11.createElement(
1043
+ import_ui10.Box,
967
1044
  {
968
1045
  key: idx,
969
1046
  role: "button",
@@ -985,10 +1062,23 @@ function ViolationRow({ audit: audit21, skipReason, violations }) {
985
1062
  }
986
1063
  }
987
1064
  },
988
- /* @__PURE__ */ React10.createElement(ViolationIcon, { violation, widgetIcon }),
989
- /* @__PURE__ */ React10.createElement(import_ui9.Box, { sx: { flex: 1 } }, /* @__PURE__ */ React10.createElement(import_ui9.Typography, { variant: "caption" }, rowLabel), violation.detail && /* @__PURE__ */ React10.createElement(import_ui9.Typography, { variant: "caption", color: "text.secondary" }, violation.detail)),
990
- violation.angieFix && /* @__PURE__ */ React10.createElement(FixViolationWithAngie, { prompt: buildAngiePrompt(rowLabel) }),
991
- /* @__PURE__ */ React10.createElement(import_icons7.EyeIcon, { className: "violation-hover-icon", fontSize: "tiny", "aria-hidden": true })
1065
+ /* @__PURE__ */ React11.createElement(ViolationIcon, { violation, widgetIcon }),
1066
+ /* @__PURE__ */ React11.createElement(import_ui10.Box, { sx: { flex: 1 } }, /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption" }, rowLabel), violation.detail && /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", color: "text.secondary" }, violation.detail)),
1067
+ violation.angieFix && /* @__PURE__ */ React11.createElement(FixViolationWithAngie, { prompt: buildAngiePrompt(rowLabel) }),
1068
+ violation.ctaLabel && violation.externalUrl ? /* @__PURE__ */ React11.createElement(
1069
+ ViolationCtaButton,
1070
+ {
1071
+ ctaLabel: violation.ctaLabel,
1072
+ externalUrl: violation.externalUrl
1073
+ }
1074
+ ) : /* @__PURE__ */ React11.createElement(
1075
+ import_icons7.EyeIcon,
1076
+ {
1077
+ className: "violation-hover-icon",
1078
+ fontSize: "tiny",
1079
+ "aria-hidden": true
1080
+ }
1081
+ )
992
1082
  );
993
1083
  }))));
994
1084
  }
@@ -999,7 +1089,7 @@ function AllAuditsPage({ initialExpandedStatus, onBack, report }) {
999
1089
  const expandFail = !initialExpandedStatus || initialExpandedStatus === "fail";
1000
1090
  const expandPass = initialExpandedStatus === "pass";
1001
1091
  const expandSkipped = initialExpandedStatus === "skipped";
1002
- return /* @__PURE__ */ React11.createElement(import_ui10.Box, { key: initialExpandedStatus ?? "default" }, /* @__PURE__ */ React11.createElement(SubpageHeader, { title: (0, import_i18n12.__)("All audits", "elementor"), onBack }), /* @__PURE__ */ React11.createElement(import_ui10.Box, { sx: { p: 1 } }, /* @__PURE__ */ React11.createElement(
1092
+ return /* @__PURE__ */ React12.createElement(import_ui11.Box, { key: initialExpandedStatus ?? "default" }, /* @__PURE__ */ React12.createElement(SubpageHeader, { title: (0, import_i18n12.__)("All audits", "elementor"), onBack }), /* @__PURE__ */ React12.createElement(import_ui11.Box, { sx: { p: 1 } }, /* @__PURE__ */ React12.createElement(
1003
1093
  StatusSection,
1004
1094
  {
1005
1095
  label: auditStatusLabel("fail"),
@@ -1007,8 +1097,8 @@ function AllAuditsPage({ initialExpandedStatus, onBack, report }) {
1007
1097
  color: auditStatusColor("fail"),
1008
1098
  defaultExpanded: expandFail
1009
1099
  },
1010
- failed.map((r) => /* @__PURE__ */ React11.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, violations: r.result.violations }))
1011
- ), /* @__PURE__ */ React11.createElement(
1100
+ failed.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, violations: r.result.violations }))
1101
+ ), /* @__PURE__ */ React12.createElement(
1012
1102
  StatusSection,
1013
1103
  {
1014
1104
  label: auditStatusLabel("pass"),
@@ -1016,8 +1106,8 @@ function AllAuditsPage({ initialExpandedStatus, onBack, report }) {
1016
1106
  color: auditStatusColor("pass"),
1017
1107
  defaultExpanded: expandPass
1018
1108
  },
1019
- passed.map((r) => /* @__PURE__ */ React11.createElement(ViolationRow, { key: r.audit.id, audit: r.audit }))
1020
- ), /* @__PURE__ */ React11.createElement(
1109
+ passed.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit }))
1110
+ ), /* @__PURE__ */ React12.createElement(
1021
1111
  StatusSection,
1022
1112
  {
1023
1113
  label: auditStatusLabel("skipped"),
@@ -1025,13 +1115,13 @@ function AllAuditsPage({ initialExpandedStatus, onBack, report }) {
1025
1115
  color: auditStatusColor("skipped"),
1026
1116
  defaultExpanded: expandSkipped
1027
1117
  },
1028
- skipped.map((r) => /* @__PURE__ */ React11.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, skipReason: r.result.reason }))
1118
+ skipped.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, skipReason: r.result.reason }))
1029
1119
  )));
1030
1120
  }
1031
1121
 
1032
1122
  // src/components/pages/category-page.tsx
1033
- var React12 = __toESM(require("react"));
1034
- var import_ui11 = require("@elementor/ui");
1123
+ var React13 = __toESM(require("react"));
1124
+ var import_ui12 = require("@elementor/ui");
1035
1125
  var import_i18n13 = require("@wordpress/i18n");
1036
1126
 
1037
1127
  // src/components/category-icons.ts
@@ -1048,15 +1138,15 @@ var CATEGORY_ICONS = {
1048
1138
  function CategoryPage({ category, report, onBack }) {
1049
1139
  const Icon = CATEGORY_ICONS[category];
1050
1140
  const { failed, passed, totalViolations } = partitionAuditResults(report, { category });
1051
- return /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(
1141
+ return /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(
1052
1142
  SubpageHeader,
1053
1143
  {
1054
1144
  title: CATEGORY_LABELS[category],
1055
1145
  onBack,
1056
1146
  backLabel: (0, import_i18n13.__)("Back to all issues", "elementor"),
1057
- icon: /* @__PURE__ */ React12.createElement(Icon, { fontSize: "small", color: "action" })
1147
+ icon: /* @__PURE__ */ React13.createElement(Icon, { fontSize: "small", color: "action" })
1058
1148
  }
1059
- ), /* @__PURE__ */ React12.createElement(import_ui11.Box, { sx: { p: 1 } }, /* @__PURE__ */ React12.createElement(
1149
+ ), /* @__PURE__ */ React13.createElement(import_ui12.Box, { sx: { p: 1 } }, /* @__PURE__ */ React13.createElement(
1060
1150
  StatusSection,
1061
1151
  {
1062
1152
  label: (0, import_i18n13.__)("Failed audits", "elementor"),
@@ -1064,13 +1154,13 @@ function CategoryPage({ category, report, onBack }) {
1064
1154
  color: "error",
1065
1155
  defaultExpanded: true
1066
1156
  },
1067
- failed.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, violations: r.result.violations }))
1068
- ), /* @__PURE__ */ React12.createElement(StatusSection, { label: (0, import_i18n13.__)("Passed audits", "elementor"), count: passed.length, color: "success" }, passed.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit })))));
1157
+ failed.map((r) => /* @__PURE__ */ React13.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, violations: r.result.violations }))
1158
+ ), /* @__PURE__ */ React13.createElement(StatusSection, { label: (0, import_i18n13.__)("Passed audits", "elementor"), count: passed.length, color: "success" }, passed.map((r) => /* @__PURE__ */ React13.createElement(ViolationRow, { key: r.audit.id, audit: r.audit })))));
1069
1159
  }
1070
1160
 
1071
1161
  // src/components/pages/issues-page.tsx
1072
- var React16 = __toESM(require("react"));
1073
- var import_ui15 = require("@elementor/ui");
1162
+ var React17 = __toESM(require("react"));
1163
+ var import_ui16 = require("@elementor/ui");
1074
1164
  var import_i18n17 = require("@wordpress/i18n");
1075
1165
 
1076
1166
  // src/utils/severity-counts.ts
@@ -1078,14 +1168,17 @@ var import_i18n14 = require("@wordpress/i18n");
1078
1168
  var ALL_SEVERITIES = ["error", "warning", "info"];
1079
1169
  function countSeverities(report, category) {
1080
1170
  const counts = { error: 0, warning: 0, info: 0 };
1081
- for (const { audit: audit21, result } of report.auditResults) {
1171
+ for (const { audit: audit23, result } of report.auditResults) {
1082
1172
  if (result.status !== "fail") {
1083
1173
  continue;
1084
1174
  }
1085
- if (category && !audit21.categories.includes(category)) {
1175
+ if (category && !audit23.categories.includes(category)) {
1176
+ continue;
1177
+ }
1178
+ if (!isScoredAudit(audit23)) {
1086
1179
  continue;
1087
1180
  }
1088
- counts[audit21.severity] += result.violations.length;
1181
+ counts[audit23.severity] += result.violations.length;
1089
1182
  }
1090
1183
  return counts;
1091
1184
  }
@@ -1123,14 +1216,14 @@ function severityRemainingCountLabel(severity, count) {
1123
1216
  }
1124
1217
 
1125
1218
  // src/components/issues-category-row.tsx
1126
- var React13 = __toESM(require("react"));
1219
+ var React14 = __toESM(require("react"));
1127
1220
  var import_icons9 = require("@elementor/icons");
1128
- var import_ui12 = require("@elementor/ui");
1221
+ var import_ui13 = require("@elementor/ui");
1129
1222
  function IssuesCategoryRow({ category, label, counts, onClick }) {
1130
- const isRtl = "rtl" === (0, import_ui12.useTheme)().direction;
1223
+ const isRtl = "rtl" === (0, import_ui13.useTheme)().direction;
1131
1224
  const Icon = CATEGORY_ICONS[category];
1132
- return /* @__PURE__ */ React13.createElement(
1133
- import_ui12.Box,
1225
+ return /* @__PURE__ */ React14.createElement(
1226
+ import_ui13.Box,
1134
1227
  {
1135
1228
  role: "button",
1136
1229
  tabIndex: 0,
@@ -1151,16 +1244,16 @@ function IssuesCategoryRow({ category, label, counts, onClick }) {
1151
1244
  "&:focus-visible": { outline: "2px solid", outlineColor: "primary.main" }
1152
1245
  }
1153
1246
  },
1154
- /* @__PURE__ */ React13.createElement(Icon, { fontSize: "small", color: "action" }),
1155
- /* @__PURE__ */ React13.createElement(import_ui12.Typography, { variant: "body2", fontWeight: "bold", sx: { flex: 1 } }, label),
1156
- /* @__PURE__ */ React13.createElement(import_ui12.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, ALL_SEVERITIES.filter((s) => counts[s] > 0).map((severity) => /* @__PURE__ */ React13.createElement(import_ui12.Box, { key: severity, sx: { display: "flex", alignItems: "center", gap: 0.25 } }, /* @__PURE__ */ React13.createElement(SeverityIcon, { severity }), /* @__PURE__ */ React13.createElement(import_ui12.Typography, { variant: "caption", color: "text.primary", fontWeight: "bold" }, counts[severity])))),
1157
- /* @__PURE__ */ React13.createElement(import_ui12.Rotate, { in: isRtl }, /* @__PURE__ */ React13.createElement(import_icons9.ChevronRightIcon, { fontSize: "small", color: "action" }))
1247
+ /* @__PURE__ */ React14.createElement(Icon, { fontSize: "small", color: "action" }),
1248
+ /* @__PURE__ */ React14.createElement(import_ui13.Typography, { variant: "body2", fontWeight: "bold", sx: { flex: 1 } }, label),
1249
+ /* @__PURE__ */ React14.createElement(import_ui13.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, ALL_SEVERITIES.filter((s) => counts[s] > 0).map((severity) => /* @__PURE__ */ React14.createElement(import_ui13.Box, { key: severity, sx: { display: "flex", alignItems: "center", gap: 0.25 } }, /* @__PURE__ */ React14.createElement(SeverityIcon, { severity }), /* @__PURE__ */ React14.createElement(import_ui13.Typography, { variant: "caption", color: "text.primary", fontWeight: "bold" }, counts[severity])))),
1250
+ /* @__PURE__ */ React14.createElement(import_ui13.Rotate, { in: isRtl }, /* @__PURE__ */ React14.createElement(import_icons9.ChevronRightIcon, { fontSize: "small", color: "action" }))
1158
1251
  );
1159
1252
  }
1160
1253
 
1161
1254
  // src/components/promotions.tsx
1162
- var React15 = __toESM(require("react"));
1163
- var import_ui14 = require("@elementor/ui");
1255
+ var React16 = __toESM(require("react"));
1256
+ var import_ui15 = require("@elementor/ui");
1164
1257
  var import_i18n16 = require("@wordpress/i18n");
1165
1258
 
1166
1259
  // src/register-promotions.ts
@@ -1197,6 +1290,13 @@ var PROMOTIONS = [
1197
1290
  formatSubtitle: (run) => run.result.status === "fail" ? (0, import_i18n15.__)("Generate cookie policy", "elementor") : null,
1198
1291
  getCtaUrl: firstFailExternalUrl
1199
1292
  },
1293
+ {
1294
+ auditId: "audits/scan-for-cookies",
1295
+ icon: import_icons10.ElementorCookieIcon,
1296
+ ctaLabel: (0, import_i18n15.__)("Scan", "elementor"),
1297
+ formatSubtitle: (run) => run.result.status === "fail" ? (0, import_i18n15.__)("Scan this page for cookies", "elementor") : null,
1298
+ getCtaUrl: firstFailExternalUrl
1299
+ },
1200
1300
  {
1201
1301
  auditId: "audits/images-too-large",
1202
1302
  icon: import_icons10.ShieldHalfFilledIcon,
@@ -1225,11 +1325,11 @@ function findAuditRun(report, auditId) {
1225
1325
  }
1226
1326
 
1227
1327
  // src/components/promotion-card.tsx
1228
- var React14 = __toESM(require("react"));
1229
- var import_ui13 = require("@elementor/ui");
1328
+ var React15 = __toESM(require("react"));
1329
+ var import_ui14 = require("@elementor/ui");
1230
1330
  function PromotionCard({ ctaDisabled, ctaLabel, icon: Icon, onCtaClick, subtitle, title }) {
1231
- return /* @__PURE__ */ React14.createElement(
1232
- import_ui13.Box,
1331
+ return /* @__PURE__ */ React15.createElement(
1332
+ import_ui14.Box,
1233
1333
  {
1234
1334
  sx: {
1235
1335
  alignItems: "center",
@@ -1242,9 +1342,9 @@ function PromotionCard({ ctaDisabled, ctaLabel, icon: Icon, onCtaClick, subtitle
1242
1342
  py: 1.5
1243
1343
  }
1244
1344
  },
1245
- /* @__PURE__ */ React14.createElement(Icon, { fontSize: "small", color: "action" }),
1246
- /* @__PURE__ */ React14.createElement(import_ui13.Box, { sx: { display: "flex", flex: 1, flexDirection: "column", gap: 0.25, minWidth: 0 } }, /* @__PURE__ */ React14.createElement(import_ui13.Typography, { variant: "body2", fontWeight: "bold" }, title), /* @__PURE__ */ React14.createElement(import_ui13.Typography, { variant: "caption", color: "text.secondary" }, subtitle)),
1247
- /* @__PURE__ */ React14.createElement(import_ui13.Button, { variant: "outlined", color: "secondary", size: "small", disabled: ctaDisabled, onClick: onCtaClick }, ctaLabel)
1345
+ /* @__PURE__ */ React15.createElement(Icon, { fontSize: "small", color: "action" }),
1346
+ /* @__PURE__ */ React15.createElement(import_ui14.Box, { sx: { display: "flex", flex: 1, flexDirection: "column", gap: 0.25, minWidth: 0 } }, /* @__PURE__ */ React15.createElement(import_ui14.Typography, { variant: "body2", fontWeight: "bold" }, title), /* @__PURE__ */ React15.createElement(import_ui14.Typography, { variant: "caption", color: "text.secondary" }, subtitle)),
1347
+ /* @__PURE__ */ React15.createElement(import_ui14.Button, { variant: "outlined", color: "secondary", size: "small", disabled: ctaDisabled, onClick: onCtaClick }, ctaLabel)
1248
1348
  );
1249
1349
  }
1250
1350
 
@@ -1273,7 +1373,7 @@ function Promotions({ report }) {
1273
1373
  if (cards.length === 0) {
1274
1374
  return null;
1275
1375
  }
1276
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(import_ui14.Typography, { variant: "subtitle1", fontWeight: "bold" }, (0, import_i18n16.__)("Quick wins", "elementor")), /* @__PURE__ */ React15.createElement(import_ui14.Box, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, cards.map(({ config, ctaUrl, key, run, subtitle }) => /* @__PURE__ */ React15.createElement(
1376
+ return /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(import_ui15.Typography, { variant: "subtitle1", fontWeight: "bold" }, (0, import_i18n16.__)("Quick wins", "elementor")), /* @__PURE__ */ React16.createElement(import_ui15.Box, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, cards.map(({ config, ctaUrl, key, run, subtitle }) => /* @__PURE__ */ React16.createElement(
1277
1377
  PromotionCard,
1278
1378
  {
1279
1379
  key,
@@ -1294,8 +1394,8 @@ function Promotions({ report }) {
1294
1394
  // src/components/pages/issues-page.tsx
1295
1395
  function IssuesPage({ report, onCategoryClick, onAllAuditsClick }) {
1296
1396
  const populatedCategories = getPopulatedCategories(report.categories, ALL_CATEGORIES);
1297
- return /* @__PURE__ */ React16.createElement(import_ui15.Box, { sx: { display: "flex", flexDirection: "column", gap: 4, p: 2 } }, /* @__PURE__ */ React16.createElement(
1298
- import_ui15.Link,
1397
+ return /* @__PURE__ */ React17.createElement(import_ui16.Box, { sx: { display: "flex", flexDirection: "column", gap: 4, p: 2 } }, /* @__PURE__ */ React17.createElement(
1398
+ import_ui16.Link,
1299
1399
  {
1300
1400
  component: "button",
1301
1401
  underline: "none",
@@ -1303,8 +1403,8 @@ function IssuesPage({ report, onCategoryClick, onAllAuditsClick }) {
1303
1403
  onClick: onAllAuditsClick,
1304
1404
  sx: { textAlign: "start" }
1305
1405
  },
1306
- /* @__PURE__ */ React16.createElement(import_ui15.Typography, { variant: "subtitle1", component: "h2" }, (0, import_i18n17.__)("All issues", "elementor"))
1307
- ), /* @__PURE__ */ React16.createElement(import_ui15.Box, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, populatedCategories.map((category) => /* @__PURE__ */ React16.createElement(
1406
+ /* @__PURE__ */ React17.createElement(import_ui16.Typography, { variant: "subtitle1", component: "h2" }, (0, import_i18n17.__)("All issues", "elementor"))
1407
+ ), /* @__PURE__ */ React17.createElement(import_ui16.Box, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, populatedCategories.map((category) => /* @__PURE__ */ React17.createElement(
1308
1408
  IssuesCategoryRow,
1309
1409
  {
1310
1410
  key: category,
@@ -1313,12 +1413,12 @@ function IssuesPage({ report, onCategoryClick, onAllAuditsClick }) {
1313
1413
  counts: countSeverities(report, category),
1314
1414
  onClick: () => onCategoryClick(category)
1315
1415
  }
1316
- ))), /* @__PURE__ */ React16.createElement(Promotions, { report }));
1416
+ ))), /* @__PURE__ */ React17.createElement(Promotions, { report }));
1317
1417
  }
1318
1418
 
1319
1419
  // src/components/pages/overview-page.tsx
1320
- var React20 = __toESM(require("react"));
1321
- var import_ui19 = require("@elementor/ui");
1420
+ var React21 = __toESM(require("react"));
1421
+ var import_ui20 = require("@elementor/ui");
1322
1422
  var import_i18n19 = require("@wordpress/i18n");
1323
1423
 
1324
1424
  // src/utils/score-thresholds.ts
@@ -1336,8 +1436,8 @@ function getScoreTier(score) {
1336
1436
  }
1337
1437
 
1338
1438
  // src/components/count-summary-circle.tsx
1339
- var React17 = __toESM(require("react"));
1340
- var import_ui16 = require("@elementor/ui");
1439
+ var React18 = __toESM(require("react"));
1440
+ var import_ui17 = require("@elementor/ui");
1341
1441
  var COUNT_SUMMARY_CIRCLE_SIZE = 64;
1342
1442
  var COUNT_SUMMARY_BORDER_WIDTH = 4;
1343
1443
  function chipBorderColor(theme, color) {
@@ -1347,8 +1447,8 @@ function chipBorderColor(theme, color) {
1347
1447
  return theme.palette[color].main;
1348
1448
  }
1349
1449
  function CountSummaryCircle({ ariaLabel, color, count, label, onClick }) {
1350
- return /* @__PURE__ */ React17.createElement(
1351
- import_ui16.Box,
1450
+ return /* @__PURE__ */ React18.createElement(
1451
+ import_ui17.Box,
1352
1452
  {
1353
1453
  "aria-label": ariaLabel,
1354
1454
  component: "button",
@@ -1366,8 +1466,8 @@ function CountSummaryCircle({ ariaLabel, color, count, label, onClick }) {
1366
1466
  padding: 0
1367
1467
  }
1368
1468
  },
1369
- /* @__PURE__ */ React17.createElement(
1370
- import_ui16.Chip,
1469
+ /* @__PURE__ */ React18.createElement(
1470
+ import_ui17.Chip,
1371
1471
  {
1372
1472
  color,
1373
1473
  label: count,
@@ -1388,18 +1488,18 @@ function CountSummaryCircle({ ariaLabel, color, count, label, onClick }) {
1388
1488
  })
1389
1489
  }
1390
1490
  ),
1391
- /* @__PURE__ */ React17.createElement(import_ui16.Typography, { color: "text.secondary", sx: { textAlign: "center" }, variant: "caption" }, label)
1491
+ /* @__PURE__ */ React18.createElement(import_ui17.Typography, { color: "text.secondary", sx: { textAlign: "center" }, variant: "caption" }, label)
1392
1492
  );
1393
1493
  }
1394
1494
 
1395
1495
  // src/components/score-bar.tsx
1396
- var React18 = __toESM(require("react"));
1496
+ var React19 = __toESM(require("react"));
1397
1497
  var import_icons11 = require("@elementor/icons");
1398
- var import_ui17 = require("@elementor/ui");
1498
+ var import_ui18 = require("@elementor/ui");
1399
1499
  function ScoreBar({ label, score, onClick }) {
1400
- const isRtl = "rtl" === (0, import_ui17.useTheme)().direction;
1401
- return /* @__PURE__ */ React18.createElement(
1402
- import_ui17.Box,
1500
+ const isRtl = "rtl" === (0, import_ui18.useTheme)().direction;
1501
+ return /* @__PURE__ */ React19.createElement(
1502
+ import_ui18.Box,
1403
1503
  {
1404
1504
  role: onClick ? "button" : void 0,
1405
1505
  tabIndex: onClick ? 0 : void 0,
@@ -1415,9 +1515,9 @@ function ScoreBar({ label, score, onClick }) {
1415
1515
  py: 0.5
1416
1516
  }
1417
1517
  },
1418
- /* @__PURE__ */ React18.createElement(import_ui17.Typography, { variant: "body2", sx: { minWidth: 96 } }, label),
1419
- /* @__PURE__ */ React18.createElement(
1420
- import_ui17.LinearProgress,
1518
+ /* @__PURE__ */ React19.createElement(import_ui18.Typography, { variant: "body2", sx: { minWidth: 96 } }, label),
1519
+ /* @__PURE__ */ React19.createElement(
1520
+ import_ui18.LinearProgress,
1421
1521
  {
1422
1522
  variant: "determinate",
1423
1523
  value: score,
@@ -1425,8 +1525,8 @@ function ScoreBar({ label, score, onClick }) {
1425
1525
  sx: { flex: 1, height: 6, borderRadius: 4, bgcolor: "action.disabledBackground" }
1426
1526
  }
1427
1527
  ),
1428
- /* @__PURE__ */ React18.createElement(
1429
- import_ui17.Typography,
1528
+ /* @__PURE__ */ React19.createElement(
1529
+ import_ui18.Typography,
1430
1530
  {
1431
1531
  variant: "body2",
1432
1532
  color: "text.primary",
@@ -1434,19 +1534,19 @@ function ScoreBar({ label, score, onClick }) {
1434
1534
  },
1435
1535
  score
1436
1536
  ),
1437
- onClick && /* @__PURE__ */ React18.createElement(import_ui17.Rotate, { in: isRtl }, /* @__PURE__ */ React18.createElement(import_icons11.ChevronRightIcon, { fontSize: "small", color: "action" }))
1537
+ onClick && /* @__PURE__ */ React19.createElement(import_ui18.Rotate, { in: isRtl }, /* @__PURE__ */ React19.createElement(import_icons11.ChevronRightIcon, { fontSize: "small", color: "action" }))
1438
1538
  );
1439
1539
  }
1440
1540
 
1441
1541
  // src/components/score-circle.tsx
1442
- var React19 = __toESM(require("react"));
1443
- var import_ui18 = require("@elementor/ui");
1542
+ var React20 = __toESM(require("react"));
1543
+ var import_ui19 = require("@elementor/ui");
1444
1544
  var SCORE_CIRCLE_SIZE = 88;
1445
1545
  var SCORE_CIRCLE_THICKNESS = 3;
1446
1546
  function ScoreCircle({ color, score }) {
1447
1547
  const progressColor = color ?? getScoreTier(score).color;
1448
- return /* @__PURE__ */ React19.createElement(
1449
- import_ui18.Box,
1548
+ return /* @__PURE__ */ React20.createElement(
1549
+ import_ui19.Box,
1450
1550
  {
1451
1551
  role: "progressbar",
1452
1552
  "aria-valuenow": score,
@@ -1454,8 +1554,8 @@ function ScoreCircle({ color, score }) {
1454
1554
  "aria-valuemax": 100,
1455
1555
  sx: { position: "relative", display: "inline-flex" }
1456
1556
  },
1457
- /* @__PURE__ */ React19.createElement(
1458
- import_ui18.CircularProgress,
1557
+ /* @__PURE__ */ React20.createElement(
1558
+ import_ui19.CircularProgress,
1459
1559
  {
1460
1560
  variant: "determinate",
1461
1561
  value: 100,
@@ -1464,8 +1564,8 @@ function ScoreCircle({ color, score }) {
1464
1564
  sx: { color: "action.disabledBackground" }
1465
1565
  }
1466
1566
  ),
1467
- /* @__PURE__ */ React19.createElement(
1468
- import_ui18.CircularProgress,
1567
+ /* @__PURE__ */ React20.createElement(
1568
+ import_ui19.CircularProgress,
1469
1569
  {
1470
1570
  variant: "determinate",
1471
1571
  value: score,
@@ -1475,8 +1575,8 @@ function ScoreCircle({ color, score }) {
1475
1575
  sx: { position: "absolute", left: 0 }
1476
1576
  }
1477
1577
  ),
1478
- /* @__PURE__ */ React19.createElement(
1479
- import_ui18.Box,
1578
+ /* @__PURE__ */ React20.createElement(
1579
+ import_ui19.Box,
1480
1580
  {
1481
1581
  sx: {
1482
1582
  position: "absolute",
@@ -1486,8 +1586,8 @@ function ScoreCircle({ color, score }) {
1486
1586
  justifyContent: "center"
1487
1587
  }
1488
1588
  },
1489
- /* @__PURE__ */ React19.createElement(
1490
- import_ui18.Typography,
1589
+ /* @__PURE__ */ React20.createElement(
1590
+ import_ui19.Typography,
1491
1591
  {
1492
1592
  variant: "h4",
1493
1593
  component: "span",
@@ -1524,8 +1624,8 @@ function OverviewPage({ onCategoryClick, onStatusClick, report }) {
1524
1624
  const severityCounts = countSeverities(report);
1525
1625
  const statusCounts = auditStatusDisplayCounts(report);
1526
1626
  const overallScore = getScoreTier(report.overall);
1527
- return /* @__PURE__ */ React20.createElement(import_ui19.Box, { sx: { display: "flex", flexDirection: "column", gap: 4, p: 2 } }, /* @__PURE__ */ React20.createElement(import_ui19.Box, { sx: { display: "flex", alignItems: "center", gap: 2 } }, /* @__PURE__ */ React20.createElement(ScoreCircle, { color: overallScore.color, score: report.overall }), /* @__PURE__ */ React20.createElement(import_ui19.Box, { sx: { display: "flex", flexDirection: "column", gap: 0.5 } }, /* @__PURE__ */ React20.createElement(
1528
- import_ui19.Chip,
1627
+ return /* @__PURE__ */ React21.createElement(import_ui20.Box, { sx: { display: "flex", flexDirection: "column", gap: 4, p: 2 } }, /* @__PURE__ */ React21.createElement(import_ui20.Box, { sx: { display: "flex", alignItems: "center", gap: 2 } }, /* @__PURE__ */ React21.createElement(ScoreCircle, { color: overallScore.color, score: report.overall }), /* @__PURE__ */ React21.createElement(import_ui20.Box, { sx: { display: "flex", flexDirection: "column", gap: 0.5 } }, /* @__PURE__ */ React21.createElement(
1628
+ import_ui20.Chip,
1529
1629
  {
1530
1630
  label: overallScore.label,
1531
1631
  color: overallScore.color,
@@ -1533,7 +1633,7 @@ function OverviewPage({ onCategoryClick, onStatusClick, report }) {
1533
1633
  size: "small",
1534
1634
  sx: { fontWeight: 600, alignSelf: "flex-start" }
1535
1635
  }
1536
- ), /* @__PURE__ */ React20.createElement(import_ui19.Typography, { variant: "body2", color: "text.secondary" }, (0, import_i18n19.__)("Overall score", "elementor")))), /* @__PURE__ */ React20.createElement(import_ui19.Box, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, populatedCategories.map((category) => /* @__PURE__ */ React20.createElement(
1636
+ ), /* @__PURE__ */ React21.createElement(import_ui20.Typography, { variant: "body2", color: "text.secondary" }, (0, import_i18n19.__)("Overall score", "elementor")))), /* @__PURE__ */ React21.createElement(import_ui20.Box, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, populatedCategories.map((category) => /* @__PURE__ */ React21.createElement(
1537
1637
  ScoreBar,
1538
1638
  {
1539
1639
  key: category,
@@ -1541,7 +1641,7 @@ function OverviewPage({ onCategoryClick, onStatusClick, report }) {
1541
1641
  score: report.categories[category].score,
1542
1642
  onClick: () => onCategoryClick(category)
1543
1643
  }
1544
- ))), /* @__PURE__ */ React20.createElement(import_ui19.Divider, null), /* @__PURE__ */ React20.createElement(import_ui19.Typography, { variant: "subtitle1", fontWeight: "bold" }, (0, import_i18n19.__)("Audit statuses", "elementor")), /* @__PURE__ */ React20.createElement(import_ui19.Box, { sx: { display: "flex", justifyContent: "space-around", gap: 2 } }, STATUS_GROUPS.map((status) => /* @__PURE__ */ React20.createElement(
1644
+ ))), /* @__PURE__ */ React21.createElement(import_ui20.Divider, null), /* @__PURE__ */ React21.createElement(import_ui20.Typography, { variant: "subtitle1", fontWeight: "bold" }, (0, import_i18n19.__)("Audit statuses", "elementor")), /* @__PURE__ */ React21.createElement(import_ui20.Box, { sx: { display: "flex", justifyContent: "space-around", gap: 2 } }, STATUS_GROUPS.map((status) => /* @__PURE__ */ React21.createElement(
1545
1645
  CountSummaryCircle,
1546
1646
  {
1547
1647
  key: status,
@@ -1551,8 +1651,8 @@ function OverviewPage({ onCategoryClick, onStatusClick, report }) {
1551
1651
  label: auditStatusLabel(status),
1552
1652
  onClick: () => onStatusClick(status)
1553
1653
  }
1554
- ))), /* @__PURE__ */ React20.createElement(import_ui19.Divider, null), /* @__PURE__ */ React20.createElement(import_ui19.Typography, { variant: "subtitle1", fontWeight: "bold" }, (0, import_i18n19.__)("Remaining issues", "elementor")), /* @__PURE__ */ React20.createElement(
1555
- import_ui19.Box,
1654
+ ))), /* @__PURE__ */ React21.createElement(import_ui20.Divider, null), /* @__PURE__ */ React21.createElement(import_ui20.Typography, { variant: "subtitle1", fontWeight: "bold" }, (0, import_i18n19.__)("Remaining issues", "elementor")), /* @__PURE__ */ React21.createElement(
1655
+ import_ui20.Box,
1556
1656
  {
1557
1657
  component: "ul",
1558
1658
  sx: {
@@ -1566,17 +1666,17 @@ function OverviewPage({ onCategoryClick, onStatusClick, report }) {
1566
1666
  },
1567
1667
  ALL_SEVERITIES.map((severity) => {
1568
1668
  const count = severityCounts[severity];
1569
- return /* @__PURE__ */ React20.createElement(
1570
- import_ui19.Box,
1669
+ return /* @__PURE__ */ React21.createElement(
1670
+ import_ui20.Box,
1571
1671
  {
1572
1672
  "aria-label": severityRemainingCountLabel(severity, count),
1573
1673
  component: "li",
1574
1674
  key: severity,
1575
1675
  sx: { alignItems: "center", display: "flex", gap: 0.5 }
1576
1676
  },
1577
- /* @__PURE__ */ React20.createElement(SeverityIcon, { severity }),
1578
- /* @__PURE__ */ React20.createElement(import_ui19.Typography, { variant: "body2", fontWeight: "bold" }, count),
1579
- /* @__PURE__ */ React20.createElement(import_ui19.Typography, { variant: "body2" }, severityPluralLabel(severity))
1677
+ /* @__PURE__ */ React21.createElement(SeverityIcon, { severity }),
1678
+ /* @__PURE__ */ React21.createElement(import_ui20.Typography, { variant: "body2", fontWeight: "bold" }, count),
1679
+ /* @__PURE__ */ React21.createElement(import_ui20.Typography, { variant: "body2" }, severityPluralLabel(severity))
1580
1680
  );
1581
1681
  })
1582
1682
  ));
@@ -1615,8 +1715,8 @@ function ReportShell({ report }) {
1615
1715
  setActivePage(activePage.backTo);
1616
1716
  }
1617
1717
  };
1618
- return /* @__PURE__ */ React21.createElement(import_ui20.Box, null, /* @__PURE__ */ React21.createElement(
1619
- import_ui20.Tabs,
1718
+ return /* @__PURE__ */ React22.createElement(import_ui21.Box, null, /* @__PURE__ */ React22.createElement(
1719
+ import_ui21.Tabs,
1620
1720
  {
1621
1721
  "aria-label": (0, import_i18n20.__)("Audit navigation", "elementor"),
1622
1722
  value: currentTab,
@@ -1627,30 +1727,30 @@ function ReportShell({ report }) {
1627
1727
  centered: true,
1628
1728
  variant: "fullWidth"
1629
1729
  },
1630
- /* @__PURE__ */ React21.createElement(import_ui20.Tab, { value: "overview", label: (0, import_i18n20.__)("Overview", "elementor") }),
1631
- /* @__PURE__ */ React21.createElement(import_ui20.Tab, { value: "issues", label: (0, import_i18n20.__)("Issues", "elementor") })
1632
- ), /* @__PURE__ */ React21.createElement(import_ui20.Divider, null), activePage === "overview" && /* @__PURE__ */ React21.createElement(
1730
+ /* @__PURE__ */ React22.createElement(import_ui21.Tab, { value: "overview", label: (0, import_i18n20.__)("Overview", "elementor") }),
1731
+ /* @__PURE__ */ React22.createElement(import_ui21.Tab, { value: "issues", label: (0, import_i18n20.__)("Issues", "elementor") })
1732
+ ), /* @__PURE__ */ React22.createElement(import_ui21.Divider, null), activePage === "overview" && /* @__PURE__ */ React22.createElement(
1633
1733
  OverviewPage,
1634
1734
  {
1635
1735
  report,
1636
1736
  onCategoryClick: (category) => openCategory(category, "overview"),
1637
1737
  onStatusClick: (status) => openAllAudits(status, "overview")
1638
1738
  }
1639
- ), activePage === "issues" && /* @__PURE__ */ React21.createElement(
1739
+ ), activePage === "issues" && /* @__PURE__ */ React22.createElement(
1640
1740
  IssuesPage,
1641
1741
  {
1642
1742
  report,
1643
1743
  onCategoryClick: (category) => openCategory(category, "issues"),
1644
1744
  onAllAuditsClick: () => openAllAudits()
1645
1745
  }
1646
- ), isAllAuditsPage(activePage) && /* @__PURE__ */ React21.createElement(
1746
+ ), isAllAuditsPage(activePage) && /* @__PURE__ */ React22.createElement(
1647
1747
  AllAuditsPage,
1648
1748
  {
1649
1749
  report,
1650
1750
  initialExpandedStatus: activePage.expand,
1651
1751
  onBack: backFromSubPage
1652
1752
  }
1653
- ), isCategoryPage(activePage) && /* @__PURE__ */ React21.createElement(CategoryPage, { category: activePage.category, report, onBack: backFromSubPage }));
1753
+ ), isCategoryPage(activePage) && /* @__PURE__ */ React22.createElement(CategoryPage, { category: activePage.category, report, onBack: backFromSubPage }));
1654
1754
  }
1655
1755
 
1656
1756
  // src/components/audit-panel.tsx
@@ -1659,7 +1759,7 @@ function AuditPanel() {
1659
1759
  const currentDocumentId = (0, import_editor_elements5.getCurrentDocumentId)() ?? 0;
1660
1760
  const onRun = () => run(currentDocumentId);
1661
1761
  const lastScanLabel = report ? new Date(report.runAt).toLocaleTimeString() : null;
1662
- return /* @__PURE__ */ React22.createElement(React22.Fragment, null, /* @__PURE__ */ React22.createElement(
1762
+ return /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement(
1663
1763
  import_editor_floating_panels.FloatingPanelHeader,
1664
1764
  {
1665
1765
  panelId: "audit-panel",
@@ -1667,8 +1767,8 @@ function AuditPanel() {
1667
1767
  badge: (0, import_i18n21.__)("Beta", "elementor"),
1668
1768
  titleVariant: "subtitle2"
1669
1769
  }
1670
- ), /* @__PURE__ */ React22.createElement(import_editor_floating_panels.FloatingPanelBody, null, status === "idle" && /* @__PURE__ */ React22.createElement(WelcomePage, null), status === "loading" && /* @__PURE__ */ React22.createElement(LoadingPage, null), status === "error" && /* @__PURE__ */ React22.createElement(ErrorPage, { message: error ?? "", onRetry: onRun }), status === "ready" && report && /* @__PURE__ */ React22.createElement(ReportShell, { report })), /* @__PURE__ */ React22.createElement(import_editor_floating_panels.FloatingPanelFooter, null, lastScanLabel ? /* @__PURE__ */ React22.createElement(import_ui21.Typography, { variant: "caption", sx: { flex: 1 } }, (0, import_i18n21.__)("Last scan:", "elementor"), " ", lastScanLabel) : /* @__PURE__ */ React22.createElement(import_ui21.Box, { sx: { flex: 1 } }), lastScanLabel && /* @__PURE__ */ React22.createElement(AuditFeedback, null), /* @__PURE__ */ React22.createElement(
1671
- import_ui21.Button,
1770
+ ), /* @__PURE__ */ React23.createElement(import_editor_floating_panels.FloatingPanelBody, null, status === "idle" && /* @__PURE__ */ React23.createElement(WelcomePage, null), status === "loading" && /* @__PURE__ */ React23.createElement(LoadingPage, null), status === "error" && /* @__PURE__ */ React23.createElement(ErrorPage, { message: error ?? "", onRetry: onRun }), status === "ready" && report && /* @__PURE__ */ React23.createElement(ReportShell, { report })), /* @__PURE__ */ React23.createElement(import_editor_floating_panels.FloatingPanelFooter, null, lastScanLabel ? /* @__PURE__ */ React23.createElement(import_ui22.Typography, { variant: "caption", sx: { flex: 1 } }, (0, import_i18n21.__)("Last scan:", "elementor"), " ", lastScanLabel) : /* @__PURE__ */ React23.createElement(import_ui22.Box, { sx: { flex: 1 } }), lastScanLabel && /* @__PURE__ */ React23.createElement(AuditFeedback, null), /* @__PURE__ */ React23.createElement(
1771
+ import_ui22.Button,
1672
1772
  {
1673
1773
  variant: "contained",
1674
1774
  size: "small",
@@ -1889,8 +1989,107 @@ var audit5 = {
1889
1989
  }
1890
1990
  };
1891
1991
 
1892
- // src/audits/heading-structure.ts
1992
+ // src/audits/google-consent-mode.ts
1893
1993
  var import_i18n28 = require("@wordpress/i18n");
1994
+
1995
+ // src/utils/fetch-rendered-html.ts
1996
+ var FETCH_TIMEOUT_MS = 1e4;
1997
+ async function fetchRenderedHtml(url) {
1998
+ if (!url) {
1999
+ return null;
2000
+ }
2001
+ const timeoutController = new AbortController();
2002
+ const timeoutId = setTimeout(() => timeoutController.abort(), FETCH_TIMEOUT_MS);
2003
+ try {
2004
+ const response = await fetch(url, { credentials: "omit", signal: timeoutController.signal });
2005
+ if (!response.ok) {
2006
+ return null;
2007
+ }
2008
+ return await response.text();
2009
+ } catch {
2010
+ return null;
2011
+ } finally {
2012
+ clearTimeout(timeoutId);
2013
+ }
2014
+ }
2015
+
2016
+ // src/utils/scan-consent-signals.ts
2017
+ var GOOGLE_TRACKING_PATTERNS = [
2018
+ /googletagmanager\.com\/gtag\/js\?id=(G|AW)-/,
2019
+ /googletagmanager\.com\/gtm\.js/,
2020
+ /\bGTM-[A-Z0-9]+\b/,
2021
+ /gtag\(\s*['"]config['"]\s*,\s*['"](G|AW)-/
2022
+ ];
2023
+ var CONSENT_DEFAULT_PATTERNS = [
2024
+ /gtag\(\s*['"]consent['"]\s*,\s*['"]default['"]/,
2025
+ /dataLayer\.push\(\s*\[\s*['"]consent['"]\s*,\s*['"]default['"]/
2026
+ ];
2027
+ function hasGoogleTracking(html) {
2028
+ return GOOGLE_TRACKING_PATTERNS.some((pattern) => pattern.test(html));
2029
+ }
2030
+ function hasConsentDefaultCall(html) {
2031
+ return CONSENT_DEFAULT_PATTERNS.some((pattern) => pattern.test(html));
2032
+ }
2033
+
2034
+ // src/audits/google-consent-mode.ts
2035
+ var audit6 = {
2036
+ id: "audits/google-consent-mode",
2037
+ title: (0, import_i18n28.__)("Google Consent Mode", "elementor"),
2038
+ description: (0, import_i18n28.__)(
2039
+ "Google Analytics, Ads, or Tag Manager are running on this site and may not receive a consent signal, which risks losing conversion/analytics data and compliance exposure.",
2040
+ "elementor"
2041
+ ),
2042
+ fixHint: (0, import_i18n28.__)(
2043
+ "Turn on Google Consent Mode in your cookie consent plugin so Google tags respect visitor consent choices.",
2044
+ "elementor"
2045
+ ),
2046
+ categories: ["compliance"],
2047
+ severity: "warning",
2048
+ weight: 1,
2049
+ evaluate: async (ctx) => {
2050
+ if (!ctx.pageContext.frontend_url) {
2051
+ return { status: "skipped", reason: (0, import_i18n28.__)("Page is not published.", "elementor") };
2052
+ }
2053
+ const html = await fetchRenderedHtml(ctx.pageContext.frontend_url);
2054
+ if (!html) {
2055
+ return { status: "skipped", reason: (0, import_i18n28.__)("Could not fetch the published page.", "elementor") };
2056
+ }
2057
+ if (!hasGoogleTracking(html)) {
2058
+ return { status: "skipped", reason: (0, import_i18n28.__)("No Google tracking product detected.", "elementor") };
2059
+ }
2060
+ if (hasConsentDefaultCall(html)) {
2061
+ return { status: "pass" };
2062
+ }
2063
+ const label = (0, import_i18n28.__)("No Google Consent Mode signal was found on the page.", "elementor");
2064
+ const isCookiezReady = ctx.pageContext.cookiez_plugin_installed && ctx.pageContext.cookiez_plugin_active;
2065
+ if (isCookiezReady) {
2066
+ return {
2067
+ status: "fail",
2068
+ violations: [
2069
+ {
2070
+ auditId: audit6.id,
2071
+ label,
2072
+ ctaLabel: (0, import_i18n28.__)("Enable", "elementor"),
2073
+ externalUrl: ctx.pageContext.cookiez_consent_mode_settings_url
2074
+ }
2075
+ ]
2076
+ };
2077
+ }
2078
+ return {
2079
+ status: "fail",
2080
+ violations: [
2081
+ {
2082
+ auditId: audit6.id,
2083
+ label,
2084
+ externalUrl: ctx.pageContext.cookiez_plugin_action_url
2085
+ }
2086
+ ]
2087
+ };
2088
+ }
2089
+ };
2090
+
2091
+ // src/audits/heading-structure.ts
2092
+ var import_i18n29 = require("@wordpress/i18n");
1894
2093
  var HEADING_LEVELS = ["h1", "h2", "h3", "h4", "h5", "h6"];
1895
2094
  var HEADING_CONTROLS_REGISTRY = {
1896
2095
  // Core
@@ -2066,47 +2265,47 @@ function extractPageHeadings(tree) {
2066
2265
  });
2067
2266
  return headings;
2068
2267
  }
2069
- var audit6 = {
2268
+ var audit7 = {
2070
2269
  id: "audits/heading-structure",
2071
- title: (0, import_i18n28.__)("Heading structure", "elementor"),
2072
- description: (0, import_i18n28.__)("Pages should have exactly one H1 and a non-skipping heading order.", "elementor"),
2073
- fixHint: (0, import_i18n28.__)("Ensure your page has one H1 and that heading levels do not skip (no H2 \u2192 H4).", "elementor"),
2270
+ title: (0, import_i18n29.__)("Heading structure", "elementor"),
2271
+ description: (0, import_i18n29.__)("Pages should have exactly one H1 and a non-skipping heading order.", "elementor"),
2272
+ fixHint: (0, import_i18n29.__)("Ensure your page has one H1 and that heading levels do not skip (no H2 \u2192 H4).", "elementor"),
2074
2273
  categories: ["seo", "accessibility"],
2075
2274
  severity: "error",
2076
2275
  weight: 10,
2077
2276
  evaluate: (ctx) => {
2078
2277
  if (ctx.elements.tree.length === 0) {
2079
- return { status: "skipped", reason: (0, import_i18n28.__)("No elements", "elementor") };
2278
+ return { status: "skipped", reason: (0, import_i18n29.__)("No elements", "elementor") };
2080
2279
  }
2081
2280
  const headings = extractPageHeadings(ctx.elements.tree);
2082
2281
  const violations = [];
2083
2282
  if (headings.length === 0) {
2084
2283
  violations.push({
2085
- auditId: audit6.id,
2086
- label: (0, import_i18n28.__)("No headings found on the page.", "elementor")
2284
+ auditId: audit7.id,
2285
+ label: (0, import_i18n29.__)("No headings found on the page.", "elementor")
2087
2286
  });
2088
2287
  } else {
2089
2288
  const h1Count = headings.filter((h) => h.level === 1).length;
2090
2289
  if (h1Count === 0) {
2091
- violations.push({ auditId: audit6.id, label: (0, import_i18n28.__)("No H1 on the page.", "elementor") });
2290
+ violations.push({ auditId: audit7.id, label: (0, import_i18n29.__)("No H1 on the page.", "elementor") });
2092
2291
  }
2093
2292
  if (h1Count > 1) {
2094
2293
  headings.filter((h) => h.level === 1).slice(1).forEach(
2095
2294
  (h) => violations.push({
2096
- auditId: audit6.id,
2295
+ auditId: audit7.id,
2097
2296
  elementId: h.elementId,
2098
2297
  targetHint: "element-settings",
2099
- label: (0, import_i18n28.__)("More than one H1 on the page.", "elementor")
2298
+ label: (0, import_i18n29.__)("More than one H1 on the page.", "elementor")
2100
2299
  })
2101
2300
  );
2102
2301
  }
2103
2302
  for (let i = 1; i < headings.length; i++) {
2104
2303
  if (headings[i].level - headings[i - 1].level > 1) {
2105
2304
  violations.push({
2106
- auditId: audit6.id,
2305
+ auditId: audit7.id,
2107
2306
  elementId: headings[i].elementId,
2108
2307
  targetHint: "element-settings",
2109
- label: (0, import_i18n28.__)("Heading level skipped.", "elementor")
2308
+ label: (0, import_i18n29.__)("Heading level skipped.", "elementor")
2110
2309
  });
2111
2310
  }
2112
2311
  }
@@ -2116,33 +2315,33 @@ var audit6 = {
2116
2315
  };
2117
2316
 
2118
2317
  // src/audits/hidden-elements.ts
2119
- var import_i18n29 = require("@wordpress/i18n");
2318
+ var import_i18n30 = require("@wordpress/i18n");
2120
2319
  function isHiddenOnAllDevices(settings) {
2121
2320
  return Boolean(settings.hide_desktop) && Boolean(settings.hide_tablet) && Boolean(settings.hide_mobile);
2122
2321
  }
2123
- var audit7 = {
2322
+ var audit8 = {
2124
2323
  id: "audits/hidden-elements",
2125
- title: (0, import_i18n29.__)("Hidden elements", "elementor"),
2126
- description: (0, import_i18n29.__)(
2324
+ title: (0, import_i18n30.__)("Hidden elements", "elementor"),
2325
+ description: (0, import_i18n30.__)(
2127
2326
  "Elements hidden on all devices simultaneously are effectively dead code in the page tree.",
2128
2327
  "elementor"
2129
2328
  ),
2130
- fixHint: (0, import_i18n29.__)("Remove the element or make it visible on at least one device breakpoint.", "elementor"),
2329
+ fixHint: (0, import_i18n30.__)("Remove the element or make it visible on at least one device breakpoint.", "elementor"),
2131
2330
  categories: ["best-practices", "performance"],
2132
2331
  severity: "info",
2133
2332
  weight: 3,
2134
2333
  evaluate: (ctx) => {
2135
2334
  if (ctx.elements.tree.length === 0) {
2136
- return { status: "skipped", reason: (0, import_i18n29.__)("No elements", "elementor") };
2335
+ return { status: "skipped", reason: (0, import_i18n30.__)("No elements", "elementor") };
2137
2336
  }
2138
2337
  const violations = [];
2139
2338
  walkElements(ctx.elements.tree, (node) => {
2140
2339
  if (isHiddenOnAllDevices(node.settings)) {
2141
2340
  violations.push({
2142
- auditId: audit7.id,
2341
+ auditId: audit8.id,
2143
2342
  elementId: node.id,
2144
2343
  targetHint: "element-settings",
2145
- label: (0, import_i18n29.__)("Element is hidden on all devices.", "elementor")
2344
+ label: (0, import_i18n30.__)("Element is hidden on all devices.", "elementor")
2146
2345
  });
2147
2346
  }
2148
2347
  });
@@ -2151,7 +2350,7 @@ var audit7 = {
2151
2350
  };
2152
2351
 
2153
2352
  // src/audits/images-alt-text.ts
2154
- var import_i18n30 = require("@wordpress/i18n");
2353
+ var import_i18n31 = require("@wordpress/i18n");
2155
2354
 
2156
2355
  // src/utils/image-alt.ts
2157
2356
  function hasMeaningfulAlt(media, pageContext) {
@@ -2169,17 +2368,17 @@ function isImageSourcePresent(media) {
2169
2368
  }
2170
2369
 
2171
2370
  // src/audits/images-alt-text.ts
2172
- var audit8 = {
2371
+ var audit9 = {
2173
2372
  id: "audits/images-alt-text",
2174
- title: (0, import_i18n30.__)("Images alt text", "elementor"),
2175
- description: (0, import_i18n30.__)("Every image needs a meaningful alt attribute for screen readers and image-search.", "elementor"),
2176
- fixHint: (0, import_i18n30.__)("Open the image's settings and add an Alt Text describing the image.", "elementor"),
2373
+ title: (0, import_i18n31.__)("Images alt text", "elementor"),
2374
+ description: (0, import_i18n31.__)("Every image needs a meaningful alt attribute for screen readers and image-search.", "elementor"),
2375
+ fixHint: (0, import_i18n31.__)("Open the image's settings and add an Alt Text describing the image.", "elementor"),
2177
2376
  categories: ["seo", "accessibility"],
2178
2377
  severity: "error",
2179
2378
  weight: 10,
2180
2379
  evaluate: (ctx) => {
2181
2380
  if (!hasPageImages(ctx.elements.tree)) {
2182
- return { status: "skipped", reason: (0, import_i18n30.__)("No images", "elementor") };
2381
+ return { status: "skipped", reason: (0, import_i18n31.__)("No images", "elementor") };
2183
2382
  }
2184
2383
  const violations = [];
2185
2384
  const failedWidgetIds = /* @__PURE__ */ new Set();
@@ -2192,10 +2391,10 @@ var audit8 = {
2192
2391
  if (!failedWidgetIds.has(node.id)) {
2193
2392
  failedWidgetIds.add(node.id);
2194
2393
  violations.push({
2195
- auditId: audit8.id,
2394
+ auditId: audit9.id,
2196
2395
  elementId: node.id,
2197
2396
  targetHint: "element-settings",
2198
- label: (0, import_i18n30.__)("Image is missing alt text.", "elementor"),
2397
+ label: (0, import_i18n31.__)("Image is missing alt text.", "elementor"),
2199
2398
  externalUrl: ctx.pageContext.ally_plugin_url
2200
2399
  });
2201
2400
  }
@@ -2205,20 +2404,20 @@ var audit8 = {
2205
2404
  };
2206
2405
 
2207
2406
  // src/audits/images-too-large.ts
2208
- var import_i18n31 = require("@wordpress/i18n");
2407
+ var import_i18n32 = require("@wordpress/i18n");
2209
2408
  var SIZE_THRESHOLD_BYTES = 500 * 1024;
2210
2409
  var BYTES_PER_KB = 1024;
2211
- var audit9 = {
2410
+ var audit10 = {
2212
2411
  id: "audits/images-too-large",
2213
- title: (0, import_i18n31.__)("Oversized images", "elementor"),
2214
- description: (0, import_i18n31.__)("Large image files slow down the page.", "elementor"),
2215
- fixHint: (0, import_i18n31.__)("Replace the image with a smaller version or enable image optimization.", "elementor"),
2412
+ title: (0, import_i18n32.__)("Oversized images", "elementor"),
2413
+ description: (0, import_i18n32.__)("Large image files slow down the page.", "elementor"),
2414
+ fixHint: (0, import_i18n32.__)("Replace the image with a smaller version or enable image optimization.", "elementor"),
2216
2415
  categories: ["performance"],
2217
2416
  severity: "warning",
2218
2417
  weight: 7,
2219
2418
  evaluate: (ctx) => {
2220
2419
  if (!hasPageImages(ctx.elements.tree)) {
2221
- return { status: "skipped", reason: (0, import_i18n31.__)("No images", "elementor") };
2420
+ return { status: "skipped", reason: (0, import_i18n32.__)("No images", "elementor") };
2222
2421
  }
2223
2422
  const widgetMaxKb = /* @__PURE__ */ new Map();
2224
2423
  let oversizedImageCount = 0;
@@ -2237,12 +2436,12 @@ var audit9 = {
2237
2436
  widgetMaxKb.set(node.id, Math.max(currentMax, kb));
2238
2437
  });
2239
2438
  const violations = Array.from(widgetMaxKb.entries()).map(([elementId, kb]) => ({
2240
- auditId: audit9.id,
2439
+ auditId: audit10.id,
2241
2440
  elementId,
2242
2441
  targetHint: "element-settings",
2243
- label: (0, import_i18n31.sprintf)(
2442
+ label: (0, import_i18n32.sprintf)(
2244
2443
  /* translators: %d is the image file size in kilobytes. */
2245
- (0, import_i18n31.__)("Image is %d KB (over 500 KB).", "elementor"),
2444
+ (0, import_i18n32.__)("Image is %d KB (over 500 KB).", "elementor"),
2246
2445
  kb
2247
2446
  ),
2248
2447
  externalUrl: ctx.pageContext.image_optimization_plugin_url
@@ -2252,21 +2451,21 @@ var audit9 = {
2252
2451
  };
2253
2452
 
2254
2453
  // src/audits/nested-boxed-containers.ts
2255
- var import_i18n32 = require("@wordpress/i18n");
2454
+ var import_i18n33 = require("@wordpress/i18n");
2256
2455
  function isBoxed(settings) {
2257
2456
  return settings.content_width === "boxed";
2258
2457
  }
2259
- var audit10 = {
2458
+ var audit11 = {
2260
2459
  id: "audits/nested-boxed-containers",
2261
- title: (0, import_i18n32.__)("Boxed container nested inside a boxed parent", "elementor"),
2262
- description: (0, import_i18n32.__)("An inner container does not need to be boxed when its parent already is.", "elementor"),
2263
- fixHint: (0, import_i18n32.__)("Change the inner container's content width to Full Width.", "elementor"),
2460
+ title: (0, import_i18n33.__)("Boxed container nested inside a boxed parent", "elementor"),
2461
+ description: (0, import_i18n33.__)("An inner container does not need to be boxed when its parent already is.", "elementor"),
2462
+ fixHint: (0, import_i18n33.__)("Change the inner container's content width to Full Width.", "elementor"),
2264
2463
  categories: ["best-practices", "performance"],
2265
2464
  severity: "info",
2266
2465
  weight: 5,
2267
2466
  evaluate: (ctx) => {
2268
2467
  if (ctx.elements.tree.length === 0) {
2269
- return { status: "skipped", reason: (0, import_i18n32.__)("No elements", "elementor") };
2468
+ return { status: "skipped", reason: (0, import_i18n33.__)("No elements", "elementor") };
2270
2469
  }
2271
2470
  const violations = [];
2272
2471
  walkElements(ctx.elements.tree, (node, parents) => {
@@ -2276,10 +2475,10 @@ var audit10 = {
2276
2475
  const nearestContainerAncestor = [...parents].reverse().find((p) => p.elType === "container");
2277
2476
  if (nearestContainerAncestor && isBoxed(nearestContainerAncestor.settings)) {
2278
2477
  violations.push({
2279
- auditId: audit10.id,
2478
+ auditId: audit11.id,
2280
2479
  elementId: node.id,
2281
2480
  targetHint: "element-settings",
2282
- label: (0, import_i18n32.__)("Nested boxed container.", "elementor")
2481
+ label: (0, import_i18n33.__)("Nested boxed container.", "elementor")
2283
2482
  });
2284
2483
  }
2285
2484
  });
@@ -2288,12 +2487,12 @@ var audit10 = {
2288
2487
  };
2289
2488
 
2290
2489
  // src/audits/page-excerpt.ts
2291
- var import_i18n33 = require("@wordpress/i18n");
2292
- var audit11 = {
2490
+ var import_i18n34 = require("@wordpress/i18n");
2491
+ var audit12 = {
2293
2492
  id: "audits/page-excerpt",
2294
- title: (0, import_i18n33.__)("Page excerpt", "elementor"),
2295
- description: (0, import_i18n33.__)("A descriptive excerpt helps search engines and previews summarize the page.", "elementor"),
2296
- fixHint: (0, import_i18n33.__)("Open Page Settings and write a short excerpt.", "elementor"),
2493
+ title: (0, import_i18n34.__)("Page excerpt", "elementor"),
2494
+ description: (0, import_i18n34.__)("A descriptive excerpt helps search engines and previews summarize the page.", "elementor"),
2495
+ fixHint: (0, import_i18n34.__)("Open Page Settings and write a short excerpt.", "elementor"),
2297
2496
  categories: ["seo"],
2298
2497
  severity: "warning",
2299
2498
  weight: 5,
@@ -2305,8 +2504,8 @@ var audit11 = {
2305
2504
  status: "fail",
2306
2505
  violations: [
2307
2506
  {
2308
- auditId: audit11.id,
2309
- label: (0, import_i18n33.__)("Page has no excerpt.", "elementor"),
2507
+ auditId: audit12.id,
2508
+ label: (0, import_i18n34.__)("Page has no excerpt.", "elementor"),
2310
2509
  targetHint: "page-settings",
2311
2510
  angieFix: true
2312
2511
  }
@@ -2316,12 +2515,12 @@ var audit11 = {
2316
2515
  };
2317
2516
 
2318
2517
  // src/audits/page-featured-image.ts
2319
- var import_i18n34 = require("@wordpress/i18n");
2320
- var audit12 = {
2518
+ var import_i18n35 = require("@wordpress/i18n");
2519
+ var audit13 = {
2321
2520
  id: "audits/page-featured-image",
2322
- title: (0, import_i18n34.__)("Page featured image", "elementor"),
2323
- description: (0, import_i18n34.__)("Featured images are used by social shares and many themes for hero visuals.", "elementor"),
2324
- fixHint: (0, import_i18n34.__)("Open Page Settings and set a featured image.", "elementor"),
2521
+ title: (0, import_i18n35.__)("Page featured image", "elementor"),
2522
+ description: (0, import_i18n35.__)("Featured images are used by social shares and many themes for hero visuals.", "elementor"),
2523
+ fixHint: (0, import_i18n35.__)("Open Page Settings and set a featured image.", "elementor"),
2325
2524
  categories: ["seo"],
2326
2525
  severity: "info",
2327
2526
  weight: 5,
@@ -2333,8 +2532,8 @@ var audit12 = {
2333
2532
  status: "fail",
2334
2533
  violations: [
2335
2534
  {
2336
- auditId: audit12.id,
2337
- label: (0, import_i18n34.__)("No featured image set.", "elementor"),
2535
+ auditId: audit13.id,
2536
+ label: (0, import_i18n35.__)("No featured image set.", "elementor"),
2338
2537
  targetHint: "page-settings"
2339
2538
  }
2340
2539
  ]
@@ -2343,13 +2542,13 @@ var audit12 = {
2343
2542
  };
2344
2543
 
2345
2544
  // src/audits/page-title.ts
2346
- var import_i18n35 = require("@wordpress/i18n");
2545
+ var import_i18n36 = require("@wordpress/i18n");
2347
2546
  var MAX_TITLE_LENGTH = 60;
2348
- var audit13 = {
2547
+ var audit14 = {
2349
2548
  id: "audits/page-title",
2350
- title: (0, import_i18n35.__)("Page title", "elementor"),
2351
- description: (0, import_i18n35.__)("Pages need a clear title for SEO and screen-reader navigation.", "elementor"),
2352
- fixHint: (0, import_i18n35.__)("Open Page Settings and add a title.", "elementor"),
2549
+ title: (0, import_i18n36.__)("Page title", "elementor"),
2550
+ description: (0, import_i18n36.__)("Pages need a clear title for SEO and screen-reader navigation.", "elementor"),
2551
+ fixHint: (0, import_i18n36.__)("Open Page Settings and add a title.", "elementor"),
2353
2552
  categories: ["seo"],
2354
2553
  severity: "error",
2355
2554
  weight: 10,
@@ -2360,8 +2559,8 @@ var audit13 = {
2360
2559
  status: "fail",
2361
2560
  violations: [
2362
2561
  {
2363
- auditId: audit13.id,
2364
- label: (0, import_i18n35.__)("Page has no title.", "elementor"),
2562
+ auditId: audit14.id,
2563
+ label: (0, import_i18n36.__)("Page has no title.", "elementor"),
2365
2564
  targetHint: "page-settings",
2366
2565
  angieFix: true
2367
2566
  }
@@ -2373,8 +2572,8 @@ var audit13 = {
2373
2572
  status: "fail",
2374
2573
  violations: [
2375
2574
  {
2376
- auditId: audit13.id,
2377
- label: (0, import_i18n35.__)("Page title is too long.", "elementor"),
2575
+ auditId: audit14.id,
2576
+ label: (0, import_i18n36.__)("Page title is too long.", "elementor"),
2378
2577
  targetHint: "page-settings",
2379
2578
  angieFix: true
2380
2579
  }
@@ -2386,7 +2585,7 @@ var audit13 = {
2386
2585
  };
2387
2586
 
2388
2587
  // src/audits/prefer-global-colors.ts
2389
- var import_i18n36 = require("@wordpress/i18n");
2588
+ var import_i18n37 = require("@wordpress/i18n");
2390
2589
 
2391
2590
  // src/utils/collect-hardcoded-colors.ts
2392
2591
  var RESERVED_SETTING_KEYS = /* @__PURE__ */ new Set(["__globals__", "__dynamic__"]);
@@ -2452,20 +2651,20 @@ function findMatchingGlobalByValue(value, globals) {
2452
2651
  }
2453
2652
 
2454
2653
  // src/audits/prefer-global-colors.ts
2455
- var audit14 = {
2654
+ var audit15 = {
2456
2655
  id: "audits/prefer-global-colors",
2457
- title: (0, import_i18n36.__)("Prefer global colors", "elementor"),
2458
- description: (0, import_i18n36.__)("Global colors make the design consistent and easy to update site-wide.", "elementor"),
2459
- fixHint: (0, import_i18n36.__)("Replace the hard-coded value with the matching global color.", "elementor"),
2656
+ title: (0, import_i18n37.__)("Prefer global colors", "elementor"),
2657
+ description: (0, import_i18n37.__)("Global colors make the design consistent and easy to update site-wide.", "elementor"),
2658
+ fixHint: (0, import_i18n37.__)("Replace the hard-coded value with the matching global color.", "elementor"),
2460
2659
  categories: ["best-practices"],
2461
2660
  severity: "info",
2462
2661
  weight: 3,
2463
2662
  evaluate: (ctx) => {
2464
2663
  if (ctx.elements.tree.length === 0) {
2465
- return { status: "skipped", reason: (0, import_i18n36.__)("No elements", "elementor") };
2664
+ return { status: "skipped", reason: (0, import_i18n37.__)("No elements", "elementor") };
2466
2665
  }
2467
2666
  if (ctx.kit.globals.colors.length === 0) {
2468
- return { status: "skipped", reason: (0, import_i18n36.__)("No global colors", "elementor") };
2667
+ return { status: "skipped", reason: (0, import_i18n37.__)("No global colors", "elementor") };
2469
2668
  }
2470
2669
  const violations = [];
2471
2670
  walkElements(ctx.elements.tree, (node) => {
@@ -2475,13 +2674,13 @@ var audit14 = {
2475
2674
  continue;
2476
2675
  }
2477
2676
  violations.push({
2478
- auditId: audit14.id,
2677
+ auditId: audit15.id,
2479
2678
  elementId: node.id,
2480
2679
  targetHint: "element-settings",
2481
2680
  angieFix: true,
2482
- label: (0, import_i18n36.sprintf)(
2681
+ label: (0, import_i18n37.sprintf)(
2483
2682
  /* translators: 1: hard-coded color value, 2: global color title. */
2484
- (0, import_i18n36.__)('replace "%1$s" with "%2$s" global color', "elementor"),
2683
+ (0, import_i18n37.__)('replace "%1$s" with "%2$s" global color', "elementor"),
2485
2684
  value,
2486
2685
  global.title
2487
2686
  )
@@ -2493,7 +2692,7 @@ var audit14 = {
2493
2692
  };
2494
2693
 
2495
2694
  // src/audits/prefer-global-fonts.ts
2496
- var import_i18n37 = require("@wordpress/i18n");
2695
+ var import_i18n38 = require("@wordpress/i18n");
2497
2696
 
2498
2697
  // src/utils/collect-hardcoded-fonts.ts
2499
2698
  var RESERVED_SETTING_KEYS2 = /* @__PURE__ */ new Set(["__globals__", "__dynamic__"]);
@@ -2551,20 +2750,20 @@ function collectHardcodedFonts(settings, pathPrefix = "") {
2551
2750
  }
2552
2751
 
2553
2752
  // src/audits/prefer-global-fonts.ts
2554
- var audit15 = {
2753
+ var audit16 = {
2555
2754
  id: "audits/prefer-global-fonts",
2556
- title: (0, import_i18n37.__)("Prefer global fonts", "elementor"),
2557
- description: (0, import_i18n37.__)("Global fonts make the design consistent and easy to update site-wide.", "elementor"),
2558
- fixHint: (0, import_i18n37.__)("Replace the hard-coded value with the matching global font.", "elementor"),
2755
+ title: (0, import_i18n38.__)("Prefer global fonts", "elementor"),
2756
+ description: (0, import_i18n38.__)("Global fonts make the design consistent and easy to update site-wide.", "elementor"),
2757
+ fixHint: (0, import_i18n38.__)("Replace the hard-coded value with the matching global font.", "elementor"),
2559
2758
  categories: ["best-practices", "performance"],
2560
2759
  severity: "info",
2561
2760
  weight: 3,
2562
2761
  evaluate: (ctx) => {
2563
2762
  if (ctx.elements.tree.length === 0) {
2564
- return { status: "skipped", reason: (0, import_i18n37.__)("No elements", "elementor") };
2763
+ return { status: "skipped", reason: (0, import_i18n38.__)("No elements", "elementor") };
2565
2764
  }
2566
2765
  if (ctx.kit.globals.fonts.length === 0) {
2567
- return { status: "skipped", reason: (0, import_i18n37.__)("No global fonts", "elementor") };
2766
+ return { status: "skipped", reason: (0, import_i18n38.__)("No global fonts", "elementor") };
2568
2767
  }
2569
2768
  const violations = [];
2570
2769
  walkElements(ctx.elements.tree, (node) => {
@@ -2574,12 +2773,12 @@ var audit15 = {
2574
2773
  continue;
2575
2774
  }
2576
2775
  violations.push({
2577
- auditId: audit15.id,
2776
+ auditId: audit16.id,
2578
2777
  elementId: node.id,
2579
2778
  targetHint: "element-settings",
2580
- label: (0, import_i18n37.sprintf)(
2779
+ label: (0, import_i18n38.sprintf)(
2581
2780
  /* translators: %s: global font title. */
2582
- (0, import_i18n37.__)('replace hardcoded typography with "%s" global font', "elementor"),
2781
+ (0, import_i18n38.__)('replace hardcoded typography with "%s" global font', "elementor"),
2583
2782
  global.title
2584
2783
  )
2585
2784
  });
@@ -2590,15 +2789,15 @@ var audit15 = {
2590
2789
  };
2591
2790
 
2592
2791
  // src/audits/privacy-policy.ts
2593
- var import_i18n38 = require("@wordpress/i18n");
2594
- var audit16 = {
2792
+ var import_i18n39 = require("@wordpress/i18n");
2793
+ var audit17 = {
2595
2794
  id: "audits/privacy-policy",
2596
- title: (0, import_i18n38.__)("Privacy policy", "elementor"),
2597
- description: (0, import_i18n38.__)(
2795
+ title: (0, import_i18n39.__)("Privacy policy", "elementor"),
2796
+ description: (0, import_i18n39.__)(
2598
2797
  "A privacy policy page is required by privacy regulations such as GDPR (EU) and CCPA (US).",
2599
2798
  "elementor"
2600
2799
  ),
2601
- fixHint: (0, import_i18n38.__)(
2800
+ fixHint: (0, import_i18n39.__)(
2602
2801
  "Go to Settings > Privacy in the WordPress admin and assign a published privacy policy page.",
2603
2802
  "elementor"
2604
2803
  ),
@@ -2613,9 +2812,10 @@ var audit16 = {
2613
2812
  status: "fail",
2614
2813
  violations: [
2615
2814
  {
2616
- auditId: audit16.id,
2617
- label: (0, import_i18n38.__)("No privacy policy page is set.", "elementor"),
2618
- externalUrl: ctx.pageContext.privacy_settings_url
2815
+ auditId: audit17.id,
2816
+ label: (0, import_i18n39.__)("No privacy policy page is set.", "elementor"),
2817
+ externalUrl: ctx.pageContext.privacy_settings_url,
2818
+ ctaLabel: (0, import_i18n39.__)("Create", "elementor")
2619
2819
  }
2620
2820
  ]
2621
2821
  };
@@ -2623,15 +2823,15 @@ var audit16 = {
2623
2823
  };
2624
2824
 
2625
2825
  // src/audits/robots-noindex.ts
2626
- var import_i18n39 = require("@wordpress/i18n");
2627
- var audit17 = {
2826
+ var import_i18n40 = require("@wordpress/i18n");
2827
+ var audit18 = {
2628
2828
  id: "audits/robots-noindex",
2629
- title: (0, import_i18n39.__)("Search engine visibility", "elementor"),
2630
- description: (0, import_i18n39.__)(
2829
+ title: (0, import_i18n40.__)("Search engine visibility", "elementor"),
2830
+ description: (0, import_i18n40.__)(
2631
2831
  "When search engine visibility is checked in WordPress Settings -> Reading -> Search Engine Visibility, search engines are discouraged from indexing the website.",
2632
2832
  "elementor"
2633
2833
  ),
2634
- fixHint: (0, import_i18n39.__)(
2834
+ fixHint: (0, import_i18n40.__)(
2635
2835
  'Go to Settings \u2192 Reading in the WordPress admin and uncheck "Discourage search engines from indexing this site" when the website should be indexed.',
2636
2836
  "elementor"
2637
2837
  ),
@@ -2646,8 +2846,8 @@ var audit17 = {
2646
2846
  status: "fail",
2647
2847
  violations: [
2648
2848
  {
2649
- auditId: audit17.id,
2650
- label: (0, import_i18n39.__)("Search engines are discouraged from indexing this website.", "elementor"),
2849
+ auditId: audit18.id,
2850
+ label: (0, import_i18n40.__)("Search engines are discouraged from indexing this website.", "elementor"),
2651
2851
  externalUrl: ctx.pageContext.reading_settings_url
2652
2852
  }
2653
2853
  ]
@@ -2655,30 +2855,58 @@ var audit17 = {
2655
2855
  }
2656
2856
  };
2657
2857
 
2858
+ // src/audits/scan-for-cookies.ts
2859
+ var import_i18n41 = require("@wordpress/i18n");
2860
+ var audit19 = {
2861
+ id: "audits/scan-for-cookies",
2862
+ title: (0, import_i18n41.__)("Scan for cookies", "elementor"),
2863
+ description: (0, import_i18n41.__)(
2864
+ "Your site may be setting cookies you haven't disclosed. Some countries require a banner even without a full consent policy. Scan to see what needs disclosure.",
2865
+ "elementor"
2866
+ ),
2867
+ fixHint: (0, import_i18n41.__)("Run a Cookiez scan on this page to see which cookies need disclosure.", "elementor"),
2868
+ categories: ["compliance"],
2869
+ severity: "info",
2870
+ weight: 0,
2871
+ evaluate: (ctx) => {
2872
+ const isReady = ctx.pageContext.cookiez_plugin_installed && ctx.pageContext.cookiez_plugin_active;
2873
+ return {
2874
+ status: "fail",
2875
+ violations: [
2876
+ {
2877
+ auditId: audit19.id,
2878
+ label: (0, import_i18n41.__)("This page has not been scanned for cookies yet.", "elementor"),
2879
+ externalUrl: isReady ? ctx.pageContext.cookiez_scan_url : ctx.pageContext.cookiez_plugin_action_url
2880
+ }
2881
+ ]
2882
+ };
2883
+ }
2884
+ };
2885
+
2658
2886
  // src/audits/sections-and-columns.ts
2659
- var import_i18n40 = require("@wordpress/i18n");
2660
- var audit18 = {
2887
+ var import_i18n42 = require("@wordpress/i18n");
2888
+ var audit20 = {
2661
2889
  id: "audits/sections-and-columns",
2662
- title: (0, import_i18n40.__)("Sections and columns", "elementor"),
2663
- description: (0, import_i18n40.__)(
2890
+ title: (0, import_i18n42.__)("Sections and columns", "elementor"),
2891
+ description: (0, import_i18n42.__)(
2664
2892
  "Sections and columns are legacy elements. Containers render fewer DOM nodes and are more flexible.",
2665
2893
  "elementor"
2666
2894
  ),
2667
- fixHint: (0, import_i18n40.__)("Use the Container Converter to replace each section/column with a container.", "elementor"),
2895
+ fixHint: (0, import_i18n42.__)("Use the Container Converter to replace each section/column with a container.", "elementor"),
2668
2896
  categories: ["best-practices", "performance"],
2669
2897
  severity: "warning",
2670
2898
  weight: 7,
2671
2899
  evaluate: (ctx) => {
2672
2900
  if (ctx.elements.tree.length === 0) {
2673
- return { status: "skipped", reason: (0, import_i18n40.__)("No elements", "elementor") };
2901
+ return { status: "skipped", reason: (0, import_i18n42.__)("No elements", "elementor") };
2674
2902
  }
2675
2903
  const violations = [];
2676
2904
  walkElements(ctx.elements.tree, (node) => {
2677
2905
  if (node.elType === "section" || node.elType === "column") {
2678
2906
  violations.push({
2679
- auditId: audit18.id,
2907
+ auditId: audit20.id,
2680
2908
  elementId: node.id,
2681
- label: node.elType === "section" ? (0, import_i18n40.__)("Section element", "elementor") : (0, import_i18n40.__)("Column element", "elementor")
2909
+ label: node.elType === "section" ? (0, import_i18n42.__)("Section element", "elementor") : (0, import_i18n42.__)("Column element", "elementor")
2682
2910
  });
2683
2911
  }
2684
2912
  });
@@ -2687,15 +2915,15 @@ var audit18 = {
2687
2915
  };
2688
2916
 
2689
2917
  // src/audits/site-identity.ts
2690
- var import_i18n41 = require("@wordpress/i18n");
2691
- var audit19 = {
2918
+ var import_i18n43 = require("@wordpress/i18n");
2919
+ var audit21 = {
2692
2920
  id: "audits/site-identity",
2693
- title: (0, import_i18n41.__)("Site identity", "elementor"),
2694
- description: (0, import_i18n41.__)(
2921
+ title: (0, import_i18n43.__)("Site identity", "elementor"),
2922
+ description: (0, import_i18n43.__)(
2695
2923
  "Site name, description, logo, and favicon establish your brand and appear in search results and browser tabs.",
2696
2924
  "elementor"
2697
2925
  ),
2698
- fixHint: (0, import_i18n41.__)("Open Site Settings \u2192 Site Identity and complete all the missing fields.", "elementor"),
2926
+ fixHint: (0, import_i18n43.__)("Open Site Settings \u2192 Site Identity and complete all the missing fields.", "elementor"),
2699
2927
  categories: ["best-practices", "seo"],
2700
2928
  severity: "info",
2701
2929
  weight: 7,
@@ -2704,30 +2932,30 @@ var audit19 = {
2704
2932
  const violations = [];
2705
2933
  if (!identity.site_name_set) {
2706
2934
  violations.push({
2707
- auditId: audit19.id,
2708
- label: (0, import_i18n41.__)("Site name is missing or still uses the default.", "elementor"),
2935
+ auditId: audit21.id,
2936
+ label: (0, import_i18n43.__)("Site name is missing or still uses the default.", "elementor"),
2709
2937
  targetHint: "site-identity-settings"
2710
2938
  });
2711
2939
  }
2712
2940
  if (!identity.site_description_set) {
2713
2941
  violations.push({
2714
- auditId: audit19.id,
2715
- label: (0, import_i18n41.__)("Site description is missing or still uses the default.", "elementor"),
2942
+ auditId: audit21.id,
2943
+ label: (0, import_i18n43.__)("Site description is missing or still uses the default.", "elementor"),
2716
2944
  targetHint: "site-identity-settings",
2717
2945
  angieFix: true
2718
2946
  });
2719
2947
  }
2720
2948
  if (!identity.site_logo_set) {
2721
2949
  violations.push({
2722
- auditId: audit19.id,
2723
- label: (0, import_i18n41.__)("Site logo is not set.", "elementor"),
2950
+ auditId: audit21.id,
2951
+ label: (0, import_i18n43.__)("Site logo is not set.", "elementor"),
2724
2952
  targetHint: "site-identity-settings"
2725
2953
  });
2726
2954
  }
2727
2955
  if (!identity.site_favicon_set) {
2728
2956
  violations.push({
2729
- auditId: audit19.id,
2730
- label: (0, import_i18n41.__)("Site favicon is not set.", "elementor"),
2957
+ auditId: audit21.id,
2958
+ label: (0, import_i18n43.__)("Site favicon is not set.", "elementor"),
2731
2959
  targetHint: "site-identity-settings"
2732
2960
  });
2733
2961
  }
@@ -2742,19 +2970,19 @@ var audit19 = {
2742
2970
  };
2743
2971
 
2744
2972
  // src/audits/too-many-widgets.ts
2745
- var import_i18n42 = require("@wordpress/i18n");
2973
+ var import_i18n44 = require("@wordpress/i18n");
2746
2974
  var WIDGET_COUNT_THRESHOLD = 100;
2747
- var audit20 = {
2975
+ var audit22 = {
2748
2976
  id: "audits/too-many-widgets",
2749
- title: (0, import_i18n42.__)("Too many widgets", "elementor"),
2750
- description: (0, import_i18n42.__)("Excessive DOM size caused by too many widgets degrades rendering performance.", "elementor"),
2751
- fixHint: (0, import_i18n42.__)("Reduce the number of widgets on the page by removing or combining elements.", "elementor"),
2977
+ title: (0, import_i18n44.__)("Too many widgets", "elementor"),
2978
+ description: (0, import_i18n44.__)("Excessive DOM size caused by too many widgets degrades rendering performance.", "elementor"),
2979
+ fixHint: (0, import_i18n44.__)("Reduce the number of widgets on the page by removing or combining elements.", "elementor"),
2752
2980
  categories: ["best-practices", "performance"],
2753
2981
  severity: "warning",
2754
2982
  weight: 5,
2755
2983
  evaluate: (ctx) => {
2756
2984
  if (ctx.elements.tree.length === 0) {
2757
- return { status: "skipped", reason: (0, import_i18n42.__)("No elements", "elementor") };
2985
+ return { status: "skipped", reason: (0, import_i18n44.__)("No elements", "elementor") };
2758
2986
  }
2759
2987
  let widgetCount = 0;
2760
2988
  walkElements(ctx.elements.tree, (node) => {
@@ -2769,8 +2997,8 @@ var audit20 = {
2769
2997
  status: "fail",
2770
2998
  violations: [
2771
2999
  {
2772
- auditId: audit20.id,
2773
- label: (0, import_i18n42.__)("Page has too many widgets.", "elementor")
3000
+ auditId: audit22.id,
3001
+ label: (0, import_i18n44.__)("Page has too many widgets.", "elementor")
2774
3002
  }
2775
3003
  ]
2776
3004
  };
@@ -2779,30 +3007,32 @@ var audit20 = {
2779
3007
 
2780
3008
  // src/register-audits.ts
2781
3009
  var AUDITS = [
2782
- audit13,
2783
- audit11,
3010
+ audit14,
2784
3011
  audit12,
2785
- audit7,
3012
+ audit13,
3013
+ audit8,
2786
3014
  audit5,
3015
+ audit22,
2787
3016
  audit20,
2788
- audit18,
2789
3017
  audit3,
2790
- audit10,
3018
+ audit11,
2791
3019
  audit4,
2792
- audit19,
2793
- audit14,
3020
+ audit21,
2794
3021
  audit15,
2795
- audit6,
2796
- audit8,
3022
+ audit16,
3023
+ audit7,
2797
3024
  audit9,
3025
+ audit10,
3026
+ audit18,
2798
3027
  audit17,
2799
- audit16,
2800
3028
  audit,
2801
- audit2
3029
+ audit2,
3030
+ audit19,
3031
+ audit6
2802
3032
  ];
2803
3033
  function registerAllAudits() {
2804
- for (const audit21 of AUDITS) {
2805
- registerAudit(audit21);
3034
+ for (const audit23 of AUDITS) {
3035
+ registerAudit(audit23);
2806
3036
  }
2807
3037
  }
2808
3038