@elementor/editor-audits 4.3.4 → 4.3.5

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.
Files changed (52) hide show
  1. package/dist/index.d.mts +2 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +789 -1098
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +731 -1040
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +17 -17
  8. package/src/audits/accessibility-statement.ts +2 -2
  9. package/src/audits/cookie-policy.ts +1 -0
  10. package/src/audits/images-alt-text.ts +6 -4
  11. package/src/audits/images-inefficient-format.ts +8 -9
  12. package/src/audits/images-too-large.ts +8 -9
  13. package/src/audits/nested-boxed-containers.ts +5 -0
  14. package/src/components/audit-feedback.tsx +4 -1
  15. package/src/components/audit-panel.tsx +13 -13
  16. package/src/components/audit-toggle-icon.tsx +22 -0
  17. package/src/components/fix-violation-with-angie.tsx +18 -7
  18. package/src/components/pages/category-page.tsx +27 -3
  19. package/src/components/pages/overview-page.tsx +24 -29
  20. package/src/components/report-shell.tsx +10 -91
  21. package/src/components/rescan-button.tsx +38 -0
  22. package/src/components/severity-icons.tsx +5 -1
  23. package/src/components/severity-summary-cards.tsx +52 -0
  24. package/src/components/violation-cta-button.tsx +10 -2
  25. package/src/components/violation-icons.tsx +2 -2
  26. package/src/components/violation-row.tsx +118 -69
  27. package/src/hooks/use-audit-report.ts +5 -4
  28. package/src/hooks/use-audit-toggle-props.ts +2 -2
  29. package/src/hooks/use-single-expanded-card.ts +11 -0
  30. package/src/init.ts +2 -0
  31. package/src/register-audits.ts +0 -2
  32. package/src/register-promotions.ts +0 -23
  33. package/src/runner.ts +6 -2
  34. package/src/store/selectors.ts +4 -0
  35. package/src/store/slice.ts +10 -1
  36. package/src/sync-report-staleness.ts +14 -0
  37. package/src/types.ts +2 -0
  38. package/src/utils/all-image-sources.ts +17 -0
  39. package/src/utils/alt-text-image-sources.ts +24 -0
  40. package/src/utils/atomic-image-sources.ts +8 -4
  41. package/src/utils/audit-status-summary.ts +0 -57
  42. package/src/utils/image-like-sources.ts +0 -12
  43. package/src/utils/page-attachments.ts +3 -11
  44. package/src/audits/too-many-widgets.ts +0 -53
  45. package/src/components/audit-statuses-section.tsx +0 -65
  46. package/src/components/count-summary-circle.tsx +0 -66
  47. package/src/components/pages/all-audits-page.tsx +0 -65
  48. package/src/components/pages/issues-page.tsx +0 -65
  49. package/src/components/score-bar.tsx +0 -56
  50. package/src/components/score-circle.tsx +0 -60
  51. package/src/components/severity-filter-chips.tsx +0 -57
  52. package/src/utils/score-thresholds.ts +0 -23
package/dist/index.js CHANGED
@@ -37,30 +37,135 @@ __export(index_exports, {
37
37
  module.exports = __toCommonJS(index_exports);
38
38
 
39
39
  // src/init.ts
40
- var import_editor_floating_panels3 = require("@elementor/editor-floating-panels");
41
- var import_store4 = require("@elementor/store");
40
+ var import_editor_floating_panels6 = require("@elementor/editor-floating-panels");
41
+ var import_store7 = require("@elementor/store");
42
42
 
43
43
  // src/editor-app-bar.tsx
44
44
  var import_editor_app_bar = require("@elementor/editor-app-bar");
45
45
 
46
46
  // src/hooks/use-audit-toggle-props.ts
47
- var import_icons12 = require("@elementor/icons");
48
- var import_i18n24 = require("@wordpress/i18n");
47
+ var import_i18n18 = require("@wordpress/i18n");
48
+
49
+ // src/components/audit-toggle-icon.tsx
50
+ var React = __toESM(require("react"));
51
+ var import_icons = require("@elementor/icons");
52
+ var import_store2 = require("@elementor/store");
53
+ var import_ui = require("@elementor/ui");
54
+
55
+ // src/store/slice.ts
56
+ var import_store = require("@elementor/store");
57
+ var initialState = { status: "idle", report: null, error: null, isStale: false };
58
+ var slice = (0, import_store.__createSlice)({
59
+ name: "audits",
60
+ initialState,
61
+ reducers: {
62
+ runStarted(state) {
63
+ state.status = "loading";
64
+ state.error = null;
65
+ },
66
+ runSucceeded(state, action) {
67
+ state.status = "ready";
68
+ state.report = action.payload;
69
+ state.isStale = false;
70
+ },
71
+ runFailed(state, action) {
72
+ state.status = "error";
73
+ state.error = action.payload;
74
+ },
75
+ runAborted(state) {
76
+ state.status = state.report ? "ready" : "idle";
77
+ },
78
+ reportRestored(state, action) {
79
+ state.status = "ready";
80
+ state.report = action.payload;
81
+ state.error = null;
82
+ state.isStale = false;
83
+ },
84
+ reportCleared(state) {
85
+ state.status = "idle";
86
+ state.report = null;
87
+ state.error = null;
88
+ state.isStale = false;
89
+ },
90
+ reportStale(state) {
91
+ if (state.report) {
92
+ state.isStale = true;
93
+ }
94
+ }
95
+ }
96
+ });
97
+
98
+ // src/store/selectors.ts
99
+ function selectStatus(state) {
100
+ return state.audits.status;
101
+ }
102
+ function selectReport(state) {
103
+ return state.audits.report;
104
+ }
105
+ function selectError(state) {
106
+ return state.audits.error;
107
+ }
108
+ function selectIsStale(state) {
109
+ return state.audits.isStale;
110
+ }
111
+
112
+ // src/components/audit-toggle-icon.tsx
113
+ function AuditToggleIcon() {
114
+ const isStale = (0, import_store2.__useSelector)((state) => selectIsStale(state));
115
+ return /* @__PURE__ */ React.createElement(
116
+ import_ui.Badge,
117
+ {
118
+ variant: "dot",
119
+ color: "warning",
120
+ invisible: !isStale,
121
+ overlap: "circular",
122
+ slotProps: { badge: { "data-testid": "audit-stale-indicator" } }
123
+ },
124
+ /* @__PURE__ */ React.createElement(import_icons.ShieldCheckIcon, null)
125
+ );
126
+ }
49
127
 
50
128
  // src/editor-panel.tsx
51
- var import_editor_floating_panels2 = require("@elementor/editor-floating-panels");
129
+ var import_editor_floating_panels5 = require("@elementor/editor-floating-panels");
52
130
 
53
131
  // src/components/audit-panel.tsx
54
- var React25 = __toESM(require("react"));
132
+ var React21 = __toESM(require("react"));
55
133
  var import_editor_elements6 = require("@elementor/editor-elements");
56
- var import_editor_floating_panels = require("@elementor/editor-floating-panels");
57
- var import_ui24 = require("@elementor/ui");
58
- var import_i18n23 = require("@wordpress/i18n");
134
+ var import_editor_floating_panels4 = require("@elementor/editor-floating-panels");
135
+ var import_ui20 = require("@elementor/ui");
136
+ var import_i18n17 = require("@wordpress/i18n");
137
+
138
+ // src/constants.ts
139
+ var import_i18n = require("@wordpress/i18n");
140
+ var AUDIT_PANEL_ID = "audit-panel";
141
+ var REPORT_STORAGE_KEY_PREFIX = "elementor/audits/report";
142
+ var CREATE_WIDGET_EVENT = "elementor/editor/create-widget";
143
+ var ANGIE_FIX_ENTRY_POINT = "audit_violation";
144
+ var FEEDBACK_EXPERIMENT_NAME = "in_editor_feedback";
145
+ var FEEDBACK_ENTRY_POINT = "audit_panel";
146
+ var FEEDBACK_CLICKED_EVENT = "audit_feedback_clicked";
147
+ var FEEDBACK_SENT_EVENT = "audit_feedback_sent";
148
+ var FEEDBACK_CANCELLED_EVENT = "audit_feedback_cancelled";
149
+ var FEEDBACK_CLOSED_EVENT = "audit_feedback_closed";
150
+ var ALL_CATEGORIES = [
151
+ "best-practices",
152
+ "seo",
153
+ "accessibility",
154
+ "performance",
155
+ "compliance"
156
+ ];
157
+ var CATEGORY_LABELS = {
158
+ "best-practices": (0, import_i18n.__)("Best Practices", "elementor"),
159
+ seo: (0, import_i18n.__)("SEO", "elementor"),
160
+ accessibility: (0, import_i18n.__)("Accessibility", "elementor"),
161
+ performance: (0, import_i18n.__)("Performance", "elementor"),
162
+ compliance: (0, import_i18n.__)("Site compliance", "elementor")
163
+ };
59
164
 
60
165
  // src/hooks/use-audit-report.ts
61
166
  var import_react = require("react");
62
167
  var import_editor_elements3 = require("@elementor/editor-elements");
63
- var import_store2 = require("@elementor/store");
168
+ var import_store4 = require("@elementor/store");
64
169
 
65
170
  // src/runner.ts
66
171
  var import_editor_elements2 = require("@elementor/editor-elements");
@@ -141,46 +246,16 @@ async function requestPageContext(documentId, imageSizeRequests, allowNonceRetry
141
246
 
142
247
  // src/registry.ts
143
248
  var registry = /* @__PURE__ */ new Map();
144
- function registerAudit(audit26) {
145
- registry.set(audit26.id, audit26);
249
+ function registerAudit(audit25) {
250
+ registry.set(audit25.id, audit25);
146
251
  }
147
252
  function getRegisteredAudits() {
148
253
  return Array.from(registry.values());
149
254
  }
150
255
 
151
- // src/constants.ts
152
- var import_i18n = require("@wordpress/i18n");
153
- var AUDIT_PANEL_ID = "audit-panel";
154
- var REPORT_STORAGE_KEY_PREFIX = "elementor/audits/report";
155
- var CREATE_WIDGET_EVENT = "elementor/editor/create-widget";
156
- var ANGIE_FIX_ENTRY_POINT = "audit_violation";
157
- var FEEDBACK_EXPERIMENT_NAME = "in_editor_feedback";
158
- var FEEDBACK_ENTRY_POINT = "audit_panel";
159
- var FEEDBACK_CLICKED_EVENT = "audit_feedback_clicked";
160
- var FEEDBACK_SENT_EVENT = "audit_feedback_sent";
161
- var FEEDBACK_CANCELLED_EVENT = "audit_feedback_cancelled";
162
- var FEEDBACK_CLOSED_EVENT = "audit_feedback_closed";
163
- var ALL_CATEGORIES = [
164
- "best-practices",
165
- "seo",
166
- "accessibility",
167
- "performance",
168
- "compliance"
169
- ];
170
- var CATEGORY_LABELS = {
171
- "best-practices": (0, import_i18n.__)("Best Practices", "elementor"),
172
- seo: (0, import_i18n.__)("SEO", "elementor"),
173
- accessibility: (0, import_i18n.__)("Accessibility", "elementor"),
174
- performance: (0, import_i18n.__)("Performance", "elementor"),
175
- compliance: (0, import_i18n.__)("Site compliance", "elementor")
176
- };
177
-
178
- // src/utils/audit-status-summary.ts
179
- var import_i18n2 = require("@wordpress/i18n");
180
-
181
256
  // src/utils/is-scored-audit.ts
182
- function isScoredAudit(audit26) {
183
- return audit26.weight > 0;
257
+ function isScoredAudit(audit25) {
258
+ return audit25.weight > 0;
184
259
  }
185
260
 
186
261
  // src/utils/sort-failed-audits.ts
@@ -232,52 +307,6 @@ function partitionAuditResults(report, options = {}) {
232
307
  totalViolations
233
308
  };
234
309
  }
235
- function auditStatusDisplayCounts(report) {
236
- let pass = 0;
237
- let skipped = 0;
238
- let totalViolations = 0;
239
- for (const { audit: audit26, result } of report.auditResults) {
240
- if (!isScoredAudit(audit26)) {
241
- continue;
242
- }
243
- switch (result.status) {
244
- case "fail":
245
- totalViolations += result.violations.length;
246
- break;
247
- case "pass":
248
- pass++;
249
- break;
250
- case "skipped":
251
- skipped++;
252
- break;
253
- }
254
- }
255
- return {
256
- fail: totalViolations,
257
- pass,
258
- skipped
259
- };
260
- }
261
- function auditStatusColor(status) {
262
- switch (status) {
263
- case "fail":
264
- return "error";
265
- case "pass":
266
- return "success";
267
- case "skipped":
268
- return "default";
269
- }
270
- }
271
- function auditStatusLabel(status) {
272
- switch (status) {
273
- case "fail":
274
- return (0, import_i18n2.__)("Failed audits", "elementor");
275
- case "pass":
276
- return (0, import_i18n2.__)("Passed audits", "elementor");
277
- case "skipped":
278
- return (0, import_i18n2.__)("Skipped audits", "elementor");
279
- }
280
- }
281
310
  function getPopulatedCategories(categoryTotals, categories) {
282
311
  return categories.filter((category) => categoryTotals[category].total > 0);
283
312
  }
@@ -287,16 +316,16 @@ function computeReport(documentId, results) {
287
316
  const accumulators = Object.fromEntries(
288
317
  ALL_CATEGORIES.map((c) => [c, { totalWeight: 0, passedWeight: 0, total: 0, failed: 0 }])
289
318
  );
290
- for (const { audit: audit26, result } of results) {
319
+ for (const { audit: audit25, result } of results) {
291
320
  if (result.status === "skipped") {
292
321
  continue;
293
322
  }
294
- for (const category of audit26.categories) {
323
+ for (const category of audit25.categories) {
295
324
  const acc = accumulators[category];
296
325
  acc.total++;
297
- acc.totalWeight += audit26.weight;
326
+ acc.totalWeight += audit25.weight;
298
327
  if (result.status === "pass") {
299
- acc.passedWeight += audit26.weight;
328
+ acc.passedWeight += audit25.weight;
300
329
  } else {
301
330
  acc.failed++;
302
331
  }
@@ -363,13 +392,14 @@ function recurse(nodes, parents, visit) {
363
392
  }
364
393
 
365
394
  // src/utils/atomic-image-sources.ts
366
- var ATOMIC_IMAGE_PROP_KEYS = ["image", "poster"];
367
- function walkAtomicImageSources(tree, visit) {
395
+ var ATOMIC_CONTENT_IMAGE_PROP_KEYS = ["image"];
396
+ var ATOMIC_IMAGE_PROP_KEYS = [...ATOMIC_CONTENT_IMAGE_PROP_KEYS, "poster"];
397
+ function walkAtomicImageSources(tree, visit, propKeys = ATOMIC_IMAGE_PROP_KEYS) {
368
398
  walkElements(tree, (node) => {
369
399
  if (node.elType !== "widget") {
370
400
  return;
371
401
  }
372
- for (const key of ATOMIC_IMAGE_PROP_KEYS) {
402
+ for (const key of propKeys) {
373
403
  const media = extractAtomicImageMedia(node.settings[key]);
374
404
  if (media) {
375
405
  visit({ node, media });
@@ -391,8 +421,9 @@ function extractAtomicImageMedia(imageProp) {
391
421
  if (!id && !url) {
392
422
  return null;
393
423
  }
424
+ const alt = import_editor_props.stringPropTypeUtil.extract(src.alt) ?? void 0;
394
425
  const size = import_editor_props.stringPropTypeUtil.extract(image.size) ?? void 0;
395
- return { id, url, size };
426
+ return { id, url, alt, size };
396
427
  }
397
428
 
398
429
  // src/utils/atomic-background-image-sources.ts
@@ -447,15 +478,6 @@ function walkBackgroundImageSources(tree, visit) {
447
478
 
448
479
  // src/utils/image-like-sources.ts
449
480
  var IMAGE_LIKE_WIDGETS = /* @__PURE__ */ new Set(["image", "image-box", "image-carousel", "gallery", "image-gallery"]);
450
- function hasPageImages(tree) {
451
- let found = false;
452
- walkImageLikeSources(tree, ({ media }) => {
453
- if (media.id || media.url) {
454
- found = true;
455
- }
456
- });
457
- return found;
458
- }
459
481
  function walkImageLikeSources(tree, visit) {
460
482
  walkElements(tree, (node) => {
461
483
  if (node.elType !== "widget") {
@@ -487,6 +509,14 @@ function collectMediaFromNode(node) {
487
509
  return sources;
488
510
  }
489
511
 
512
+ // src/utils/all-image-sources.ts
513
+ function walkAllImageSources(tree, visit) {
514
+ walkImageLikeSources(tree, visit);
515
+ walkBackgroundImageSources(tree, visit);
516
+ walkAtomicImageSources(tree, visit);
517
+ walkAtomicBackgroundImageSources(tree, visit);
518
+ }
519
+
490
520
  // src/utils/image-size-key.ts
491
521
  function buildImageSizeKey({ id, size }) {
492
522
  return `${id}:${size || "full"}`;
@@ -495,15 +525,11 @@ function buildImageSizeKey({ id, size }) {
495
525
  // src/utils/page-attachments.ts
496
526
  function extractImageSizeRequests(tree) {
497
527
  const keys = /* @__PURE__ */ new Set();
498
- const collect = (media) => {
528
+ walkAllImageSources(tree, ({ media }) => {
499
529
  if (media.id) {
500
530
  keys.add(buildImageSizeKey({ id: media.id, size: media.size }));
501
531
  }
502
- };
503
- walkImageLikeSources(tree, ({ media }) => collect(media));
504
- walkBackgroundImageSources(tree, ({ media }) => collect(media));
505
- walkAtomicImageSources(tree, ({ media }) => collect(media));
506
- walkAtomicBackgroundImageSources(tree, ({ media }) => collect(media));
532
+ });
507
533
  return Array.from(keys).sort();
508
534
  }
509
535
 
@@ -598,7 +624,10 @@ function toSnapshot(element) {
598
624
 
599
625
  // src/runner.ts
600
626
  async function runPageAudit(documentId) {
601
- const tree = buildSnapshotTree((0, import_editor_elements2.getElements)());
627
+ if (documentId !== (0, import_editor_elements2.getHostDocumentId)()) {
628
+ throw new Error("runPageAudit: documentId must match the host document.");
629
+ }
630
+ const tree = buildSnapshotTree((0, import_editor_elements2.getHostDocumentElements)());
602
631
  const imageSizeRequests = extractImageSizeRequests(tree);
603
632
  const pageContext = await fetchPageContext(documentId, imageSizeRequests);
604
633
  const elements = { documentId, tree };
@@ -609,10 +638,10 @@ async function runPageAudit(documentId) {
609
638
  const ctx = { documentId, elements, pageContext, kit, renderedHtml };
610
639
  const registered = getRegisteredAudits();
611
640
  const auditResults = await Promise.all(
612
- registered.map(async (audit26) => {
613
- const { evaluate: _evaluate, ...meta } = audit26;
641
+ registered.map(async (audit25) => {
642
+ const { evaluate: _evaluate, ...meta } = audit25;
614
643
  try {
615
- const result = await audit26.evaluate(ctx);
644
+ const result = await audit25.evaluate(ctx);
616
645
  return { audit: meta, result };
617
646
  } catch (error) {
618
647
  const reason = error instanceof Error ? error.message : "unknown-error";
@@ -623,52 +652,6 @@ async function runPageAudit(documentId) {
623
652
  return computeReport(documentId, auditResults);
624
653
  }
625
654
 
626
- // src/store/slice.ts
627
- var import_store = require("@elementor/store");
628
- var initialState = { status: "idle", report: null, error: null };
629
- var slice = (0, import_store.__createSlice)({
630
- name: "audits",
631
- initialState,
632
- reducers: {
633
- runStarted(state) {
634
- state.status = "loading";
635
- state.error = null;
636
- },
637
- runSucceeded(state, action) {
638
- state.status = "ready";
639
- state.report = action.payload;
640
- },
641
- runFailed(state, action) {
642
- state.status = "error";
643
- state.error = action.payload;
644
- },
645
- runAborted(state) {
646
- state.status = state.report ? "ready" : "idle";
647
- },
648
- reportRestored(state, action) {
649
- state.status = "ready";
650
- state.report = action.payload;
651
- state.error = null;
652
- },
653
- reportCleared(state) {
654
- state.status = "idle";
655
- state.report = null;
656
- state.error = null;
657
- }
658
- }
659
- });
660
-
661
- // src/store/selectors.ts
662
- function selectStatus(state) {
663
- return state.audits.status;
664
- }
665
- function selectReport(state) {
666
- return state.audits.report;
667
- }
668
- function selectError(state) {
669
- return state.audits.error;
670
- }
671
-
672
655
  // src/utils/report-storage.ts
673
656
  var import_session = require("@elementor/session");
674
657
  function getStorageKey(documentId) {
@@ -683,51 +666,53 @@ function persistReport(documentId, report) {
683
666
 
684
667
  // src/hooks/use-audit-report.ts
685
668
  function useAuditReport() {
686
- const status = (0, import_store2.__useSelector)((state) => selectStatus(state));
687
- const report = (0, import_store2.__useSelector)((state) => selectReport(state));
688
- const error = (0, import_store2.__useSelector)((state) => selectError(state));
689
- const dispatch = (0, import_store2.__useDispatch)();
690
- const documentId = (0, import_editor_elements3.getCurrentDocumentId)() ?? 0;
669
+ const status = (0, import_store4.__useSelector)((state) => selectStatus(state));
670
+ const report = (0, import_store4.__useSelector)((state) => selectReport(state));
671
+ const error = (0, import_store4.__useSelector)((state) => selectError(state));
672
+ const isStale = (0, import_store4.__useSelector)((state) => selectIsStale(state));
673
+ const dispatch2 = (0, import_store4.__useDispatch)();
674
+ const documentId = (0, import_editor_elements3.getHostDocumentId)() ?? 0;
691
675
  (0, import_react.useEffect)(() => {
692
676
  if (!documentId || report?.documentId === documentId) {
693
677
  return;
694
678
  }
695
679
  const persisted = getPersistedReport(documentId);
696
680
  if (persisted) {
697
- dispatch(slice.actions.reportRestored(persisted));
681
+ dispatch2(slice.actions.reportRestored(persisted));
698
682
  } else if (report) {
699
- dispatch(slice.actions.reportCleared());
683
+ dispatch2(slice.actions.reportCleared());
700
684
  }
701
- }, [documentId, report, dispatch]);
685
+ }, [documentId, report, dispatch2]);
702
686
  const run = async (documentIdToRun) => {
703
- dispatch(slice.actions.runStarted());
687
+ dispatch2(slice.actions.runStarted());
704
688
  try {
705
689
  const nextReport = await runPageAudit(documentIdToRun);
706
- dispatch(slice.actions.runSucceeded(nextReport));
690
+ dispatch2(slice.actions.runSucceeded(nextReport));
707
691
  persistReport(documentIdToRun, nextReport);
708
692
  } catch (e) {
709
693
  if (e instanceof SessionExpiredError) {
710
- dispatch(slice.actions.runAborted());
694
+ dispatch2(slice.actions.runAborted());
711
695
  return;
712
696
  }
713
- dispatch(slice.actions.runFailed(e instanceof Error ? e.message : "Unknown error"));
697
+ dispatch2(slice.actions.runFailed(e instanceof Error ? e.message : "Unknown error"));
714
698
  }
715
699
  };
716
- return { status, report, error, run };
700
+ return { status, report, error, isStale, run };
717
701
  }
718
702
 
719
703
  // src/components/audit-feedback.tsx
720
- var React = __toESM(require("react"));
704
+ var React2 = __toESM(require("react"));
721
705
  var import_react2 = require("react");
706
+ var import_editor_floating_panels = require("@elementor/editor-floating-panels");
722
707
  var import_editor_notifications = require("@elementor/editor-notifications");
723
708
  var import_editor_v1_adapters = require("@elementor/editor-v1-adapters");
724
709
  var import_events = require("@elementor/events");
725
710
  var import_http_client2 = require("@elementor/http-client");
726
- var import_icons = require("@elementor/icons");
727
- var import_ui = require("@elementor/ui");
728
- var import_i18n3 = require("@wordpress/i18n");
711
+ var import_icons2 = require("@elementor/icons");
712
+ var import_ui2 = require("@elementor/ui");
713
+ var import_i18n2 = require("@wordpress/i18n");
729
714
  var FEEDBACK_SUBJECT = "Page Audit Tool";
730
- var FEEDBACK_SUBJECT_LABEL = (0, import_i18n3.__)("Page Audit Tool", "elementor");
715
+ var FEEDBACK_SUBJECT_LABEL = (0, import_i18n2.__)("Page Audit Tool", "elementor");
731
716
  var SUCCESS_NOTIFICATION_ID = "audit-feedback-success";
732
717
  var ERROR_NOTIFICATION_ID = "audit-feedback-error";
733
718
  var DIALOG_TITLE_ID = "audit-feedback-dialog-title";
@@ -736,18 +721,19 @@ var isUserConnected = () => Boolean(window.elementorCommon?.config?.library_conn
736
721
  var notifySuccess = () => (0, import_editor_notifications.notify)({
737
722
  id: SUCCESS_NOTIFICATION_ID,
738
723
  type: "success",
739
- message: (0, import_i18n3.__)("Feedback sent. Thanks for helping us out.", "elementor")
724
+ message: (0, import_i18n2.__)("Feedback sent. Thanks for helping us out.", "elementor")
740
725
  });
741
726
  var notifyError = () => (0, import_editor_notifications.notify)({
742
727
  id: ERROR_NOTIFICATION_ID,
743
728
  type: "error",
744
- message: (0, import_i18n3.__)("Something went wrong. Please try sending your feedback again.", "elementor")
729
+ message: (0, import_i18n2.__)("Something went wrong. Please try sending your feedback again.", "elementor")
745
730
  });
746
731
  function AuditFeedback() {
747
732
  const [isOpen, setIsOpen] = (0, import_react2.useState)(false);
748
733
  const [feedbackText, setFeedbackText] = (0, import_react2.useState)("");
749
734
  const [isSubmitting, setIsSubmitting] = (0, import_react2.useState)(false);
750
735
  const { dispatchEvent: trackEvent = (...args) => void args } = (0, import_events.useMixpanel)();
736
+ const panelZIndex = (0, import_editor_floating_panels.useFloatingPanelZIndex)(AUDIT_PANEL_ID);
751
737
  if (!(0, import_editor_v1_adapters.isExperimentActive)(FEEDBACK_EXPERIMENT_NAME)) {
752
738
  return null;
753
739
  }
@@ -756,7 +742,7 @@ function AuditFeedback() {
756
742
  if (!connected && !connectUrl) {
757
743
  return null;
758
744
  }
759
- const triggerLabel = (0, import_i18n3.__)("Give feedback", "elementor");
745
+ const triggerLabel = (0, import_i18n2.__)("Give feedback", "elementor");
760
746
  const handleOpen = () => {
761
747
  if (!connected) {
762
748
  window.open(connectUrl, "_blank", "noopener");
@@ -789,8 +775,8 @@ function AuditFeedback() {
789
775
  trackEvent(FEEDBACK_SENT_EVENT, { entry_point: FEEDBACK_ENTRY_POINT });
790
776
  }).catch(notifyError).finally(() => setIsSubmitting(false));
791
777
  };
792
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(import_ui.Tooltip, { title: triggerLabel, placement: "top" }, /* @__PURE__ */ React.createElement(import_ui.IconButton, { size: "small", "aria-label": triggerLabel, onClick: handleOpen }, /* @__PURE__ */ React.createElement(import_icons.MessageLinesIcon, { fontSize: "small" }))), /* @__PURE__ */ React.createElement(
793
- import_ui.Dialog,
778
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(import_ui2.Tooltip, { title: triggerLabel, placement: "top", PopperProps: { sx: { zIndex: panelZIndex } } }, /* @__PURE__ */ React2.createElement(import_ui2.IconButton, { size: "small", "aria-label": triggerLabel, onClick: handleOpen }, /* @__PURE__ */ React2.createElement(import_icons2.MessageLinesIcon, { fontSize: "small" }))), /* @__PURE__ */ React2.createElement(
779
+ import_ui2.Dialog,
794
780
  {
795
781
  open: isOpen,
796
782
  onClose: handleClose,
@@ -798,11 +784,11 @@ function AuditFeedback() {
798
784
  "aria-describedby": DIALOG_DESCRIPTION_ID,
799
785
  PaperProps: { sx: { borderRadius: 1 } }
800
786
  },
801
- /* @__PURE__ */ React.createElement(import_ui.DialogHeader, { logo: false, onClose: handleClose }, /* @__PURE__ */ React.createElement(import_ui.DialogTitle, { id: DIALOG_TITLE_ID }, (0, import_i18n3.__)("Let us know what you think", "elementor"))),
802
- /* @__PURE__ */ React.createElement(import_ui.DialogContent, { dividers: true }, /* @__PURE__ */ React.createElement(import_ui.Stack, { direction: "column", gap: 2 }, /* @__PURE__ */ React.createElement(import_ui.TextField, { value: FEEDBACK_SUBJECT_LABEL, disabled: true, fullWidth: true, size: "small" }), /* @__PURE__ */ React.createElement(
803
- import_ui.TextField,
787
+ /* @__PURE__ */ React2.createElement(import_ui2.DialogHeader, { logo: false, onClose: handleClose }, /* @__PURE__ */ React2.createElement(import_ui2.DialogTitle, { id: DIALOG_TITLE_ID }, (0, import_i18n2.__)("Let us know what you think", "elementor"))),
788
+ /* @__PURE__ */ React2.createElement(import_ui2.DialogContent, { dividers: true }, /* @__PURE__ */ React2.createElement(import_ui2.Stack, { direction: "column", gap: 2 }, /* @__PURE__ */ React2.createElement(import_ui2.TextField, { value: FEEDBACK_SUBJECT_LABEL, disabled: true, fullWidth: true, size: "small" }), /* @__PURE__ */ React2.createElement(
789
+ import_ui2.TextField,
804
790
  {
805
- placeholder: (0, import_i18n3.__)(
791
+ placeholder: (0, import_i18n2.__)(
806
792
  "Tell us what you had in mind, what we can improve or fix to make this feature better.",
807
793
  "elementor"
808
794
  ),
@@ -813,48 +799,48 @@ function AuditFeedback() {
813
799
  onChange: (event) => setFeedbackText(event.target.value),
814
800
  value: feedbackText
815
801
  }
816
- ), /* @__PURE__ */ React.createElement(import_ui.Typography, { id: DIALOG_DESCRIPTION_ID, variant: "caption", color: "text.secondary" }, (0, import_i18n3.__)(
802
+ ), /* @__PURE__ */ React2.createElement(import_ui2.Typography, { id: DIALOG_DESCRIPTION_ID, variant: "caption", color: "text.secondary" }, (0, import_i18n2.__)(
817
803
  "We appreciate your feedback! While we review all submissions, we can't guarantee that every suggestion will result in a change or update.",
818
804
  "elementor"
819
805
  )))),
820
- /* @__PURE__ */ React.createElement(import_ui.DialogActions, null, /* @__PURE__ */ React.createElement(import_ui.Button, { variant: "text", color: "secondary", onClick: handleCancel }, (0, import_i18n3.__)("Cancel", "elementor")), /* @__PURE__ */ React.createElement(
821
- import_ui.Button,
806
+ /* @__PURE__ */ React2.createElement(import_ui2.DialogActions, null, /* @__PURE__ */ React2.createElement(import_ui2.Button, { variant: "text", color: "secondary", onClick: handleCancel }, (0, import_i18n2.__)("Cancel", "elementor")), /* @__PURE__ */ React2.createElement(
807
+ import_ui2.Button,
822
808
  {
823
809
  variant: "contained",
824
810
  color: "primary",
825
811
  disabled: isSubmitting || feedbackText.trim().length === 0,
826
812
  onClick: handleSubmit
827
813
  },
828
- (0, import_i18n3.__)("Send", "elementor")
814
+ (0, import_i18n2.__)("Send", "elementor")
829
815
  ))
830
816
  ));
831
817
  }
832
818
 
833
819
  // src/components/pages/error-page.tsx
834
- var React2 = __toESM(require("react"));
835
- var import_ui2 = require("@elementor/ui");
836
- var import_i18n4 = require("@wordpress/i18n");
820
+ var React3 = __toESM(require("react"));
821
+ var import_ui3 = require("@elementor/ui");
822
+ var import_i18n3 = require("@wordpress/i18n");
837
823
  function ErrorPage({ message, onRetry }) {
838
- return /* @__PURE__ */ React2.createElement(import_ui2.Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 1.5, p: 4 } }, /* @__PURE__ */ React2.createElement(import_ui2.Typography, { variant: "body2", color: "error", textAlign: "center" }, message), /* @__PURE__ */ React2.createElement(import_ui2.Button, { variant: "contained", size: "small", onClick: onRetry }, (0, import_i18n4.__)("Try again", "elementor")));
824
+ return /* @__PURE__ */ React3.createElement(import_ui3.Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 1.5, p: 4 } }, /* @__PURE__ */ React3.createElement(import_ui3.Typography, { variant: "body2", color: "error", textAlign: "center" }, message), /* @__PURE__ */ React3.createElement(import_ui3.Button, { variant: "contained", size: "small", onClick: onRetry }, (0, import_i18n3.__)("Try again", "elementor")));
839
825
  }
840
826
 
841
827
  // src/components/pages/loading-page.tsx
842
- var React3 = __toESM(require("react"));
843
- var import_ui3 = require("@elementor/ui");
844
- var import_i18n5 = require("@wordpress/i18n");
828
+ var React4 = __toESM(require("react"));
829
+ var import_ui4 = require("@elementor/ui");
830
+ var import_i18n4 = require("@wordpress/i18n");
845
831
  function LoadingPage() {
846
- return /* @__PURE__ */ React3.createElement(import_ui3.Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 1.5, p: 4 } }, /* @__PURE__ */ React3.createElement(import_ui3.CircularProgress, { size: 32 }), /* @__PURE__ */ React3.createElement(import_ui3.Typography, { variant: "body2" }, (0, import_i18n5.__)("Audit in progress.", "elementor")));
832
+ return /* @__PURE__ */ React4.createElement(import_ui4.Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 1.5, p: 4 } }, /* @__PURE__ */ React4.createElement(import_ui4.CircularProgress, { size: 32 }), /* @__PURE__ */ React4.createElement(import_ui4.Typography, { variant: "body2" }, (0, import_i18n4.__)("Audit in progress.", "elementor")));
847
833
  }
848
834
 
849
835
  // src/components/pages/welcome-page.tsx
850
- var React4 = __toESM(require("react"));
851
- var import_ui4 = require("@elementor/ui");
852
- var import_i18n6 = require("@wordpress/i18n");
836
+ var React5 = __toESM(require("react"));
837
+ var import_ui5 = require("@elementor/ui");
838
+ var import_i18n5 = require("@wordpress/i18n");
853
839
  var INFORMATION_IMAGE_PATH = "images/information.svg";
854
840
  function WelcomePage() {
855
841
  const assetsUrl = window.elementorCommon?.config?.urls?.assets ?? "";
856
- return /* @__PURE__ */ React4.createElement(
857
- import_ui4.Box,
842
+ return /* @__PURE__ */ React5.createElement(
843
+ import_ui5.Box,
858
844
  {
859
845
  sx: {
860
846
  display: "flex",
@@ -865,17 +851,17 @@ function WelcomePage() {
865
851
  p: 3
866
852
  }
867
853
  },
868
- /* @__PURE__ */ React4.createElement(
854
+ /* @__PURE__ */ React5.createElement(
869
855
  "img",
870
856
  {
871
857
  className: "elementor-nerd-box-icon",
872
858
  src: `${assetsUrl}${INFORMATION_IMAGE_PATH}`,
873
859
  loading: "lazy",
874
- alt: (0, import_i18n6.__)("Elementor", "elementor")
860
+ alt: (0, import_i18n5.__)("Elementor", "elementor")
875
861
  }
876
862
  ),
877
- /* @__PURE__ */ React4.createElement(import_ui4.Typography, { variant: "subtitle1", component: "h3", sx: { marginBlockStart: 2 } }, (0, import_i18n6.__)("Audit your page!", "elementor")),
878
- /* @__PURE__ */ React4.createElement(import_ui4.Typography, { variant: "caption", textAlign: "center" }, (0, import_i18n6.__)(
863
+ /* @__PURE__ */ React5.createElement(import_ui5.Typography, { variant: "subtitle1", component: "h3", sx: { marginBlockStart: 2 } }, (0, import_i18n5.__)("Audit your page!", "elementor")),
864
+ /* @__PURE__ */ React5.createElement(import_ui5.Typography, { variant: "caption", textAlign: "center" }, (0, import_i18n5.__)(
879
865
  'Check SEO, accessibility, performance, and best practices. Click "Run page audit" to begin.',
880
866
  "elementor"
881
867
  ))
@@ -883,41 +869,60 @@ function WelcomePage() {
883
869
  }
884
870
 
885
871
  // src/components/report-shell.tsx
886
- var React24 = __toESM(require("react"));
887
- var import_react6 = require("react");
888
- var import_ui23 = require("@elementor/ui");
889
- var import_i18n22 = require("@wordpress/i18n");
872
+ var React19 = __toESM(require("react"));
873
+ var import_react5 = require("react");
874
+ var import_ui18 = require("@elementor/ui");
890
875
 
891
- // src/components/pages/all-audits-page.tsx
892
- var React12 = __toESM(require("react"));
893
- var import_ui11 = require("@elementor/ui");
894
- var import_i18n12 = require("@wordpress/i18n");
876
+ // src/components/pages/category-page.tsx
877
+ var React13 = __toESM(require("react"));
878
+ var import_ui12 = require("@elementor/ui");
879
+ var import_i18n11 = require("@wordpress/i18n");
895
880
 
896
- // src/components/status-section.tsx
897
- var React5 = __toESM(require("react"));
881
+ // src/hooks/use-single-expanded-card.ts
898
882
  var import_react3 = require("react");
899
- var import_icons2 = require("@elementor/icons");
900
- var import_ui5 = require("@elementor/ui");
901
- var import_i18n7 = require("@wordpress/i18n");
883
+ function useSingleExpandedCard(initialId = null) {
884
+ const [expandedId, setExpandedId] = (0, import_react3.useState)(initialId);
885
+ const toggle = (id) => {
886
+ setExpandedId((current) => current === id ? null : id);
887
+ };
888
+ return { expandedId, toggle };
889
+ }
890
+
891
+ // src/components/category-icons.ts
892
+ var import_icons3 = require("@elementor/icons");
893
+ var CATEGORY_ICONS = {
894
+ "best-practices": import_icons3.Settings2Icon,
895
+ seo: import_icons3.FileSearchIcon,
896
+ accessibility: import_icons3.ElementorAccessibilityIcon,
897
+ performance: import_icons3.BoltIcon,
898
+ compliance: import_icons3.ShieldCheckIcon
899
+ };
900
+
901
+ // src/components/status-section.tsx
902
+ var React6 = __toESM(require("react"));
903
+ var import_react4 = require("react");
904
+ var import_icons4 = require("@elementor/icons");
905
+ var import_ui6 = require("@elementor/ui");
906
+ var import_i18n6 = require("@wordpress/i18n");
902
907
  function StatusSection({ label, count, color = "default", defaultExpanded = false, children }) {
903
- const [expanded, setExpanded] = (0, import_react3.useState)(defaultExpanded);
904
- if (React5.Children.count(children) === 0) {
908
+ const [expanded, setExpanded] = (0, import_react4.useState)(defaultExpanded);
909
+ if (React6.Children.count(children) === 0) {
905
910
  return null;
906
911
  }
907
- return /* @__PURE__ */ React5.createElement(import_ui5.Box, { sx: { paddingBlock: 1 } }, /* @__PURE__ */ React5.createElement(
908
- import_ui5.Box,
912
+ return /* @__PURE__ */ React6.createElement(import_ui6.Box, { sx: { paddingBlock: 1 } }, /* @__PURE__ */ React6.createElement(
913
+ import_ui6.Box,
909
914
  {
910
915
  sx: { display: "flex", alignItems: "center", gap: 0.5, cursor: "pointer" },
911
916
  onClick: () => setExpanded((v) => !v)
912
917
  },
913
- /* @__PURE__ */ React5.createElement(
914
- import_ui5.IconButton,
918
+ /* @__PURE__ */ React6.createElement(
919
+ import_ui6.IconButton,
915
920
  {
916
921
  size: "small",
917
- "aria-label": expanded ? (0, import_i18n7.__)("Collapse", "elementor") : (0, import_i18n7.__)("Expand", "elementor")
922
+ "aria-label": expanded ? (0, import_i18n6.__)("Collapse", "elementor") : (0, import_i18n6.__)("Expand", "elementor")
918
923
  },
919
- /* @__PURE__ */ React5.createElement(
920
- import_icons2.ChevronDownIcon,
924
+ /* @__PURE__ */ React6.createElement(
925
+ import_icons4.ChevronDownIcon,
921
926
  {
922
927
  fontSize: "small",
923
928
  sx: {
@@ -927,27 +932,27 @@ function StatusSection({ label, count, color = "default", defaultExpanded = fals
927
932
  }
928
933
  )
929
934
  ),
930
- /* @__PURE__ */ React5.createElement(import_ui5.Chip, { label: `${label} (${count})`, size: "small", color, variant: "standard" })
931
- ), /* @__PURE__ */ React5.createElement(import_ui5.Collapse, { in: expanded }, /* @__PURE__ */ React5.createElement(import_ui5.Box, { sx: { paddingInlineStart: 4.25, paddingBlock: 2 } }, children)));
935
+ /* @__PURE__ */ React6.createElement(import_ui6.Chip, { label: `${label} (${count})`, size: "small", color, variant: "standard" })
936
+ ), /* @__PURE__ */ React6.createElement(import_ui6.Collapse, { in: expanded }, /* @__PURE__ */ React6.createElement(import_ui6.Box, { sx: { paddingInlineStart: 4.25, paddingBlock: 2 } }, children)));
932
937
  }
933
938
 
934
939
  // src/components/subpage-header.tsx
935
- var React6 = __toESM(require("react"));
936
- var import_icons3 = require("@elementor/icons");
937
- var import_ui6 = require("@elementor/ui");
938
- var import_i18n8 = require("@wordpress/i18n");
940
+ var React7 = __toESM(require("react"));
941
+ var import_icons5 = require("@elementor/icons");
942
+ var import_ui7 = require("@elementor/ui");
943
+ var import_i18n7 = require("@wordpress/i18n");
939
944
  function SubpageHeader({ title, onBack, backLabel, icon }) {
940
- const isRtl = "rtl" === (0, import_ui6.useTheme)().direction;
941
- return /* @__PURE__ */ React6.createElement(import_ui6.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5, p: 1 } }, /* @__PURE__ */ React6.createElement(import_ui6.IconButton, { size: "small", onClick: onBack, "aria-label": backLabel ?? (0, import_i18n8.__)("Back", "elementor") }, /* @__PURE__ */ React6.createElement(import_ui6.Rotate, { in: isRtl }, /* @__PURE__ */ React6.createElement(import_icons3.ArrowLeftIcon, { fontSize: "small" }))), icon, /* @__PURE__ */ React6.createElement(import_ui6.Typography, { variant: "subtitle2", fontWeight: "bold" }, title));
945
+ const isRtl = "rtl" === (0, import_ui7.useTheme)().direction;
946
+ return /* @__PURE__ */ React7.createElement(import_ui7.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5, p: 1 } }, /* @__PURE__ */ React7.createElement(import_ui7.IconButton, { size: "small", onClick: onBack, "aria-label": backLabel ?? (0, import_i18n7.__)("Back", "elementor") }, /* @__PURE__ */ React7.createElement(import_ui7.Rotate, { in: isRtl }, /* @__PURE__ */ React7.createElement(import_icons5.ArrowLeftIcon, { fontSize: "small" }))), icon, /* @__PURE__ */ React7.createElement(import_ui7.Typography, { variant: "subtitle2", fontWeight: "bold" }, title));
942
947
  }
943
948
 
944
949
  // src/components/violation-row.tsx
945
- var React11 = __toESM(require("react"));
946
- var import_react4 = require("react");
950
+ var React12 = __toESM(require("react"));
947
951
  var import_editor_elements5 = require("@elementor/editor-elements");
948
- var import_icons7 = require("@elementor/icons");
949
- var import_ui10 = require("@elementor/ui");
950
- var import_i18n11 = require("@wordpress/i18n");
952
+ var import_editor_floating_panels2 = require("@elementor/editor-floating-panels");
953
+ var import_icons10 = require("@elementor/icons");
954
+ var import_ui11 = require("@elementor/ui");
955
+ var import_i18n10 = require("@wordpress/i18n");
951
956
 
952
957
  // src/hooks/focus-violation.ts
953
958
  var import_editor_elements4 = require("@elementor/editor-elements");
@@ -988,11 +993,11 @@ function focusViolation(violation) {
988
993
  }
989
994
 
990
995
  // src/utils/build-angie-prompt.ts
991
- var import_i18n9 = require("@wordpress/i18n");
996
+ var import_i18n8 = require("@wordpress/i18n");
992
997
  function buildAngiePrompt(violationText) {
993
- return (0, import_i18n9.sprintf)(
998
+ return (0, import_i18n8.sprintf)(
994
999
  /* translators: %s: audit violation description. */
995
- (0, import_i18n9.__)("Help me fix: %s", "elementor"),
1000
+ (0, import_i18n8.__)("Help me fix: %s", "elementor"),
996
1001
  violationText
997
1002
  );
998
1003
  }
@@ -1008,13 +1013,13 @@ function onKeyboardClick(callback) {
1008
1013
  }
1009
1014
 
1010
1015
  // src/components/fix-violation-with-angie.tsx
1011
- var React7 = __toESM(require("react"));
1016
+ var React8 = __toESM(require("react"));
1012
1017
  var import_editor_mcp = require("@elementor/editor-mcp");
1013
- var import_icons4 = require("@elementor/icons");
1014
- var import_ui7 = require("@elementor/ui");
1015
- var import_i18n10 = require("@wordpress/i18n");
1016
- function FixViolationWithAngie({ prompt }) {
1017
- const label = (0, import_i18n10.__)("Fix with Angie", "elementor");
1018
+ var import_icons6 = require("@elementor/icons");
1019
+ var import_ui8 = require("@elementor/ui");
1020
+ var import_i18n9 = require("@wordpress/i18n");
1021
+ function FixViolationWithAngie({ prompt, panelZIndex, variant = "icon" }) {
1022
+ const label = (0, import_i18n9.__)("Fix with Angie", "elementor");
1018
1023
  const href = `#angie-prompt=${encodeURIComponent(prompt)}`;
1019
1024
  const handleClick = (event) => {
1020
1025
  event.stopPropagation();
@@ -1032,53 +1037,70 @@ function FixViolationWithAngie({ prompt }) {
1032
1037
  })
1033
1038
  );
1034
1039
  };
1035
- return /* @__PURE__ */ React7.createElement(import_ui7.Tooltip, { title: label }, /* @__PURE__ */ React7.createElement(
1036
- import_ui7.IconButton,
1037
- {
1038
- className: "violation-hover-icon",
1039
- component: "a",
1040
- href,
1041
- size: "small",
1042
- "aria-label": label,
1043
- onClick: handleClick
1044
- },
1045
- /* @__PURE__ */ React7.createElement(import_icons4.AngieIcon, { fontSize: "tiny" })
1046
- ));
1040
+ if (variant === "button") {
1041
+ return /* @__PURE__ */ React8.createElement(
1042
+ import_ui8.Button,
1043
+ {
1044
+ component: "a",
1045
+ href,
1046
+ size: "small",
1047
+ color: "secondary",
1048
+ variant: "outlined",
1049
+ startIcon: /* @__PURE__ */ React8.createElement(import_icons6.AngieIcon, { fontSize: "tiny", "aria-hidden": true }),
1050
+ onClick: handleClick
1051
+ },
1052
+ label
1053
+ );
1054
+ }
1055
+ return /* @__PURE__ */ React8.createElement(import_ui8.Tooltip, { title: label, placement: "top", PopperProps: { sx: { zIndex: panelZIndex } } }, /* @__PURE__ */ React8.createElement(import_ui8.IconButton, { component: "a", href, size: "small", "aria-label": label, onClick: handleClick }, /* @__PURE__ */ React8.createElement(import_icons6.AngieIcon, { fontSize: "tiny" })));
1047
1056
  }
1048
1057
 
1049
1058
  // src/components/severity-icons.tsx
1050
- var React8 = __toESM(require("react"));
1051
- var import_icons5 = require("@elementor/icons");
1059
+ var React9 = __toESM(require("react"));
1060
+ var import_icons7 = require("@elementor/icons");
1052
1061
  var SEVERITY_CONFIG = {
1053
- error: { Icon: import_icons5.AlertTriangleFilledIcon, color: "error" },
1054
- warning: { Icon: import_icons5.AlertCircleIcon, color: "warning" },
1055
- info: { Icon: import_icons5.BulbIcon, color: "info" }
1062
+ error: { Icon: import_icons7.AlertTriangleFilledIcon, color: "error" },
1063
+ warning: { Icon: import_icons7.AlertCircleIcon, color: "warning" },
1064
+ info: { Icon: import_icons7.BulbIcon, color: "info" }
1056
1065
  };
1066
+ var INFO_ICON_VERTICAL_NUDGE = "translateY(-2px)";
1057
1067
  function SeverityIcon({ severity }) {
1058
1068
  const { Icon, color } = SEVERITY_CONFIG[severity];
1059
- return /* @__PURE__ */ React8.createElement(Icon, { fontSize: "small", color });
1069
+ const sx = "info" === severity ? { transform: INFO_ICON_VERTICAL_NUDGE } : void 0;
1070
+ return /* @__PURE__ */ React9.createElement(Icon, { fontSize: "small", color, sx });
1060
1071
  }
1061
1072
 
1062
1073
  // src/components/violation-cta-button.tsx
1063
- var React9 = __toESM(require("react"));
1064
- var import_ui8 = require("@elementor/ui");
1065
- function ViolationCtaButton({ ctaLabel, externalUrl, variant = "outlined" }) {
1074
+ var React10 = __toESM(require("react"));
1075
+ var import_icons8 = require("@elementor/icons");
1076
+ var import_ui9 = require("@elementor/ui");
1077
+ function ViolationCtaButton({ ctaLabel, externalUrl, variant = "outlined", withIcon = false }) {
1066
1078
  const handleClick = (event) => {
1067
1079
  event.stopPropagation();
1068
1080
  event.preventDefault();
1069
1081
  window.open(externalUrl, "_blank", "noopener");
1070
1082
  };
1071
- return /* @__PURE__ */ React9.createElement(import_ui8.Button, { variant, color: "secondary", size: "small", onClick: handleClick }, ctaLabel);
1083
+ return /* @__PURE__ */ React10.createElement(
1084
+ import_ui9.Button,
1085
+ {
1086
+ variant,
1087
+ color: "secondary",
1088
+ size: "small",
1089
+ startIcon: withIcon ? /* @__PURE__ */ React10.createElement(import_icons8.WandIcon, { fontSize: "tiny", "aria-hidden": true }) : void 0,
1090
+ onClick: handleClick
1091
+ },
1092
+ ctaLabel
1093
+ );
1072
1094
  }
1073
1095
 
1074
1096
  // src/components/violation-icons.tsx
1075
- var React10 = __toESM(require("react"));
1076
- var import_icons6 = require("@elementor/icons");
1077
- var import_ui9 = require("@elementor/ui");
1097
+ var React11 = __toESM(require("react"));
1098
+ var import_icons9 = require("@elementor/icons");
1099
+ var import_ui10 = require("@elementor/ui");
1078
1100
  function ViolationIcon({ violation, widgetIcon }) {
1079
1101
  if (widgetIcon) {
1080
- return /* @__PURE__ */ React10.createElement(
1081
- import_ui9.Box,
1102
+ return /* @__PURE__ */ React11.createElement(
1103
+ import_ui10.Box,
1082
1104
  {
1083
1105
  component: "i",
1084
1106
  className: widgetIcon,
@@ -1088,29 +1110,74 @@ function ViolationIcon({ violation, widgetIcon }) {
1088
1110
  );
1089
1111
  }
1090
1112
  if (violation.targetHint === "page-settings") {
1091
- return /* @__PURE__ */ React10.createElement(import_icons6.FileSettingsIcon, { fontSize: "inherit", "aria-hidden": true });
1113
+ return /* @__PURE__ */ React11.createElement(import_icons9.FileSettingsIcon, { fontSize: "inherit", "aria-hidden": true });
1092
1114
  }
1093
1115
  if (violation.targetHint === "site-settings" || violation.targetHint === "site-identity-settings") {
1094
- return /* @__PURE__ */ React10.createElement(import_icons6.SettingsIcon, { fontSize: "inherit", "aria-hidden": true });
1116
+ return /* @__PURE__ */ React11.createElement(import_icons9.SettingsIcon, { fontSize: "inherit", "aria-hidden": true });
1095
1117
  }
1096
- return /* @__PURE__ */ React10.createElement(import_icons6.ShieldCheckIcon, { fontSize: "inherit", "aria-hidden": true });
1118
+ return /* @__PURE__ */ React11.createElement(import_icons9.SettingsIcon, { fontSize: "inherit", "aria-hidden": true });
1097
1119
  }
1098
1120
 
1099
1121
  // src/components/violation-row.tsx
1100
1122
  function SkipReasonTooltip({ reason }) {
1101
- 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" })));
1123
+ return /* @__PURE__ */ React12.createElement(import_ui11.Tooltip, { title: reason, placement: "top" }, /* @__PURE__ */ React12.createElement(import_ui11.Box, { "aria-label": reason, component: "span", sx: { display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React12.createElement(import_icons10.HelpIcon, { fontSize: "small", color: "action" })));
1102
1124
  }
1103
- function StatusIndicator({ audit: audit26, violations }) {
1125
+ function StatusIndicator({ audit: audit25, violations }) {
1104
1126
  if (violations) {
1105
- return /* @__PURE__ */ React11.createElement(React11.Fragment, null, isScoredAudit(audit26) && /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", color: "text.secondary", fontWeight: "bold" }, violations.length), /* @__PURE__ */ React11.createElement(SeverityIcon, { severity: audit26.severity }));
1127
+ return /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(SeverityIcon, { severity: audit25.severity }), isScoredAudit(audit25) && /* @__PURE__ */ React12.createElement(import_ui11.Typography, { variant: "caption", color: "text.secondary", fontWeight: "bold" }, violations.length));
1106
1128
  }
1107
- return /* @__PURE__ */ React11.createElement(import_icons7.CheckIcon, { fontSize: "small", color: "success" });
1129
+ return /* @__PURE__ */ React12.createElement(import_icons10.CheckIcon, { fontSize: "small", color: "success" });
1108
1130
  }
1109
- function ViolationRow({ audit: audit26, skipReason, violations }) {
1110
- const [expanded, setExpanded] = (0, import_react4.useState)(false);
1111
- const toggleExpanded = () => setExpanded((value) => !value);
1112
- 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(
1113
- import_ui10.Box,
1131
+ function GuidanceAction({
1132
+ violation,
1133
+ sharedAngiePrompt,
1134
+ panelZIndex
1135
+ }) {
1136
+ const hasPrimaryCta = !!(violation.ctaLabel && violation.externalUrl);
1137
+ if (!hasPrimaryCta && !sharedAngiePrompt) {
1138
+ return null;
1139
+ }
1140
+ return /* @__PURE__ */ React12.createElement(import_ui11.Box, { display: "flex", alignItems: "center", gap: 1, mt: 1 }, hasPrimaryCta && /* @__PURE__ */ React12.createElement(
1141
+ ViolationCtaButton,
1142
+ {
1143
+ ctaLabel: violation.ctaLabel,
1144
+ externalUrl: violation.externalUrl,
1145
+ withIcon: true
1146
+ }
1147
+ ), hasPrimaryCta && violation.secondaryCtaLabel && violation.secondaryCtaUrl && /* @__PURE__ */ React12.createElement(
1148
+ ViolationCtaButton,
1149
+ {
1150
+ ctaLabel: violation.secondaryCtaLabel,
1151
+ externalUrl: violation.secondaryCtaUrl,
1152
+ variant: "text"
1153
+ }
1154
+ ), sharedAngiePrompt && /* @__PURE__ */ React12.createElement(FixViolationWithAngie, { prompt: sharedAngiePrompt, panelZIndex, variant: "button" }));
1155
+ }
1156
+ function getSharedAngiePrompt(violations) {
1157
+ if (!violations || violations.length < 2) {
1158
+ return void 0;
1159
+ }
1160
+ const [first, ...rest] = violations;
1161
+ if (!first.angieFix || !first.angiePrompt) {
1162
+ return void 0;
1163
+ }
1164
+ const allShareTheSameFix = rest.every(
1165
+ (violation) => violation.angieFix && violation.angiePrompt === first.angiePrompt
1166
+ );
1167
+ return allShareTheSameFix ? first.angiePrompt : void 0;
1168
+ }
1169
+ function ViolationRow({ audit: audit25, expanded, onToggleExpand, skipReason, violations }) {
1170
+ const primaryViolation = violations?.[0];
1171
+ const isGuidanceFocusable = !!primaryViolation;
1172
+ const panelZIndex = (0, import_editor_floating_panels2.useFloatingPanelZIndex)(AUDIT_PANEL_ID);
1173
+ const sharedAngiePrompt = getSharedAngiePrompt(violations);
1174
+ const handleGuidanceClick = () => {
1175
+ if (isGuidanceFocusable && primaryViolation) {
1176
+ focusViolation(primaryViolation);
1177
+ }
1178
+ };
1179
+ return /* @__PURE__ */ React12.createElement(import_ui11.Box, { sx: { borderBottom: 1, borderColor: "divider", paddingBlock: 0.5 } }, /* @__PURE__ */ React12.createElement(import_ui11.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React12.createElement(
1180
+ import_ui11.Box,
1114
1181
  {
1115
1182
  sx: {
1116
1183
  alignItems: "center",
@@ -1120,19 +1187,19 @@ function ViolationRow({ audit: audit26, skipReason, violations }) {
1120
1187
  gap: 0.5,
1121
1188
  minWidth: 0
1122
1189
  },
1123
- onClick: toggleExpanded
1190
+ onClick: onToggleExpand
1124
1191
  },
1125
- /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "body2", sx: { flex: 1 } }, audit26.title),
1126
- !skipReason && /* @__PURE__ */ React11.createElement(StatusIndicator, { audit: audit26, violations })
1127
- ), skipReason && /* @__PURE__ */ React11.createElement(SkipReasonTooltip, { reason: skipReason }), /* @__PURE__ */ React11.createElement(
1128
- import_ui10.IconButton,
1192
+ /* @__PURE__ */ React12.createElement(import_ui11.Typography, { variant: "subtitle2", color: "text.secondary", sx: { flex: 1 } }, audit25.title),
1193
+ !skipReason && /* @__PURE__ */ React12.createElement(StatusIndicator, { audit: audit25, violations })
1194
+ ), skipReason && /* @__PURE__ */ React12.createElement(SkipReasonTooltip, { reason: skipReason }), /* @__PURE__ */ React12.createElement(
1195
+ import_ui11.IconButton,
1129
1196
  {
1130
1197
  size: "small",
1131
- "aria-label": expanded ? (0, import_i18n11.__)("Collapse", "elementor") : (0, import_i18n11.__)("Expand", "elementor"),
1132
- onClick: toggleExpanded
1198
+ "aria-label": expanded ? (0, import_i18n10.__)("Collapse", "elementor") : (0, import_i18n10.__)("Expand", "elementor"),
1199
+ onClick: onToggleExpand
1133
1200
  },
1134
- /* @__PURE__ */ React11.createElement(
1135
- import_icons7.ChevronDownIcon,
1201
+ /* @__PURE__ */ React12.createElement(
1202
+ import_icons10.ChevronDownIcon,
1136
1203
  {
1137
1204
  fontSize: "small",
1138
1205
  sx: {
@@ -1141,30 +1208,34 @@ function ViolationRow({ audit: audit26, skipReason, violations }) {
1141
1208
  }
1142
1209
  }
1143
1210
  )
1144
- )), /* @__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(
1145
- import_ui10.Alert,
1211
+ )), /* @__PURE__ */ React12.createElement(import_ui11.Collapse, { in: expanded }, /* @__PURE__ */ React12.createElement(import_ui11.Box, { paddingBlock: 1 }, /* @__PURE__ */ React12.createElement(import_ui11.Tooltip, { title: audit25.fixHint, placement: "top" }, /* @__PURE__ */ React12.createElement(
1212
+ import_ui11.Alert,
1146
1213
  {
1147
1214
  severity: "secondary",
1148
- sx: { p: 1 },
1149
- icon: /* @__PURE__ */ React11.createElement(import_icons7.AlertCircleIcon, { fontSize: "small", color: "secondary", "aria-hidden": true })
1150
- },
1151
- /* @__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"))),
1152
- /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit26.description)
1153
- ), /* @__PURE__ */ React11.createElement(
1154
- import_ui10.Alert,
1155
- {
1156
- severity: "info",
1157
- sx: { p: 1 },
1158
- icon: /* @__PURE__ */ React11.createElement(import_icons7.BulbIcon, { fontSize: "small", color: "info", "aria-hidden": true })
1215
+ sx: { p: 1, cursor: isGuidanceFocusable ? "pointer" : void 0 },
1216
+ icon: /* @__PURE__ */ React12.createElement(import_icons10.InfoCircleIcon, { fontSize: "small", "aria-hidden": true }),
1217
+ role: isGuidanceFocusable ? "button" : void 0,
1218
+ tabIndex: isGuidanceFocusable ? 0 : void 0,
1219
+ onClick: isGuidanceFocusable ? handleGuidanceClick : void 0,
1220
+ onKeyDown: isGuidanceFocusable ? onKeyboardClick(handleGuidanceClick) : void 0
1159
1221
  },
1160
- /* @__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"))),
1161
- /* @__PURE__ */ React11.createElement(import_ui10.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit26.fixHint)
1162
- )), violations && violations.length > 0 && /* @__PURE__ */ React11.createElement(import_ui10.Box, { role: "list", sx: { paddingBlockEnd: 1, paddingInlineStart: 2 } }, violations.map((violation, idx) => {
1222
+ /* @__PURE__ */ React12.createElement(import_ui11.Typography, { variant: "caption", component: "p", color: "text.secondary" }, audit25.description),
1223
+ primaryViolation && /* @__PURE__ */ React12.createElement(
1224
+ GuidanceAction,
1225
+ {
1226
+ violation: primaryViolation,
1227
+ sharedAngiePrompt,
1228
+ panelZIndex
1229
+ }
1230
+ )
1231
+ ))), violations && violations.length > 0 && /* @__PURE__ */ React12.createElement(import_ui11.Box, { role: "list", sx: { paddingBlockEnd: 1 } }, violations.map((violation, idx) => {
1163
1232
  const widgetIcon = violation.elementId ? (0, import_editor_elements5.getElementIcon)(violation.elementId) : null;
1164
1233
  const elementTitle = violation.elementId ? (0, import_editor_elements5.getElementTitle)(violation.elementId) : null;
1165
- const rowLabel = elementTitle ? `${elementTitle} - ${violation.label}` : violation.label;
1166
- return /* @__PURE__ */ React11.createElement(
1167
- import_ui10.Box,
1234
+ const displayLabel = elementTitle ?? violation.label;
1235
+ const angiePromptContext = elementTitle ? `${elementTitle} - ${violation.label}` : violation.label;
1236
+ const showsOwnFixAction = !!violation.angieFix && !sharedAngiePrompt;
1237
+ return /* @__PURE__ */ React12.createElement(
1238
+ import_ui11.Box,
1168
1239
  {
1169
1240
  key: idx,
1170
1241
  role: "button",
@@ -1176,265 +1247,130 @@ function ViolationRow({ audit: audit26, skipReason, violations }) {
1176
1247
  alignItems: "center",
1177
1248
  gap: 1,
1178
1249
  paddingBlock: 0.5,
1179
- paddingInline: 2,
1250
+ paddingInline: 1,
1180
1251
  borderRadius: 1,
1181
1252
  cursor: "pointer",
1182
- "& .violation-hover-icon": { opacity: 0, transition: "opacity .15s" },
1183
1253
  "&:hover": { bgcolor: "action.hover" },
1184
- "&:hover .violation-hover-icon, &:focus-visible .violation-hover-icon": {
1254
+ "&:hover .violation-row-fix-action, &:focus-visible .violation-row-fix-action": {
1185
1255
  opacity: 1
1186
1256
  }
1187
1257
  }
1188
1258
  },
1189
- /* @__PURE__ */ React11.createElement(ViolationIcon, { violation, widgetIcon }),
1190
- /* @__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)),
1191
- violation.angieFix && /* @__PURE__ */ React11.createElement(
1192
- FixViolationWithAngie,
1193
- {
1194
- prompt: violation.angiePrompt ?? buildAngiePrompt(rowLabel)
1195
- }
1196
- ),
1197
- violation.ctaLabel && violation.externalUrl ? /* @__PURE__ */ React11.createElement(import_ui10.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React11.createElement(
1198
- ViolationCtaButton,
1199
- {
1200
- ctaLabel: violation.ctaLabel,
1201
- externalUrl: violation.externalUrl
1202
- }
1203
- ), violation.secondaryCtaLabel && violation.secondaryCtaUrl && /* @__PURE__ */ React11.createElement(
1204
- ViolationCtaButton,
1259
+ /* @__PURE__ */ React12.createElement(ViolationIcon, { violation, widgetIcon }),
1260
+ /* @__PURE__ */ React12.createElement(import_ui11.Box, { sx: { flex: 1 } }, /* @__PURE__ */ React12.createElement(import_ui11.Typography, { variant: "caption" }, displayLabel), violation.detail && /* @__PURE__ */ React12.createElement(import_ui11.Typography, { variant: "caption", color: "text.secondary" }, violation.detail)),
1261
+ showsOwnFixAction && /* @__PURE__ */ React12.createElement(
1262
+ import_ui11.Box,
1205
1263
  {
1206
- ctaLabel: violation.secondaryCtaLabel,
1207
- externalUrl: violation.secondaryCtaUrl,
1208
- variant: "text"
1209
- }
1210
- )) : /* @__PURE__ */ React11.createElement(
1211
- import_icons7.EyeIcon,
1212
- {
1213
- className: "violation-hover-icon",
1214
- fontSize: "tiny",
1215
- "aria-hidden": true
1216
- }
1264
+ className: "violation-row-fix-action",
1265
+ sx: { opacity: 0, transition: "opacity .15s" }
1266
+ },
1267
+ /* @__PURE__ */ React12.createElement(
1268
+ FixViolationWithAngie,
1269
+ {
1270
+ prompt: violation.angiePrompt ?? buildAngiePrompt(angiePromptContext),
1271
+ panelZIndex
1272
+ }
1273
+ )
1217
1274
  )
1218
1275
  );
1219
1276
  }))));
1220
1277
  }
1221
1278
 
1222
- // src/components/pages/all-audits-page.tsx
1223
- function AllAuditsPage({ initialExpandedStatus, onBack, report }) {
1224
- const { failed, passed, skipped, totalViolations } = partitionAuditResults(report);
1225
- const expandFail = !initialExpandedStatus || initialExpandedStatus === "fail";
1226
- const expandPass = initialExpandedStatus === "pass";
1227
- const expandSkipped = initialExpandedStatus === "skipped";
1228
- 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(
1229
- StatusSection,
1230
- {
1231
- label: auditStatusLabel("fail"),
1232
- count: totalViolations,
1233
- color: auditStatusColor("fail"),
1234
- defaultExpanded: expandFail
1235
- },
1236
- failed.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, violations: r.result.violations }))
1237
- ), /* @__PURE__ */ React12.createElement(
1238
- StatusSection,
1239
- {
1240
- label: auditStatusLabel("pass"),
1241
- count: passed.length,
1242
- color: auditStatusColor("pass"),
1243
- defaultExpanded: expandPass
1244
- },
1245
- passed.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit }))
1246
- ), /* @__PURE__ */ React12.createElement(
1247
- StatusSection,
1248
- {
1249
- label: auditStatusLabel("skipped"),
1250
- count: skipped.length,
1251
- color: auditStatusColor("skipped"),
1252
- defaultExpanded: expandSkipped
1253
- },
1254
- skipped.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, skipReason: r.result.reason }))
1255
- )));
1256
- }
1257
-
1258
- // src/components/pages/category-page.tsx
1259
- var React13 = __toESM(require("react"));
1260
- var import_ui12 = require("@elementor/ui");
1261
- var import_i18n13 = require("@wordpress/i18n");
1262
-
1263
- // src/components/category-icons.ts
1264
- var import_icons8 = require("@elementor/icons");
1265
- var CATEGORY_ICONS = {
1266
- "best-practices": import_icons8.Settings2Icon,
1267
- seo: import_icons8.FileSearchIcon,
1268
- accessibility: import_icons8.ElementorAccessibilityIcon,
1269
- performance: import_icons8.BoltIcon,
1270
- compliance: import_icons8.ShieldCheckIcon
1271
- };
1272
-
1273
1279
  // src/components/pages/category-page.tsx
1274
1280
  function CategoryPage({ category, report, onBack }) {
1275
1281
  const Icon = CATEGORY_ICONS[category];
1276
- const { failed, passed, totalViolations } = partitionAuditResults(report, { category });
1282
+ const { failed, passed, skipped, totalViolations } = partitionAuditResults(report, { category });
1283
+ const { expandedId, toggle } = useSingleExpandedCard();
1277
1284
  return /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(
1278
1285
  SubpageHeader,
1279
1286
  {
1280
1287
  title: CATEGORY_LABELS[category],
1281
1288
  onBack,
1282
- backLabel: (0, import_i18n13.__)("Back to all issues", "elementor"),
1289
+ backLabel: (0, import_i18n11.__)("Back to all issues", "elementor"),
1283
1290
  icon: /* @__PURE__ */ React13.createElement(Icon, { fontSize: "small", color: "action" })
1284
1291
  }
1285
1292
  ), /* @__PURE__ */ React13.createElement(import_ui12.Box, { sx: { p: 1 } }, /* @__PURE__ */ React13.createElement(
1286
1293
  StatusSection,
1287
1294
  {
1288
- label: (0, import_i18n13.__)("Failed audits", "elementor"),
1295
+ label: (0, import_i18n11.__)("Failed audits", "elementor"),
1289
1296
  count: totalViolations,
1290
1297
  color: "error",
1291
1298
  defaultExpanded: true
1292
1299
  },
1293
- failed.map((r) => /* @__PURE__ */ React13.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, violations: r.result.violations }))
1294
- ), /* @__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 })))));
1300
+ failed.map((r) => /* @__PURE__ */ React13.createElement(
1301
+ ViolationRow,
1302
+ {
1303
+ key: r.audit.id,
1304
+ audit: r.audit,
1305
+ violations: r.result.violations,
1306
+ expanded: expandedId === r.audit.id,
1307
+ onToggleExpand: () => toggle(r.audit.id)
1308
+ }
1309
+ ))
1310
+ ), /* @__PURE__ */ React13.createElement(StatusSection, { label: (0, import_i18n11.__)("Passed audits", "elementor"), count: passed.length, color: "success" }, passed.map((r) => /* @__PURE__ */ React13.createElement(
1311
+ ViolationRow,
1312
+ {
1313
+ key: r.audit.id,
1314
+ audit: r.audit,
1315
+ expanded: expandedId === r.audit.id,
1316
+ onToggleExpand: () => toggle(r.audit.id)
1317
+ }
1318
+ ))), /* @__PURE__ */ React13.createElement(StatusSection, { label: (0, import_i18n11.__)("Skipped audits", "elementor"), count: skipped.length, color: "default" }, skipped.map((r) => /* @__PURE__ */ React13.createElement(
1319
+ ViolationRow,
1320
+ {
1321
+ key: r.audit.id,
1322
+ audit: r.audit,
1323
+ skipReason: r.result.reason,
1324
+ expanded: expandedId === r.audit.id,
1325
+ onToggleExpand: () => toggle(r.audit.id)
1326
+ }
1327
+ )))));
1295
1328
  }
1296
1329
 
1297
- // src/components/pages/issues-page.tsx
1330
+ // src/components/pages/overview-page.tsx
1298
1331
  var React18 = __toESM(require("react"));
1299
- var import_react5 = require("react");
1300
1332
  var import_ui17 = require("@elementor/ui");
1301
- var import_i18n17 = require("@wordpress/i18n");
1333
+ var import_i18n15 = require("@wordpress/i18n");
1302
1334
 
1303
1335
  // src/utils/severity-counts.ts
1304
- var import_i18n14 = require("@wordpress/i18n");
1336
+ var import_i18n12 = require("@wordpress/i18n");
1305
1337
  var ALL_SEVERITIES = ["error", "warning", "info"];
1306
1338
  function countSeverities(report, category) {
1307
1339
  const counts = { error: 0, warning: 0, info: 0 };
1308
- for (const { audit: audit26, result } of report.auditResults) {
1309
- if (result.status !== "fail") {
1310
- continue;
1311
- }
1312
- if (category && !audit26.categories.includes(category)) {
1313
- continue;
1314
- }
1315
- if (!isScoredAudit(audit26)) {
1316
- continue;
1317
- }
1318
- counts[audit26.severity] += result.violations.length;
1319
- }
1320
- return counts;
1321
- }
1322
- function severityPluralLabel(severity) {
1323
- switch (severity) {
1324
- case "error":
1325
- return (0, import_i18n14.__)("Errors", "elementor");
1326
- case "warning":
1327
- return (0, import_i18n14.__)("Warnings", "elementor");
1328
- case "info":
1329
- return (0, import_i18n14.__)("Suggestions", "elementor");
1330
- }
1331
- }
1332
-
1333
- // src/components/audit-statuses-section.tsx
1334
- var React15 = __toESM(require("react"));
1335
- var import_ui14 = require("@elementor/ui");
1336
- var import_i18n15 = require("@wordpress/i18n");
1337
-
1338
- // src/components/count-summary-circle.tsx
1339
- var React14 = __toESM(require("react"));
1340
- var import_ui13 = require("@elementor/ui");
1341
- var COUNT_SUMMARY_CIRCLE_SIZE = 64;
1342
- var COUNT_SUMMARY_BORDER_WIDTH = 4;
1343
- function chipBorderColor(theme, color) {
1344
- if (color === "default" || !color) {
1345
- return theme.palette.text.secondary;
1346
- }
1347
- return theme.palette[color].main;
1348
- }
1349
- function CountSummaryCircle({ ariaLabel, color, count, label, onClick }) {
1350
- return /* @__PURE__ */ React14.createElement(
1351
- import_ui13.Box,
1352
- {
1353
- "aria-label": ariaLabel,
1354
- component: "button",
1355
- onClick,
1356
- type: "button",
1357
- sx: {
1358
- alignItems: "center",
1359
- background: "none",
1360
- border: "none",
1361
- cursor: "pointer",
1362
- display: "flex",
1363
- flex: 1,
1364
- flexDirection: "column",
1365
- gap: 0.5,
1366
- padding: 0
1367
- }
1368
- },
1369
- /* @__PURE__ */ React14.createElement(
1370
- import_ui13.Chip,
1371
- {
1372
- color,
1373
- label: count,
1374
- size: "small",
1375
- variant: "standard",
1376
- sx: (theme) => ({
1377
- border: `${COUNT_SUMMARY_BORDER_WIDTH}px solid ${chipBorderColor(theme, color)}`,
1378
- borderRadius: "50%",
1379
- height: COUNT_SUMMARY_CIRCLE_SIZE,
1380
- maxWidth: COUNT_SUMMARY_CIRCLE_SIZE,
1381
- width: COUNT_SUMMARY_CIRCLE_SIZE,
1382
- "& .MuiChip-label": {
1383
- fontSize: theme.typography.body2.fontSize,
1384
- fontWeight: 700,
1385
- lineHeight: 1,
1386
- padding: 0
1387
- }
1388
- })
1389
- }
1390
- ),
1391
- /* @__PURE__ */ React14.createElement(import_ui13.Typography, { color: "text.secondary", sx: { textAlign: "center" }, variant: "caption" }, label)
1392
- );
1393
- }
1394
-
1395
- // src/components/audit-statuses-section.tsx
1396
- var STATUS_ARIA_LABELS = {
1397
- pass: (count) => (0, import_i18n15.sprintf)(
1398
- /* translators: %d: number of passed audits. */
1399
- (0, import_i18n15.__)("%d passed audits, view all", "elementor"),
1400
- count
1401
- ),
1402
- fail: (count) => (0, import_i18n15.sprintf)(
1403
- /* translators: %d: number of failed audit violations. */
1404
- (0, import_i18n15.__)("%d failed audits, view all", "elementor"),
1405
- count
1406
- ),
1407
- skipped: (count) => (0, import_i18n15.sprintf)(
1408
- /* translators: %d: number of skipped audits. */
1409
- (0, import_i18n15.__)("%d skipped audits, view all", "elementor"),
1410
- count
1411
- )
1412
- };
1413
- var STATUS_GROUPS = ["fail", "pass", "skipped"];
1414
- function AuditStatusesSection({ report, onStatusClick }) {
1415
- const statusCounts = auditStatusDisplayCounts(report);
1416
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(import_ui14.Typography, { variant: "subtitle1", fontWeight: "bold" }, (0, import_i18n15.__)("Audit statuses", "elementor")), /* @__PURE__ */ React15.createElement(import_ui14.Box, { sx: { display: "flex", justifyContent: "space-around", gap: 2 } }, STATUS_GROUPS.map((status) => /* @__PURE__ */ React15.createElement(
1417
- CountSummaryCircle,
1418
- {
1419
- key: status,
1420
- ariaLabel: STATUS_ARIA_LABELS[status](statusCounts[status]),
1421
- color: auditStatusColor(status),
1422
- count: statusCounts[status],
1423
- label: auditStatusLabel(status),
1424
- onClick: () => onStatusClick(status)
1340
+ for (const { audit: audit25, result } of report.auditResults) {
1341
+ if (result.status !== "fail") {
1342
+ continue;
1343
+ }
1344
+ if (category && !audit25.categories.includes(category)) {
1345
+ continue;
1425
1346
  }
1426
- ))), /* @__PURE__ */ React15.createElement(import_ui14.Divider, null));
1347
+ if (!isScoredAudit(audit25)) {
1348
+ continue;
1349
+ }
1350
+ counts[audit25.severity] += result.violations.length;
1351
+ }
1352
+ return counts;
1353
+ }
1354
+ function severityPluralLabel(severity) {
1355
+ switch (severity) {
1356
+ case "error":
1357
+ return (0, import_i18n12.__)("Errors", "elementor");
1358
+ case "warning":
1359
+ return (0, import_i18n12.__)("Warnings", "elementor");
1360
+ case "info":
1361
+ return (0, import_i18n12.__)("Suggestions", "elementor");
1362
+ }
1427
1363
  }
1428
1364
 
1429
1365
  // src/components/issues-category-row.tsx
1430
- var React16 = __toESM(require("react"));
1431
- var import_icons9 = require("@elementor/icons");
1432
- var import_ui15 = require("@elementor/ui");
1366
+ var React14 = __toESM(require("react"));
1367
+ var import_icons11 = require("@elementor/icons");
1368
+ var import_ui13 = require("@elementor/ui");
1433
1369
  function IssuesCategoryRow({ category, label, counts, onClick }) {
1434
- const isRtl = "rtl" === (0, import_ui15.useTheme)().direction;
1370
+ const isRtl = "rtl" === (0, import_ui13.useTheme)().direction;
1435
1371
  const Icon = CATEGORY_ICONS[category];
1436
- return /* @__PURE__ */ React16.createElement(
1437
- import_ui15.Box,
1372
+ return /* @__PURE__ */ React14.createElement(
1373
+ import_ui13.Box,
1438
1374
  {
1439
1375
  role: "button",
1440
1376
  tabIndex: 0,
@@ -1455,120 +1391,29 @@ function IssuesCategoryRow({ category, label, counts, onClick }) {
1455
1391
  "&:focus-visible": { outline: "2px solid", outlineColor: "primary.main" }
1456
1392
  }
1457
1393
  },
1458
- /* @__PURE__ */ React16.createElement(Icon, { fontSize: "small", color: "action" }),
1459
- /* @__PURE__ */ React16.createElement(import_ui15.Typography, { variant: "body2", fontWeight: "bold", sx: { flex: 1 } }, label),
1460
- /* @__PURE__ */ React16.createElement(import_ui15.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, ALL_SEVERITIES.filter((s) => counts[s] > 0).map((severity) => /* @__PURE__ */ React16.createElement(import_ui15.Box, { key: severity, sx: { display: "flex", alignItems: "center", gap: 0.25 } }, /* @__PURE__ */ React16.createElement(SeverityIcon, { severity }), /* @__PURE__ */ React16.createElement(import_ui15.Typography, { variant: "caption", color: "text.primary", fontWeight: "bold" }, counts[severity])))),
1461
- /* @__PURE__ */ React16.createElement(import_ui15.Rotate, { in: isRtl }, /* @__PURE__ */ React16.createElement(import_icons9.ChevronRightIcon, { fontSize: "small", color: "action" }))
1462
- );
1463
- }
1464
-
1465
- // src/components/severity-filter-chips.tsx
1466
- var React17 = __toESM(require("react"));
1467
- var import_ui16 = require("@elementor/ui");
1468
- var import_i18n16 = require("@wordpress/i18n");
1469
- function SeverityFilterChips({ selected, onChange }) {
1470
- return /* @__PURE__ */ React17.createElement(import_ui16.Box, { sx: { display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ React17.createElement(
1471
- FilterChip,
1472
- {
1473
- label: (0, import_i18n16.__)("All", "elementor"),
1474
- isSelected: selected === "all",
1475
- onClick: () => onChange("all")
1476
- }
1477
- ), ALL_SEVERITIES.map((severity) => /* @__PURE__ */ React17.createElement(
1478
- FilterChip,
1479
- {
1480
- key: severity,
1481
- label: severityPluralLabel(severity),
1482
- icon: /* @__PURE__ */ React17.createElement(SeverityIcon, { severity }),
1483
- isSelected: selected === severity,
1484
- onClick: () => onChange(severity)
1485
- }
1486
- )));
1487
- }
1488
- function FilterChip({ label, icon, isSelected, onClick }) {
1489
- return /* @__PURE__ */ React17.createElement(
1490
- import_ui16.Chip,
1491
- {
1492
- label,
1493
- icon,
1494
- size: "small",
1495
- variant: "filled",
1496
- color: "default",
1497
- onClick,
1498
- "aria-pressed": isSelected,
1499
- sx: { bgcolor: isSelected ? "action.selected" : "transparent" }
1500
- }
1394
+ /* @__PURE__ */ React14.createElement(Icon, { fontSize: "small", color: "action" }),
1395
+ /* @__PURE__ */ React14.createElement(import_ui13.Typography, { variant: "body2", fontWeight: "bold", sx: { flex: 1 } }, label),
1396
+ /* @__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])))),
1397
+ /* @__PURE__ */ React14.createElement(import_ui13.Rotate, { in: isRtl }, /* @__PURE__ */ React14.createElement(import_icons11.ChevronRightIcon, { fontSize: "small", color: "action" }))
1501
1398
  );
1502
1399
  }
1503
1400
 
1504
- // src/components/pages/issues-page.tsx
1505
- function filterCounts(counts, filter) {
1506
- if (filter === "all") {
1507
- return counts;
1508
- }
1509
- return { error: 0, warning: 0, info: 0, [filter]: counts[filter] };
1510
- }
1511
- function IssuesPage({ report, onCategoryClick, onAllAuditsClick, onStatusClick }) {
1512
- const [filter, setFilter] = (0, import_react5.useState)("all");
1513
- const populatedCategories = getPopulatedCategories(report.categories, ALL_CATEGORIES);
1514
- const categoryRows = populatedCategories.map((category) => ({ category, counts: filterCounts(countSeverities(report, category), filter) })).filter(({ counts }) => Object.values(counts).some((count) => count > 0));
1515
- return /* @__PURE__ */ React18.createElement(import_ui17.Box, { sx: { display: "flex", flexDirection: "column", gap: 4, p: 2 } }, /* @__PURE__ */ React18.createElement(AuditStatusesSection, { report, onStatusClick }), /* @__PURE__ */ React18.createElement(
1516
- import_ui17.Link,
1517
- {
1518
- component: "button",
1519
- underline: "none",
1520
- color: "inherit",
1521
- onClick: onAllAuditsClick,
1522
- sx: { textAlign: "start" }
1523
- },
1524
- /* @__PURE__ */ React18.createElement(import_ui17.Typography, { variant: "subtitle1", component: "h2" }, (0, import_i18n17.__)("All issues", "elementor"))
1525
- ), /* @__PURE__ */ React18.createElement(SeverityFilterChips, { selected: filter, onChange: setFilter }), /* @__PURE__ */ React18.createElement(import_ui17.Box, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, categoryRows.map(({ category, counts }) => /* @__PURE__ */ React18.createElement(
1526
- IssuesCategoryRow,
1527
- {
1528
- key: category,
1529
- category,
1530
- label: CATEGORY_LABELS[category],
1531
- counts,
1532
- onClick: () => onCategoryClick(category)
1533
- }
1534
- ))));
1535
- }
1536
-
1537
- // src/components/pages/overview-page.tsx
1538
- var React23 = __toESM(require("react"));
1539
- var import_ui22 = require("@elementor/ui");
1540
- var import_i18n21 = require("@wordpress/i18n");
1541
-
1542
- // src/utils/score-thresholds.ts
1543
- var import_i18n18 = require("@wordpress/i18n");
1544
- var GOOD_THRESHOLD = 90;
1545
- var OK_THRESHOLD = 50;
1546
- function getScoreTier(score) {
1547
- if (score >= GOOD_THRESHOLD) {
1548
- return { color: "success", label: (0, import_i18n18.__)("Good", "elementor") };
1549
- }
1550
- if (score >= OK_THRESHOLD) {
1551
- return { color: "warning", label: (0, import_i18n18.__)("Needs work", "elementor") };
1552
- }
1553
- return { color: "error", label: (0, import_i18n18.__)("At risk", "elementor") };
1554
- }
1555
-
1556
1401
  // src/components/promotions.tsx
1557
- var React20 = __toESM(require("react"));
1558
- var import_ui19 = require("@elementor/ui");
1559
- var import_i18n20 = require("@wordpress/i18n");
1402
+ var React16 = __toESM(require("react"));
1403
+ var import_ui15 = require("@elementor/ui");
1404
+ var import_i18n14 = require("@wordpress/i18n");
1560
1405
 
1561
1406
  // src/register-promotions.ts
1562
- var import_icons10 = require("@elementor/icons");
1563
- var import_i18n19 = require("@wordpress/i18n");
1407
+ var import_icons12 = require("@elementor/icons");
1408
+ var import_i18n13 = require("@wordpress/i18n");
1564
1409
  function firstFailExternalUrl(run) {
1565
1410
  return run.result.status === "fail" ? run.result.violations[0]?.externalUrl : void 0;
1566
1411
  }
1567
1412
  var PROMOTIONS = [
1568
1413
  {
1569
1414
  auditId: "audits/images-alt-text",
1570
- icon: import_icons10.PhotoIcon,
1571
- ctaLabel: (0, import_i18n19.__)("Fix with Ally", "elementor"),
1415
+ icon: import_icons12.PhotoIcon,
1416
+ ctaLabel: (0, import_i18n13.__)("Fix with Ally", "elementor"),
1572
1417
  formatSubtitle: (run) => {
1573
1418
  if (run.result.status !== "fail") {
1574
1419
  return null;
@@ -1577,9 +1422,9 @@ var PROMOTIONS = [
1577
1422
  if (count === 0) {
1578
1423
  return null;
1579
1424
  }
1580
- return (0, import_i18n19.sprintf)(
1425
+ return (0, import_i18n13.sprintf)(
1581
1426
  /* translators: %d: number of images missing alt text. */
1582
- (0, import_i18n19._n)("%d image", "%d images", count, "elementor"),
1427
+ (0, import_i18n13._n)("%d image", "%d images", count, "elementor"),
1583
1428
  count
1584
1429
  );
1585
1430
  },
@@ -1587,22 +1432,22 @@ var PROMOTIONS = [
1587
1432
  },
1588
1433
  {
1589
1434
  auditId: "audits/cookie-policy",
1590
- icon: import_icons10.ElementorCookieIcon,
1591
- ctaLabel: (0, import_i18n19.__)("Fix with Cookiez", "elementor"),
1592
- formatSubtitle: (run) => run.result.status === "fail" ? (0, import_i18n19.__)("Generate cookie policy", "elementor") : null,
1435
+ icon: import_icons12.ElementorCookieIcon,
1436
+ ctaLabel: (0, import_i18n13.__)("Fix with Cookiez", "elementor"),
1437
+ formatSubtitle: (run) => run.result.status === "fail" ? (0, import_i18n13.__)("Generate cookie policy", "elementor") : null,
1593
1438
  getCtaUrl: firstFailExternalUrl
1594
1439
  },
1595
1440
  {
1596
1441
  auditId: "audits/scan-for-cookies",
1597
- icon: import_icons10.ElementorCookieIcon,
1598
- ctaLabel: (0, import_i18n19.__)("Scan", "elementor"),
1599
- formatSubtitle: (run) => run.result.status === "fail" ? (0, import_i18n19.__)("Scan this page for cookies", "elementor") : null,
1442
+ icon: import_icons12.ElementorCookieIcon,
1443
+ ctaLabel: (0, import_i18n13.__)("Scan", "elementor"),
1444
+ formatSubtitle: (run) => run.result.status === "fail" ? (0, import_i18n13.__)("Scan this page for cookies", "elementor") : null,
1600
1445
  getCtaUrl: firstFailExternalUrl
1601
1446
  },
1602
1447
  {
1603
1448
  auditId: "audits/images-too-large",
1604
- icon: import_icons10.ShieldHalfFilledIcon,
1605
- ctaLabel: (0, import_i18n19.__)("Optimize all", "elementor"),
1449
+ icon: import_icons12.ShieldHalfFilledIcon,
1450
+ ctaLabel: (0, import_i18n13.__)("Optimize all", "elementor"),
1606
1451
  formatSubtitle: (run) => {
1607
1452
  if (run.result.status !== "fail") {
1608
1453
  return null;
@@ -1611,29 +1456,9 @@ var PROMOTIONS = [
1611
1456
  if (count === 0) {
1612
1457
  return null;
1613
1458
  }
1614
- return (0, import_i18n19.sprintf)(
1459
+ return (0, import_i18n13.sprintf)(
1615
1460
  /* translators: %d: number of oversized images. */
1616
- (0, import_i18n19._n)("%d image", "%d images", count, "elementor"),
1617
- count
1618
- );
1619
- },
1620
- getCtaUrl: firstFailExternalUrl
1621
- },
1622
- {
1623
- auditId: "audits/images-inefficient-format",
1624
- icon: import_icons10.ShieldHalfFilledIcon,
1625
- ctaLabel: (0, import_i18n19.__)("Optimize all", "elementor"),
1626
- formatSubtitle: (run) => {
1627
- if (run.result.status !== "fail") {
1628
- return null;
1629
- }
1630
- const count = run.result.metadata?.inefficientImageFormatCount ?? 0;
1631
- if (count === 0) {
1632
- return null;
1633
- }
1634
- return (0, import_i18n19.sprintf)(
1635
- /* translators: %d: number of images served in an inefficient format. */
1636
- (0, import_i18n19._n)("%d image", "%d images", count, "elementor"),
1461
+ (0, import_i18n13._n)("%d image", "%d images", count, "elementor"),
1637
1462
  count
1638
1463
  );
1639
1464
  },
@@ -1647,11 +1472,11 @@ function findAuditRun(report, auditId) {
1647
1472
  }
1648
1473
 
1649
1474
  // src/components/promotion-card.tsx
1650
- var React19 = __toESM(require("react"));
1651
- var import_ui18 = require("@elementor/ui");
1475
+ var React15 = __toESM(require("react"));
1476
+ var import_ui14 = require("@elementor/ui");
1652
1477
  function PromotionCard({ ctaDisabled, ctaLabel, icon: Icon, onCtaClick, subtitle, title }) {
1653
- return /* @__PURE__ */ React19.createElement(
1654
- import_ui18.Box,
1478
+ return /* @__PURE__ */ React15.createElement(
1479
+ import_ui14.Box,
1655
1480
  {
1656
1481
  sx: {
1657
1482
  alignItems: "center",
@@ -1664,9 +1489,9 @@ function PromotionCard({ ctaDisabled, ctaLabel, icon: Icon, onCtaClick, subtitle
1664
1489
  py: 1.5
1665
1490
  }
1666
1491
  },
1667
- /* @__PURE__ */ React19.createElement(Icon, { fontSize: "small", color: "action" }),
1668
- /* @__PURE__ */ React19.createElement(import_ui18.Box, { sx: { display: "flex", flex: 1, flexDirection: "column", gap: 0.25, minWidth: 0 } }, /* @__PURE__ */ React19.createElement(import_ui18.Typography, { variant: "body2", fontWeight: "bold" }, title), /* @__PURE__ */ React19.createElement(import_ui18.Typography, { variant: "caption", color: "text.secondary" }, subtitle)),
1669
- /* @__PURE__ */ React19.createElement(import_ui18.Button, { variant: "outlined", color: "secondary", size: "small", disabled: ctaDisabled, onClick: onCtaClick }, ctaLabel)
1492
+ /* @__PURE__ */ React15.createElement(Icon, { fontSize: "small", color: "action" }),
1493
+ /* @__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)),
1494
+ /* @__PURE__ */ React15.createElement(import_ui14.Button, { variant: "outlined", color: "secondary", size: "small", disabled: ctaDisabled, onClick: onCtaClick }, ctaLabel)
1670
1495
  );
1671
1496
  }
1672
1497
 
@@ -1695,7 +1520,7 @@ function Promotions({ report }) {
1695
1520
  if (cards.length === 0) {
1696
1521
  return null;
1697
1522
  }
1698
- return /* @__PURE__ */ React20.createElement(React20.Fragment, null, /* @__PURE__ */ React20.createElement(import_ui19.Divider, null), /* @__PURE__ */ React20.createElement(import_ui19.Typography, { variant: "subtitle1", fontWeight: "bold" }, (0, import_i18n20.__)("Quick actions", "elementor")), /* @__PURE__ */ React20.createElement(import_ui19.Box, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, cards.map(({ config, ctaUrl, key, run, subtitle }) => /* @__PURE__ */ React20.createElement(
1523
+ return /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(import_ui15.Divider, null), /* @__PURE__ */ React16.createElement(import_ui15.Typography, { variant: "subtitle1", fontWeight: "bold" }, (0, import_i18n14.__)("Quick actions", "elementor")), /* @__PURE__ */ React16.createElement(import_ui15.Box, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, cards.map(({ config, ctaUrl, key, run, subtitle }) => /* @__PURE__ */ React16.createElement(
1699
1524
  PromotionCard,
1700
1525
  {
1701
1526
  key,
@@ -1713,246 +1538,123 @@ function Promotions({ report }) {
1713
1538
  ))));
1714
1539
  }
1715
1540
 
1716
- // src/components/score-bar.tsx
1717
- var React21 = __toESM(require("react"));
1718
- var import_icons11 = require("@elementor/icons");
1719
- var import_ui20 = require("@elementor/ui");
1720
- function ScoreBar({ label, score, onClick }) {
1721
- const isRtl = "rtl" === (0, import_ui20.useTheme)().direction;
1722
- return /* @__PURE__ */ React21.createElement(
1723
- import_ui20.Box,
1724
- {
1725
- role: onClick ? "button" : void 0,
1726
- tabIndex: onClick ? 0 : void 0,
1727
- onClick,
1728
- onKeyDown: onClick ? onKeyboardClick(onClick) : void 0,
1729
- sx: {
1730
- display: "flex",
1731
- alignItems: "center",
1732
- gap: 1,
1733
- cursor: onClick ? "pointer" : "default",
1734
- outline: "none",
1735
- "&:focus-visible": { outline: "2px solid", outlineColor: "primary.main", borderRadius: 1 },
1736
- py: 0.5
1737
- }
1738
- },
1739
- /* @__PURE__ */ React21.createElement(import_ui20.Typography, { variant: "body2", sx: { minWidth: 96 } }, label),
1740
- /* @__PURE__ */ React21.createElement(
1741
- import_ui20.LinearProgress,
1742
- {
1743
- variant: "determinate",
1744
- value: score,
1745
- color: getScoreTier(score).color,
1746
- sx: { flex: 1, height: 6, borderRadius: 4, bgcolor: "action.disabledBackground" }
1747
- }
1748
- ),
1749
- /* @__PURE__ */ React21.createElement(
1750
- import_ui20.Typography,
1751
- {
1752
- variant: "body2",
1753
- color: "text.primary",
1754
- sx: { minWidth: 24, textAlign: "right", fontWeight: 900 }
1755
- },
1756
- score
1757
- ),
1758
- onClick && /* @__PURE__ */ React21.createElement(import_ui20.Rotate, { in: isRtl }, /* @__PURE__ */ React21.createElement(import_icons11.ChevronRightIcon, { fontSize: "small", color: "action" }))
1759
- );
1541
+ // src/components/severity-summary-cards.tsx
1542
+ var React17 = __toESM(require("react"));
1543
+ var import_ui16 = require("@elementor/ui");
1544
+ function SeveritySummaryCards({ report }) {
1545
+ const counts = countSeverities(report);
1546
+ return /* @__PURE__ */ React17.createElement(import_ui16.Box, { display: "flex", gap: 1.5 }, ALL_SEVERITIES.map((severity) => /* @__PURE__ */ React17.createElement(SeverityCard, { key: severity, severity, count: counts[severity] })));
1760
1547
  }
1761
-
1762
- // src/components/score-circle.tsx
1763
- var React22 = __toESM(require("react"));
1764
- var import_ui21 = require("@elementor/ui");
1765
- var SCORE_CIRCLE_SIZE = 88;
1766
- var SCORE_CIRCLE_THICKNESS = 3;
1767
- function ScoreCircle({ color, score }) {
1768
- const progressColor = color ?? getScoreTier(score).color;
1769
- return /* @__PURE__ */ React22.createElement(
1770
- import_ui21.Box,
1548
+ function SeverityCard({ severity, count }) {
1549
+ return /* @__PURE__ */ React17.createElement(
1550
+ import_ui16.Box,
1771
1551
  {
1772
- role: "progressbar",
1773
- "aria-valuenow": score,
1774
- "aria-valuemin": 0,
1775
- "aria-valuemax": 100,
1776
- sx: { position: "relative", display: "inline-flex" }
1552
+ display: "flex",
1553
+ flex: 1,
1554
+ flexDirection: "column",
1555
+ gap: 1,
1556
+ bgcolor: "action.hover",
1557
+ borderRadius: 1.5,
1558
+ px: 1.5,
1559
+ py: 1
1777
1560
  },
1778
- /* @__PURE__ */ React22.createElement(
1779
- import_ui21.CircularProgress,
1780
- {
1781
- variant: "determinate",
1782
- value: 100,
1783
- size: SCORE_CIRCLE_SIZE,
1784
- thickness: SCORE_CIRCLE_THICKNESS,
1785
- sx: { color: "action.disabledBackground" }
1786
- }
1787
- ),
1788
- /* @__PURE__ */ React22.createElement(
1789
- import_ui21.CircularProgress,
1790
- {
1791
- variant: "determinate",
1792
- value: score,
1793
- size: SCORE_CIRCLE_SIZE,
1794
- thickness: SCORE_CIRCLE_THICKNESS,
1795
- color: progressColor,
1796
- sx: { position: "absolute", left: 0 }
1797
- }
1798
- ),
1799
- /* @__PURE__ */ React22.createElement(
1800
- import_ui21.Box,
1801
- {
1802
- sx: {
1803
- position: "absolute",
1804
- inset: 0,
1805
- display: "flex",
1806
- alignItems: "center",
1807
- justifyContent: "center"
1808
- }
1809
- },
1810
- /* @__PURE__ */ React22.createElement(
1811
- import_ui21.Typography,
1812
- {
1813
- variant: "h4",
1814
- component: "span",
1815
- color: "text.primary",
1816
- sx: { fontWeight: 700, lineHeight: 1 }
1817
- },
1818
- score
1819
- )
1820
- )
1561
+ /* @__PURE__ */ React17.createElement(SeverityIcon, { severity }),
1562
+ /* @__PURE__ */ React17.createElement(import_ui16.Box, { display: "flex", flexDirection: "column" }, /* @__PURE__ */ React17.createElement(import_ui16.Typography, { variant: "h5", fontWeight: 700, component: "span" }, count), /* @__PURE__ */ React17.createElement(import_ui16.Typography, { variant: "caption", color: "text.secondary" }, severityPluralLabel(severity)))
1821
1563
  );
1822
1564
  }
1823
1565
 
1824
1566
  // src/components/pages/overview-page.tsx
1825
1567
  function OverviewPage({ onCategoryClick, report }) {
1826
1568
  const populatedCategories = getPopulatedCategories(report.categories, ALL_CATEGORIES);
1827
- const overallScore = getScoreTier(report.overall);
1828
- return /* @__PURE__ */ React23.createElement(import_ui22.Box, { sx: { display: "flex", flexDirection: "column", gap: 4, p: 2 } }, /* @__PURE__ */ React23.createElement(import_ui22.Box, { sx: { display: "flex", alignItems: "center", gap: 2 } }, /* @__PURE__ */ React23.createElement(ScoreCircle, { color: overallScore.color, score: report.overall }), /* @__PURE__ */ React23.createElement(import_ui22.Box, { sx: { display: "flex", flexDirection: "column", gap: 0.5 } }, /* @__PURE__ */ React23.createElement(
1829
- import_ui22.Chip,
1830
- {
1831
- label: overallScore.label,
1832
- color: overallScore.color,
1833
- variant: "standard",
1834
- size: "small",
1835
- sx: { fontWeight: 600, alignSelf: "flex-start" }
1836
- }
1837
- ), /* @__PURE__ */ React23.createElement(import_ui22.Typography, { variant: "body2", color: "text.secondary" }, (0, import_i18n21.__)("Overall score", "elementor")))), /* @__PURE__ */ React23.createElement(import_ui22.Box, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, populatedCategories.map((category) => /* @__PURE__ */ React23.createElement(
1838
- ScoreBar,
1569
+ const categoryRows = populatedCategories.map((category) => ({ category, counts: countSeverities(report, category) })).filter(({ counts }) => Object.values(counts).some((count) => count > 0));
1570
+ return /* @__PURE__ */ React18.createElement(import_ui17.Box, { display: "flex", flexDirection: "column", gap: 3, p: 2 }, /* @__PURE__ */ React18.createElement(SeveritySummaryCards, { report }), /* @__PURE__ */ React18.createElement(import_ui17.Box, { display: "flex", flexDirection: "column", gap: 1 }, /* @__PURE__ */ React18.createElement(import_ui17.Typography, { variant: "subtitle1", component: "h2" }, (0, import_i18n15.__)("All issues", "elementor")), /* @__PURE__ */ React18.createElement(import_ui17.Box, { display: "flex", flexDirection: "column", gap: 1 }, categoryRows.map(({ category, counts }) => /* @__PURE__ */ React18.createElement(
1571
+ IssuesCategoryRow,
1839
1572
  {
1840
1573
  key: category,
1574
+ category,
1841
1575
  label: CATEGORY_LABELS[category],
1842
- score: report.categories[category].score,
1576
+ counts,
1843
1577
  onClick: () => onCategoryClick(category)
1844
1578
  }
1845
- ))), /* @__PURE__ */ React23.createElement(Promotions, { report }));
1579
+ )))), /* @__PURE__ */ React18.createElement(Promotions, { report }));
1846
1580
  }
1847
1581
 
1848
1582
  // src/components/report-shell.tsx
1849
- function activeTab(page) {
1850
- if (page === "overview") {
1851
- return "overview";
1852
- }
1853
- if (page === "issues") {
1854
- return "issues";
1855
- }
1856
- return page.backTo;
1857
- }
1858
- function isAllAuditsPage(page) {
1859
- return typeof page === "object" && "page" in page && page.page === "all-audits";
1860
- }
1861
1583
  function isCategoryPage(page) {
1862
1584
  return typeof page === "object" && "category" in page;
1863
1585
  }
1864
1586
  function ReportShell({ report }) {
1865
- const [activePage, setActivePage] = (0, import_react6.useState)("overview");
1866
- const currentTab = activeTab(activePage);
1867
- const handleTabChange = (_, value) => {
1868
- setActivePage(value);
1869
- };
1870
- const openCategory = (category, backTo) => {
1871
- setActivePage({ category, backTo });
1872
- };
1873
- const openAllAudits = (expand, backTo = "issues") => {
1874
- setActivePage({ page: "all-audits", expand, backTo });
1875
- };
1876
- const backFromSubPage = () => {
1877
- if (isAllAuditsPage(activePage) || isCategoryPage(activePage)) {
1878
- setActivePage(activePage.backTo);
1879
- }
1880
- };
1881
- return /* @__PURE__ */ React24.createElement(import_ui23.Box, { sx: { display: "flex", flexDirection: "column", flex: 1, minHeight: 0 } }, /* @__PURE__ */ React24.createElement(import_ui23.Box, { sx: { flexShrink: 0 } }, /* @__PURE__ */ React24.createElement(
1882
- import_ui23.Tabs,
1587
+ const [activePage, setActivePage] = (0, import_react5.useState)("overview");
1588
+ const openCategory = (category) => setActivePage({ category });
1589
+ const backToOverview = () => setActivePage("overview");
1590
+ return /* @__PURE__ */ React19.createElement(import_ui18.Box, { sx: { flex: 1, minHeight: 0, overflowY: "auto" } }, activePage === "overview" && /* @__PURE__ */ React19.createElement(OverviewPage, { report, onCategoryClick: openCategory }), isCategoryPage(activePage) && /* @__PURE__ */ React19.createElement(CategoryPage, { category: activePage.category, report, onBack: backToOverview }));
1591
+ }
1592
+
1593
+ // src/components/rescan-button.tsx
1594
+ var React20 = __toESM(require("react"));
1595
+ var import_editor_floating_panels3 = require("@elementor/editor-floating-panels");
1596
+ var import_icons13 = require("@elementor/icons");
1597
+ var import_ui19 = require("@elementor/ui");
1598
+ var import_i18n16 = require("@wordpress/i18n");
1599
+ function RescanButton({ hasScanned, isStale, disabled, onClick }) {
1600
+ const panelZIndex = (0, import_editor_floating_panels3.useFloatingPanelZIndex)(AUDIT_PANEL_ID);
1601
+ return /* @__PURE__ */ React20.createElement(
1602
+ import_ui19.Tooltip,
1883
1603
  {
1884
- "aria-label": (0, import_i18n22.__)("Audit navigation", "elementor"),
1885
- value: currentTab,
1886
- onChange: handleTabChange,
1887
- textColor: "secondary",
1888
- indicatorColor: "secondary",
1889
- size: "small",
1890
- centered: true,
1891
- variant: "fullWidth"
1604
+ title: (0, import_i18n16.__)("Page content changed - rescan advised", "elementor"),
1605
+ disableHoverListener: !isStale,
1606
+ PopperProps: { sx: { zIndex: panelZIndex } }
1892
1607
  },
1893
- /* @__PURE__ */ React24.createElement(import_ui23.Tab, { value: "overview", label: (0, import_i18n22.__)("Overview", "elementor") }),
1894
- /* @__PURE__ */ React24.createElement(import_ui23.Tab, { value: "issues", label: (0, import_i18n22.__)("Issues", "elementor") })
1895
- ), /* @__PURE__ */ React24.createElement(import_ui23.Divider, null)), /* @__PURE__ */ React24.createElement(import_ui23.Box, { sx: { flex: 1, minHeight: 0, overflowY: "auto" } }, activePage === "overview" && /* @__PURE__ */ React24.createElement(
1896
- OverviewPage,
1897
- {
1898
- report,
1899
- onCategoryClick: (category) => openCategory(category, "overview")
1900
- }
1901
- ), activePage === "issues" && /* @__PURE__ */ React24.createElement(
1902
- IssuesPage,
1903
- {
1904
- report,
1905
- onCategoryClick: (category) => openCategory(category, "issues"),
1906
- onAllAuditsClick: () => openAllAudits(),
1907
- onStatusClick: (status) => openAllAudits(status)
1908
- }
1909
- ), isAllAuditsPage(activePage) && /* @__PURE__ */ React24.createElement(
1910
- AllAuditsPage,
1911
- {
1912
- report,
1913
- initialExpandedStatus: activePage.expand,
1914
- onBack: backFromSubPage
1915
- }
1916
- ), isCategoryPage(activePage) && /* @__PURE__ */ React24.createElement(CategoryPage, { category: activePage.category, report, onBack: backFromSubPage })));
1608
+ /* @__PURE__ */ React20.createElement(import_ui19.Badge, { variant: "dot", color: "warning", invisible: !isStale, overlap: "rectangular" }, /* @__PURE__ */ React20.createElement(
1609
+ import_ui19.Button,
1610
+ {
1611
+ variant: "contained",
1612
+ size: "small",
1613
+ startIcon: hasScanned ? /* @__PURE__ */ React20.createElement(import_icons13.ReloadIcon, { fontSize: "small" }) : void 0,
1614
+ onClick,
1615
+ disabled
1616
+ },
1617
+ hasScanned ? (0, import_i18n16.__)("Rescan", "elementor") : (0, import_i18n16.__)("Run page audit", "elementor")
1618
+ ))
1619
+ );
1917
1620
  }
1918
1621
 
1919
1622
  // src/components/audit-panel.tsx
1920
1623
  function AuditPanel() {
1921
- const { status, report, error, run } = useAuditReport();
1922
- const currentDocumentId = (0, import_editor_elements6.getCurrentDocumentId)() ?? 0;
1923
- const onRun = () => run(currentDocumentId);
1624
+ const { status, report, error, isStale, run } = useAuditReport();
1625
+ const hostDocumentId = (0, import_editor_elements6.getHostDocumentId)() ?? 0;
1626
+ const onRun = () => run(hostDocumentId);
1924
1627
  const lastScanLabel = report ? new Date(report.runAt).toLocaleTimeString() : null;
1925
- return /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement(
1926
- import_editor_floating_panels.FloatingPanelHeader,
1628
+ return /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(
1629
+ import_editor_floating_panels4.FloatingPanelHeader,
1927
1630
  {
1928
- panelId: "audit-panel",
1929
- title: (0, import_i18n23.__)("Page Audit", "elementor"),
1930
- badge: (0, import_i18n23.__)("Beta", "elementor"),
1631
+ panelId: AUDIT_PANEL_ID,
1632
+ title: (0, import_i18n17.__)("Page Audit", "elementor"),
1633
+ badge: (0, import_i18n17.__)("Beta", "elementor"),
1931
1634
  titleVariant: "subtitle2"
1932
1635
  }
1933
- ), /* @__PURE__ */ React25.createElement(
1934
- import_editor_floating_panels.FloatingPanelBody,
1636
+ ), /* @__PURE__ */ React21.createElement(
1637
+ import_editor_floating_panels4.FloatingPanelBody,
1935
1638
  {
1936
1639
  sx: status === "ready" ? { display: "flex", flexDirection: "column", overflow: "hidden", minHeight: 0 } : void 0
1937
1640
  },
1938
- status === "idle" && /* @__PURE__ */ React25.createElement(WelcomePage, null),
1939
- status === "loading" && /* @__PURE__ */ React25.createElement(LoadingPage, null),
1940
- status === "error" && /* @__PURE__ */ React25.createElement(ErrorPage, { message: error ?? "", onRetry: onRun }),
1941
- status === "ready" && report && /* @__PURE__ */ React25.createElement(ReportShell, { report })
1942
- ), /* @__PURE__ */ React25.createElement(import_editor_floating_panels.FloatingPanelFooter, null, lastScanLabel ? /* @__PURE__ */ React25.createElement(import_ui24.Typography, { variant: "caption", sx: { flex: 1 } }, (0, import_i18n23.__)("Last scan:", "elementor"), " ", lastScanLabel) : /* @__PURE__ */ React25.createElement(import_ui24.Box, { sx: { flex: 1 } }), lastScanLabel && /* @__PURE__ */ React25.createElement(AuditFeedback, null), /* @__PURE__ */ React25.createElement(
1943
- import_ui24.Button,
1641
+ status === "idle" && /* @__PURE__ */ React21.createElement(WelcomePage, null),
1642
+ status === "loading" && /* @__PURE__ */ React21.createElement(LoadingPage, null),
1643
+ status === "error" && /* @__PURE__ */ React21.createElement(ErrorPage, { message: error ?? "", onRetry: onRun }),
1644
+ status === "ready" && report && /* @__PURE__ */ React21.createElement(ReportShell, { report })
1645
+ ), /* @__PURE__ */ React21.createElement(import_editor_floating_panels4.FloatingPanelFooter, null, lastScanLabel ? /* @__PURE__ */ React21.createElement(import_ui20.Typography, { variant: "caption", sx: { flex: 1 } }, (0, import_i18n17.__)("Last scan:", "elementor"), " ", lastScanLabel) : /* @__PURE__ */ React21.createElement(import_ui20.Box, { sx: { flex: 1 } }), lastScanLabel && /* @__PURE__ */ React21.createElement(AuditFeedback, null), /* @__PURE__ */ React21.createElement(
1646
+ RescanButton,
1944
1647
  {
1945
- variant: "contained",
1946
- size: "small",
1947
- onClick: onRun,
1948
- disabled: status === "loading" || currentDocumentId === 0
1949
- },
1950
- lastScanLabel ? (0, import_i18n23.__)("Re-scan", "elementor") : (0, import_i18n23.__)("Run page audit", "elementor")
1648
+ hasScanned: Boolean(lastScanLabel),
1649
+ isStale,
1650
+ disabled: status === "loading" || hostDocumentId === 0,
1651
+ onClick: onRun
1652
+ }
1951
1653
  )));
1952
1654
  }
1953
1655
 
1954
1656
  // src/editor-panel.tsx
1955
- var auditPanel = (0, import_editor_floating_panels2.createFloatingPanel)({
1657
+ var auditPanel = (0, import_editor_floating_panels5.createFloatingPanel)({
1956
1658
  id: AUDIT_PANEL_ID,
1957
1659
  title: "Audit",
1958
1660
  icon: () => null,
@@ -1972,8 +1674,8 @@ function useAuditToggleProps() {
1972
1674
  const { isOpen } = auditPanel.useFloatingPanelStatus();
1973
1675
  const { toggle } = auditPanel.useFloatingPanelActions();
1974
1676
  return {
1975
- title: (0, import_i18n24.__)("Audit Page", "elementor"),
1976
- icon: import_icons12.ShieldCheckIcon,
1677
+ title: (0, import_i18n18.__)("Audit Page", "elementor"),
1678
+ icon: AuditToggleIcon,
1977
1679
  selected: isOpen,
1978
1680
  onClick: () => toggle()
1979
1681
  };
@@ -1990,15 +1692,15 @@ function registerAppBarAuditsToggle() {
1990
1692
  }
1991
1693
 
1992
1694
  // src/audits/accessibility-policy.ts
1993
- var import_i18n25 = require("@wordpress/i18n");
1695
+ var import_i18n19 = require("@wordpress/i18n");
1994
1696
  var audit = {
1995
1697
  id: "audits/accessibility-policy",
1996
- title: (0, import_i18n25.__)("Accessibility policy", "elementor"),
1997
- description: (0, import_i18n25.__)(
1698
+ title: (0, import_i18n19.__)("Accessibility policy", "elementor"),
1699
+ description: (0, import_i18n19.__)(
1998
1700
  "An accessibility policy demonstrates your commitment to digital inclusion and may be legally required in some regions.",
1999
1701
  "elementor"
2000
1702
  ),
2001
- fixHint: (0, import_i18n25.__)("Install the Ally plugin to add an accessibility policy to your website.", "elementor"),
1703
+ fixHint: (0, import_i18n19.__)("Install the Ally plugin to add an accessibility policy to your website.", "elementor"),
2002
1704
  categories: ["compliance"],
2003
1705
  severity: "info",
2004
1706
  weight: 1,
@@ -2011,7 +1713,7 @@ var audit = {
2011
1713
  violations: [
2012
1714
  {
2013
1715
  auditId: audit.id,
2014
- label: (0, import_i18n25.__)("Ally plugin is not installed or active.", "elementor"),
1716
+ label: (0, import_i18n19.__)("Ally plugin is not installed or active.", "elementor"),
2015
1717
  externalUrl: ctx.pageContext.ally_plugin_url
2016
1718
  }
2017
1719
  ]
@@ -2020,15 +1722,15 @@ var audit = {
2020
1722
  };
2021
1723
 
2022
1724
  // src/audits/accessibility-statement.ts
2023
- var import_i18n26 = require("@wordpress/i18n");
1725
+ var import_i18n20 = require("@wordpress/i18n");
2024
1726
  var audit2 = {
2025
1727
  id: "audits/accessibility-statement",
2026
- title: (0, import_i18n26.__)("Create an accessibility statement", "elementor"),
2027
- description: (0, import_i18n26.__)(
1728
+ title: (0, import_i18n20.__)("Create an accessibility statement", "elementor"),
1729
+ description: (0, import_i18n20.__)(
2028
1730
  "An accessibility statement showcases your efforts to create an inclusive online space, highlighting helpful features and a commitment to accessibility.",
2029
1731
  "elementor"
2030
1732
  ),
2031
- fixHint: (0, import_i18n26.__)("Use Ally to create and publish an accessibility statement for your site.", "elementor"),
1733
+ fixHint: (0, import_i18n20.__)("Use Ally to create and publish an accessibility statement for your site.", "elementor"),
2032
1734
  categories: ["accessibility"],
2033
1735
  severity: "info",
2034
1736
  weight: 1,
@@ -2042,11 +1744,11 @@ var audit2 = {
2042
1744
  violations: [
2043
1745
  {
2044
1746
  auditId: audit2.id,
2045
- label: (0, import_i18n26.__)("No accessibility statement has been created for this site.", "elementor"),
1747
+ label: (0, import_i18n20.__)("No accessibility statement has been created for this site.", "elementor"),
2046
1748
  externalUrl: isReady ? ctx.pageContext.ally_accessibility_statement_url : ctx.pageContext.ally_plugin_url,
2047
- ctaLabel: isReady ? (0, import_i18n26.__)("Create", "elementor") : void 0,
2048
- secondaryCtaLabel: (0, import_i18n26.__)("Learn more", "elementor"),
2049
- secondaryCtaUrl: "https://go.elementor.com/acc-plg-learn-more"
1749
+ ctaLabel: (0, import_i18n20.__)("Create", "elementor"),
1750
+ secondaryCtaLabel: (0, import_i18n20.__)("Learn more", "elementor"),
1751
+ secondaryCtaUrl: "https://elementor.com/help/start-using-ally/#statement"
2050
1752
  }
2051
1753
  ]
2052
1754
  };
@@ -2054,7 +1756,7 @@ var audit2 = {
2054
1756
  };
2055
1757
 
2056
1758
  // src/audits/accessibility-widget.ts
2057
- var import_i18n27 = require("@wordpress/i18n");
1759
+ var import_i18n21 = require("@wordpress/i18n");
2058
1760
 
2059
1761
  // src/utils/scan-accessibility-widgets.ts
2060
1762
  var ACCESSIBILITY_WIDGET_SCRIPT_SOURCES = ["cdn.elementor.com/a11y/widget.js"];
@@ -2065,18 +1767,18 @@ function hasAccessibilityWidgetScript(html) {
2065
1767
  // src/audits/accessibility-widget.ts
2066
1768
  var audit3 = {
2067
1769
  id: "audits/accessibility-widget",
2068
- title: (0, import_i18n27.__)("Add an accessibility widget", "elementor"),
2069
- description: (0, import_i18n27.__)("Make your site more inclusive by adding an accessibility widget.", "elementor"),
2070
- fixHint: (0, import_i18n27.__)("Activate Ally\u2019s accessibility widget to give visitors on-page accessibility tools.", "elementor"),
1770
+ title: (0, import_i18n21.__)("Add an accessibility widget", "elementor"),
1771
+ description: (0, import_i18n21.__)("Make your site more inclusive by adding an accessibility widget.", "elementor"),
1772
+ fixHint: (0, import_i18n21.__)("Activate Ally\u2019s accessibility widget to give visitors on-page accessibility tools.", "elementor"),
2071
1773
  categories: ["accessibility"],
2072
1774
  severity: "info",
2073
1775
  weight: 1,
2074
1776
  evaluate: (ctx) => {
2075
1777
  if (!ctx.pageContext.frontend_url) {
2076
- return { status: "skipped", reason: (0, import_i18n27.__)("Page is not published.", "elementor") };
1778
+ return { status: "skipped", reason: (0, import_i18n21.__)("Page is not published.", "elementor") };
2077
1779
  }
2078
1780
  if (!ctx.renderedHtml) {
2079
- return { status: "skipped", reason: (0, import_i18n27.__)("Could not fetch the published page.", "elementor") };
1781
+ return { status: "skipped", reason: (0, import_i18n21.__)("Could not fetch the published page.", "elementor") };
2080
1782
  }
2081
1783
  if (hasAccessibilityWidgetScript(ctx.renderedHtml)) {
2082
1784
  return { status: "pass" };
@@ -2087,9 +1789,9 @@ var audit3 = {
2087
1789
  violations: [
2088
1790
  {
2089
1791
  auditId: audit3.id,
2090
- label: (0, import_i18n27.__)("No accessibility widget is active on this site.", "elementor"),
1792
+ label: (0, import_i18n21.__)("No accessibility widget is active on this site.", "elementor"),
2091
1793
  externalUrl: isReady ? ctx.pageContext.ally_widget_settings_url : ctx.pageContext.ally_plugin_url,
2092
- ctaLabel: (0, import_i18n27.__)("Activate", "elementor")
1794
+ ctaLabel: (0, import_i18n21.__)("Activate", "elementor")
2093
1795
  }
2094
1796
  ]
2095
1797
  };
@@ -2097,15 +1799,15 @@ var audit3 = {
2097
1799
  };
2098
1800
 
2099
1801
  // src/audits/cookie-policy.ts
2100
- var import_i18n28 = require("@wordpress/i18n");
1802
+ var import_i18n22 = require("@wordpress/i18n");
2101
1803
  var audit4 = {
2102
1804
  id: "audits/cookie-policy",
2103
- title: (0, import_i18n28.__)("Cookie policy", "elementor"),
2104
- description: (0, import_i18n28.__)(
1805
+ title: (0, import_i18n22.__)("Cookie policy", "elementor"),
1806
+ description: (0, import_i18n22.__)(
2105
1807
  "A cookie policy is required by privacy regulations such as GDPR (EU) or CCPA (US) to inform visitors about tracking and data collection.",
2106
1808
  "elementor"
2107
1809
  ),
2108
- fixHint: (0, import_i18n28.__)("Install the Cookiez plugin to add a cookie policy to your website.", "elementor"),
1810
+ fixHint: (0, import_i18n22.__)("Install the Cookiez plugin to add a cookie policy to your website.", "elementor"),
2109
1811
  categories: ["compliance"],
2110
1812
  severity: "info",
2111
1813
  weight: 1,
@@ -2118,8 +1820,9 @@ var audit4 = {
2118
1820
  violations: [
2119
1821
  {
2120
1822
  auditId: audit4.id,
2121
- label: (0, import_i18n28.__)("Cookiez plugin is not installed or active.", "elementor"),
2122
- externalUrl: ctx.pageContext.cookiez_plugin_url
1823
+ label: (0, import_i18n22.__)("Cookiez plugin is not installed or active.", "elementor"),
1824
+ externalUrl: ctx.pageContext.cookiez_plugin_url,
1825
+ ctaLabel: (0, import_i18n22.__)("Set up cookie consent", "elementor")
2123
1826
  }
2124
1827
  ]
2125
1828
  };
@@ -2128,7 +1831,7 @@ var audit4 = {
2128
1831
 
2129
1832
  // src/audits/deep-nesting.ts
2130
1833
  var import_editor_elements7 = require("@elementor/editor-elements");
2131
- var import_i18n29 = require("@wordpress/i18n");
1834
+ var import_i18n23 = require("@wordpress/i18n");
2132
1835
  var MAX_NESTING_DEPTH = 6;
2133
1836
  function isNestingElement(node, widgetsCache) {
2134
1837
  if (node.elType === "container") {
@@ -2138,18 +1841,18 @@ function isNestingElement(node, widgetsCache) {
2138
1841
  }
2139
1842
  var audit5 = {
2140
1843
  id: "audits/deep-nesting",
2141
- title: (0, import_i18n29.__)("Deep container nesting", "elementor"),
2142
- description: (0, import_i18n29.__)(
1844
+ title: (0, import_i18n23.__)("Deep container nesting", "elementor"),
1845
+ description: (0, import_i18n23.__)(
2143
1846
  "Container nesting exceeds the recommended depth. Deep DOM trees hurt rendering performance and readability.",
2144
1847
  "elementor"
2145
1848
  ),
2146
- fixHint: (0, import_i18n29.__)("Flatten the layout by removing unnecessary wrapper containers.", "elementor"),
1849
+ fixHint: (0, import_i18n23.__)("Flatten the layout by removing unnecessary wrapper containers.", "elementor"),
2147
1850
  categories: ["performance", "best-practices"],
2148
1851
  severity: "warning",
2149
1852
  weight: 5,
2150
1853
  evaluate: (ctx) => {
2151
1854
  if (ctx.elements.tree.length === 0) {
2152
- return { status: "skipped", reason: (0, import_i18n29.__)("No elements", "elementor") };
1855
+ return { status: "skipped", reason: (0, import_i18n23.__)("No elements", "elementor") };
2153
1856
  }
2154
1857
  const widgetsCache = (0, import_editor_elements7.getWidgetsCache)();
2155
1858
  const violations = [];
@@ -2163,7 +1866,7 @@ var audit5 = {
2163
1866
  auditId: audit5.id,
2164
1867
  elementId: node.id,
2165
1868
  targetHint: "element-settings",
2166
- label: (0, import_i18n29.__)("Container nesting is too deep.", "elementor")
1869
+ label: (0, import_i18n23.__)("Container nesting is too deep.", "elementor")
2167
1870
  });
2168
1871
  }
2169
1872
  });
@@ -2172,15 +1875,15 @@ var audit5 = {
2172
1875
  };
2173
1876
 
2174
1877
  // src/audits/default-design-system.ts
2175
- var import_i18n30 = require("@wordpress/i18n");
1878
+ var import_i18n24 = require("@wordpress/i18n");
2176
1879
  var audit6 = {
2177
1880
  id: "audits/default-design-system",
2178
- title: (0, import_i18n30.__)("Default website kit", "elementor"),
2179
- description: (0, import_i18n30.__)(
1881
+ title: (0, import_i18n24.__)("Default website kit", "elementor"),
1882
+ description: (0, import_i18n24.__)(
2180
1883
  "Your website is using the default design system colors and fonts. Custom branding makes the website feel uniquely yours.",
2181
1884
  "elementor"
2182
1885
  ),
2183
- fixHint: (0, import_i18n30.__)("Open Site Settings and customize your kit (colors, fonts, layout).", "elementor"),
1886
+ fixHint: (0, import_i18n24.__)("Open Site Settings and customize your kit (colors, fonts, layout).", "elementor"),
2184
1887
  categories: ["best-practices"],
2185
1888
  severity: "info",
2186
1889
  weight: 1,
@@ -2193,7 +1896,7 @@ var audit6 = {
2193
1896
  violations: [
2194
1897
  {
2195
1898
  auditId: audit6.id,
2196
- label: (0, import_i18n30.__)("Kit appears unchanged from default.", "elementor"),
1899
+ label: (0, import_i18n24.__)("Kit appears unchanged from default.", "elementor"),
2197
1900
  targetHint: "site-settings"
2198
1901
  }
2199
1902
  ]
@@ -2203,16 +1906,16 @@ var audit6 = {
2203
1906
 
2204
1907
  // src/audits/deprecated-widgets.ts
2205
1908
  var import_editor_elements8 = require("@elementor/editor-elements");
2206
- var import_i18n31 = require("@wordpress/i18n");
1909
+ var import_i18n25 = require("@wordpress/i18n");
2207
1910
  var DEPRECATION_CONTROL_NAME = "deprecation_message";
2208
1911
  var audit7 = {
2209
1912
  id: "audits/deprecated-widgets",
2210
- title: (0, import_i18n31.__)("Deprecated widgets", "elementor"),
2211
- description: (0, import_i18n31.__)(
1913
+ title: (0, import_i18n25.__)("Deprecated widgets", "elementor"),
1914
+ description: (0, import_i18n25.__)(
2212
1915
  "Deprecated widgets should not be used. For better capabilities use the recommended replacement.",
2213
1916
  "elementor"
2214
1917
  ),
2215
- fixHint: (0, import_i18n31.__)("Replace each deprecated widget with the recommended new widget shown in its panel.", "elementor"),
1918
+ fixHint: (0, import_i18n25.__)("Replace each deprecated widget with the recommended new widget shown in its panel.", "elementor"),
2216
1919
  categories: ["best-practices", "performance"],
2217
1920
  severity: "warning",
2218
1921
  weight: 7,
@@ -2239,7 +1942,7 @@ var audit7 = {
2239
1942
  auditId: audit7.id,
2240
1943
  elementId: node.id,
2241
1944
  targetHint: "element-settings",
2242
- label: (0, import_i18n31.__)("Using deprecated widget.", "elementor")
1945
+ label: (0, import_i18n25.__)("Using deprecated widget.", "elementor")
2243
1946
  });
2244
1947
  });
2245
1948
  return violations.length === 0 ? { status: "pass" } : { status: "fail", violations };
@@ -2247,7 +1950,7 @@ var audit7 = {
2247
1950
  };
2248
1951
 
2249
1952
  // src/audits/google-consent-mode.ts
2250
- var import_i18n32 = require("@wordpress/i18n");
1953
+ var import_i18n26 = require("@wordpress/i18n");
2251
1954
 
2252
1955
  // src/utils/scan-consent-signals.ts
2253
1956
  var GOOGLE_TRACKING_PATTERNS = [
@@ -2270,12 +1973,12 @@ function hasConsentDefaultCall(html) {
2270
1973
  // src/audits/google-consent-mode.ts
2271
1974
  var audit8 = {
2272
1975
  id: "audits/google-consent-mode",
2273
- title: (0, import_i18n32.__)("Google Consent Mode", "elementor"),
2274
- description: (0, import_i18n32.__)(
1976
+ title: (0, import_i18n26.__)("Google Consent Mode", "elementor"),
1977
+ description: (0, import_i18n26.__)(
2275
1978
  "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.",
2276
1979
  "elementor"
2277
1980
  ),
2278
- fixHint: (0, import_i18n32.__)(
1981
+ fixHint: (0, import_i18n26.__)(
2279
1982
  "Turn on Google Consent Mode in your cookie consent plugin so Google tags respect visitor consent choices.",
2280
1983
  "elementor"
2281
1984
  ),
@@ -2284,18 +1987,18 @@ var audit8 = {
2284
1987
  weight: 1,
2285
1988
  evaluate: (ctx) => {
2286
1989
  if (!ctx.pageContext.frontend_url) {
2287
- return { status: "skipped", reason: (0, import_i18n32.__)("Page is not published.", "elementor") };
1990
+ return { status: "skipped", reason: (0, import_i18n26.__)("Page is not published.", "elementor") };
2288
1991
  }
2289
1992
  if (!ctx.renderedHtml) {
2290
- return { status: "skipped", reason: (0, import_i18n32.__)("Could not fetch the published page.", "elementor") };
1993
+ return { status: "skipped", reason: (0, import_i18n26.__)("Could not fetch the published page.", "elementor") };
2291
1994
  }
2292
1995
  if (!hasGoogleTracking(ctx.renderedHtml)) {
2293
- return { status: "skipped", reason: (0, import_i18n32.__)("No Google tracking product detected.", "elementor") };
1996
+ return { status: "skipped", reason: (0, import_i18n26.__)("No Google tracking product detected.", "elementor") };
2294
1997
  }
2295
1998
  if (hasConsentDefaultCall(ctx.renderedHtml)) {
2296
1999
  return { status: "pass" };
2297
2000
  }
2298
- const label = (0, import_i18n32.__)("No Google Consent Mode signal was found on the page.", "elementor");
2001
+ const label = (0, import_i18n26.__)("No Google Consent Mode signal was found on the page.", "elementor");
2299
2002
  const isCookiezReady = ctx.pageContext.cookiez_plugin_installed && ctx.pageContext.cookiez_plugin_active;
2300
2003
  if (isCookiezReady) {
2301
2004
  return {
@@ -2304,7 +2007,7 @@ var audit8 = {
2304
2007
  {
2305
2008
  auditId: audit8.id,
2306
2009
  label,
2307
- ctaLabel: (0, import_i18n32.__)("Enable", "elementor"),
2010
+ ctaLabel: (0, import_i18n26.__)("Enable", "elementor"),
2308
2011
  externalUrl: ctx.pageContext.cookiez_consent_mode_settings_url
2309
2012
  }
2310
2013
  ]
@@ -2324,7 +2027,7 @@ var audit8 = {
2324
2027
  };
2325
2028
 
2326
2029
  // src/audits/heading-structure.ts
2327
- var import_i18n33 = require("@wordpress/i18n");
2030
+ var import_i18n27 = require("@wordpress/i18n");
2328
2031
  var HEADING_LEVELS = ["h1", "h2", "h3", "h4", "h5", "h6"];
2329
2032
  var HEADING_CONTROLS_REGISTRY = {
2330
2033
  // Core
@@ -2502,27 +2205,27 @@ function extractPageHeadings(tree) {
2502
2205
  }
2503
2206
  var audit9 = {
2504
2207
  id: "audits/heading-structure",
2505
- title: (0, import_i18n33.__)("Heading structure", "elementor"),
2506
- description: (0, import_i18n33.__)("Pages should have exactly one H1 and a non-skipping heading order.", "elementor"),
2507
- fixHint: (0, import_i18n33.__)("Ensure your page has one H1 and that heading levels do not skip (no H2 \u2192 H4).", "elementor"),
2208
+ title: (0, import_i18n27.__)("Heading structure", "elementor"),
2209
+ description: (0, import_i18n27.__)("Pages should have exactly one H1 and a non-skipping heading order.", "elementor"),
2210
+ fixHint: (0, import_i18n27.__)("Ensure your page has one H1 and that heading levels do not skip (no H2 \u2192 H4).", "elementor"),
2508
2211
  categories: ["seo", "accessibility"],
2509
2212
  severity: "error",
2510
2213
  weight: 10,
2511
2214
  evaluate: (ctx) => {
2512
2215
  if (ctx.elements.tree.length === 0) {
2513
- return { status: "skipped", reason: (0, import_i18n33.__)("No elements", "elementor") };
2216
+ return { status: "skipped", reason: (0, import_i18n27.__)("No elements", "elementor") };
2514
2217
  }
2515
2218
  const headings = extractPageHeadings(ctx.elements.tree);
2516
2219
  const violations = [];
2517
2220
  if (headings.length === 0) {
2518
2221
  violations.push({
2519
2222
  auditId: audit9.id,
2520
- label: (0, import_i18n33.__)("No headings found on the page.", "elementor")
2223
+ label: (0, import_i18n27.__)("No headings found on the page.", "elementor")
2521
2224
  });
2522
2225
  } else {
2523
2226
  const h1Count = headings.filter((h) => h.level === 1).length;
2524
2227
  if (h1Count === 0) {
2525
- violations.push({ auditId: audit9.id, label: (0, import_i18n33.__)("No H1 on the page.", "elementor") });
2228
+ violations.push({ auditId: audit9.id, label: (0, import_i18n27.__)("No H1 on the page.", "elementor") });
2526
2229
  }
2527
2230
  if (h1Count > 1) {
2528
2231
  headings.filter((h) => h.level === 1).slice(1).forEach(
@@ -2530,7 +2233,7 @@ var audit9 = {
2530
2233
  auditId: audit9.id,
2531
2234
  elementId: h.elementId,
2532
2235
  targetHint: "element-settings",
2533
- label: (0, import_i18n33.__)("More than one H1 on the page.", "elementor")
2236
+ label: (0, import_i18n27.__)("More than one H1 on the page.", "elementor")
2534
2237
  })
2535
2238
  );
2536
2239
  }
@@ -2540,7 +2243,7 @@ var audit9 = {
2540
2243
  auditId: audit9.id,
2541
2244
  elementId: headings[i].elementId,
2542
2245
  targetHint: "element-settings",
2543
- label: (0, import_i18n33.__)("Heading level skipped.", "elementor")
2246
+ label: (0, import_i18n27.__)("Heading level skipped.", "elementor")
2544
2247
  });
2545
2248
  }
2546
2249
  }
@@ -2550,24 +2253,24 @@ var audit9 = {
2550
2253
  };
2551
2254
 
2552
2255
  // src/audits/hidden-elements.ts
2553
- var import_i18n34 = require("@wordpress/i18n");
2256
+ var import_i18n28 = require("@wordpress/i18n");
2554
2257
  function isHiddenOnAllDevices(settings) {
2555
2258
  return Boolean(settings.hide_desktop) && Boolean(settings.hide_tablet) && Boolean(settings.hide_mobile);
2556
2259
  }
2557
2260
  var audit10 = {
2558
2261
  id: "audits/hidden-elements",
2559
- title: (0, import_i18n34.__)("Hidden elements", "elementor"),
2560
- description: (0, import_i18n34.__)(
2262
+ title: (0, import_i18n28.__)("Hidden elements", "elementor"),
2263
+ description: (0, import_i18n28.__)(
2561
2264
  "Elements hidden on all devices simultaneously are effectively dead code in the page tree.",
2562
2265
  "elementor"
2563
2266
  ),
2564
- fixHint: (0, import_i18n34.__)("Remove the element or make it visible on at least one device breakpoint.", "elementor"),
2267
+ fixHint: (0, import_i18n28.__)("Remove the element or make it visible on at least one device breakpoint.", "elementor"),
2565
2268
  categories: ["best-practices", "performance"],
2566
2269
  severity: "info",
2567
2270
  weight: 3,
2568
2271
  evaluate: (ctx) => {
2569
2272
  if (ctx.elements.tree.length === 0) {
2570
- return { status: "skipped", reason: (0, import_i18n34.__)("No elements", "elementor") };
2273
+ return { status: "skipped", reason: (0, import_i18n28.__)("No elements", "elementor") };
2571
2274
  }
2572
2275
  const violations = [];
2573
2276
  walkElements(ctx.elements.tree, (node) => {
@@ -2576,7 +2279,7 @@ var audit10 = {
2576
2279
  auditId: audit10.id,
2577
2280
  elementId: node.id,
2578
2281
  targetHint: "element-settings",
2579
- label: (0, import_i18n34.__)("Element is hidden on all devices.", "elementor")
2282
+ label: (0, import_i18n28.__)("Element is hidden on all devices.", "elementor")
2580
2283
  });
2581
2284
  }
2582
2285
  });
@@ -2585,7 +2288,22 @@ var audit10 = {
2585
2288
  };
2586
2289
 
2587
2290
  // src/audits/images-alt-text.ts
2588
- var import_i18n35 = require("@wordpress/i18n");
2291
+ var import_i18n29 = require("@wordpress/i18n");
2292
+
2293
+ // src/utils/alt-text-image-sources.ts
2294
+ function walkAltTextImageSources(tree, visit) {
2295
+ walkImageLikeSources(tree, visit);
2296
+ walkAtomicImageSources(tree, visit, ATOMIC_CONTENT_IMAGE_PROP_KEYS);
2297
+ }
2298
+ function hasAltTextImages(tree) {
2299
+ let found = false;
2300
+ walkAltTextImageSources(tree, ({ media }) => {
2301
+ if (media.id || media.url) {
2302
+ found = true;
2303
+ }
2304
+ });
2305
+ return found;
2306
+ }
2589
2307
 
2590
2308
  // src/utils/image-alt.ts
2591
2309
  function hasMeaningfulAlt(media, pageContext) {
@@ -2606,20 +2324,21 @@ function isImageSourcePresent(media) {
2606
2324
  // src/audits/images-alt-text.ts
2607
2325
  var audit11 = {
2608
2326
  id: "audits/images-alt-text",
2609
- title: (0, import_i18n35.__)("Images alt text", "elementor"),
2610
- description: (0, import_i18n35.__)("Every image needs a meaningful alt attribute for screen readers and image-search.", "elementor"),
2611
- fixHint: (0, import_i18n35.__)("Open the image's settings and add an Alt Text describing the image.", "elementor"),
2327
+ title: (0, import_i18n29.__)("Images alt text", "elementor"),
2328
+ description: (0, import_i18n29.__)("Every image needs a meaningful alt attribute for screen readers and image-search.", "elementor"),
2329
+ fixHint: (0, import_i18n29.__)("Open the image's settings and add an Alt Text describing the image.", "elementor"),
2612
2330
  categories: ["seo", "accessibility"],
2613
2331
  severity: "error",
2614
2332
  weight: 10,
2615
2333
  evaluate: (ctx) => {
2616
- if (!hasPageImages(ctx.elements.tree)) {
2617
- return { status: "skipped", reason: (0, import_i18n35.__)("No images", "elementor") };
2334
+ if (!hasAltTextImages(ctx.elements.tree)) {
2335
+ return { status: "skipped", reason: (0, import_i18n29.__)("No images", "elementor") };
2618
2336
  }
2337
+ const isReady = ctx.pageContext.ally_plugin_active;
2619
2338
  const violations = [];
2620
2339
  const failedWidgetIds = /* @__PURE__ */ new Set();
2621
2340
  let missingAltImageCount = 0;
2622
- walkImageLikeSources(ctx.elements.tree, ({ node, media }) => {
2341
+ walkAltTextImageSources(ctx.elements.tree, ({ node, media }) => {
2623
2342
  if (!isImageSourcePresent(media) || hasMeaningfulAlt(media, ctx.pageContext)) {
2624
2343
  return;
2625
2344
  }
@@ -2630,8 +2349,9 @@ var audit11 = {
2630
2349
  auditId: audit11.id,
2631
2350
  elementId: node.id,
2632
2351
  targetHint: "element-settings",
2633
- label: (0, import_i18n35.__)("Image is missing alt text.", "elementor"),
2634
- externalUrl: ctx.pageContext.ally_plugin_url
2352
+ label: (0, import_i18n29.__)("Image is missing alt text.", "elementor"),
2353
+ externalUrl: isReady ? ctx.pageContext.ally_scan_url : ctx.pageContext.ally_plugin_url,
2354
+ ctaLabel: (0, import_i18n29.__)("Fix with Ally", "elementor")
2635
2355
  });
2636
2356
  }
2637
2357
  });
@@ -2640,17 +2360,18 @@ var audit11 = {
2640
2360
  };
2641
2361
 
2642
2362
  // src/audits/images-inefficient-format.ts
2643
- var import_i18n36 = require("@wordpress/i18n");
2363
+ var import_i18n30 = require("@wordpress/i18n");
2644
2364
  var LEGACY_RASTER_MIME_TYPES = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/gif", "image/bmp", "image/tiff"]);
2645
2365
  var audit12 = {
2646
2366
  id: "audits/images-inefficient-format",
2647
- title: (0, import_i18n36.__)("Inefficient image format", "elementor"),
2648
- description: (0, import_i18n36.__)("Image isn't served in a modern, efficient format like WebP or AVIF.", "elementor"),
2649
- fixHint: (0, import_i18n36.__)("Convert the image to a modern format like WebP or AVIF, or enable image optimization.", "elementor"),
2367
+ title: (0, import_i18n30.__)("Inefficient image format", "elementor"),
2368
+ description: (0, import_i18n30.__)("Image isn't served in a modern, efficient format like WebP or AVIF.", "elementor"),
2369
+ fixHint: (0, import_i18n30.__)("Convert the image to a modern format like WebP or AVIF, or enable image optimization.", "elementor"),
2650
2370
  categories: ["performance"],
2651
2371
  severity: "info",
2652
2372
  weight: 1,
2653
2373
  evaluate: (ctx) => {
2374
+ const isReady = ctx.pageContext.image_optimization_plugin_active;
2654
2375
  const offendingElementIds = /* @__PURE__ */ new Set();
2655
2376
  let hasAnyImage = false;
2656
2377
  let inefficientImageFormatCount = 0;
@@ -2669,37 +2390,36 @@ var audit12 = {
2669
2390
  inefficientImageFormatCount++;
2670
2391
  offendingElementIds.add(node.id);
2671
2392
  };
2672
- walkImageLikeSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2673
- walkBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2674
- walkAtomicImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2675
- walkAtomicBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2393
+ walkAllImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2676
2394
  if (!hasAnyImage) {
2677
- return { status: "skipped", reason: (0, import_i18n36.__)("No images", "elementor") };
2395
+ return { status: "skipped", reason: (0, import_i18n30.__)("No images", "elementor") };
2678
2396
  }
2679
2397
  const violations = Array.from(offendingElementIds).map((elementId) => ({
2680
2398
  auditId: audit12.id,
2681
2399
  elementId,
2682
2400
  targetHint: "element-settings",
2683
- label: (0, import_i18n36.__)("Image isn't served in a modern format like WebP or AVIF.", "elementor"),
2684
- externalUrl: ctx.pageContext.image_optimization_plugin_url
2401
+ label: (0, import_i18n30.__)("Image isn't served in a modern format like WebP or AVIF.", "elementor"),
2402
+ externalUrl: isReady ? ctx.pageContext.image_optimization_settings_url : ctx.pageContext.image_optimization_plugin_url,
2403
+ ctaLabel: (0, import_i18n30.__)("Optimize all", "elementor")
2685
2404
  }));
2686
2405
  return violations.length === 0 ? { status: "pass" } : { status: "fail", violations, metadata: { inefficientImageFormatCount } };
2687
2406
  }
2688
2407
  };
2689
2408
 
2690
2409
  // src/audits/images-too-large.ts
2691
- var import_i18n37 = require("@wordpress/i18n");
2410
+ var import_i18n31 = require("@wordpress/i18n");
2692
2411
  var SIZE_THRESHOLD_BYTES = 500 * 1024;
2693
2412
  var BYTES_PER_KB = 1024;
2694
2413
  var audit13 = {
2695
2414
  id: "audits/images-too-large",
2696
- title: (0, import_i18n37.__)("Oversized images", "elementor"),
2697
- description: (0, import_i18n37.__)("Large image files slow down the page.", "elementor"),
2698
- fixHint: (0, import_i18n37.__)("Replace the image with a smaller version or enable image optimization.", "elementor"),
2415
+ title: (0, import_i18n31.__)("Oversized images", "elementor"),
2416
+ description: (0, import_i18n31.__)("Large image files slow down the page.", "elementor"),
2417
+ fixHint: (0, import_i18n31.__)("Replace the image with a smaller version or enable image optimization.", "elementor"),
2699
2418
  categories: ["performance"],
2700
2419
  severity: "warning",
2701
2420
  weight: 7,
2702
2421
  evaluate: (ctx) => {
2422
+ const isReady = ctx.pageContext.image_optimization_plugin_active;
2703
2423
  const widgetMaxKb = /* @__PURE__ */ new Map();
2704
2424
  let oversizedImageCount = 0;
2705
2425
  let hasAnyImage = false;
@@ -2720,44 +2440,42 @@ var audit13 = {
2720
2440
  const currentMax = widgetMaxKb.get(node.id) ?? 0;
2721
2441
  widgetMaxKb.set(node.id, Math.max(currentMax, kb));
2722
2442
  };
2723
- walkImageLikeSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2724
- walkBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2725
- walkAtomicImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2726
- walkAtomicBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2443
+ walkAllImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2727
2444
  if (!hasAnyImage) {
2728
- return { status: "skipped", reason: (0, import_i18n37.__)("No images", "elementor") };
2445
+ return { status: "skipped", reason: (0, import_i18n31.__)("No images", "elementor") };
2729
2446
  }
2730
2447
  const violations = Array.from(widgetMaxKb.entries()).map(([elementId, kb]) => ({
2731
2448
  auditId: audit13.id,
2732
2449
  elementId,
2733
2450
  targetHint: "element-settings",
2734
- label: (0, import_i18n37.sprintf)(
2451
+ label: (0, import_i18n31.sprintf)(
2735
2452
  /* translators: %d is the image file size in kilobytes. */
2736
- (0, import_i18n37.__)("Image is %d KB (over 500 KB).", "elementor"),
2453
+ (0, import_i18n31.__)("Image is %d KB (over 500 KB).", "elementor"),
2737
2454
  kb
2738
2455
  ),
2739
- externalUrl: ctx.pageContext.image_optimization_plugin_url
2456
+ externalUrl: isReady ? ctx.pageContext.image_optimization_settings_url : ctx.pageContext.image_optimization_plugin_url,
2457
+ ctaLabel: (0, import_i18n31.__)("Optimize all", "elementor")
2740
2458
  }));
2741
2459
  return violations.length === 0 ? { status: "pass" } : { status: "fail", violations, metadata: { oversizedImageCount } };
2742
2460
  }
2743
2461
  };
2744
2462
 
2745
2463
  // src/audits/nested-boxed-containers.ts
2746
- var import_i18n38 = require("@wordpress/i18n");
2464
+ var import_i18n32 = require("@wordpress/i18n");
2747
2465
  function isBoxed(settings) {
2748
2466
  return settings.content_width === "boxed";
2749
2467
  }
2750
2468
  var audit14 = {
2751
2469
  id: "audits/nested-boxed-containers",
2752
- title: (0, import_i18n38.__)("Boxed container nested inside a boxed parent", "elementor"),
2753
- description: (0, import_i18n38.__)("An inner container does not need to be boxed when its parent already is.", "elementor"),
2754
- fixHint: (0, import_i18n38.__)("Change the inner container's content width to Full Width.", "elementor"),
2470
+ title: (0, import_i18n32.__)("Boxed container nested inside a boxed parent", "elementor"),
2471
+ description: (0, import_i18n32.__)("An inner container does not need to be boxed when its parent already is.", "elementor"),
2472
+ fixHint: (0, import_i18n32.__)("Change the inner container's content width to Full Width.", "elementor"),
2755
2473
  categories: ["best-practices", "performance"],
2756
2474
  severity: "info",
2757
2475
  weight: 5,
2758
2476
  evaluate: (ctx) => {
2759
2477
  if (ctx.elements.tree.length === 0) {
2760
- return { status: "skipped", reason: (0, import_i18n38.__)("No elements", "elementor") };
2478
+ return { status: "skipped", reason: (0, import_i18n32.__)("No elements", "elementor") };
2761
2479
  }
2762
2480
  const violations = [];
2763
2481
  walkElements(ctx.elements.tree, (node, parents) => {
@@ -2770,7 +2488,12 @@ var audit14 = {
2770
2488
  auditId: audit14.id,
2771
2489
  elementId: node.id,
2772
2490
  targetHint: "element-settings",
2773
- label: (0, import_i18n38.__)("Nested boxed container.", "elementor")
2491
+ label: (0, import_i18n32.__)("Nested boxed container.", "elementor"),
2492
+ angieFix: true,
2493
+ angiePrompt: (0, import_i18n32.__)(
2494
+ "Find containers that have nested containers. Containers can be deep-nested. Update the nested container styling so that when a parent container already uses a boxed setting, any inner container inside it is set to Full Width. Keep the first parent container boxed, remove all boxed styling from the nested inner containers, and preserve the inner container\u2019s content, spacing, alignment, and hierarchy.",
2495
+ "elementor"
2496
+ )
2774
2497
  });
2775
2498
  }
2776
2499
  });
@@ -2779,12 +2502,12 @@ var audit14 = {
2779
2502
  };
2780
2503
 
2781
2504
  // src/audits/page-excerpt.ts
2782
- var import_i18n39 = require("@wordpress/i18n");
2505
+ var import_i18n33 = require("@wordpress/i18n");
2783
2506
  var audit15 = {
2784
2507
  id: "audits/page-excerpt",
2785
- title: (0, import_i18n39.__)("Page excerpt", "elementor"),
2786
- description: (0, import_i18n39.__)("A descriptive excerpt helps search engines and previews summarize the page.", "elementor"),
2787
- fixHint: (0, import_i18n39.__)("Open Page Settings and write a short excerpt.", "elementor"),
2508
+ title: (0, import_i18n33.__)("Page excerpt", "elementor"),
2509
+ description: (0, import_i18n33.__)("A descriptive excerpt helps search engines and previews summarize the page.", "elementor"),
2510
+ fixHint: (0, import_i18n33.__)("Open Page Settings and write a short excerpt.", "elementor"),
2788
2511
  categories: ["seo"],
2789
2512
  severity: "warning",
2790
2513
  weight: 5,
@@ -2797,7 +2520,7 @@ var audit15 = {
2797
2520
  violations: [
2798
2521
  {
2799
2522
  auditId: audit15.id,
2800
- label: (0, import_i18n39.__)("Page has no excerpt.", "elementor"),
2523
+ label: (0, import_i18n33.__)("Page has no excerpt.", "elementor"),
2801
2524
  targetHint: "page-settings",
2802
2525
  angieFix: true
2803
2526
  }
@@ -2807,12 +2530,12 @@ var audit15 = {
2807
2530
  };
2808
2531
 
2809
2532
  // src/audits/page-featured-image.ts
2810
- var import_i18n40 = require("@wordpress/i18n");
2533
+ var import_i18n34 = require("@wordpress/i18n");
2811
2534
  var audit16 = {
2812
2535
  id: "audits/page-featured-image",
2813
- title: (0, import_i18n40.__)("Page featured image", "elementor"),
2814
- description: (0, import_i18n40.__)("Featured images are used by social shares and many themes for hero visuals.", "elementor"),
2815
- fixHint: (0, import_i18n40.__)("Open Page Settings and set a featured image.", "elementor"),
2536
+ title: (0, import_i18n34.__)("Page featured image", "elementor"),
2537
+ description: (0, import_i18n34.__)("Featured images are used by social shares and many themes for hero visuals.", "elementor"),
2538
+ fixHint: (0, import_i18n34.__)("Open Page Settings and set a featured image.", "elementor"),
2816
2539
  categories: ["seo"],
2817
2540
  severity: "info",
2818
2541
  weight: 5,
@@ -2825,7 +2548,7 @@ var audit16 = {
2825
2548
  violations: [
2826
2549
  {
2827
2550
  auditId: audit16.id,
2828
- label: (0, import_i18n40.__)("No featured image set.", "elementor"),
2551
+ label: (0, import_i18n34.__)("No featured image set.", "elementor"),
2829
2552
  targetHint: "page-settings"
2830
2553
  }
2831
2554
  ]
@@ -2834,13 +2557,13 @@ var audit16 = {
2834
2557
  };
2835
2558
 
2836
2559
  // src/audits/page-title.ts
2837
- var import_i18n41 = require("@wordpress/i18n");
2560
+ var import_i18n35 = require("@wordpress/i18n");
2838
2561
  var MAX_TITLE_LENGTH = 60;
2839
2562
  var audit17 = {
2840
2563
  id: "audits/page-title",
2841
- title: (0, import_i18n41.__)("Page title", "elementor"),
2842
- description: (0, import_i18n41.__)("Pages need a clear title for SEO and screen-reader navigation.", "elementor"),
2843
- fixHint: (0, import_i18n41.__)("Open Page Settings and add a title.", "elementor"),
2564
+ title: (0, import_i18n35.__)("Page title", "elementor"),
2565
+ description: (0, import_i18n35.__)("Pages need a clear title for SEO and screen-reader navigation.", "elementor"),
2566
+ fixHint: (0, import_i18n35.__)("Open Page Settings and add a title.", "elementor"),
2844
2567
  categories: ["seo"],
2845
2568
  severity: "error",
2846
2569
  weight: 10,
@@ -2852,10 +2575,10 @@ var audit17 = {
2852
2575
  violations: [
2853
2576
  {
2854
2577
  auditId: audit17.id,
2855
- label: (0, import_i18n41.__)("Page has no title.", "elementor"),
2578
+ label: (0, import_i18n35.__)("Page has no title.", "elementor"),
2856
2579
  targetHint: "page-settings",
2857
2580
  angieFix: true,
2858
- angiePrompt: (0, import_i18n41.__)(
2581
+ angiePrompt: (0, import_i18n35.__)(
2859
2582
  "Based on this page's contents, generate a title for this page.",
2860
2583
  "elementor"
2861
2584
  )
@@ -2869,7 +2592,7 @@ var audit17 = {
2869
2592
  violations: [
2870
2593
  {
2871
2594
  auditId: audit17.id,
2872
- label: (0, import_i18n41.__)("Page title is too long.", "elementor"),
2595
+ label: (0, import_i18n35.__)("Page title is too long.", "elementor"),
2873
2596
  targetHint: "page-settings",
2874
2597
  angieFix: true
2875
2598
  }
@@ -2881,7 +2604,7 @@ var audit17 = {
2881
2604
  };
2882
2605
 
2883
2606
  // src/audits/prefer-global-colors.ts
2884
- var import_i18n42 = require("@wordpress/i18n");
2607
+ var import_i18n36 = require("@wordpress/i18n");
2885
2608
 
2886
2609
  // src/utils/collect-hardcoded-colors.ts
2887
2610
  var RESERVED_SETTING_KEYS = /* @__PURE__ */ new Set(["__globals__", "__dynamic__"]);
@@ -2949,18 +2672,18 @@ function findMatchingGlobalByValue(value, globals) {
2949
2672
  // src/audits/prefer-global-colors.ts
2950
2673
  var audit18 = {
2951
2674
  id: "audits/prefer-global-colors",
2952
- title: (0, import_i18n42.__)("Prefer global colors", "elementor"),
2953
- description: (0, import_i18n42.__)("Global colors make the design consistent and easy to update site-wide.", "elementor"),
2954
- fixHint: (0, import_i18n42.__)("Replace the hard-coded value with the matching global color.", "elementor"),
2675
+ title: (0, import_i18n36.__)("Prefer global colors", "elementor"),
2676
+ description: (0, import_i18n36.__)("Global colors make the design consistent and easy to update site-wide.", "elementor"),
2677
+ fixHint: (0, import_i18n36.__)("Replace the hard-coded value with the matching global color.", "elementor"),
2955
2678
  categories: ["best-practices"],
2956
2679
  severity: "info",
2957
2680
  weight: 3,
2958
2681
  evaluate: (ctx) => {
2959
2682
  if (ctx.elements.tree.length === 0) {
2960
- return { status: "skipped", reason: (0, import_i18n42.__)("No elements", "elementor") };
2683
+ return { status: "skipped", reason: (0, import_i18n36.__)("No elements", "elementor") };
2961
2684
  }
2962
2685
  if (ctx.kit.globals.colors.length === 0) {
2963
- return { status: "skipped", reason: (0, import_i18n42.__)("No global colors", "elementor") };
2686
+ return { status: "skipped", reason: (0, import_i18n36.__)("No global colors", "elementor") };
2964
2687
  }
2965
2688
  const violations = [];
2966
2689
  walkElements(ctx.elements.tree, (node) => {
@@ -2974,9 +2697,9 @@ var audit18 = {
2974
2697
  elementId: node.id,
2975
2698
  targetHint: "element-settings",
2976
2699
  angieFix: true,
2977
- label: (0, import_i18n42.sprintf)(
2700
+ label: (0, import_i18n36.sprintf)(
2978
2701
  /* translators: 1: hard-coded color value, 2: global color title. */
2979
- (0, import_i18n42.__)('replace "%1$s" with "%2$s" global color', "elementor"),
2702
+ (0, import_i18n36.__)('replace "%1$s" with "%2$s" global color', "elementor"),
2980
2703
  value,
2981
2704
  global.title
2982
2705
  )
@@ -2988,7 +2711,7 @@ var audit18 = {
2988
2711
  };
2989
2712
 
2990
2713
  // src/audits/prefer-global-fonts.ts
2991
- var import_i18n43 = require("@wordpress/i18n");
2714
+ var import_i18n37 = require("@wordpress/i18n");
2992
2715
 
2993
2716
  // src/utils/collect-hardcoded-fonts.ts
2994
2717
  var RESERVED_SETTING_KEYS2 = /* @__PURE__ */ new Set(["__globals__", "__dynamic__"]);
@@ -3048,18 +2771,18 @@ function collectHardcodedFonts(settings, pathPrefix = "") {
3048
2771
  // src/audits/prefer-global-fonts.ts
3049
2772
  var audit19 = {
3050
2773
  id: "audits/prefer-global-fonts",
3051
- title: (0, import_i18n43.__)("Prefer global fonts", "elementor"),
3052
- description: (0, import_i18n43.__)("Global fonts make the design consistent and easy to update site-wide.", "elementor"),
3053
- fixHint: (0, import_i18n43.__)("Replace the hard-coded value with the matching global font.", "elementor"),
2774
+ title: (0, import_i18n37.__)("Prefer global fonts", "elementor"),
2775
+ description: (0, import_i18n37.__)("Global fonts make the design consistent and easy to update site-wide.", "elementor"),
2776
+ fixHint: (0, import_i18n37.__)("Replace the hard-coded value with the matching global font.", "elementor"),
3054
2777
  categories: ["best-practices", "performance"],
3055
2778
  severity: "info",
3056
2779
  weight: 3,
3057
2780
  evaluate: (ctx) => {
3058
2781
  if (ctx.elements.tree.length === 0) {
3059
- return { status: "skipped", reason: (0, import_i18n43.__)("No elements", "elementor") };
2782
+ return { status: "skipped", reason: (0, import_i18n37.__)("No elements", "elementor") };
3060
2783
  }
3061
2784
  if (ctx.kit.globals.fonts.length === 0) {
3062
- return { status: "skipped", reason: (0, import_i18n43.__)("No global fonts", "elementor") };
2785
+ return { status: "skipped", reason: (0, import_i18n37.__)("No global fonts", "elementor") };
3063
2786
  }
3064
2787
  const violations = [];
3065
2788
  walkElements(ctx.elements.tree, (node) => {
@@ -3072,9 +2795,9 @@ var audit19 = {
3072
2795
  auditId: audit19.id,
3073
2796
  elementId: node.id,
3074
2797
  targetHint: "element-settings",
3075
- label: (0, import_i18n43.sprintf)(
2798
+ label: (0, import_i18n37.sprintf)(
3076
2799
  /* translators: %s: global font title. */
3077
- (0, import_i18n43.__)('replace hardcoded typography with "%s" global font', "elementor"),
2800
+ (0, import_i18n37.__)('replace hardcoded typography with "%s" global font', "elementor"),
3078
2801
  global.title
3079
2802
  )
3080
2803
  });
@@ -3085,15 +2808,15 @@ var audit19 = {
3085
2808
  };
3086
2809
 
3087
2810
  // src/audits/privacy-policy.ts
3088
- var import_i18n44 = require("@wordpress/i18n");
2811
+ var import_i18n38 = require("@wordpress/i18n");
3089
2812
  var audit20 = {
3090
2813
  id: "audits/privacy-policy",
3091
- title: (0, import_i18n44.__)("Privacy policy", "elementor"),
3092
- description: (0, import_i18n44.__)(
2814
+ title: (0, import_i18n38.__)("Privacy policy", "elementor"),
2815
+ description: (0, import_i18n38.__)(
3093
2816
  "A privacy policy page is required by privacy regulations such as GDPR (EU) and CCPA (US).",
3094
2817
  "elementor"
3095
2818
  ),
3096
- fixHint: (0, import_i18n44.__)(
2819
+ fixHint: (0, import_i18n38.__)(
3097
2820
  "Go to Settings > Privacy in the WordPress admin and assign a published privacy policy page.",
3098
2821
  "elementor"
3099
2822
  ),
@@ -3109,9 +2832,9 @@ var audit20 = {
3109
2832
  violations: [
3110
2833
  {
3111
2834
  auditId: audit20.id,
3112
- label: (0, import_i18n44.__)("No privacy policy page is set.", "elementor"),
2835
+ label: (0, import_i18n38.__)("No privacy policy page is set.", "elementor"),
3113
2836
  externalUrl: ctx.pageContext.privacy_settings_url,
3114
- ctaLabel: (0, import_i18n44.__)("Create", "elementor")
2837
+ ctaLabel: (0, import_i18n38.__)("Create", "elementor")
3115
2838
  }
3116
2839
  ]
3117
2840
  };
@@ -3119,15 +2842,15 @@ var audit20 = {
3119
2842
  };
3120
2843
 
3121
2844
  // src/audits/robots-noindex.ts
3122
- var import_i18n45 = require("@wordpress/i18n");
2845
+ var import_i18n39 = require("@wordpress/i18n");
3123
2846
  var audit21 = {
3124
2847
  id: "audits/robots-noindex",
3125
- title: (0, import_i18n45.__)("Search engine visibility", "elementor"),
3126
- description: (0, import_i18n45.__)(
2848
+ title: (0, import_i18n39.__)("Search engine visibility", "elementor"),
2849
+ description: (0, import_i18n39.__)(
3127
2850
  "When search engine visibility is checked in WordPress Settings -> Reading -> Search Engine Visibility, search engines are discouraged from indexing the website.",
3128
2851
  "elementor"
3129
2852
  ),
3130
- fixHint: (0, import_i18n45.__)(
2853
+ fixHint: (0, import_i18n39.__)(
3131
2854
  'Go to Settings \u2192 Reading in the WordPress admin and uncheck "Discourage search engines from indexing this site" when the website should be indexed.',
3132
2855
  "elementor"
3133
2856
  ),
@@ -3143,7 +2866,7 @@ var audit21 = {
3143
2866
  violations: [
3144
2867
  {
3145
2868
  auditId: audit21.id,
3146
- label: (0, import_i18n45.__)("Search engines are discouraged from indexing this website.", "elementor"),
2869
+ label: (0, import_i18n39.__)("Search engines are discouraged from indexing this website.", "elementor"),
3147
2870
  externalUrl: ctx.pageContext.reading_settings_url
3148
2871
  }
3149
2872
  ]
@@ -3152,15 +2875,15 @@ var audit21 = {
3152
2875
  };
3153
2876
 
3154
2877
  // src/audits/scan-for-cookies.ts
3155
- var import_i18n46 = require("@wordpress/i18n");
2878
+ var import_i18n40 = require("@wordpress/i18n");
3156
2879
  var audit22 = {
3157
2880
  id: "audits/scan-for-cookies",
3158
- title: (0, import_i18n46.__)("Scan for cookies", "elementor"),
3159
- description: (0, import_i18n46.__)(
2881
+ title: (0, import_i18n40.__)("Scan for cookies", "elementor"),
2882
+ description: (0, import_i18n40.__)(
3160
2883
  "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.",
3161
2884
  "elementor"
3162
2885
  ),
3163
- fixHint: (0, import_i18n46.__)("Run a Cookiez scan on this page to see which cookies need disclosure.", "elementor"),
2886
+ fixHint: (0, import_i18n40.__)("Run a Cookiez scan on this page to see which cookies need disclosure.", "elementor"),
3164
2887
  categories: ["compliance"],
3165
2888
  severity: "info",
3166
2889
  weight: 0,
@@ -3171,7 +2894,7 @@ var audit22 = {
3171
2894
  violations: [
3172
2895
  {
3173
2896
  auditId: audit22.id,
3174
- label: (0, import_i18n46.__)("This page has not been scanned for cookies yet.", "elementor"),
2897
+ label: (0, import_i18n40.__)("This page has not been scanned for cookies yet.", "elementor"),
3175
2898
  externalUrl: isReady ? ctx.pageContext.cookiez_scan_url : ctx.pageContext.cookiez_plugin_action_url
3176
2899
  }
3177
2900
  ]
@@ -3180,21 +2903,21 @@ var audit22 = {
3180
2903
  };
3181
2904
 
3182
2905
  // src/audits/sections-and-columns.ts
3183
- var import_i18n47 = require("@wordpress/i18n");
2906
+ var import_i18n41 = require("@wordpress/i18n");
3184
2907
  var audit23 = {
3185
2908
  id: "audits/sections-and-columns",
3186
- title: (0, import_i18n47.__)("Sections and columns", "elementor"),
3187
- description: (0, import_i18n47.__)(
2909
+ title: (0, import_i18n41.__)("Sections and columns", "elementor"),
2910
+ description: (0, import_i18n41.__)(
3188
2911
  "Sections and columns are legacy elements. Containers render fewer DOM nodes and are more flexible.",
3189
2912
  "elementor"
3190
2913
  ),
3191
- fixHint: (0, import_i18n47.__)("Use the Container Converter to replace each section/column with a container.", "elementor"),
2914
+ fixHint: (0, import_i18n41.__)("Use the Container Converter to replace each section/column with a container.", "elementor"),
3192
2915
  categories: ["best-practices", "performance"],
3193
2916
  severity: "warning",
3194
2917
  weight: 7,
3195
2918
  evaluate: (ctx) => {
3196
2919
  if (ctx.elements.tree.length === 0) {
3197
- return { status: "skipped", reason: (0, import_i18n47.__)("No elements", "elementor") };
2920
+ return { status: "skipped", reason: (0, import_i18n41.__)("No elements", "elementor") };
3198
2921
  }
3199
2922
  const violations = [];
3200
2923
  walkElements(ctx.elements.tree, (node) => {
@@ -3202,7 +2925,7 @@ var audit23 = {
3202
2925
  violations.push({
3203
2926
  auditId: audit23.id,
3204
2927
  elementId: node.id,
3205
- label: node.elType === "section" ? (0, import_i18n47.__)("Section element", "elementor") : (0, import_i18n47.__)("Column element", "elementor")
2928
+ label: node.elType === "section" ? (0, import_i18n41.__)("Section element", "elementor") : (0, import_i18n41.__)("Column element", "elementor")
3206
2929
  });
3207
2930
  }
3208
2931
  });
@@ -3211,15 +2934,15 @@ var audit23 = {
3211
2934
  };
3212
2935
 
3213
2936
  // src/audits/site-identity.ts
3214
- var import_i18n48 = require("@wordpress/i18n");
2937
+ var import_i18n42 = require("@wordpress/i18n");
3215
2938
  var audit24 = {
3216
2939
  id: "audits/site-identity",
3217
- title: (0, import_i18n48.__)("Site identity", "elementor"),
3218
- description: (0, import_i18n48.__)(
2940
+ title: (0, import_i18n42.__)("Site identity", "elementor"),
2941
+ description: (0, import_i18n42.__)(
3219
2942
  "Site name, description, logo, and favicon establish your brand and appear in search results and browser tabs.",
3220
2943
  "elementor"
3221
2944
  ),
3222
- fixHint: (0, import_i18n48.__)("Open Site Settings \u2192 Site Identity and complete all the missing fields.", "elementor"),
2945
+ fixHint: (0, import_i18n42.__)("Open Site Settings \u2192 Site Identity and complete all the missing fields.", "elementor"),
3223
2946
  categories: ["best-practices", "seo"],
3224
2947
  severity: "info",
3225
2948
  weight: 7,
@@ -3229,14 +2952,14 @@ var audit24 = {
3229
2952
  if (!identity.site_name_set) {
3230
2953
  violations.push({
3231
2954
  auditId: audit24.id,
3232
- label: (0, import_i18n48.__)("Site name is missing or still uses the default.", "elementor"),
2955
+ label: (0, import_i18n42.__)("Site name is missing or still uses the default.", "elementor"),
3233
2956
  targetHint: "site-identity-settings"
3234
2957
  });
3235
2958
  }
3236
2959
  if (!identity.site_description_set) {
3237
2960
  violations.push({
3238
2961
  auditId: audit24.id,
3239
- label: (0, import_i18n48.__)("Site description is missing or still uses the default.", "elementor"),
2962
+ label: (0, import_i18n42.__)("Site description is missing or still uses the default.", "elementor"),
3240
2963
  targetHint: "site-identity-settings",
3241
2964
  angieFix: true
3242
2965
  });
@@ -3244,14 +2967,14 @@ var audit24 = {
3244
2967
  if (!identity.site_logo_set) {
3245
2968
  violations.push({
3246
2969
  auditId: audit24.id,
3247
- label: (0, import_i18n48.__)("Site logo is not set.", "elementor"),
2970
+ label: (0, import_i18n42.__)("Site logo is not set.", "elementor"),
3248
2971
  targetHint: "site-identity-settings"
3249
2972
  });
3250
2973
  }
3251
2974
  if (!identity.site_favicon_set) {
3252
2975
  violations.push({
3253
2976
  auditId: audit24.id,
3254
- label: (0, import_i18n48.__)("Site favicon is not set.", "elementor"),
2977
+ label: (0, import_i18n42.__)("Site favicon is not set.", "elementor"),
3255
2978
  targetHint: "site-identity-settings"
3256
2979
  });
3257
2980
  }
@@ -3265,50 +2988,6 @@ var audit24 = {
3265
2988
  }
3266
2989
  };
3267
2990
 
3268
- // src/audits/too-many-widgets.ts
3269
- var import_editor_elements9 = require("@elementor/editor-elements");
3270
- var import_i18n49 = require("@wordpress/i18n");
3271
- var WIDGET_COUNT_THRESHOLD = 100;
3272
- function isCountableWidget(node, widgetsCache) {
3273
- if (node.elType === "widget") {
3274
- return true;
3275
- }
3276
- return !!widgetsCache?.[node.elType]?.meta?.is_compound;
3277
- }
3278
- var audit25 = {
3279
- id: "audits/too-many-widgets",
3280
- title: (0, import_i18n49.__)("Too many widgets", "elementor"),
3281
- description: (0, import_i18n49.__)("Excessive DOM size caused by too many widgets degrades rendering performance.", "elementor"),
3282
- fixHint: (0, import_i18n49.__)("Reduce the number of widgets on the page by removing or combining elements.", "elementor"),
3283
- categories: ["best-practices", "performance"],
3284
- severity: "warning",
3285
- weight: 5,
3286
- evaluate: (ctx) => {
3287
- if (ctx.elements.tree.length === 0) {
3288
- return { status: "skipped", reason: (0, import_i18n49.__)("No elements", "elementor") };
3289
- }
3290
- const widgetsCache = (0, import_editor_elements9.getWidgetsCache)();
3291
- let widgetCount = 0;
3292
- walkElements(ctx.elements.tree, (node) => {
3293
- if (isCountableWidget(node, widgetsCache)) {
3294
- widgetCount++;
3295
- }
3296
- });
3297
- if (widgetCount <= WIDGET_COUNT_THRESHOLD) {
3298
- return { status: "pass" };
3299
- }
3300
- return {
3301
- status: "fail",
3302
- violations: [
3303
- {
3304
- auditId: audit25.id,
3305
- label: (0, import_i18n49.__)("Page has too many widgets.", "elementor")
3306
- }
3307
- ]
3308
- };
3309
- }
3310
- };
3311
-
3312
2991
  // src/register-audits.ts
3313
2992
  var AUDITS = [
3314
2993
  audit17,
@@ -3316,7 +2995,6 @@ var AUDITS = [
3316
2995
  audit16,
3317
2996
  audit10,
3318
2997
  audit7,
3319
- audit25,
3320
2998
  audit23,
3321
2999
  audit5,
3322
3000
  audit14,
@@ -3338,17 +3016,30 @@ var AUDITS = [
3338
3016
  audit8
3339
3017
  ];
3340
3018
  function registerAllAudits() {
3341
- for (const audit26 of AUDITS) {
3342
- registerAudit(audit26);
3019
+ for (const audit25 of AUDITS) {
3020
+ registerAudit(audit25);
3343
3021
  }
3344
3022
  }
3345
3023
 
3024
+ // src/sync-report-staleness.ts
3025
+ var import_editor_v1_adapters3 = require("@elementor/editor-v1-adapters");
3026
+ var import_store6 = require("@elementor/store");
3027
+ function syncReportStaleness() {
3028
+ (0, import_editor_v1_adapters3.__privateListenTo)((0, import_editor_v1_adapters3.commandEndEvent)("document/save/set-is-modified"), (e) => {
3029
+ const event = e;
3030
+ if (event.args?.status) {
3031
+ (0, import_store6.__dispatch)(slice.actions.reportStale());
3032
+ }
3033
+ });
3034
+ }
3035
+
3346
3036
  // src/init.ts
3347
3037
  function init() {
3348
- (0, import_store4.__registerSlice)(slice);
3038
+ (0, import_store7.__registerSlice)(slice);
3349
3039
  registerAllAudits();
3350
- (0, import_editor_floating_panels3.registerFloatingPanel)(auditPanel.panel);
3040
+ (0, import_editor_floating_panels6.registerFloatingPanel)(auditPanel.panel);
3351
3041
  registerAppBarAuditsToggle();
3042
+ syncReportStaleness();
3352
3043
  }
3353
3044
  // Annotate the CommonJS export names for ESM import in node:
3354
3045
  0 && (module.exports = {