@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.mjs CHANGED
@@ -6,26 +6,131 @@ import { __registerSlice as registerSlice } from "@elementor/store";
6
6
  import { utilitiesMenu } from "@elementor/editor-app-bar";
7
7
 
8
8
  // src/hooks/use-audit-toggle-props.ts
9
- import { ShieldCheckIcon as ShieldCheckIcon3 } from "@elementor/icons";
10
- import { __ as __24 } from "@wordpress/i18n";
9
+ import { __ as __18 } from "@wordpress/i18n";
10
+
11
+ // src/components/audit-toggle-icon.tsx
12
+ import * as React from "react";
13
+ import { ShieldCheckIcon } from "@elementor/icons";
14
+ import { __useSelector as useSelector } from "@elementor/store";
15
+ import { Badge } from "@elementor/ui";
16
+
17
+ // src/store/slice.ts
18
+ import { __createSlice } from "@elementor/store";
19
+ var initialState = { status: "idle", report: null, error: null, isStale: false };
20
+ var slice = __createSlice({
21
+ name: "audits",
22
+ initialState,
23
+ reducers: {
24
+ runStarted(state) {
25
+ state.status = "loading";
26
+ state.error = null;
27
+ },
28
+ runSucceeded(state, action) {
29
+ state.status = "ready";
30
+ state.report = action.payload;
31
+ state.isStale = false;
32
+ },
33
+ runFailed(state, action) {
34
+ state.status = "error";
35
+ state.error = action.payload;
36
+ },
37
+ runAborted(state) {
38
+ state.status = state.report ? "ready" : "idle";
39
+ },
40
+ reportRestored(state, action) {
41
+ state.status = "ready";
42
+ state.report = action.payload;
43
+ state.error = null;
44
+ state.isStale = false;
45
+ },
46
+ reportCleared(state) {
47
+ state.status = "idle";
48
+ state.report = null;
49
+ state.error = null;
50
+ state.isStale = false;
51
+ },
52
+ reportStale(state) {
53
+ if (state.report) {
54
+ state.isStale = true;
55
+ }
56
+ }
57
+ }
58
+ });
59
+
60
+ // src/store/selectors.ts
61
+ function selectStatus(state) {
62
+ return state.audits.status;
63
+ }
64
+ function selectReport(state) {
65
+ return state.audits.report;
66
+ }
67
+ function selectError(state) {
68
+ return state.audits.error;
69
+ }
70
+ function selectIsStale(state) {
71
+ return state.audits.isStale;
72
+ }
73
+
74
+ // src/components/audit-toggle-icon.tsx
75
+ function AuditToggleIcon() {
76
+ const isStale = useSelector((state) => selectIsStale(state));
77
+ return /* @__PURE__ */ React.createElement(
78
+ Badge,
79
+ {
80
+ variant: "dot",
81
+ color: "warning",
82
+ invisible: !isStale,
83
+ overlap: "circular",
84
+ slotProps: { badge: { "data-testid": "audit-stale-indicator" } }
85
+ },
86
+ /* @__PURE__ */ React.createElement(ShieldCheckIcon, null)
87
+ );
88
+ }
11
89
 
12
90
  // src/editor-panel.tsx
13
91
  import { createFloatingPanel } from "@elementor/editor-floating-panels";
14
92
 
15
93
  // src/components/audit-panel.tsx
16
- import * as React25 from "react";
17
- import { getCurrentDocumentId as getCurrentDocumentId2 } from "@elementor/editor-elements";
94
+ import * as React21 from "react";
95
+ import { getHostDocumentId as getHostDocumentId3 } from "@elementor/editor-elements";
18
96
  import { FloatingPanelBody, FloatingPanelFooter, FloatingPanelHeader } from "@elementor/editor-floating-panels";
19
- import { Box as Box21, Button as Button5, Typography as Typography16 } from "@elementor/ui";
20
- import { __ as __23 } from "@wordpress/i18n";
97
+ import { Box as Box15, Typography as Typography12 } from "@elementor/ui";
98
+ import { __ as __17 } from "@wordpress/i18n";
99
+
100
+ // src/constants.ts
101
+ import { __ } from "@wordpress/i18n";
102
+ var AUDIT_PANEL_ID = "audit-panel";
103
+ var REPORT_STORAGE_KEY_PREFIX = "elementor/audits/report";
104
+ var CREATE_WIDGET_EVENT = "elementor/editor/create-widget";
105
+ var ANGIE_FIX_ENTRY_POINT = "audit_violation";
106
+ var FEEDBACK_EXPERIMENT_NAME = "in_editor_feedback";
107
+ var FEEDBACK_ENTRY_POINT = "audit_panel";
108
+ var FEEDBACK_CLICKED_EVENT = "audit_feedback_clicked";
109
+ var FEEDBACK_SENT_EVENT = "audit_feedback_sent";
110
+ var FEEDBACK_CANCELLED_EVENT = "audit_feedback_cancelled";
111
+ var FEEDBACK_CLOSED_EVENT = "audit_feedback_closed";
112
+ var ALL_CATEGORIES = [
113
+ "best-practices",
114
+ "seo",
115
+ "accessibility",
116
+ "performance",
117
+ "compliance"
118
+ ];
119
+ var CATEGORY_LABELS = {
120
+ "best-practices": __("Best Practices", "elementor"),
121
+ seo: __("SEO", "elementor"),
122
+ accessibility: __("Accessibility", "elementor"),
123
+ performance: __("Performance", "elementor"),
124
+ compliance: __("Site compliance", "elementor")
125
+ };
21
126
 
22
127
  // src/hooks/use-audit-report.ts
23
128
  import { useEffect } from "react";
24
- import { getCurrentDocumentId } from "@elementor/editor-elements";
25
- import { __useDispatch as useDispatch, __useSelector as useSelector } from "@elementor/store";
129
+ import { getHostDocumentId as getHostDocumentId2 } from "@elementor/editor-elements";
130
+ import { __useDispatch as useDispatch, __useSelector as useSelector2 } from "@elementor/store";
26
131
 
27
132
  // src/runner.ts
28
- import { getElements } from "@elementor/editor-elements";
133
+ import { getHostDocumentElements, getHostDocumentId } from "@elementor/editor-elements";
29
134
 
30
135
  // src/api/page-context-client.ts
31
136
  import { httpService } from "@elementor/http-client";
@@ -103,46 +208,16 @@ async function requestPageContext(documentId, imageSizeRequests, allowNonceRetry
103
208
 
104
209
  // src/registry.ts
105
210
  var registry = /* @__PURE__ */ new Map();
106
- function registerAudit(audit26) {
107
- registry.set(audit26.id, audit26);
211
+ function registerAudit(audit25) {
212
+ registry.set(audit25.id, audit25);
108
213
  }
109
214
  function getRegisteredAudits() {
110
215
  return Array.from(registry.values());
111
216
  }
112
217
 
113
- // src/constants.ts
114
- import { __ } from "@wordpress/i18n";
115
- var AUDIT_PANEL_ID = "audit-panel";
116
- var REPORT_STORAGE_KEY_PREFIX = "elementor/audits/report";
117
- var CREATE_WIDGET_EVENT = "elementor/editor/create-widget";
118
- var ANGIE_FIX_ENTRY_POINT = "audit_violation";
119
- var FEEDBACK_EXPERIMENT_NAME = "in_editor_feedback";
120
- var FEEDBACK_ENTRY_POINT = "audit_panel";
121
- var FEEDBACK_CLICKED_EVENT = "audit_feedback_clicked";
122
- var FEEDBACK_SENT_EVENT = "audit_feedback_sent";
123
- var FEEDBACK_CANCELLED_EVENT = "audit_feedback_cancelled";
124
- var FEEDBACK_CLOSED_EVENT = "audit_feedback_closed";
125
- var ALL_CATEGORIES = [
126
- "best-practices",
127
- "seo",
128
- "accessibility",
129
- "performance",
130
- "compliance"
131
- ];
132
- var CATEGORY_LABELS = {
133
- "best-practices": __("Best Practices", "elementor"),
134
- seo: __("SEO", "elementor"),
135
- accessibility: __("Accessibility", "elementor"),
136
- performance: __("Performance", "elementor"),
137
- compliance: __("Site compliance", "elementor")
138
- };
139
-
140
- // src/utils/audit-status-summary.ts
141
- import { __ as __2 } from "@wordpress/i18n";
142
-
143
218
  // src/utils/is-scored-audit.ts
144
- function isScoredAudit(audit26) {
145
- return audit26.weight > 0;
219
+ function isScoredAudit(audit25) {
220
+ return audit25.weight > 0;
146
221
  }
147
222
 
148
223
  // src/utils/sort-failed-audits.ts
@@ -194,52 +269,6 @@ function partitionAuditResults(report, options = {}) {
194
269
  totalViolations
195
270
  };
196
271
  }
197
- function auditStatusDisplayCounts(report) {
198
- let pass = 0;
199
- let skipped = 0;
200
- let totalViolations = 0;
201
- for (const { audit: audit26, result } of report.auditResults) {
202
- if (!isScoredAudit(audit26)) {
203
- continue;
204
- }
205
- switch (result.status) {
206
- case "fail":
207
- totalViolations += result.violations.length;
208
- break;
209
- case "pass":
210
- pass++;
211
- break;
212
- case "skipped":
213
- skipped++;
214
- break;
215
- }
216
- }
217
- return {
218
- fail: totalViolations,
219
- pass,
220
- skipped
221
- };
222
- }
223
- function auditStatusColor(status) {
224
- switch (status) {
225
- case "fail":
226
- return "error";
227
- case "pass":
228
- return "success";
229
- case "skipped":
230
- return "default";
231
- }
232
- }
233
- function auditStatusLabel(status) {
234
- switch (status) {
235
- case "fail":
236
- return __2("Failed audits", "elementor");
237
- case "pass":
238
- return __2("Passed audits", "elementor");
239
- case "skipped":
240
- return __2("Skipped audits", "elementor");
241
- }
242
- }
243
272
  function getPopulatedCategories(categoryTotals, categories) {
244
273
  return categories.filter((category) => categoryTotals[category].total > 0);
245
274
  }
@@ -249,16 +278,16 @@ function computeReport(documentId, results) {
249
278
  const accumulators = Object.fromEntries(
250
279
  ALL_CATEGORIES.map((c) => [c, { totalWeight: 0, passedWeight: 0, total: 0, failed: 0 }])
251
280
  );
252
- for (const { audit: audit26, result } of results) {
281
+ for (const { audit: audit25, result } of results) {
253
282
  if (result.status === "skipped") {
254
283
  continue;
255
284
  }
256
- for (const category of audit26.categories) {
285
+ for (const category of audit25.categories) {
257
286
  const acc = accumulators[category];
258
287
  acc.total++;
259
- acc.totalWeight += audit26.weight;
288
+ acc.totalWeight += audit25.weight;
260
289
  if (result.status === "pass") {
261
- acc.passedWeight += audit26.weight;
290
+ acc.passedWeight += audit25.weight;
262
291
  } else {
263
292
  acc.failed++;
264
293
  }
@@ -335,13 +364,14 @@ function recurse(nodes, parents, visit) {
335
364
  }
336
365
 
337
366
  // src/utils/atomic-image-sources.ts
338
- var ATOMIC_IMAGE_PROP_KEYS = ["image", "poster"];
339
- function walkAtomicImageSources(tree, visit) {
367
+ var ATOMIC_CONTENT_IMAGE_PROP_KEYS = ["image"];
368
+ var ATOMIC_IMAGE_PROP_KEYS = [...ATOMIC_CONTENT_IMAGE_PROP_KEYS, "poster"];
369
+ function walkAtomicImageSources(tree, visit, propKeys = ATOMIC_IMAGE_PROP_KEYS) {
340
370
  walkElements(tree, (node) => {
341
371
  if (node.elType !== "widget") {
342
372
  return;
343
373
  }
344
- for (const key of ATOMIC_IMAGE_PROP_KEYS) {
374
+ for (const key of propKeys) {
345
375
  const media = extractAtomicImageMedia(node.settings[key]);
346
376
  if (media) {
347
377
  visit({ node, media });
@@ -363,8 +393,9 @@ function extractAtomicImageMedia(imageProp) {
363
393
  if (!id && !url) {
364
394
  return null;
365
395
  }
396
+ const alt = stringPropTypeUtil.extract(src.alt) ?? void 0;
366
397
  const size = stringPropTypeUtil.extract(image.size) ?? void 0;
367
- return { id, url, size };
398
+ return { id, url, alt, size };
368
399
  }
369
400
 
370
401
  // src/utils/atomic-background-image-sources.ts
@@ -419,15 +450,6 @@ function walkBackgroundImageSources(tree, visit) {
419
450
 
420
451
  // src/utils/image-like-sources.ts
421
452
  var IMAGE_LIKE_WIDGETS = /* @__PURE__ */ new Set(["image", "image-box", "image-carousel", "gallery", "image-gallery"]);
422
- function hasPageImages(tree) {
423
- let found = false;
424
- walkImageLikeSources(tree, ({ media }) => {
425
- if (media.id || media.url) {
426
- found = true;
427
- }
428
- });
429
- return found;
430
- }
431
453
  function walkImageLikeSources(tree, visit) {
432
454
  walkElements(tree, (node) => {
433
455
  if (node.elType !== "widget") {
@@ -459,6 +481,14 @@ function collectMediaFromNode(node) {
459
481
  return sources;
460
482
  }
461
483
 
484
+ // src/utils/all-image-sources.ts
485
+ function walkAllImageSources(tree, visit) {
486
+ walkImageLikeSources(tree, visit);
487
+ walkBackgroundImageSources(tree, visit);
488
+ walkAtomicImageSources(tree, visit);
489
+ walkAtomicBackgroundImageSources(tree, visit);
490
+ }
491
+
462
492
  // src/utils/image-size-key.ts
463
493
  function buildImageSizeKey({ id, size }) {
464
494
  return `${id}:${size || "full"}`;
@@ -467,15 +497,11 @@ function buildImageSizeKey({ id, size }) {
467
497
  // src/utils/page-attachments.ts
468
498
  function extractImageSizeRequests(tree) {
469
499
  const keys = /* @__PURE__ */ new Set();
470
- const collect = (media) => {
500
+ walkAllImageSources(tree, ({ media }) => {
471
501
  if (media.id) {
472
502
  keys.add(buildImageSizeKey({ id: media.id, size: media.size }));
473
503
  }
474
- };
475
- walkImageLikeSources(tree, ({ media }) => collect(media));
476
- walkBackgroundImageSources(tree, ({ media }) => collect(media));
477
- walkAtomicImageSources(tree, ({ media }) => collect(media));
478
- walkAtomicBackgroundImageSources(tree, ({ media }) => collect(media));
504
+ });
479
505
  return Array.from(keys).sort();
480
506
  }
481
507
 
@@ -570,7 +596,10 @@ function toSnapshot(element) {
570
596
 
571
597
  // src/runner.ts
572
598
  async function runPageAudit(documentId) {
573
- const tree = buildSnapshotTree(getElements());
599
+ if (documentId !== getHostDocumentId()) {
600
+ throw new Error("runPageAudit: documentId must match the host document.");
601
+ }
602
+ const tree = buildSnapshotTree(getHostDocumentElements());
574
603
  const imageSizeRequests = extractImageSizeRequests(tree);
575
604
  const pageContext = await fetchPageContext(documentId, imageSizeRequests);
576
605
  const elements = { documentId, tree };
@@ -581,10 +610,10 @@ async function runPageAudit(documentId) {
581
610
  const ctx = { documentId, elements, pageContext, kit, renderedHtml };
582
611
  const registered = getRegisteredAudits();
583
612
  const auditResults = await Promise.all(
584
- registered.map(async (audit26) => {
585
- const { evaluate: _evaluate, ...meta } = audit26;
613
+ registered.map(async (audit25) => {
614
+ const { evaluate: _evaluate, ...meta } = audit25;
586
615
  try {
587
- const result = await audit26.evaluate(ctx);
616
+ const result = await audit25.evaluate(ctx);
588
617
  return { audit: meta, result };
589
618
  } catch (error) {
590
619
  const reason = error instanceof Error ? error.message : "unknown-error";
@@ -595,52 +624,6 @@ async function runPageAudit(documentId) {
595
624
  return computeReport(documentId, auditResults);
596
625
  }
597
626
 
598
- // src/store/slice.ts
599
- import { __createSlice } from "@elementor/store";
600
- var initialState = { status: "idle", report: null, error: null };
601
- var slice = __createSlice({
602
- name: "audits",
603
- initialState,
604
- reducers: {
605
- runStarted(state) {
606
- state.status = "loading";
607
- state.error = null;
608
- },
609
- runSucceeded(state, action) {
610
- state.status = "ready";
611
- state.report = action.payload;
612
- },
613
- runFailed(state, action) {
614
- state.status = "error";
615
- state.error = action.payload;
616
- },
617
- runAborted(state) {
618
- state.status = state.report ? "ready" : "idle";
619
- },
620
- reportRestored(state, action) {
621
- state.status = "ready";
622
- state.report = action.payload;
623
- state.error = null;
624
- },
625
- reportCleared(state) {
626
- state.status = "idle";
627
- state.report = null;
628
- state.error = null;
629
- }
630
- }
631
- });
632
-
633
- // src/store/selectors.ts
634
- function selectStatus(state) {
635
- return state.audits.status;
636
- }
637
- function selectReport(state) {
638
- return state.audits.report;
639
- }
640
- function selectError(state) {
641
- return state.audits.error;
642
- }
643
-
644
627
  // src/utils/report-storage.ts
645
628
  import { getSessionStorageItem, setSessionStorageItem } from "@elementor/session";
646
629
  function getStorageKey(documentId) {
@@ -655,42 +638,44 @@ function persistReport(documentId, report) {
655
638
 
656
639
  // src/hooks/use-audit-report.ts
657
640
  function useAuditReport() {
658
- const status = useSelector((state) => selectStatus(state));
659
- const report = useSelector((state) => selectReport(state));
660
- const error = useSelector((state) => selectError(state));
661
- const dispatch = useDispatch();
662
- const documentId = getCurrentDocumentId() ?? 0;
641
+ const status = useSelector2((state) => selectStatus(state));
642
+ const report = useSelector2((state) => selectReport(state));
643
+ const error = useSelector2((state) => selectError(state));
644
+ const isStale = useSelector2((state) => selectIsStale(state));
645
+ const dispatch2 = useDispatch();
646
+ const documentId = getHostDocumentId2() ?? 0;
663
647
  useEffect(() => {
664
648
  if (!documentId || report?.documentId === documentId) {
665
649
  return;
666
650
  }
667
651
  const persisted = getPersistedReport(documentId);
668
652
  if (persisted) {
669
- dispatch(slice.actions.reportRestored(persisted));
653
+ dispatch2(slice.actions.reportRestored(persisted));
670
654
  } else if (report) {
671
- dispatch(slice.actions.reportCleared());
655
+ dispatch2(slice.actions.reportCleared());
672
656
  }
673
- }, [documentId, report, dispatch]);
657
+ }, [documentId, report, dispatch2]);
674
658
  const run = async (documentIdToRun) => {
675
- dispatch(slice.actions.runStarted());
659
+ dispatch2(slice.actions.runStarted());
676
660
  try {
677
661
  const nextReport = await runPageAudit(documentIdToRun);
678
- dispatch(slice.actions.runSucceeded(nextReport));
662
+ dispatch2(slice.actions.runSucceeded(nextReport));
679
663
  persistReport(documentIdToRun, nextReport);
680
664
  } catch (e) {
681
665
  if (e instanceof SessionExpiredError) {
682
- dispatch(slice.actions.runAborted());
666
+ dispatch2(slice.actions.runAborted());
683
667
  return;
684
668
  }
685
- dispatch(slice.actions.runFailed(e instanceof Error ? e.message : "Unknown error"));
669
+ dispatch2(slice.actions.runFailed(e instanceof Error ? e.message : "Unknown error"));
686
670
  }
687
671
  };
688
- return { status, report, error, run };
672
+ return { status, report, error, isStale, run };
689
673
  }
690
674
 
691
675
  // src/components/audit-feedback.tsx
692
- import * as React from "react";
676
+ import * as React2 from "react";
693
677
  import { useState } from "react";
678
+ import { useFloatingPanelZIndex } from "@elementor/editor-floating-panels";
694
679
  import { notify } from "@elementor/editor-notifications";
695
680
  import { isExperimentActive } from "@elementor/editor-v1-adapters";
696
681
  import { useMixpanel } from "@elementor/events";
@@ -709,9 +694,9 @@ import {
709
694
  Tooltip,
710
695
  Typography
711
696
  } from "@elementor/ui";
712
- import { __ as __3 } from "@wordpress/i18n";
697
+ import { __ as __2 } from "@wordpress/i18n";
713
698
  var FEEDBACK_SUBJECT = "Page Audit Tool";
714
- var FEEDBACK_SUBJECT_LABEL = __3("Page Audit Tool", "elementor");
699
+ var FEEDBACK_SUBJECT_LABEL = __2("Page Audit Tool", "elementor");
715
700
  var SUCCESS_NOTIFICATION_ID = "audit-feedback-success";
716
701
  var ERROR_NOTIFICATION_ID = "audit-feedback-error";
717
702
  var DIALOG_TITLE_ID = "audit-feedback-dialog-title";
@@ -720,18 +705,19 @@ var isUserConnected = () => Boolean(window.elementorCommon?.config?.library_conn
720
705
  var notifySuccess = () => notify({
721
706
  id: SUCCESS_NOTIFICATION_ID,
722
707
  type: "success",
723
- message: __3("Feedback sent. Thanks for helping us out.", "elementor")
708
+ message: __2("Feedback sent. Thanks for helping us out.", "elementor")
724
709
  });
725
710
  var notifyError = () => notify({
726
711
  id: ERROR_NOTIFICATION_ID,
727
712
  type: "error",
728
- message: __3("Something went wrong. Please try sending your feedback again.", "elementor")
713
+ message: __2("Something went wrong. Please try sending your feedback again.", "elementor")
729
714
  });
730
715
  function AuditFeedback() {
731
716
  const [isOpen, setIsOpen] = useState(false);
732
717
  const [feedbackText, setFeedbackText] = useState("");
733
718
  const [isSubmitting, setIsSubmitting] = useState(false);
734
719
  const { dispatchEvent: trackEvent = (...args) => void args } = useMixpanel();
720
+ const panelZIndex = useFloatingPanelZIndex(AUDIT_PANEL_ID);
735
721
  if (!isExperimentActive(FEEDBACK_EXPERIMENT_NAME)) {
736
722
  return null;
737
723
  }
@@ -740,7 +726,7 @@ function AuditFeedback() {
740
726
  if (!connected && !connectUrl) {
741
727
  return null;
742
728
  }
743
- const triggerLabel = __3("Give feedback", "elementor");
729
+ const triggerLabel = __2("Give feedback", "elementor");
744
730
  const handleOpen = () => {
745
731
  if (!connected) {
746
732
  window.open(connectUrl, "_blank", "noopener");
@@ -773,7 +759,7 @@ function AuditFeedback() {
773
759
  trackEvent(FEEDBACK_SENT_EVENT, { entry_point: FEEDBACK_ENTRY_POINT });
774
760
  }).catch(notifyError).finally(() => setIsSubmitting(false));
775
761
  };
776
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Tooltip, { title: triggerLabel, placement: "top" }, /* @__PURE__ */ React.createElement(IconButton, { size: "small", "aria-label": triggerLabel, onClick: handleOpen }, /* @__PURE__ */ React.createElement(MessageLinesIcon, { fontSize: "small" }))), /* @__PURE__ */ React.createElement(
762
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Tooltip, { title: triggerLabel, placement: "top", PopperProps: { sx: { zIndex: panelZIndex } } }, /* @__PURE__ */ React2.createElement(IconButton, { size: "small", "aria-label": triggerLabel, onClick: handleOpen }, /* @__PURE__ */ React2.createElement(MessageLinesIcon, { fontSize: "small" }))), /* @__PURE__ */ React2.createElement(
777
763
  Dialog,
778
764
  {
779
765
  open: isOpen,
@@ -782,11 +768,11 @@ function AuditFeedback() {
782
768
  "aria-describedby": DIALOG_DESCRIPTION_ID,
783
769
  PaperProps: { sx: { borderRadius: 1 } }
784
770
  },
785
- /* @__PURE__ */ React.createElement(DialogHeader, { logo: false, onClose: handleClose }, /* @__PURE__ */ React.createElement(DialogTitle, { id: DIALOG_TITLE_ID }, __3("Let us know what you think", "elementor"))),
786
- /* @__PURE__ */ React.createElement(DialogContent, { dividers: true }, /* @__PURE__ */ React.createElement(Stack, { direction: "column", gap: 2 }, /* @__PURE__ */ React.createElement(TextField, { value: FEEDBACK_SUBJECT_LABEL, disabled: true, fullWidth: true, size: "small" }), /* @__PURE__ */ React.createElement(
771
+ /* @__PURE__ */ React2.createElement(DialogHeader, { logo: false, onClose: handleClose }, /* @__PURE__ */ React2.createElement(DialogTitle, { id: DIALOG_TITLE_ID }, __2("Let us know what you think", "elementor"))),
772
+ /* @__PURE__ */ React2.createElement(DialogContent, { dividers: true }, /* @__PURE__ */ React2.createElement(Stack, { direction: "column", gap: 2 }, /* @__PURE__ */ React2.createElement(TextField, { value: FEEDBACK_SUBJECT_LABEL, disabled: true, fullWidth: true, size: "small" }), /* @__PURE__ */ React2.createElement(
787
773
  TextField,
788
774
  {
789
- placeholder: __3(
775
+ placeholder: __2(
790
776
  "Tell us what you had in mind, what we can improve or fix to make this feature better.",
791
777
  "elementor"
792
778
  ),
@@ -797,11 +783,11 @@ function AuditFeedback() {
797
783
  onChange: (event) => setFeedbackText(event.target.value),
798
784
  value: feedbackText
799
785
  }
800
- ), /* @__PURE__ */ React.createElement(Typography, { id: DIALOG_DESCRIPTION_ID, variant: "caption", color: "text.secondary" }, __3(
786
+ ), /* @__PURE__ */ React2.createElement(Typography, { id: DIALOG_DESCRIPTION_ID, variant: "caption", color: "text.secondary" }, __2(
801
787
  "We appreciate your feedback! While we review all submissions, we can't guarantee that every suggestion will result in a change or update.",
802
788
  "elementor"
803
789
  )))),
804
- /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { variant: "text", color: "secondary", onClick: handleCancel }, __3("Cancel", "elementor")), /* @__PURE__ */ React.createElement(
790
+ /* @__PURE__ */ React2.createElement(DialogActions, null, /* @__PURE__ */ React2.createElement(Button, { variant: "text", color: "secondary", onClick: handleCancel }, __2("Cancel", "elementor")), /* @__PURE__ */ React2.createElement(
805
791
  Button,
806
792
  {
807
793
  variant: "contained",
@@ -809,35 +795,35 @@ function AuditFeedback() {
809
795
  disabled: isSubmitting || feedbackText.trim().length === 0,
810
796
  onClick: handleSubmit
811
797
  },
812
- __3("Send", "elementor")
798
+ __2("Send", "elementor")
813
799
  ))
814
800
  ));
815
801
  }
816
802
 
817
803
  // src/components/pages/error-page.tsx
818
- import * as React2 from "react";
804
+ import * as React3 from "react";
819
805
  import { Box, Button as Button2, Typography as Typography2 } from "@elementor/ui";
820
- import { __ as __4 } from "@wordpress/i18n";
806
+ import { __ as __3 } from "@wordpress/i18n";
821
807
  function ErrorPage({ message, onRetry }) {
822
- return /* @__PURE__ */ React2.createElement(Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 1.5, p: 4 } }, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "error", textAlign: "center" }, message), /* @__PURE__ */ React2.createElement(Button2, { variant: "contained", size: "small", onClick: onRetry }, __4("Try again", "elementor")));
808
+ return /* @__PURE__ */ React3.createElement(Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 1.5, p: 4 } }, /* @__PURE__ */ React3.createElement(Typography2, { variant: "body2", color: "error", textAlign: "center" }, message), /* @__PURE__ */ React3.createElement(Button2, { variant: "contained", size: "small", onClick: onRetry }, __3("Try again", "elementor")));
823
809
  }
824
810
 
825
811
  // src/components/pages/loading-page.tsx
826
- import * as React3 from "react";
812
+ import * as React4 from "react";
827
813
  import { Box as Box2, CircularProgress, Typography as Typography3 } from "@elementor/ui";
828
- import { __ as __5 } from "@wordpress/i18n";
814
+ import { __ as __4 } from "@wordpress/i18n";
829
815
  function LoadingPage() {
830
- return /* @__PURE__ */ React3.createElement(Box2, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 1.5, p: 4 } }, /* @__PURE__ */ React3.createElement(CircularProgress, { size: 32 }), /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2" }, __5("Audit in progress.", "elementor")));
816
+ return /* @__PURE__ */ React4.createElement(Box2, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 1.5, p: 4 } }, /* @__PURE__ */ React4.createElement(CircularProgress, { size: 32 }), /* @__PURE__ */ React4.createElement(Typography3, { variant: "body2" }, __4("Audit in progress.", "elementor")));
831
817
  }
832
818
 
833
819
  // src/components/pages/welcome-page.tsx
834
- import * as React4 from "react";
820
+ import * as React5 from "react";
835
821
  import { Box as Box3, Typography as Typography4 } from "@elementor/ui";
836
- import { __ as __6 } from "@wordpress/i18n";
822
+ import { __ as __5 } from "@wordpress/i18n";
837
823
  var INFORMATION_IMAGE_PATH = "images/information.svg";
838
824
  function WelcomePage() {
839
825
  const assetsUrl = window.elementorCommon?.config?.urls?.assets ?? "";
840
- return /* @__PURE__ */ React4.createElement(
826
+ return /* @__PURE__ */ React5.createElement(
841
827
  Box3,
842
828
  {
843
829
  sx: {
@@ -849,17 +835,17 @@ function WelcomePage() {
849
835
  p: 3
850
836
  }
851
837
  },
852
- /* @__PURE__ */ React4.createElement(
838
+ /* @__PURE__ */ React5.createElement(
853
839
  "img",
854
840
  {
855
841
  className: "elementor-nerd-box-icon",
856
842
  src: `${assetsUrl}${INFORMATION_IMAGE_PATH}`,
857
843
  loading: "lazy",
858
- alt: __6("Elementor", "elementor")
844
+ alt: __5("Elementor", "elementor")
859
845
  }
860
846
  ),
861
- /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle1", component: "h3", sx: { marginBlockStart: 2 } }, __6("Audit your page!", "elementor")),
862
- /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption", textAlign: "center" }, __6(
847
+ /* @__PURE__ */ React5.createElement(Typography4, { variant: "subtitle1", component: "h3", sx: { marginBlockStart: 2 } }, __5("Audit your page!", "elementor")),
848
+ /* @__PURE__ */ React5.createElement(Typography4, { variant: "caption", textAlign: "center" }, __5(
863
849
  'Check SEO, accessibility, performance, and best practices. Click "Run page audit" to begin.',
864
850
  "elementor"
865
851
  ))
@@ -867,40 +853,59 @@ function WelcomePage() {
867
853
  }
868
854
 
869
855
  // src/components/report-shell.tsx
870
- import * as React24 from "react";
871
- import { useState as useState5 } from "react";
872
- import { Box as Box20, Divider as Divider3, Tab, Tabs } from "@elementor/ui";
873
- import { __ as __22 } from "@wordpress/i18n";
856
+ import * as React19 from "react";
857
+ import { useState as useState4 } from "react";
858
+ import { Box as Box14 } from "@elementor/ui";
874
859
 
875
- // src/components/pages/all-audits-page.tsx
876
- import * as React12 from "react";
860
+ // src/components/pages/category-page.tsx
861
+ import * as React13 from "react";
877
862
  import { Box as Box8 } from "@elementor/ui";
878
- import { __ as __12 } from "@wordpress/i18n";
863
+ import { __ as __11 } from "@wordpress/i18n";
879
864
 
880
- // src/components/status-section.tsx
881
- import * as React5 from "react";
865
+ // src/hooks/use-single-expanded-card.ts
882
866
  import { useState as useState2 } from "react";
867
+ function useSingleExpandedCard(initialId = null) {
868
+ const [expandedId, setExpandedId] = useState2(initialId);
869
+ const toggle = (id) => {
870
+ setExpandedId((current) => current === id ? null : id);
871
+ };
872
+ return { expandedId, toggle };
873
+ }
874
+
875
+ // src/components/category-icons.ts
876
+ import { BoltIcon, ElementorAccessibilityIcon, FileSearchIcon, Settings2Icon, ShieldCheckIcon as ShieldCheckIcon2 } from "@elementor/icons";
877
+ var CATEGORY_ICONS = {
878
+ "best-practices": Settings2Icon,
879
+ seo: FileSearchIcon,
880
+ accessibility: ElementorAccessibilityIcon,
881
+ performance: BoltIcon,
882
+ compliance: ShieldCheckIcon2
883
+ };
884
+
885
+ // src/components/status-section.tsx
886
+ import * as React6 from "react";
887
+ import { useState as useState3 } from "react";
883
888
  import { ChevronDownIcon } from "@elementor/icons";
884
889
  import { Box as Box4, Chip, Collapse, IconButton as IconButton2 } from "@elementor/ui";
885
- import { __ as __7 } from "@wordpress/i18n";
890
+ import { __ as __6 } from "@wordpress/i18n";
886
891
  function StatusSection({ label, count, color = "default", defaultExpanded = false, children }) {
887
- const [expanded, setExpanded] = useState2(defaultExpanded);
888
- if (React5.Children.count(children) === 0) {
892
+ const [expanded, setExpanded] = useState3(defaultExpanded);
893
+ if (React6.Children.count(children) === 0) {
889
894
  return null;
890
895
  }
891
- return /* @__PURE__ */ React5.createElement(Box4, { sx: { paddingBlock: 1 } }, /* @__PURE__ */ React5.createElement(
896
+ return /* @__PURE__ */ React6.createElement(Box4, { sx: { paddingBlock: 1 } }, /* @__PURE__ */ React6.createElement(
892
897
  Box4,
893
898
  {
894
899
  sx: { display: "flex", alignItems: "center", gap: 0.5, cursor: "pointer" },
895
900
  onClick: () => setExpanded((v) => !v)
896
901
  },
897
- /* @__PURE__ */ React5.createElement(
902
+ /* @__PURE__ */ React6.createElement(
898
903
  IconButton2,
899
904
  {
900
905
  size: "small",
901
- "aria-label": expanded ? __7("Collapse", "elementor") : __7("Expand", "elementor")
906
+ "aria-label": expanded ? __6("Collapse", "elementor") : __6("Expand", "elementor")
902
907
  },
903
- /* @__PURE__ */ React5.createElement(
908
+ /* @__PURE__ */ React6.createElement(
904
909
  ChevronDownIcon,
905
910
  {
906
911
  fontSize: "small",
@@ -911,27 +916,27 @@ function StatusSection({ label, count, color = "default", defaultExpanded = fals
911
916
  }
912
917
  )
913
918
  ),
914
- /* @__PURE__ */ React5.createElement(Chip, { label: `${label} (${count})`, size: "small", color, variant: "standard" })
915
- ), /* @__PURE__ */ React5.createElement(Collapse, { in: expanded }, /* @__PURE__ */ React5.createElement(Box4, { sx: { paddingInlineStart: 4.25, paddingBlock: 2 } }, children)));
919
+ /* @__PURE__ */ React6.createElement(Chip, { label: `${label} (${count})`, size: "small", color, variant: "standard" })
920
+ ), /* @__PURE__ */ React6.createElement(Collapse, { in: expanded }, /* @__PURE__ */ React6.createElement(Box4, { sx: { paddingInlineStart: 4.25, paddingBlock: 2 } }, children)));
916
921
  }
917
922
 
918
923
  // src/components/subpage-header.tsx
919
- import * as React6 from "react";
924
+ import * as React7 from "react";
920
925
  import { ArrowLeftIcon } from "@elementor/icons";
921
926
  import { Box as Box5, IconButton as IconButton3, Rotate, Typography as Typography5, useTheme } from "@elementor/ui";
922
- import { __ as __8 } from "@wordpress/i18n";
927
+ import { __ as __7 } from "@wordpress/i18n";
923
928
  function SubpageHeader({ title, onBack, backLabel, icon }) {
924
929
  const isRtl = "rtl" === useTheme().direction;
925
- return /* @__PURE__ */ React6.createElement(Box5, { sx: { display: "flex", alignItems: "center", gap: 0.5, p: 1 } }, /* @__PURE__ */ React6.createElement(IconButton3, { size: "small", onClick: onBack, "aria-label": backLabel ?? __8("Back", "elementor") }, /* @__PURE__ */ React6.createElement(Rotate, { in: isRtl }, /* @__PURE__ */ React6.createElement(ArrowLeftIcon, { fontSize: "small" }))), icon, /* @__PURE__ */ React6.createElement(Typography5, { variant: "subtitle2", fontWeight: "bold" }, title));
930
+ return /* @__PURE__ */ React7.createElement(Box5, { sx: { display: "flex", alignItems: "center", gap: 0.5, p: 1 } }, /* @__PURE__ */ React7.createElement(IconButton3, { size: "small", onClick: onBack, "aria-label": backLabel ?? __7("Back", "elementor") }, /* @__PURE__ */ React7.createElement(Rotate, { in: isRtl }, /* @__PURE__ */ React7.createElement(ArrowLeftIcon, { fontSize: "small" }))), icon, /* @__PURE__ */ React7.createElement(Typography5, { variant: "subtitle2", fontWeight: "bold" }, title));
926
931
  }
927
932
 
928
933
  // src/components/violation-row.tsx
929
- import * as React11 from "react";
930
- import { useState as useState3 } from "react";
934
+ import * as React12 from "react";
931
935
  import { getElementIcon, getElementTitle } from "@elementor/editor-elements";
932
- import { AlertCircleIcon as AlertCircleIcon2, BulbIcon as BulbIcon2, CheckIcon, ChevronDownIcon as ChevronDownIcon2, EyeIcon, HelpIcon } from "@elementor/icons";
933
- import { Alert, AlertTitle, Box as Box7, Collapse as Collapse2, IconButton as IconButton5, Tooltip as Tooltip3, Typography as Typography6 } from "@elementor/ui";
934
- import { __ as __11 } from "@wordpress/i18n";
936
+ import { useFloatingPanelZIndex as useFloatingPanelZIndex2 } from "@elementor/editor-floating-panels";
937
+ import { CheckIcon, ChevronDownIcon as ChevronDownIcon2, HelpIcon, InfoCircleIcon } from "@elementor/icons";
938
+ import { Alert, Box as Box7, Collapse as Collapse2, IconButton as IconButton5, Tooltip as Tooltip3, Typography as Typography6 } from "@elementor/ui";
939
+ import { __ as __10 } from "@wordpress/i18n";
935
940
 
936
941
  // src/hooks/focus-violation.ts
937
942
  import { getContainer, selectElement } from "@elementor/editor-elements";
@@ -972,11 +977,11 @@ function focusViolation(violation) {
972
977
  }
973
978
 
974
979
  // src/utils/build-angie-prompt.ts
975
- import { __ as __9, sprintf } from "@wordpress/i18n";
980
+ import { __ as __8, sprintf } from "@wordpress/i18n";
976
981
  function buildAngiePrompt(violationText) {
977
982
  return sprintf(
978
983
  /* translators: %s: audit violation description. */
979
- __9("Help me fix: %s", "elementor"),
984
+ __8("Help me fix: %s", "elementor"),
980
985
  violationText
981
986
  );
982
987
  }
@@ -992,13 +997,13 @@ function onKeyboardClick(callback) {
992
997
  }
993
998
 
994
999
  // src/components/fix-violation-with-angie.tsx
995
- import * as React7 from "react";
1000
+ import * as React8 from "react";
996
1001
  import { isAngieAvailable, sendPromptToAngie } from "@elementor/editor-mcp";
997
1002
  import { AngieIcon } from "@elementor/icons";
998
- import { IconButton as IconButton4, Tooltip as Tooltip2 } from "@elementor/ui";
999
- import { __ as __10 } from "@wordpress/i18n";
1000
- function FixViolationWithAngie({ prompt }) {
1001
- const label = __10("Fix with Angie", "elementor");
1003
+ import { Button as Button3, IconButton as IconButton4, Tooltip as Tooltip2 } from "@elementor/ui";
1004
+ import { __ as __9 } from "@wordpress/i18n";
1005
+ function FixViolationWithAngie({ prompt, panelZIndex, variant = "icon" }) {
1006
+ const label = __9("Fix with Angie", "elementor");
1002
1007
  const href = `#angie-prompt=${encodeURIComponent(prompt)}`;
1003
1008
  const handleClick = (event) => {
1004
1009
  event.stopPropagation();
@@ -1016,52 +1021,69 @@ function FixViolationWithAngie({ prompt }) {
1016
1021
  })
1017
1022
  );
1018
1023
  };
1019
- return /* @__PURE__ */ React7.createElement(Tooltip2, { title: label }, /* @__PURE__ */ React7.createElement(
1020
- IconButton4,
1021
- {
1022
- className: "violation-hover-icon",
1023
- component: "a",
1024
- href,
1025
- size: "small",
1026
- "aria-label": label,
1027
- onClick: handleClick
1028
- },
1029
- /* @__PURE__ */ React7.createElement(AngieIcon, { fontSize: "tiny" })
1030
- ));
1024
+ if (variant === "button") {
1025
+ return /* @__PURE__ */ React8.createElement(
1026
+ Button3,
1027
+ {
1028
+ component: "a",
1029
+ href,
1030
+ size: "small",
1031
+ color: "secondary",
1032
+ variant: "outlined",
1033
+ startIcon: /* @__PURE__ */ React8.createElement(AngieIcon, { fontSize: "tiny", "aria-hidden": true }),
1034
+ onClick: handleClick
1035
+ },
1036
+ label
1037
+ );
1038
+ }
1039
+ return /* @__PURE__ */ React8.createElement(Tooltip2, { title: label, placement: "top", PopperProps: { sx: { zIndex: panelZIndex } } }, /* @__PURE__ */ React8.createElement(IconButton4, { component: "a", href, size: "small", "aria-label": label, onClick: handleClick }, /* @__PURE__ */ React8.createElement(AngieIcon, { fontSize: "tiny" })));
1031
1040
  }
1032
1041
 
1033
1042
  // src/components/severity-icons.tsx
1034
- import * as React8 from "react";
1043
+ import * as React9 from "react";
1035
1044
  import { AlertCircleIcon, AlertTriangleFilledIcon, BulbIcon } from "@elementor/icons";
1036
1045
  var SEVERITY_CONFIG = {
1037
1046
  error: { Icon: AlertTriangleFilledIcon, color: "error" },
1038
1047
  warning: { Icon: AlertCircleIcon, color: "warning" },
1039
1048
  info: { Icon: BulbIcon, color: "info" }
1040
1049
  };
1050
+ var INFO_ICON_VERTICAL_NUDGE = "translateY(-2px)";
1041
1051
  function SeverityIcon({ severity }) {
1042
1052
  const { Icon, color } = SEVERITY_CONFIG[severity];
1043
- return /* @__PURE__ */ React8.createElement(Icon, { fontSize: "small", color });
1053
+ const sx = "info" === severity ? { transform: INFO_ICON_VERTICAL_NUDGE } : void 0;
1054
+ return /* @__PURE__ */ React9.createElement(Icon, { fontSize: "small", color, sx });
1044
1055
  }
1045
1056
 
1046
1057
  // src/components/violation-cta-button.tsx
1047
- import * as React9 from "react";
1048
- import { Button as Button3 } from "@elementor/ui";
1049
- function ViolationCtaButton({ ctaLabel, externalUrl, variant = "outlined" }) {
1058
+ import * as React10 from "react";
1059
+ import { WandIcon } from "@elementor/icons";
1060
+ import { Button as Button4 } from "@elementor/ui";
1061
+ function ViolationCtaButton({ ctaLabel, externalUrl, variant = "outlined", withIcon = false }) {
1050
1062
  const handleClick = (event) => {
1051
1063
  event.stopPropagation();
1052
1064
  event.preventDefault();
1053
1065
  window.open(externalUrl, "_blank", "noopener");
1054
1066
  };
1055
- return /* @__PURE__ */ React9.createElement(Button3, { variant, color: "secondary", size: "small", onClick: handleClick }, ctaLabel);
1067
+ return /* @__PURE__ */ React10.createElement(
1068
+ Button4,
1069
+ {
1070
+ variant,
1071
+ color: "secondary",
1072
+ size: "small",
1073
+ startIcon: withIcon ? /* @__PURE__ */ React10.createElement(WandIcon, { fontSize: "tiny", "aria-hidden": true }) : void 0,
1074
+ onClick: handleClick
1075
+ },
1076
+ ctaLabel
1077
+ );
1056
1078
  }
1057
1079
 
1058
1080
  // src/components/violation-icons.tsx
1059
- import * as React10 from "react";
1060
- import { FileSettingsIcon, SettingsIcon, ShieldCheckIcon } from "@elementor/icons";
1081
+ import * as React11 from "react";
1082
+ import { FileSettingsIcon, SettingsIcon } from "@elementor/icons";
1061
1083
  import { Box as Box6 } from "@elementor/ui";
1062
1084
  function ViolationIcon({ violation, widgetIcon }) {
1063
1085
  if (widgetIcon) {
1064
- return /* @__PURE__ */ React10.createElement(
1086
+ return /* @__PURE__ */ React11.createElement(
1065
1087
  Box6,
1066
1088
  {
1067
1089
  component: "i",
@@ -1072,28 +1094,73 @@ function ViolationIcon({ violation, widgetIcon }) {
1072
1094
  );
1073
1095
  }
1074
1096
  if (violation.targetHint === "page-settings") {
1075
- return /* @__PURE__ */ React10.createElement(FileSettingsIcon, { fontSize: "inherit", "aria-hidden": true });
1097
+ return /* @__PURE__ */ React11.createElement(FileSettingsIcon, { fontSize: "inherit", "aria-hidden": true });
1076
1098
  }
1077
1099
  if (violation.targetHint === "site-settings" || violation.targetHint === "site-identity-settings") {
1078
- return /* @__PURE__ */ React10.createElement(SettingsIcon, { fontSize: "inherit", "aria-hidden": true });
1100
+ return /* @__PURE__ */ React11.createElement(SettingsIcon, { fontSize: "inherit", "aria-hidden": true });
1079
1101
  }
1080
- return /* @__PURE__ */ React10.createElement(ShieldCheckIcon, { fontSize: "inherit", "aria-hidden": true });
1102
+ return /* @__PURE__ */ React11.createElement(SettingsIcon, { fontSize: "inherit", "aria-hidden": true });
1081
1103
  }
1082
1104
 
1083
1105
  // src/components/violation-row.tsx
1084
1106
  function SkipReasonTooltip({ reason }) {
1085
- return /* @__PURE__ */ React11.createElement(Tooltip3, { title: reason, placement: "top" }, /* @__PURE__ */ React11.createElement(Box7, { "aria-label": reason, component: "span", sx: { display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React11.createElement(HelpIcon, { fontSize: "small", color: "action" })));
1107
+ return /* @__PURE__ */ React12.createElement(Tooltip3, { title: reason, placement: "top" }, /* @__PURE__ */ React12.createElement(Box7, { "aria-label": reason, component: "span", sx: { display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React12.createElement(HelpIcon, { fontSize: "small", color: "action" })));
1086
1108
  }
1087
- function StatusIndicator({ audit: audit26, violations }) {
1109
+ function StatusIndicator({ audit: audit25, violations }) {
1088
1110
  if (violations) {
1089
- return /* @__PURE__ */ React11.createElement(React11.Fragment, null, isScoredAudit(audit26) && /* @__PURE__ */ React11.createElement(Typography6, { variant: "caption", color: "text.secondary", fontWeight: "bold" }, violations.length), /* @__PURE__ */ React11.createElement(SeverityIcon, { severity: audit26.severity }));
1111
+ return /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(SeverityIcon, { severity: audit25.severity }), isScoredAudit(audit25) && /* @__PURE__ */ React12.createElement(Typography6, { variant: "caption", color: "text.secondary", fontWeight: "bold" }, violations.length));
1112
+ }
1113
+ return /* @__PURE__ */ React12.createElement(CheckIcon, { fontSize: "small", color: "success" });
1114
+ }
1115
+ function GuidanceAction({
1116
+ violation,
1117
+ sharedAngiePrompt,
1118
+ panelZIndex
1119
+ }) {
1120
+ const hasPrimaryCta = !!(violation.ctaLabel && violation.externalUrl);
1121
+ if (!hasPrimaryCta && !sharedAngiePrompt) {
1122
+ return null;
1123
+ }
1124
+ return /* @__PURE__ */ React12.createElement(Box7, { display: "flex", alignItems: "center", gap: 1, mt: 1 }, hasPrimaryCta && /* @__PURE__ */ React12.createElement(
1125
+ ViolationCtaButton,
1126
+ {
1127
+ ctaLabel: violation.ctaLabel,
1128
+ externalUrl: violation.externalUrl,
1129
+ withIcon: true
1130
+ }
1131
+ ), hasPrimaryCta && violation.secondaryCtaLabel && violation.secondaryCtaUrl && /* @__PURE__ */ React12.createElement(
1132
+ ViolationCtaButton,
1133
+ {
1134
+ ctaLabel: violation.secondaryCtaLabel,
1135
+ externalUrl: violation.secondaryCtaUrl,
1136
+ variant: "text"
1137
+ }
1138
+ ), sharedAngiePrompt && /* @__PURE__ */ React12.createElement(FixViolationWithAngie, { prompt: sharedAngiePrompt, panelZIndex, variant: "button" }));
1139
+ }
1140
+ function getSharedAngiePrompt(violations) {
1141
+ if (!violations || violations.length < 2) {
1142
+ return void 0;
1143
+ }
1144
+ const [first, ...rest] = violations;
1145
+ if (!first.angieFix || !first.angiePrompt) {
1146
+ return void 0;
1090
1147
  }
1091
- return /* @__PURE__ */ React11.createElement(CheckIcon, { fontSize: "small", color: "success" });
1148
+ const allShareTheSameFix = rest.every(
1149
+ (violation) => violation.angieFix && violation.angiePrompt === first.angiePrompt
1150
+ );
1151
+ return allShareTheSameFix ? first.angiePrompt : void 0;
1092
1152
  }
1093
- function ViolationRow({ audit: audit26, skipReason, violations }) {
1094
- const [expanded, setExpanded] = useState3(false);
1095
- const toggleExpanded = () => setExpanded((value) => !value);
1096
- return /* @__PURE__ */ React11.createElement(Box7, { sx: { borderBottom: 1, borderColor: "divider", paddingBlock: 0.5 } }, /* @__PURE__ */ React11.createElement(Box7, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React11.createElement(
1153
+ function ViolationRow({ audit: audit25, expanded, onToggleExpand, skipReason, violations }) {
1154
+ const primaryViolation = violations?.[0];
1155
+ const isGuidanceFocusable = !!primaryViolation;
1156
+ const panelZIndex = useFloatingPanelZIndex2(AUDIT_PANEL_ID);
1157
+ const sharedAngiePrompt = getSharedAngiePrompt(violations);
1158
+ const handleGuidanceClick = () => {
1159
+ if (isGuidanceFocusable && primaryViolation) {
1160
+ focusViolation(primaryViolation);
1161
+ }
1162
+ };
1163
+ return /* @__PURE__ */ React12.createElement(Box7, { sx: { borderBottom: 1, borderColor: "divider", paddingBlock: 0.5 } }, /* @__PURE__ */ React12.createElement(Box7, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React12.createElement(
1097
1164
  Box7,
1098
1165
  {
1099
1166
  sx: {
@@ -1104,18 +1171,18 @@ function ViolationRow({ audit: audit26, skipReason, violations }) {
1104
1171
  gap: 0.5,
1105
1172
  minWidth: 0
1106
1173
  },
1107
- onClick: toggleExpanded
1174
+ onClick: onToggleExpand
1108
1175
  },
1109
- /* @__PURE__ */ React11.createElement(Typography6, { variant: "body2", sx: { flex: 1 } }, audit26.title),
1110
- !skipReason && /* @__PURE__ */ React11.createElement(StatusIndicator, { audit: audit26, violations })
1111
- ), skipReason && /* @__PURE__ */ React11.createElement(SkipReasonTooltip, { reason: skipReason }), /* @__PURE__ */ React11.createElement(
1176
+ /* @__PURE__ */ React12.createElement(Typography6, { variant: "subtitle2", color: "text.secondary", sx: { flex: 1 } }, audit25.title),
1177
+ !skipReason && /* @__PURE__ */ React12.createElement(StatusIndicator, { audit: audit25, violations })
1178
+ ), skipReason && /* @__PURE__ */ React12.createElement(SkipReasonTooltip, { reason: skipReason }), /* @__PURE__ */ React12.createElement(
1112
1179
  IconButton5,
1113
1180
  {
1114
1181
  size: "small",
1115
- "aria-label": expanded ? __11("Collapse", "elementor") : __11("Expand", "elementor"),
1116
- onClick: toggleExpanded
1182
+ "aria-label": expanded ? __10("Collapse", "elementor") : __10("Expand", "elementor"),
1183
+ onClick: onToggleExpand
1117
1184
  },
1118
- /* @__PURE__ */ React11.createElement(
1185
+ /* @__PURE__ */ React12.createElement(
1119
1186
  ChevronDownIcon2,
1120
1187
  {
1121
1188
  fontSize: "small",
@@ -1125,29 +1192,33 @@ function ViolationRow({ audit: audit26, skipReason, violations }) {
1125
1192
  }
1126
1193
  }
1127
1194
  )
1128
- )), /* @__PURE__ */ React11.createElement(Collapse2, { in: expanded }, /* @__PURE__ */ React11.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1, paddingBlock: 1 } }, /* @__PURE__ */ React11.createElement(
1195
+ )), /* @__PURE__ */ React12.createElement(Collapse2, { in: expanded }, /* @__PURE__ */ React12.createElement(Box7, { paddingBlock: 1 }, /* @__PURE__ */ React12.createElement(Tooltip3, { title: audit25.fixHint, placement: "top" }, /* @__PURE__ */ React12.createElement(
1129
1196
  Alert,
1130
1197
  {
1131
1198
  severity: "secondary",
1132
- sx: { p: 1 },
1133
- icon: /* @__PURE__ */ React11.createElement(AlertCircleIcon2, { fontSize: "small", color: "secondary", "aria-hidden": true })
1134
- },
1135
- /* @__PURE__ */ React11.createElement(AlertTitle, null, /* @__PURE__ */ React11.createElement(Typography6, { variant: "caption", component: "p", color: "text.primary", fontWeight: "bold" }, __11("What's the issue", "elementor"))),
1136
- /* @__PURE__ */ React11.createElement(Typography6, { variant: "caption", component: "p", color: "text.secondary" }, audit26.description)
1137
- ), /* @__PURE__ */ React11.createElement(
1138
- Alert,
1139
- {
1140
- severity: "info",
1141
- sx: { p: 1 },
1142
- icon: /* @__PURE__ */ React11.createElement(BulbIcon2, { fontSize: "small", color: "info", "aria-hidden": true })
1199
+ sx: { p: 1, cursor: isGuidanceFocusable ? "pointer" : void 0 },
1200
+ icon: /* @__PURE__ */ React12.createElement(InfoCircleIcon, { fontSize: "small", "aria-hidden": true }),
1201
+ role: isGuidanceFocusable ? "button" : void 0,
1202
+ tabIndex: isGuidanceFocusable ? 0 : void 0,
1203
+ onClick: isGuidanceFocusable ? handleGuidanceClick : void 0,
1204
+ onKeyDown: isGuidanceFocusable ? onKeyboardClick(handleGuidanceClick) : void 0
1143
1205
  },
1144
- /* @__PURE__ */ React11.createElement(AlertTitle, null, /* @__PURE__ */ React11.createElement(Typography6, { variant: "caption", component: "p", color: "text.primary", fontWeight: "bold" }, __11("How to resolve", "elementor"))),
1145
- /* @__PURE__ */ React11.createElement(Typography6, { variant: "caption", component: "p", color: "text.secondary" }, audit26.fixHint)
1146
- )), violations && violations.length > 0 && /* @__PURE__ */ React11.createElement(Box7, { role: "list", sx: { paddingBlockEnd: 1, paddingInlineStart: 2 } }, violations.map((violation, idx) => {
1206
+ /* @__PURE__ */ React12.createElement(Typography6, { variant: "caption", component: "p", color: "text.secondary" }, audit25.description),
1207
+ primaryViolation && /* @__PURE__ */ React12.createElement(
1208
+ GuidanceAction,
1209
+ {
1210
+ violation: primaryViolation,
1211
+ sharedAngiePrompt,
1212
+ panelZIndex
1213
+ }
1214
+ )
1215
+ ))), violations && violations.length > 0 && /* @__PURE__ */ React12.createElement(Box7, { role: "list", sx: { paddingBlockEnd: 1 } }, violations.map((violation, idx) => {
1147
1216
  const widgetIcon = violation.elementId ? getElementIcon(violation.elementId) : null;
1148
1217
  const elementTitle = violation.elementId ? getElementTitle(violation.elementId) : null;
1149
- const rowLabel = elementTitle ? `${elementTitle} - ${violation.label}` : violation.label;
1150
- return /* @__PURE__ */ React11.createElement(
1218
+ const displayLabel = elementTitle ?? violation.label;
1219
+ const angiePromptContext = elementTitle ? `${elementTitle} - ${violation.label}` : violation.label;
1220
+ const showsOwnFixAction = !!violation.angieFix && !sharedAngiePrompt;
1221
+ return /* @__PURE__ */ React12.createElement(
1151
1222
  Box7,
1152
1223
  {
1153
1224
  key: idx,
@@ -1160,265 +1231,130 @@ function ViolationRow({ audit: audit26, skipReason, violations }) {
1160
1231
  alignItems: "center",
1161
1232
  gap: 1,
1162
1233
  paddingBlock: 0.5,
1163
- paddingInline: 2,
1234
+ paddingInline: 1,
1164
1235
  borderRadius: 1,
1165
1236
  cursor: "pointer",
1166
- "& .violation-hover-icon": { opacity: 0, transition: "opacity .15s" },
1167
1237
  "&:hover": { bgcolor: "action.hover" },
1168
- "&:hover .violation-hover-icon, &:focus-visible .violation-hover-icon": {
1238
+ "&:hover .violation-row-fix-action, &:focus-visible .violation-row-fix-action": {
1169
1239
  opacity: 1
1170
1240
  }
1171
1241
  }
1172
1242
  },
1173
- /* @__PURE__ */ React11.createElement(ViolationIcon, { violation, widgetIcon }),
1174
- /* @__PURE__ */ React11.createElement(Box7, { sx: { flex: 1 } }, /* @__PURE__ */ React11.createElement(Typography6, { variant: "caption" }, rowLabel), violation.detail && /* @__PURE__ */ React11.createElement(Typography6, { variant: "caption", color: "text.secondary" }, violation.detail)),
1175
- violation.angieFix && /* @__PURE__ */ React11.createElement(
1176
- FixViolationWithAngie,
1177
- {
1178
- prompt: violation.angiePrompt ?? buildAngiePrompt(rowLabel)
1179
- }
1180
- ),
1181
- violation.ctaLabel && violation.externalUrl ? /* @__PURE__ */ React11.createElement(Box7, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React11.createElement(
1182
- ViolationCtaButton,
1183
- {
1184
- ctaLabel: violation.ctaLabel,
1185
- externalUrl: violation.externalUrl
1186
- }
1187
- ), violation.secondaryCtaLabel && violation.secondaryCtaUrl && /* @__PURE__ */ React11.createElement(
1188
- ViolationCtaButton,
1189
- {
1190
- ctaLabel: violation.secondaryCtaLabel,
1191
- externalUrl: violation.secondaryCtaUrl,
1192
- variant: "text"
1193
- }
1194
- )) : /* @__PURE__ */ React11.createElement(
1195
- EyeIcon,
1243
+ /* @__PURE__ */ React12.createElement(ViolationIcon, { violation, widgetIcon }),
1244
+ /* @__PURE__ */ React12.createElement(Box7, { sx: { flex: 1 } }, /* @__PURE__ */ React12.createElement(Typography6, { variant: "caption" }, displayLabel), violation.detail && /* @__PURE__ */ React12.createElement(Typography6, { variant: "caption", color: "text.secondary" }, violation.detail)),
1245
+ showsOwnFixAction && /* @__PURE__ */ React12.createElement(
1246
+ Box7,
1196
1247
  {
1197
- className: "violation-hover-icon",
1198
- fontSize: "tiny",
1199
- "aria-hidden": true
1200
- }
1248
+ className: "violation-row-fix-action",
1249
+ sx: { opacity: 0, transition: "opacity .15s" }
1250
+ },
1251
+ /* @__PURE__ */ React12.createElement(
1252
+ FixViolationWithAngie,
1253
+ {
1254
+ prompt: violation.angiePrompt ?? buildAngiePrompt(angiePromptContext),
1255
+ panelZIndex
1256
+ }
1257
+ )
1201
1258
  )
1202
1259
  );
1203
1260
  }))));
1204
1261
  }
1205
1262
 
1206
- // src/components/pages/all-audits-page.tsx
1207
- function AllAuditsPage({ initialExpandedStatus, onBack, report }) {
1208
- const { failed, passed, skipped, totalViolations } = partitionAuditResults(report);
1209
- const expandFail = !initialExpandedStatus || initialExpandedStatus === "fail";
1210
- const expandPass = initialExpandedStatus === "pass";
1211
- const expandSkipped = initialExpandedStatus === "skipped";
1212
- return /* @__PURE__ */ React12.createElement(Box8, { key: initialExpandedStatus ?? "default" }, /* @__PURE__ */ React12.createElement(SubpageHeader, { title: __12("All audits", "elementor"), onBack }), /* @__PURE__ */ React12.createElement(Box8, { sx: { p: 1 } }, /* @__PURE__ */ React12.createElement(
1213
- StatusSection,
1214
- {
1215
- label: auditStatusLabel("fail"),
1216
- count: totalViolations,
1217
- color: auditStatusColor("fail"),
1218
- defaultExpanded: expandFail
1219
- },
1220
- failed.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, violations: r.result.violations }))
1221
- ), /* @__PURE__ */ React12.createElement(
1222
- StatusSection,
1223
- {
1224
- label: auditStatusLabel("pass"),
1225
- count: passed.length,
1226
- color: auditStatusColor("pass"),
1227
- defaultExpanded: expandPass
1228
- },
1229
- passed.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit }))
1230
- ), /* @__PURE__ */ React12.createElement(
1231
- StatusSection,
1232
- {
1233
- label: auditStatusLabel("skipped"),
1234
- count: skipped.length,
1235
- color: auditStatusColor("skipped"),
1236
- defaultExpanded: expandSkipped
1237
- },
1238
- skipped.map((r) => /* @__PURE__ */ React12.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, skipReason: r.result.reason }))
1239
- )));
1240
- }
1241
-
1242
- // src/components/pages/category-page.tsx
1243
- import * as React13 from "react";
1244
- import { Box as Box9 } from "@elementor/ui";
1245
- import { __ as __13 } from "@wordpress/i18n";
1246
-
1247
- // src/components/category-icons.ts
1248
- import { BoltIcon, ElementorAccessibilityIcon, FileSearchIcon, Settings2Icon, ShieldCheckIcon as ShieldCheckIcon2 } from "@elementor/icons";
1249
- var CATEGORY_ICONS = {
1250
- "best-practices": Settings2Icon,
1251
- seo: FileSearchIcon,
1252
- accessibility: ElementorAccessibilityIcon,
1253
- performance: BoltIcon,
1254
- compliance: ShieldCheckIcon2
1255
- };
1256
-
1257
1263
  // src/components/pages/category-page.tsx
1258
1264
  function CategoryPage({ category, report, onBack }) {
1259
1265
  const Icon = CATEGORY_ICONS[category];
1260
- const { failed, passed, totalViolations } = partitionAuditResults(report, { category });
1266
+ const { failed, passed, skipped, totalViolations } = partitionAuditResults(report, { category });
1267
+ const { expandedId, toggle } = useSingleExpandedCard();
1261
1268
  return /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(
1262
1269
  SubpageHeader,
1263
1270
  {
1264
1271
  title: CATEGORY_LABELS[category],
1265
1272
  onBack,
1266
- backLabel: __13("Back to all issues", "elementor"),
1273
+ backLabel: __11("Back to all issues", "elementor"),
1267
1274
  icon: /* @__PURE__ */ React13.createElement(Icon, { fontSize: "small", color: "action" })
1268
1275
  }
1269
- ), /* @__PURE__ */ React13.createElement(Box9, { sx: { p: 1 } }, /* @__PURE__ */ React13.createElement(
1276
+ ), /* @__PURE__ */ React13.createElement(Box8, { sx: { p: 1 } }, /* @__PURE__ */ React13.createElement(
1270
1277
  StatusSection,
1271
1278
  {
1272
- label: __13("Failed audits", "elementor"),
1279
+ label: __11("Failed audits", "elementor"),
1273
1280
  count: totalViolations,
1274
1281
  color: "error",
1275
1282
  defaultExpanded: true
1276
1283
  },
1277
- failed.map((r) => /* @__PURE__ */ React13.createElement(ViolationRow, { key: r.audit.id, audit: r.audit, violations: r.result.violations }))
1278
- ), /* @__PURE__ */ React13.createElement(StatusSection, { label: __13("Passed audits", "elementor"), count: passed.length, color: "success" }, passed.map((r) => /* @__PURE__ */ React13.createElement(ViolationRow, { key: r.audit.id, audit: r.audit })))));
1284
+ failed.map((r) => /* @__PURE__ */ React13.createElement(
1285
+ ViolationRow,
1286
+ {
1287
+ key: r.audit.id,
1288
+ audit: r.audit,
1289
+ violations: r.result.violations,
1290
+ expanded: expandedId === r.audit.id,
1291
+ onToggleExpand: () => toggle(r.audit.id)
1292
+ }
1293
+ ))
1294
+ ), /* @__PURE__ */ React13.createElement(StatusSection, { label: __11("Passed audits", "elementor"), count: passed.length, color: "success" }, passed.map((r) => /* @__PURE__ */ React13.createElement(
1295
+ ViolationRow,
1296
+ {
1297
+ key: r.audit.id,
1298
+ audit: r.audit,
1299
+ expanded: expandedId === r.audit.id,
1300
+ onToggleExpand: () => toggle(r.audit.id)
1301
+ }
1302
+ ))), /* @__PURE__ */ React13.createElement(StatusSection, { label: __11("Skipped audits", "elementor"), count: skipped.length, color: "default" }, skipped.map((r) => /* @__PURE__ */ React13.createElement(
1303
+ ViolationRow,
1304
+ {
1305
+ key: r.audit.id,
1306
+ audit: r.audit,
1307
+ skipReason: r.result.reason,
1308
+ expanded: expandedId === r.audit.id,
1309
+ onToggleExpand: () => toggle(r.audit.id)
1310
+ }
1311
+ )))));
1279
1312
  }
1280
1313
 
1281
- // src/components/pages/issues-page.tsx
1282
- import * as React18 from "react";
1283
- import { useState as useState4 } from "react";
1284
- import { Box as Box14, Link, Typography as Typography10 } from "@elementor/ui";
1285
- import { __ as __17 } from "@wordpress/i18n";
1314
+ // src/components/pages/overview-page.tsx
1315
+ import * as React18 from "react";
1316
+ import { Box as Box13, Typography as Typography11 } from "@elementor/ui";
1317
+ import { __ as __15 } from "@wordpress/i18n";
1286
1318
 
1287
1319
  // src/utils/severity-counts.ts
1288
- import { __ as __14 } from "@wordpress/i18n";
1320
+ import { __ as __12 } from "@wordpress/i18n";
1289
1321
  var ALL_SEVERITIES = ["error", "warning", "info"];
1290
1322
  function countSeverities(report, category) {
1291
1323
  const counts = { error: 0, warning: 0, info: 0 };
1292
- for (const { audit: audit26, result } of report.auditResults) {
1324
+ for (const { audit: audit25, result } of report.auditResults) {
1293
1325
  if (result.status !== "fail") {
1294
1326
  continue;
1295
1327
  }
1296
- if (category && !audit26.categories.includes(category)) {
1328
+ if (category && !audit25.categories.includes(category)) {
1297
1329
  continue;
1298
1330
  }
1299
- if (!isScoredAudit(audit26)) {
1331
+ if (!isScoredAudit(audit25)) {
1300
1332
  continue;
1301
1333
  }
1302
- counts[audit26.severity] += result.violations.length;
1334
+ counts[audit25.severity] += result.violations.length;
1303
1335
  }
1304
1336
  return counts;
1305
1337
  }
1306
1338
  function severityPluralLabel(severity) {
1307
1339
  switch (severity) {
1308
1340
  case "error":
1309
- return __14("Errors", "elementor");
1341
+ return __12("Errors", "elementor");
1310
1342
  case "warning":
1311
- return __14("Warnings", "elementor");
1343
+ return __12("Warnings", "elementor");
1312
1344
  case "info":
1313
- return __14("Suggestions", "elementor");
1314
- }
1315
- }
1316
-
1317
- // src/components/audit-statuses-section.tsx
1318
- import * as React15 from "react";
1319
- import { Box as Box11, Divider, Typography as Typography8 } from "@elementor/ui";
1320
- import { __ as __15, sprintf as sprintf2 } from "@wordpress/i18n";
1321
-
1322
- // src/components/count-summary-circle.tsx
1323
- import * as React14 from "react";
1324
- import { Box as Box10, Chip as Chip2, Typography as Typography7 } from "@elementor/ui";
1325
- var COUNT_SUMMARY_CIRCLE_SIZE = 64;
1326
- var COUNT_SUMMARY_BORDER_WIDTH = 4;
1327
- function chipBorderColor(theme, color) {
1328
- if (color === "default" || !color) {
1329
- return theme.palette.text.secondary;
1345
+ return __12("Suggestions", "elementor");
1330
1346
  }
1331
- return theme.palette[color].main;
1332
- }
1333
- function CountSummaryCircle({ ariaLabel, color, count, label, onClick }) {
1334
- return /* @__PURE__ */ React14.createElement(
1335
- Box10,
1336
- {
1337
- "aria-label": ariaLabel,
1338
- component: "button",
1339
- onClick,
1340
- type: "button",
1341
- sx: {
1342
- alignItems: "center",
1343
- background: "none",
1344
- border: "none",
1345
- cursor: "pointer",
1346
- display: "flex",
1347
- flex: 1,
1348
- flexDirection: "column",
1349
- gap: 0.5,
1350
- padding: 0
1351
- }
1352
- },
1353
- /* @__PURE__ */ React14.createElement(
1354
- Chip2,
1355
- {
1356
- color,
1357
- label: count,
1358
- size: "small",
1359
- variant: "standard",
1360
- sx: (theme) => ({
1361
- border: `${COUNT_SUMMARY_BORDER_WIDTH}px solid ${chipBorderColor(theme, color)}`,
1362
- borderRadius: "50%",
1363
- height: COUNT_SUMMARY_CIRCLE_SIZE,
1364
- maxWidth: COUNT_SUMMARY_CIRCLE_SIZE,
1365
- width: COUNT_SUMMARY_CIRCLE_SIZE,
1366
- "& .MuiChip-label": {
1367
- fontSize: theme.typography.body2.fontSize,
1368
- fontWeight: 700,
1369
- lineHeight: 1,
1370
- padding: 0
1371
- }
1372
- })
1373
- }
1374
- ),
1375
- /* @__PURE__ */ React14.createElement(Typography7, { color: "text.secondary", sx: { textAlign: "center" }, variant: "caption" }, label)
1376
- );
1377
- }
1378
-
1379
- // src/components/audit-statuses-section.tsx
1380
- var STATUS_ARIA_LABELS = {
1381
- pass: (count) => sprintf2(
1382
- /* translators: %d: number of passed audits. */
1383
- __15("%d passed audits, view all", "elementor"),
1384
- count
1385
- ),
1386
- fail: (count) => sprintf2(
1387
- /* translators: %d: number of failed audit violations. */
1388
- __15("%d failed audits, view all", "elementor"),
1389
- count
1390
- ),
1391
- skipped: (count) => sprintf2(
1392
- /* translators: %d: number of skipped audits. */
1393
- __15("%d skipped audits, view all", "elementor"),
1394
- count
1395
- )
1396
- };
1397
- var STATUS_GROUPS = ["fail", "pass", "skipped"];
1398
- function AuditStatusesSection({ report, onStatusClick }) {
1399
- const statusCounts = auditStatusDisplayCounts(report);
1400
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Typography8, { variant: "subtitle1", fontWeight: "bold" }, __15("Audit statuses", "elementor")), /* @__PURE__ */ React15.createElement(Box11, { sx: { display: "flex", justifyContent: "space-around", gap: 2 } }, STATUS_GROUPS.map((status) => /* @__PURE__ */ React15.createElement(
1401
- CountSummaryCircle,
1402
- {
1403
- key: status,
1404
- ariaLabel: STATUS_ARIA_LABELS[status](statusCounts[status]),
1405
- color: auditStatusColor(status),
1406
- count: statusCounts[status],
1407
- label: auditStatusLabel(status),
1408
- onClick: () => onStatusClick(status)
1409
- }
1410
- ))), /* @__PURE__ */ React15.createElement(Divider, null));
1411
1347
  }
1412
1348
 
1413
1349
  // src/components/issues-category-row.tsx
1414
- import * as React16 from "react";
1350
+ import * as React14 from "react";
1415
1351
  import { ChevronRightIcon } from "@elementor/icons";
1416
- import { Box as Box12, Rotate as Rotate2, Typography as Typography9, useTheme as useTheme2 } from "@elementor/ui";
1352
+ import { Box as Box9, Rotate as Rotate2, Typography as Typography7, useTheme as useTheme2 } from "@elementor/ui";
1417
1353
  function IssuesCategoryRow({ category, label, counts, onClick }) {
1418
1354
  const isRtl = "rtl" === useTheme2().direction;
1419
1355
  const Icon = CATEGORY_ICONS[category];
1420
- return /* @__PURE__ */ React16.createElement(
1421
- Box12,
1356
+ return /* @__PURE__ */ React14.createElement(
1357
+ Box9,
1422
1358
  {
1423
1359
  role: "button",
1424
1360
  tabIndex: 0,
@@ -1439,112 +1375,21 @@ function IssuesCategoryRow({ category, label, counts, onClick }) {
1439
1375
  "&:focus-visible": { outline: "2px solid", outlineColor: "primary.main" }
1440
1376
  }
1441
1377
  },
1442
- /* @__PURE__ */ React16.createElement(Icon, { fontSize: "small", color: "action" }),
1443
- /* @__PURE__ */ React16.createElement(Typography9, { variant: "body2", fontWeight: "bold", sx: { flex: 1 } }, label),
1444
- /* @__PURE__ */ React16.createElement(Box12, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, ALL_SEVERITIES.filter((s) => counts[s] > 0).map((severity) => /* @__PURE__ */ React16.createElement(Box12, { key: severity, sx: { display: "flex", alignItems: "center", gap: 0.25 } }, /* @__PURE__ */ React16.createElement(SeverityIcon, { severity }), /* @__PURE__ */ React16.createElement(Typography9, { variant: "caption", color: "text.primary", fontWeight: "bold" }, counts[severity])))),
1445
- /* @__PURE__ */ React16.createElement(Rotate2, { in: isRtl }, /* @__PURE__ */ React16.createElement(ChevronRightIcon, { fontSize: "small", color: "action" }))
1446
- );
1447
- }
1448
-
1449
- // src/components/severity-filter-chips.tsx
1450
- import * as React17 from "react";
1451
- import { Box as Box13, Chip as Chip3 } from "@elementor/ui";
1452
- import { __ as __16 } from "@wordpress/i18n";
1453
- function SeverityFilterChips({ selected, onChange }) {
1454
- return /* @__PURE__ */ React17.createElement(Box13, { sx: { display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ React17.createElement(
1455
- FilterChip,
1456
- {
1457
- label: __16("All", "elementor"),
1458
- isSelected: selected === "all",
1459
- onClick: () => onChange("all")
1460
- }
1461
- ), ALL_SEVERITIES.map((severity) => /* @__PURE__ */ React17.createElement(
1462
- FilterChip,
1463
- {
1464
- key: severity,
1465
- label: severityPluralLabel(severity),
1466
- icon: /* @__PURE__ */ React17.createElement(SeverityIcon, { severity }),
1467
- isSelected: selected === severity,
1468
- onClick: () => onChange(severity)
1469
- }
1470
- )));
1471
- }
1472
- function FilterChip({ label, icon, isSelected, onClick }) {
1473
- return /* @__PURE__ */ React17.createElement(
1474
- Chip3,
1475
- {
1476
- label,
1477
- icon,
1478
- size: "small",
1479
- variant: "filled",
1480
- color: "default",
1481
- onClick,
1482
- "aria-pressed": isSelected,
1483
- sx: { bgcolor: isSelected ? "action.selected" : "transparent" }
1484
- }
1378
+ /* @__PURE__ */ React14.createElement(Icon, { fontSize: "small", color: "action" }),
1379
+ /* @__PURE__ */ React14.createElement(Typography7, { variant: "body2", fontWeight: "bold", sx: { flex: 1 } }, label),
1380
+ /* @__PURE__ */ React14.createElement(Box9, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, ALL_SEVERITIES.filter((s) => counts[s] > 0).map((severity) => /* @__PURE__ */ React14.createElement(Box9, { key: severity, sx: { display: "flex", alignItems: "center", gap: 0.25 } }, /* @__PURE__ */ React14.createElement(SeverityIcon, { severity }), /* @__PURE__ */ React14.createElement(Typography7, { variant: "caption", color: "text.primary", fontWeight: "bold" }, counts[severity])))),
1381
+ /* @__PURE__ */ React14.createElement(Rotate2, { in: isRtl }, /* @__PURE__ */ React14.createElement(ChevronRightIcon, { fontSize: "small", color: "action" }))
1485
1382
  );
1486
1383
  }
1487
1384
 
1488
- // src/components/pages/issues-page.tsx
1489
- function filterCounts(counts, filter) {
1490
- if (filter === "all") {
1491
- return counts;
1492
- }
1493
- return { error: 0, warning: 0, info: 0, [filter]: counts[filter] };
1494
- }
1495
- function IssuesPage({ report, onCategoryClick, onAllAuditsClick, onStatusClick }) {
1496
- const [filter, setFilter] = useState4("all");
1497
- const populatedCategories = getPopulatedCategories(report.categories, ALL_CATEGORIES);
1498
- const categoryRows = populatedCategories.map((category) => ({ category, counts: filterCounts(countSeverities(report, category), filter) })).filter(({ counts }) => Object.values(counts).some((count) => count > 0));
1499
- return /* @__PURE__ */ React18.createElement(Box14, { sx: { display: "flex", flexDirection: "column", gap: 4, p: 2 } }, /* @__PURE__ */ React18.createElement(AuditStatusesSection, { report, onStatusClick }), /* @__PURE__ */ React18.createElement(
1500
- Link,
1501
- {
1502
- component: "button",
1503
- underline: "none",
1504
- color: "inherit",
1505
- onClick: onAllAuditsClick,
1506
- sx: { textAlign: "start" }
1507
- },
1508
- /* @__PURE__ */ React18.createElement(Typography10, { variant: "subtitle1", component: "h2" }, __17("All issues", "elementor"))
1509
- ), /* @__PURE__ */ React18.createElement(SeverityFilterChips, { selected: filter, onChange: setFilter }), /* @__PURE__ */ React18.createElement(Box14, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, categoryRows.map(({ category, counts }) => /* @__PURE__ */ React18.createElement(
1510
- IssuesCategoryRow,
1511
- {
1512
- key: category,
1513
- category,
1514
- label: CATEGORY_LABELS[category],
1515
- counts,
1516
- onClick: () => onCategoryClick(category)
1517
- }
1518
- ))));
1519
- }
1520
-
1521
- // src/components/pages/overview-page.tsx
1522
- import * as React23 from "react";
1523
- import { Box as Box19, Chip as Chip4, Typography as Typography15 } from "@elementor/ui";
1524
- import { __ as __21 } from "@wordpress/i18n";
1525
-
1526
- // src/utils/score-thresholds.ts
1527
- import { __ as __18 } from "@wordpress/i18n";
1528
- var GOOD_THRESHOLD = 90;
1529
- var OK_THRESHOLD = 50;
1530
- function getScoreTier(score) {
1531
- if (score >= GOOD_THRESHOLD) {
1532
- return { color: "success", label: __18("Good", "elementor") };
1533
- }
1534
- if (score >= OK_THRESHOLD) {
1535
- return { color: "warning", label: __18("Needs work", "elementor") };
1536
- }
1537
- return { color: "error", label: __18("At risk", "elementor") };
1538
- }
1539
-
1540
1385
  // src/components/promotions.tsx
1541
- import * as React20 from "react";
1542
- import { Box as Box16, Divider as Divider2, Typography as Typography12 } from "@elementor/ui";
1543
- import { __ as __20 } from "@wordpress/i18n";
1386
+ import * as React16 from "react";
1387
+ import { Box as Box11, Divider, Typography as Typography9 } from "@elementor/ui";
1388
+ import { __ as __14 } from "@wordpress/i18n";
1544
1389
 
1545
1390
  // src/register-promotions.ts
1546
1391
  import { ElementorCookieIcon, PhotoIcon, ShieldHalfFilledIcon } from "@elementor/icons";
1547
- import { __ as __19, _n, sprintf as sprintf3 } from "@wordpress/i18n";
1392
+ import { __ as __13, _n, sprintf as sprintf2 } from "@wordpress/i18n";
1548
1393
  function firstFailExternalUrl(run) {
1549
1394
  return run.result.status === "fail" ? run.result.violations[0]?.externalUrl : void 0;
1550
1395
  }
@@ -1552,7 +1397,7 @@ var PROMOTIONS = [
1552
1397
  {
1553
1398
  auditId: "audits/images-alt-text",
1554
1399
  icon: PhotoIcon,
1555
- ctaLabel: __19("Fix with Ally", "elementor"),
1400
+ ctaLabel: __13("Fix with Ally", "elementor"),
1556
1401
  formatSubtitle: (run) => {
1557
1402
  if (run.result.status !== "fail") {
1558
1403
  return null;
@@ -1561,7 +1406,7 @@ var PROMOTIONS = [
1561
1406
  if (count === 0) {
1562
1407
  return null;
1563
1408
  }
1564
- return sprintf3(
1409
+ return sprintf2(
1565
1410
  /* translators: %d: number of images missing alt text. */
1566
1411
  _n("%d image", "%d images", count, "elementor"),
1567
1412
  count
@@ -1572,21 +1417,21 @@ var PROMOTIONS = [
1572
1417
  {
1573
1418
  auditId: "audits/cookie-policy",
1574
1419
  icon: ElementorCookieIcon,
1575
- ctaLabel: __19("Fix with Cookiez", "elementor"),
1576
- formatSubtitle: (run) => run.result.status === "fail" ? __19("Generate cookie policy", "elementor") : null,
1420
+ ctaLabel: __13("Fix with Cookiez", "elementor"),
1421
+ formatSubtitle: (run) => run.result.status === "fail" ? __13("Generate cookie policy", "elementor") : null,
1577
1422
  getCtaUrl: firstFailExternalUrl
1578
1423
  },
1579
1424
  {
1580
1425
  auditId: "audits/scan-for-cookies",
1581
1426
  icon: ElementorCookieIcon,
1582
- ctaLabel: __19("Scan", "elementor"),
1583
- formatSubtitle: (run) => run.result.status === "fail" ? __19("Scan this page for cookies", "elementor") : null,
1427
+ ctaLabel: __13("Scan", "elementor"),
1428
+ formatSubtitle: (run) => run.result.status === "fail" ? __13("Scan this page for cookies", "elementor") : null,
1584
1429
  getCtaUrl: firstFailExternalUrl
1585
1430
  },
1586
1431
  {
1587
1432
  auditId: "audits/images-too-large",
1588
1433
  icon: ShieldHalfFilledIcon,
1589
- ctaLabel: __19("Optimize all", "elementor"),
1434
+ ctaLabel: __13("Optimize all", "elementor"),
1590
1435
  formatSubtitle: (run) => {
1591
1436
  if (run.result.status !== "fail") {
1592
1437
  return null;
@@ -1595,33 +1440,13 @@ var PROMOTIONS = [
1595
1440
  if (count === 0) {
1596
1441
  return null;
1597
1442
  }
1598
- return sprintf3(
1443
+ return sprintf2(
1599
1444
  /* translators: %d: number of oversized images. */
1600
1445
  _n("%d image", "%d images", count, "elementor"),
1601
1446
  count
1602
1447
  );
1603
1448
  },
1604
1449
  getCtaUrl: firstFailExternalUrl
1605
- },
1606
- {
1607
- auditId: "audits/images-inefficient-format",
1608
- icon: ShieldHalfFilledIcon,
1609
- ctaLabel: __19("Optimize all", "elementor"),
1610
- formatSubtitle: (run) => {
1611
- if (run.result.status !== "fail") {
1612
- return null;
1613
- }
1614
- const count = run.result.metadata?.inefficientImageFormatCount ?? 0;
1615
- if (count === 0) {
1616
- return null;
1617
- }
1618
- return sprintf3(
1619
- /* translators: %d: number of images served in an inefficient format. */
1620
- _n("%d image", "%d images", count, "elementor"),
1621
- count
1622
- );
1623
- },
1624
- getCtaUrl: firstFailExternalUrl
1625
1450
  }
1626
1451
  ];
1627
1452
 
@@ -1631,11 +1456,11 @@ function findAuditRun(report, auditId) {
1631
1456
  }
1632
1457
 
1633
1458
  // src/components/promotion-card.tsx
1634
- import * as React19 from "react";
1635
- import { Box as Box15, Button as Button4, Typography as Typography11 } from "@elementor/ui";
1459
+ import * as React15 from "react";
1460
+ import { Box as Box10, Button as Button5, Typography as Typography8 } from "@elementor/ui";
1636
1461
  function PromotionCard({ ctaDisabled, ctaLabel, icon: Icon, onCtaClick, subtitle, title }) {
1637
- return /* @__PURE__ */ React19.createElement(
1638
- Box15,
1462
+ return /* @__PURE__ */ React15.createElement(
1463
+ Box10,
1639
1464
  {
1640
1465
  sx: {
1641
1466
  alignItems: "center",
@@ -1648,9 +1473,9 @@ function PromotionCard({ ctaDisabled, ctaLabel, icon: Icon, onCtaClick, subtitle
1648
1473
  py: 1.5
1649
1474
  }
1650
1475
  },
1651
- /* @__PURE__ */ React19.createElement(Icon, { fontSize: "small", color: "action" }),
1652
- /* @__PURE__ */ React19.createElement(Box15, { sx: { display: "flex", flex: 1, flexDirection: "column", gap: 0.25, minWidth: 0 } }, /* @__PURE__ */ React19.createElement(Typography11, { variant: "body2", fontWeight: "bold" }, title), /* @__PURE__ */ React19.createElement(Typography11, { variant: "caption", color: "text.secondary" }, subtitle)),
1653
- /* @__PURE__ */ React19.createElement(Button4, { variant: "outlined", color: "secondary", size: "small", disabled: ctaDisabled, onClick: onCtaClick }, ctaLabel)
1476
+ /* @__PURE__ */ React15.createElement(Icon, { fontSize: "small", color: "action" }),
1477
+ /* @__PURE__ */ React15.createElement(Box10, { sx: { display: "flex", flex: 1, flexDirection: "column", gap: 0.25, minWidth: 0 } }, /* @__PURE__ */ React15.createElement(Typography8, { variant: "body2", fontWeight: "bold" }, title), /* @__PURE__ */ React15.createElement(Typography8, { variant: "caption", color: "text.secondary" }, subtitle)),
1478
+ /* @__PURE__ */ React15.createElement(Button5, { variant: "outlined", color: "secondary", size: "small", disabled: ctaDisabled, onClick: onCtaClick }, ctaLabel)
1654
1479
  );
1655
1480
  }
1656
1481
 
@@ -1679,7 +1504,7 @@ function Promotions({ report }) {
1679
1504
  if (cards.length === 0) {
1680
1505
  return null;
1681
1506
  }
1682
- return /* @__PURE__ */ React20.createElement(React20.Fragment, null, /* @__PURE__ */ React20.createElement(Divider2, null), /* @__PURE__ */ React20.createElement(Typography12, { variant: "subtitle1", fontWeight: "bold" }, __20("Quick actions", "elementor")), /* @__PURE__ */ React20.createElement(Box16, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, cards.map(({ config, ctaUrl, key, run, subtitle }) => /* @__PURE__ */ React20.createElement(
1507
+ return /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Divider, null), /* @__PURE__ */ React16.createElement(Typography9, { variant: "subtitle1", fontWeight: "bold" }, __14("Quick actions", "elementor")), /* @__PURE__ */ React16.createElement(Box11, { sx: { display: "flex", flexDirection: "column", gap: 1 } }, cards.map(({ config, ctaUrl, key, run, subtitle }) => /* @__PURE__ */ React16.createElement(
1683
1508
  PromotionCard,
1684
1509
  {
1685
1510
  key,
@@ -1697,241 +1522,118 @@ function Promotions({ report }) {
1697
1522
  ))));
1698
1523
  }
1699
1524
 
1700
- // src/components/score-bar.tsx
1701
- import * as React21 from "react";
1702
- import { ChevronRightIcon as ChevronRightIcon2 } from "@elementor/icons";
1703
- import { Box as Box17, LinearProgress, Rotate as Rotate3, Typography as Typography13, useTheme as useTheme3 } from "@elementor/ui";
1704
- function ScoreBar({ label, score, onClick }) {
1705
- const isRtl = "rtl" === useTheme3().direction;
1706
- return /* @__PURE__ */ React21.createElement(
1707
- Box17,
1708
- {
1709
- role: onClick ? "button" : void 0,
1710
- tabIndex: onClick ? 0 : void 0,
1711
- onClick,
1712
- onKeyDown: onClick ? onKeyboardClick(onClick) : void 0,
1713
- sx: {
1714
- display: "flex",
1715
- alignItems: "center",
1716
- gap: 1,
1717
- cursor: onClick ? "pointer" : "default",
1718
- outline: "none",
1719
- "&:focus-visible": { outline: "2px solid", outlineColor: "primary.main", borderRadius: 1 },
1720
- py: 0.5
1721
- }
1722
- },
1723
- /* @__PURE__ */ React21.createElement(Typography13, { variant: "body2", sx: { minWidth: 96 } }, label),
1724
- /* @__PURE__ */ React21.createElement(
1725
- LinearProgress,
1726
- {
1727
- variant: "determinate",
1728
- value: score,
1729
- color: getScoreTier(score).color,
1730
- sx: { flex: 1, height: 6, borderRadius: 4, bgcolor: "action.disabledBackground" }
1731
- }
1732
- ),
1733
- /* @__PURE__ */ React21.createElement(
1734
- Typography13,
1735
- {
1736
- variant: "body2",
1737
- color: "text.primary",
1738
- sx: { minWidth: 24, textAlign: "right", fontWeight: 900 }
1739
- },
1740
- score
1741
- ),
1742
- onClick && /* @__PURE__ */ React21.createElement(Rotate3, { in: isRtl }, /* @__PURE__ */ React21.createElement(ChevronRightIcon2, { fontSize: "small", color: "action" }))
1743
- );
1525
+ // src/components/severity-summary-cards.tsx
1526
+ import * as React17 from "react";
1527
+ import { Box as Box12, Typography as Typography10 } from "@elementor/ui";
1528
+ function SeveritySummaryCards({ report }) {
1529
+ const counts = countSeverities(report);
1530
+ return /* @__PURE__ */ React17.createElement(Box12, { display: "flex", gap: 1.5 }, ALL_SEVERITIES.map((severity) => /* @__PURE__ */ React17.createElement(SeverityCard, { key: severity, severity, count: counts[severity] })));
1744
1531
  }
1745
-
1746
- // src/components/score-circle.tsx
1747
- import * as React22 from "react";
1748
- import { Box as Box18, CircularProgress as CircularProgress2, Typography as Typography14 } from "@elementor/ui";
1749
- var SCORE_CIRCLE_SIZE = 88;
1750
- var SCORE_CIRCLE_THICKNESS = 3;
1751
- function ScoreCircle({ color, score }) {
1752
- const progressColor = color ?? getScoreTier(score).color;
1753
- return /* @__PURE__ */ React22.createElement(
1754
- Box18,
1532
+ function SeverityCard({ severity, count }) {
1533
+ return /* @__PURE__ */ React17.createElement(
1534
+ Box12,
1755
1535
  {
1756
- role: "progressbar",
1757
- "aria-valuenow": score,
1758
- "aria-valuemin": 0,
1759
- "aria-valuemax": 100,
1760
- sx: { position: "relative", display: "inline-flex" }
1536
+ display: "flex",
1537
+ flex: 1,
1538
+ flexDirection: "column",
1539
+ gap: 1,
1540
+ bgcolor: "action.hover",
1541
+ borderRadius: 1.5,
1542
+ px: 1.5,
1543
+ py: 1
1761
1544
  },
1762
- /* @__PURE__ */ React22.createElement(
1763
- CircularProgress2,
1764
- {
1765
- variant: "determinate",
1766
- value: 100,
1767
- size: SCORE_CIRCLE_SIZE,
1768
- thickness: SCORE_CIRCLE_THICKNESS,
1769
- sx: { color: "action.disabledBackground" }
1770
- }
1771
- ),
1772
- /* @__PURE__ */ React22.createElement(
1773
- CircularProgress2,
1774
- {
1775
- variant: "determinate",
1776
- value: score,
1777
- size: SCORE_CIRCLE_SIZE,
1778
- thickness: SCORE_CIRCLE_THICKNESS,
1779
- color: progressColor,
1780
- sx: { position: "absolute", left: 0 }
1781
- }
1782
- ),
1783
- /* @__PURE__ */ React22.createElement(
1784
- Box18,
1785
- {
1786
- sx: {
1787
- position: "absolute",
1788
- inset: 0,
1789
- display: "flex",
1790
- alignItems: "center",
1791
- justifyContent: "center"
1792
- }
1793
- },
1794
- /* @__PURE__ */ React22.createElement(
1795
- Typography14,
1796
- {
1797
- variant: "h4",
1798
- component: "span",
1799
- color: "text.primary",
1800
- sx: { fontWeight: 700, lineHeight: 1 }
1801
- },
1802
- score
1803
- )
1804
- )
1545
+ /* @__PURE__ */ React17.createElement(SeverityIcon, { severity }),
1546
+ /* @__PURE__ */ React17.createElement(Box12, { display: "flex", flexDirection: "column" }, /* @__PURE__ */ React17.createElement(Typography10, { variant: "h5", fontWeight: 700, component: "span" }, count), /* @__PURE__ */ React17.createElement(Typography10, { variant: "caption", color: "text.secondary" }, severityPluralLabel(severity)))
1805
1547
  );
1806
1548
  }
1807
1549
 
1808
1550
  // src/components/pages/overview-page.tsx
1809
1551
  function OverviewPage({ onCategoryClick, report }) {
1810
1552
  const populatedCategories = getPopulatedCategories(report.categories, ALL_CATEGORIES);
1811
- const overallScore = getScoreTier(report.overall);
1812
- return /* @__PURE__ */ React23.createElement(Box19, { sx: { display: "flex", flexDirection: "column", gap: 4, p: 2 } }, /* @__PURE__ */ React23.createElement(Box19, { sx: { display: "flex", alignItems: "center", gap: 2 } }, /* @__PURE__ */ React23.createElement(ScoreCircle, { color: overallScore.color, score: report.overall }), /* @__PURE__ */ React23.createElement(Box19, { sx: { display: "flex", flexDirection: "column", gap: 0.5 } }, /* @__PURE__ */ React23.createElement(
1813
- Chip4,
1814
- {
1815
- label: overallScore.label,
1816
- color: overallScore.color,
1817
- variant: "standard",
1818
- size: "small",
1819
- sx: { fontWeight: 600, alignSelf: "flex-start" }
1820
- }
1821
- ), /* @__PURE__ */ React23.createElement(Typography15, { variant: "body2", color: "text.secondary" }, __21("Overall score", "elementor")))), /* @__PURE__ */ React23.createElement(Box19, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, populatedCategories.map((category) => /* @__PURE__ */ React23.createElement(
1822
- ScoreBar,
1553
+ const categoryRows = populatedCategories.map((category) => ({ category, counts: countSeverities(report, category) })).filter(({ counts }) => Object.values(counts).some((count) => count > 0));
1554
+ return /* @__PURE__ */ React18.createElement(Box13, { display: "flex", flexDirection: "column", gap: 3, p: 2 }, /* @__PURE__ */ React18.createElement(SeveritySummaryCards, { report }), /* @__PURE__ */ React18.createElement(Box13, { display: "flex", flexDirection: "column", gap: 1 }, /* @__PURE__ */ React18.createElement(Typography11, { variant: "subtitle1", component: "h2" }, __15("All issues", "elementor")), /* @__PURE__ */ React18.createElement(Box13, { display: "flex", flexDirection: "column", gap: 1 }, categoryRows.map(({ category, counts }) => /* @__PURE__ */ React18.createElement(
1555
+ IssuesCategoryRow,
1823
1556
  {
1824
1557
  key: category,
1558
+ category,
1825
1559
  label: CATEGORY_LABELS[category],
1826
- score: report.categories[category].score,
1560
+ counts,
1827
1561
  onClick: () => onCategoryClick(category)
1828
1562
  }
1829
- ))), /* @__PURE__ */ React23.createElement(Promotions, { report }));
1563
+ )))), /* @__PURE__ */ React18.createElement(Promotions, { report }));
1830
1564
  }
1831
1565
 
1832
1566
  // src/components/report-shell.tsx
1833
- function activeTab(page) {
1834
- if (page === "overview") {
1835
- return "overview";
1836
- }
1837
- if (page === "issues") {
1838
- return "issues";
1839
- }
1840
- return page.backTo;
1841
- }
1842
- function isAllAuditsPage(page) {
1843
- return typeof page === "object" && "page" in page && page.page === "all-audits";
1844
- }
1845
1567
  function isCategoryPage(page) {
1846
1568
  return typeof page === "object" && "category" in page;
1847
1569
  }
1848
1570
  function ReportShell({ report }) {
1849
- const [activePage, setActivePage] = useState5("overview");
1850
- const currentTab = activeTab(activePage);
1851
- const handleTabChange = (_, value) => {
1852
- setActivePage(value);
1853
- };
1854
- const openCategory = (category, backTo) => {
1855
- setActivePage({ category, backTo });
1856
- };
1857
- const openAllAudits = (expand, backTo = "issues") => {
1858
- setActivePage({ page: "all-audits", expand, backTo });
1859
- };
1860
- const backFromSubPage = () => {
1861
- if (isAllAuditsPage(activePage) || isCategoryPage(activePage)) {
1862
- setActivePage(activePage.backTo);
1863
- }
1864
- };
1865
- return /* @__PURE__ */ React24.createElement(Box20, { sx: { display: "flex", flexDirection: "column", flex: 1, minHeight: 0 } }, /* @__PURE__ */ React24.createElement(Box20, { sx: { flexShrink: 0 } }, /* @__PURE__ */ React24.createElement(
1866
- Tabs,
1571
+ const [activePage, setActivePage] = useState4("overview");
1572
+ const openCategory = (category) => setActivePage({ category });
1573
+ const backToOverview = () => setActivePage("overview");
1574
+ return /* @__PURE__ */ React19.createElement(Box14, { 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 }));
1575
+ }
1576
+
1577
+ // src/components/rescan-button.tsx
1578
+ import * as React20 from "react";
1579
+ import { useFloatingPanelZIndex as useFloatingPanelZIndex3 } from "@elementor/editor-floating-panels";
1580
+ import { ReloadIcon } from "@elementor/icons";
1581
+ import { Badge as Badge2, Button as Button6, Tooltip as Tooltip4 } from "@elementor/ui";
1582
+ import { __ as __16 } from "@wordpress/i18n";
1583
+ function RescanButton({ hasScanned, isStale, disabled, onClick }) {
1584
+ const panelZIndex = useFloatingPanelZIndex3(AUDIT_PANEL_ID);
1585
+ return /* @__PURE__ */ React20.createElement(
1586
+ Tooltip4,
1867
1587
  {
1868
- "aria-label": __22("Audit navigation", "elementor"),
1869
- value: currentTab,
1870
- onChange: handleTabChange,
1871
- textColor: "secondary",
1872
- indicatorColor: "secondary",
1873
- size: "small",
1874
- centered: true,
1875
- variant: "fullWidth"
1588
+ title: __16("Page content changed - rescan advised", "elementor"),
1589
+ disableHoverListener: !isStale,
1590
+ PopperProps: { sx: { zIndex: panelZIndex } }
1876
1591
  },
1877
- /* @__PURE__ */ React24.createElement(Tab, { value: "overview", label: __22("Overview", "elementor") }),
1878
- /* @__PURE__ */ React24.createElement(Tab, { value: "issues", label: __22("Issues", "elementor") })
1879
- ), /* @__PURE__ */ React24.createElement(Divider3, null)), /* @__PURE__ */ React24.createElement(Box20, { sx: { flex: 1, minHeight: 0, overflowY: "auto" } }, activePage === "overview" && /* @__PURE__ */ React24.createElement(
1880
- OverviewPage,
1881
- {
1882
- report,
1883
- onCategoryClick: (category) => openCategory(category, "overview")
1884
- }
1885
- ), activePage === "issues" && /* @__PURE__ */ React24.createElement(
1886
- IssuesPage,
1887
- {
1888
- report,
1889
- onCategoryClick: (category) => openCategory(category, "issues"),
1890
- onAllAuditsClick: () => openAllAudits(),
1891
- onStatusClick: (status) => openAllAudits(status)
1892
- }
1893
- ), isAllAuditsPage(activePage) && /* @__PURE__ */ React24.createElement(
1894
- AllAuditsPage,
1895
- {
1896
- report,
1897
- initialExpandedStatus: activePage.expand,
1898
- onBack: backFromSubPage
1899
- }
1900
- ), isCategoryPage(activePage) && /* @__PURE__ */ React24.createElement(CategoryPage, { category: activePage.category, report, onBack: backFromSubPage })));
1592
+ /* @__PURE__ */ React20.createElement(Badge2, { variant: "dot", color: "warning", invisible: !isStale, overlap: "rectangular" }, /* @__PURE__ */ React20.createElement(
1593
+ Button6,
1594
+ {
1595
+ variant: "contained",
1596
+ size: "small",
1597
+ startIcon: hasScanned ? /* @__PURE__ */ React20.createElement(ReloadIcon, { fontSize: "small" }) : void 0,
1598
+ onClick,
1599
+ disabled
1600
+ },
1601
+ hasScanned ? __16("Rescan", "elementor") : __16("Run page audit", "elementor")
1602
+ ))
1603
+ );
1901
1604
  }
1902
1605
 
1903
1606
  // src/components/audit-panel.tsx
1904
1607
  function AuditPanel() {
1905
- const { status, report, error, run } = useAuditReport();
1906
- const currentDocumentId = getCurrentDocumentId2() ?? 0;
1907
- const onRun = () => run(currentDocumentId);
1608
+ const { status, report, error, isStale, run } = useAuditReport();
1609
+ const hostDocumentId = getHostDocumentId3() ?? 0;
1610
+ const onRun = () => run(hostDocumentId);
1908
1611
  const lastScanLabel = report ? new Date(report.runAt).toLocaleTimeString() : null;
1909
- return /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement(
1612
+ return /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(
1910
1613
  FloatingPanelHeader,
1911
1614
  {
1912
- panelId: "audit-panel",
1913
- title: __23("Page Audit", "elementor"),
1914
- badge: __23("Beta", "elementor"),
1615
+ panelId: AUDIT_PANEL_ID,
1616
+ title: __17("Page Audit", "elementor"),
1617
+ badge: __17("Beta", "elementor"),
1915
1618
  titleVariant: "subtitle2"
1916
1619
  }
1917
- ), /* @__PURE__ */ React25.createElement(
1620
+ ), /* @__PURE__ */ React21.createElement(
1918
1621
  FloatingPanelBody,
1919
1622
  {
1920
1623
  sx: status === "ready" ? { display: "flex", flexDirection: "column", overflow: "hidden", minHeight: 0 } : void 0
1921
1624
  },
1922
- status === "idle" && /* @__PURE__ */ React25.createElement(WelcomePage, null),
1923
- status === "loading" && /* @__PURE__ */ React25.createElement(LoadingPage, null),
1924
- status === "error" && /* @__PURE__ */ React25.createElement(ErrorPage, { message: error ?? "", onRetry: onRun }),
1925
- status === "ready" && report && /* @__PURE__ */ React25.createElement(ReportShell, { report })
1926
- ), /* @__PURE__ */ React25.createElement(FloatingPanelFooter, null, lastScanLabel ? /* @__PURE__ */ React25.createElement(Typography16, { variant: "caption", sx: { flex: 1 } }, __23("Last scan:", "elementor"), " ", lastScanLabel) : /* @__PURE__ */ React25.createElement(Box21, { sx: { flex: 1 } }), lastScanLabel && /* @__PURE__ */ React25.createElement(AuditFeedback, null), /* @__PURE__ */ React25.createElement(
1927
- Button5,
1625
+ status === "idle" && /* @__PURE__ */ React21.createElement(WelcomePage, null),
1626
+ status === "loading" && /* @__PURE__ */ React21.createElement(LoadingPage, null),
1627
+ status === "error" && /* @__PURE__ */ React21.createElement(ErrorPage, { message: error ?? "", onRetry: onRun }),
1628
+ status === "ready" && report && /* @__PURE__ */ React21.createElement(ReportShell, { report })
1629
+ ), /* @__PURE__ */ React21.createElement(FloatingPanelFooter, null, lastScanLabel ? /* @__PURE__ */ React21.createElement(Typography12, { variant: "caption", sx: { flex: 1 } }, __17("Last scan:", "elementor"), " ", lastScanLabel) : /* @__PURE__ */ React21.createElement(Box15, { sx: { flex: 1 } }), lastScanLabel && /* @__PURE__ */ React21.createElement(AuditFeedback, null), /* @__PURE__ */ React21.createElement(
1630
+ RescanButton,
1928
1631
  {
1929
- variant: "contained",
1930
- size: "small",
1931
- onClick: onRun,
1932
- disabled: status === "loading" || currentDocumentId === 0
1933
- },
1934
- lastScanLabel ? __23("Re-scan", "elementor") : __23("Run page audit", "elementor")
1632
+ hasScanned: Boolean(lastScanLabel),
1633
+ isStale,
1634
+ disabled: status === "loading" || hostDocumentId === 0,
1635
+ onClick: onRun
1636
+ }
1935
1637
  )));
1936
1638
  }
1937
1639
 
@@ -1956,8 +1658,8 @@ function useAuditToggleProps() {
1956
1658
  const { isOpen } = auditPanel.useFloatingPanelStatus();
1957
1659
  const { toggle } = auditPanel.useFloatingPanelActions();
1958
1660
  return {
1959
- title: __24("Audit Page", "elementor"),
1960
- icon: ShieldCheckIcon3,
1661
+ title: __18("Audit Page", "elementor"),
1662
+ icon: AuditToggleIcon,
1961
1663
  selected: isOpen,
1962
1664
  onClick: () => toggle()
1963
1665
  };
@@ -1974,15 +1676,15 @@ function registerAppBarAuditsToggle() {
1974
1676
  }
1975
1677
 
1976
1678
  // src/audits/accessibility-policy.ts
1977
- import { __ as __25 } from "@wordpress/i18n";
1679
+ import { __ as __19 } from "@wordpress/i18n";
1978
1680
  var audit = {
1979
1681
  id: "audits/accessibility-policy",
1980
- title: __25("Accessibility policy", "elementor"),
1981
- description: __25(
1682
+ title: __19("Accessibility policy", "elementor"),
1683
+ description: __19(
1982
1684
  "An accessibility policy demonstrates your commitment to digital inclusion and may be legally required in some regions.",
1983
1685
  "elementor"
1984
1686
  ),
1985
- fixHint: __25("Install the Ally plugin to add an accessibility policy to your website.", "elementor"),
1687
+ fixHint: __19("Install the Ally plugin to add an accessibility policy to your website.", "elementor"),
1986
1688
  categories: ["compliance"],
1987
1689
  severity: "info",
1988
1690
  weight: 1,
@@ -1995,7 +1697,7 @@ var audit = {
1995
1697
  violations: [
1996
1698
  {
1997
1699
  auditId: audit.id,
1998
- label: __25("Ally plugin is not installed or active.", "elementor"),
1700
+ label: __19("Ally plugin is not installed or active.", "elementor"),
1999
1701
  externalUrl: ctx.pageContext.ally_plugin_url
2000
1702
  }
2001
1703
  ]
@@ -2004,15 +1706,15 @@ var audit = {
2004
1706
  };
2005
1707
 
2006
1708
  // src/audits/accessibility-statement.ts
2007
- import { __ as __26 } from "@wordpress/i18n";
1709
+ import { __ as __20 } from "@wordpress/i18n";
2008
1710
  var audit2 = {
2009
1711
  id: "audits/accessibility-statement",
2010
- title: __26("Create an accessibility statement", "elementor"),
2011
- description: __26(
1712
+ title: __20("Create an accessibility statement", "elementor"),
1713
+ description: __20(
2012
1714
  "An accessibility statement showcases your efforts to create an inclusive online space, highlighting helpful features and a commitment to accessibility.",
2013
1715
  "elementor"
2014
1716
  ),
2015
- fixHint: __26("Use Ally to create and publish an accessibility statement for your site.", "elementor"),
1717
+ fixHint: __20("Use Ally to create and publish an accessibility statement for your site.", "elementor"),
2016
1718
  categories: ["accessibility"],
2017
1719
  severity: "info",
2018
1720
  weight: 1,
@@ -2026,11 +1728,11 @@ var audit2 = {
2026
1728
  violations: [
2027
1729
  {
2028
1730
  auditId: audit2.id,
2029
- label: __26("No accessibility statement has been created for this site.", "elementor"),
1731
+ label: __20("No accessibility statement has been created for this site.", "elementor"),
2030
1732
  externalUrl: isReady ? ctx.pageContext.ally_accessibility_statement_url : ctx.pageContext.ally_plugin_url,
2031
- ctaLabel: isReady ? __26("Create", "elementor") : void 0,
2032
- secondaryCtaLabel: __26("Learn more", "elementor"),
2033
- secondaryCtaUrl: "https://go.elementor.com/acc-plg-learn-more"
1733
+ ctaLabel: __20("Create", "elementor"),
1734
+ secondaryCtaLabel: __20("Learn more", "elementor"),
1735
+ secondaryCtaUrl: "https://elementor.com/help/start-using-ally/#statement"
2034
1736
  }
2035
1737
  ]
2036
1738
  };
@@ -2038,7 +1740,7 @@ var audit2 = {
2038
1740
  };
2039
1741
 
2040
1742
  // src/audits/accessibility-widget.ts
2041
- import { __ as __27 } from "@wordpress/i18n";
1743
+ import { __ as __21 } from "@wordpress/i18n";
2042
1744
 
2043
1745
  // src/utils/scan-accessibility-widgets.ts
2044
1746
  var ACCESSIBILITY_WIDGET_SCRIPT_SOURCES = ["cdn.elementor.com/a11y/widget.js"];
@@ -2049,18 +1751,18 @@ function hasAccessibilityWidgetScript(html) {
2049
1751
  // src/audits/accessibility-widget.ts
2050
1752
  var audit3 = {
2051
1753
  id: "audits/accessibility-widget",
2052
- title: __27("Add an accessibility widget", "elementor"),
2053
- description: __27("Make your site more inclusive by adding an accessibility widget.", "elementor"),
2054
- fixHint: __27("Activate Ally\u2019s accessibility widget to give visitors on-page accessibility tools.", "elementor"),
1754
+ title: __21("Add an accessibility widget", "elementor"),
1755
+ description: __21("Make your site more inclusive by adding an accessibility widget.", "elementor"),
1756
+ fixHint: __21("Activate Ally\u2019s accessibility widget to give visitors on-page accessibility tools.", "elementor"),
2055
1757
  categories: ["accessibility"],
2056
1758
  severity: "info",
2057
1759
  weight: 1,
2058
1760
  evaluate: (ctx) => {
2059
1761
  if (!ctx.pageContext.frontend_url) {
2060
- return { status: "skipped", reason: __27("Page is not published.", "elementor") };
1762
+ return { status: "skipped", reason: __21("Page is not published.", "elementor") };
2061
1763
  }
2062
1764
  if (!ctx.renderedHtml) {
2063
- return { status: "skipped", reason: __27("Could not fetch the published page.", "elementor") };
1765
+ return { status: "skipped", reason: __21("Could not fetch the published page.", "elementor") };
2064
1766
  }
2065
1767
  if (hasAccessibilityWidgetScript(ctx.renderedHtml)) {
2066
1768
  return { status: "pass" };
@@ -2071,9 +1773,9 @@ var audit3 = {
2071
1773
  violations: [
2072
1774
  {
2073
1775
  auditId: audit3.id,
2074
- label: __27("No accessibility widget is active on this site.", "elementor"),
1776
+ label: __21("No accessibility widget is active on this site.", "elementor"),
2075
1777
  externalUrl: isReady ? ctx.pageContext.ally_widget_settings_url : ctx.pageContext.ally_plugin_url,
2076
- ctaLabel: __27("Activate", "elementor")
1778
+ ctaLabel: __21("Activate", "elementor")
2077
1779
  }
2078
1780
  ]
2079
1781
  };
@@ -2081,15 +1783,15 @@ var audit3 = {
2081
1783
  };
2082
1784
 
2083
1785
  // src/audits/cookie-policy.ts
2084
- import { __ as __28 } from "@wordpress/i18n";
1786
+ import { __ as __22 } from "@wordpress/i18n";
2085
1787
  var audit4 = {
2086
1788
  id: "audits/cookie-policy",
2087
- title: __28("Cookie policy", "elementor"),
2088
- description: __28(
1789
+ title: __22("Cookie policy", "elementor"),
1790
+ description: __22(
2089
1791
  "A cookie policy is required by privacy regulations such as GDPR (EU) or CCPA (US) to inform visitors about tracking and data collection.",
2090
1792
  "elementor"
2091
1793
  ),
2092
- fixHint: __28("Install the Cookiez plugin to add a cookie policy to your website.", "elementor"),
1794
+ fixHint: __22("Install the Cookiez plugin to add a cookie policy to your website.", "elementor"),
2093
1795
  categories: ["compliance"],
2094
1796
  severity: "info",
2095
1797
  weight: 1,
@@ -2102,8 +1804,9 @@ var audit4 = {
2102
1804
  violations: [
2103
1805
  {
2104
1806
  auditId: audit4.id,
2105
- label: __28("Cookiez plugin is not installed or active.", "elementor"),
2106
- externalUrl: ctx.pageContext.cookiez_plugin_url
1807
+ label: __22("Cookiez plugin is not installed or active.", "elementor"),
1808
+ externalUrl: ctx.pageContext.cookiez_plugin_url,
1809
+ ctaLabel: __22("Set up cookie consent", "elementor")
2107
1810
  }
2108
1811
  ]
2109
1812
  };
@@ -2112,7 +1815,7 @@ var audit4 = {
2112
1815
 
2113
1816
  // src/audits/deep-nesting.ts
2114
1817
  import { getWidgetsCache } from "@elementor/editor-elements";
2115
- import { __ as __29 } from "@wordpress/i18n";
1818
+ import { __ as __23 } from "@wordpress/i18n";
2116
1819
  var MAX_NESTING_DEPTH = 6;
2117
1820
  function isNestingElement(node, widgetsCache) {
2118
1821
  if (node.elType === "container") {
@@ -2122,18 +1825,18 @@ function isNestingElement(node, widgetsCache) {
2122
1825
  }
2123
1826
  var audit5 = {
2124
1827
  id: "audits/deep-nesting",
2125
- title: __29("Deep container nesting", "elementor"),
2126
- description: __29(
1828
+ title: __23("Deep container nesting", "elementor"),
1829
+ description: __23(
2127
1830
  "Container nesting exceeds the recommended depth. Deep DOM trees hurt rendering performance and readability.",
2128
1831
  "elementor"
2129
1832
  ),
2130
- fixHint: __29("Flatten the layout by removing unnecessary wrapper containers.", "elementor"),
1833
+ fixHint: __23("Flatten the layout by removing unnecessary wrapper containers.", "elementor"),
2131
1834
  categories: ["performance", "best-practices"],
2132
1835
  severity: "warning",
2133
1836
  weight: 5,
2134
1837
  evaluate: (ctx) => {
2135
1838
  if (ctx.elements.tree.length === 0) {
2136
- return { status: "skipped", reason: __29("No elements", "elementor") };
1839
+ return { status: "skipped", reason: __23("No elements", "elementor") };
2137
1840
  }
2138
1841
  const widgetsCache = getWidgetsCache();
2139
1842
  const violations = [];
@@ -2147,7 +1850,7 @@ var audit5 = {
2147
1850
  auditId: audit5.id,
2148
1851
  elementId: node.id,
2149
1852
  targetHint: "element-settings",
2150
- label: __29("Container nesting is too deep.", "elementor")
1853
+ label: __23("Container nesting is too deep.", "elementor")
2151
1854
  });
2152
1855
  }
2153
1856
  });
@@ -2156,15 +1859,15 @@ var audit5 = {
2156
1859
  };
2157
1860
 
2158
1861
  // src/audits/default-design-system.ts
2159
- import { __ as __30 } from "@wordpress/i18n";
1862
+ import { __ as __24 } from "@wordpress/i18n";
2160
1863
  var audit6 = {
2161
1864
  id: "audits/default-design-system",
2162
- title: __30("Default website kit", "elementor"),
2163
- description: __30(
1865
+ title: __24("Default website kit", "elementor"),
1866
+ description: __24(
2164
1867
  "Your website is using the default design system colors and fonts. Custom branding makes the website feel uniquely yours.",
2165
1868
  "elementor"
2166
1869
  ),
2167
- fixHint: __30("Open Site Settings and customize your kit (colors, fonts, layout).", "elementor"),
1870
+ fixHint: __24("Open Site Settings and customize your kit (colors, fonts, layout).", "elementor"),
2168
1871
  categories: ["best-practices"],
2169
1872
  severity: "info",
2170
1873
  weight: 1,
@@ -2177,7 +1880,7 @@ var audit6 = {
2177
1880
  violations: [
2178
1881
  {
2179
1882
  auditId: audit6.id,
2180
- label: __30("Kit appears unchanged from default.", "elementor"),
1883
+ label: __24("Kit appears unchanged from default.", "elementor"),
2181
1884
  targetHint: "site-settings"
2182
1885
  }
2183
1886
  ]
@@ -2187,16 +1890,16 @@ var audit6 = {
2187
1890
 
2188
1891
  // src/audits/deprecated-widgets.ts
2189
1892
  import { getWidgetsCache as getWidgetsCache2 } from "@elementor/editor-elements";
2190
- import { __ as __31 } from "@wordpress/i18n";
1893
+ import { __ as __25 } from "@wordpress/i18n";
2191
1894
  var DEPRECATION_CONTROL_NAME = "deprecation_message";
2192
1895
  var audit7 = {
2193
1896
  id: "audits/deprecated-widgets",
2194
- title: __31("Deprecated widgets", "elementor"),
2195
- description: __31(
1897
+ title: __25("Deprecated widgets", "elementor"),
1898
+ description: __25(
2196
1899
  "Deprecated widgets should not be used. For better capabilities use the recommended replacement.",
2197
1900
  "elementor"
2198
1901
  ),
2199
- fixHint: __31("Replace each deprecated widget with the recommended new widget shown in its panel.", "elementor"),
1902
+ fixHint: __25("Replace each deprecated widget with the recommended new widget shown in its panel.", "elementor"),
2200
1903
  categories: ["best-practices", "performance"],
2201
1904
  severity: "warning",
2202
1905
  weight: 7,
@@ -2223,7 +1926,7 @@ var audit7 = {
2223
1926
  auditId: audit7.id,
2224
1927
  elementId: node.id,
2225
1928
  targetHint: "element-settings",
2226
- label: __31("Using deprecated widget.", "elementor")
1929
+ label: __25("Using deprecated widget.", "elementor")
2227
1930
  });
2228
1931
  });
2229
1932
  return violations.length === 0 ? { status: "pass" } : { status: "fail", violations };
@@ -2231,7 +1934,7 @@ var audit7 = {
2231
1934
  };
2232
1935
 
2233
1936
  // src/audits/google-consent-mode.ts
2234
- import { __ as __32 } from "@wordpress/i18n";
1937
+ import { __ as __26 } from "@wordpress/i18n";
2235
1938
 
2236
1939
  // src/utils/scan-consent-signals.ts
2237
1940
  var GOOGLE_TRACKING_PATTERNS = [
@@ -2254,12 +1957,12 @@ function hasConsentDefaultCall(html) {
2254
1957
  // src/audits/google-consent-mode.ts
2255
1958
  var audit8 = {
2256
1959
  id: "audits/google-consent-mode",
2257
- title: __32("Google Consent Mode", "elementor"),
2258
- description: __32(
1960
+ title: __26("Google Consent Mode", "elementor"),
1961
+ description: __26(
2259
1962
  "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.",
2260
1963
  "elementor"
2261
1964
  ),
2262
- fixHint: __32(
1965
+ fixHint: __26(
2263
1966
  "Turn on Google Consent Mode in your cookie consent plugin so Google tags respect visitor consent choices.",
2264
1967
  "elementor"
2265
1968
  ),
@@ -2268,18 +1971,18 @@ var audit8 = {
2268
1971
  weight: 1,
2269
1972
  evaluate: (ctx) => {
2270
1973
  if (!ctx.pageContext.frontend_url) {
2271
- return { status: "skipped", reason: __32("Page is not published.", "elementor") };
1974
+ return { status: "skipped", reason: __26("Page is not published.", "elementor") };
2272
1975
  }
2273
1976
  if (!ctx.renderedHtml) {
2274
- return { status: "skipped", reason: __32("Could not fetch the published page.", "elementor") };
1977
+ return { status: "skipped", reason: __26("Could not fetch the published page.", "elementor") };
2275
1978
  }
2276
1979
  if (!hasGoogleTracking(ctx.renderedHtml)) {
2277
- return { status: "skipped", reason: __32("No Google tracking product detected.", "elementor") };
1980
+ return { status: "skipped", reason: __26("No Google tracking product detected.", "elementor") };
2278
1981
  }
2279
1982
  if (hasConsentDefaultCall(ctx.renderedHtml)) {
2280
1983
  return { status: "pass" };
2281
1984
  }
2282
- const label = __32("No Google Consent Mode signal was found on the page.", "elementor");
1985
+ const label = __26("No Google Consent Mode signal was found on the page.", "elementor");
2283
1986
  const isCookiezReady = ctx.pageContext.cookiez_plugin_installed && ctx.pageContext.cookiez_plugin_active;
2284
1987
  if (isCookiezReady) {
2285
1988
  return {
@@ -2288,7 +1991,7 @@ var audit8 = {
2288
1991
  {
2289
1992
  auditId: audit8.id,
2290
1993
  label,
2291
- ctaLabel: __32("Enable", "elementor"),
1994
+ ctaLabel: __26("Enable", "elementor"),
2292
1995
  externalUrl: ctx.pageContext.cookiez_consent_mode_settings_url
2293
1996
  }
2294
1997
  ]
@@ -2308,7 +2011,7 @@ var audit8 = {
2308
2011
  };
2309
2012
 
2310
2013
  // src/audits/heading-structure.ts
2311
- import { __ as __33 } from "@wordpress/i18n";
2014
+ import { __ as __27 } from "@wordpress/i18n";
2312
2015
  var HEADING_LEVELS = ["h1", "h2", "h3", "h4", "h5", "h6"];
2313
2016
  var HEADING_CONTROLS_REGISTRY = {
2314
2017
  // Core
@@ -2486,27 +2189,27 @@ function extractPageHeadings(tree) {
2486
2189
  }
2487
2190
  var audit9 = {
2488
2191
  id: "audits/heading-structure",
2489
- title: __33("Heading structure", "elementor"),
2490
- description: __33("Pages should have exactly one H1 and a non-skipping heading order.", "elementor"),
2491
- fixHint: __33("Ensure your page has one H1 and that heading levels do not skip (no H2 \u2192 H4).", "elementor"),
2192
+ title: __27("Heading structure", "elementor"),
2193
+ description: __27("Pages should have exactly one H1 and a non-skipping heading order.", "elementor"),
2194
+ fixHint: __27("Ensure your page has one H1 and that heading levels do not skip (no H2 \u2192 H4).", "elementor"),
2492
2195
  categories: ["seo", "accessibility"],
2493
2196
  severity: "error",
2494
2197
  weight: 10,
2495
2198
  evaluate: (ctx) => {
2496
2199
  if (ctx.elements.tree.length === 0) {
2497
- return { status: "skipped", reason: __33("No elements", "elementor") };
2200
+ return { status: "skipped", reason: __27("No elements", "elementor") };
2498
2201
  }
2499
2202
  const headings = extractPageHeadings(ctx.elements.tree);
2500
2203
  const violations = [];
2501
2204
  if (headings.length === 0) {
2502
2205
  violations.push({
2503
2206
  auditId: audit9.id,
2504
- label: __33("No headings found on the page.", "elementor")
2207
+ label: __27("No headings found on the page.", "elementor")
2505
2208
  });
2506
2209
  } else {
2507
2210
  const h1Count = headings.filter((h) => h.level === 1).length;
2508
2211
  if (h1Count === 0) {
2509
- violations.push({ auditId: audit9.id, label: __33("No H1 on the page.", "elementor") });
2212
+ violations.push({ auditId: audit9.id, label: __27("No H1 on the page.", "elementor") });
2510
2213
  }
2511
2214
  if (h1Count > 1) {
2512
2215
  headings.filter((h) => h.level === 1).slice(1).forEach(
@@ -2514,7 +2217,7 @@ var audit9 = {
2514
2217
  auditId: audit9.id,
2515
2218
  elementId: h.elementId,
2516
2219
  targetHint: "element-settings",
2517
- label: __33("More than one H1 on the page.", "elementor")
2220
+ label: __27("More than one H1 on the page.", "elementor")
2518
2221
  })
2519
2222
  );
2520
2223
  }
@@ -2524,7 +2227,7 @@ var audit9 = {
2524
2227
  auditId: audit9.id,
2525
2228
  elementId: headings[i].elementId,
2526
2229
  targetHint: "element-settings",
2527
- label: __33("Heading level skipped.", "elementor")
2230
+ label: __27("Heading level skipped.", "elementor")
2528
2231
  });
2529
2232
  }
2530
2233
  }
@@ -2534,24 +2237,24 @@ var audit9 = {
2534
2237
  };
2535
2238
 
2536
2239
  // src/audits/hidden-elements.ts
2537
- import { __ as __34 } from "@wordpress/i18n";
2240
+ import { __ as __28 } from "@wordpress/i18n";
2538
2241
  function isHiddenOnAllDevices(settings) {
2539
2242
  return Boolean(settings.hide_desktop) && Boolean(settings.hide_tablet) && Boolean(settings.hide_mobile);
2540
2243
  }
2541
2244
  var audit10 = {
2542
2245
  id: "audits/hidden-elements",
2543
- title: __34("Hidden elements", "elementor"),
2544
- description: __34(
2246
+ title: __28("Hidden elements", "elementor"),
2247
+ description: __28(
2545
2248
  "Elements hidden on all devices simultaneously are effectively dead code in the page tree.",
2546
2249
  "elementor"
2547
2250
  ),
2548
- fixHint: __34("Remove the element or make it visible on at least one device breakpoint.", "elementor"),
2251
+ fixHint: __28("Remove the element or make it visible on at least one device breakpoint.", "elementor"),
2549
2252
  categories: ["best-practices", "performance"],
2550
2253
  severity: "info",
2551
2254
  weight: 3,
2552
2255
  evaluate: (ctx) => {
2553
2256
  if (ctx.elements.tree.length === 0) {
2554
- return { status: "skipped", reason: __34("No elements", "elementor") };
2257
+ return { status: "skipped", reason: __28("No elements", "elementor") };
2555
2258
  }
2556
2259
  const violations = [];
2557
2260
  walkElements(ctx.elements.tree, (node) => {
@@ -2560,7 +2263,7 @@ var audit10 = {
2560
2263
  auditId: audit10.id,
2561
2264
  elementId: node.id,
2562
2265
  targetHint: "element-settings",
2563
- label: __34("Element is hidden on all devices.", "elementor")
2266
+ label: __28("Element is hidden on all devices.", "elementor")
2564
2267
  });
2565
2268
  }
2566
2269
  });
@@ -2569,7 +2272,22 @@ var audit10 = {
2569
2272
  };
2570
2273
 
2571
2274
  // src/audits/images-alt-text.ts
2572
- import { __ as __35 } from "@wordpress/i18n";
2275
+ import { __ as __29 } from "@wordpress/i18n";
2276
+
2277
+ // src/utils/alt-text-image-sources.ts
2278
+ function walkAltTextImageSources(tree, visit) {
2279
+ walkImageLikeSources(tree, visit);
2280
+ walkAtomicImageSources(tree, visit, ATOMIC_CONTENT_IMAGE_PROP_KEYS);
2281
+ }
2282
+ function hasAltTextImages(tree) {
2283
+ let found = false;
2284
+ walkAltTextImageSources(tree, ({ media }) => {
2285
+ if (media.id || media.url) {
2286
+ found = true;
2287
+ }
2288
+ });
2289
+ return found;
2290
+ }
2573
2291
 
2574
2292
  // src/utils/image-alt.ts
2575
2293
  function hasMeaningfulAlt(media, pageContext) {
@@ -2590,20 +2308,21 @@ function isImageSourcePresent(media) {
2590
2308
  // src/audits/images-alt-text.ts
2591
2309
  var audit11 = {
2592
2310
  id: "audits/images-alt-text",
2593
- title: __35("Images alt text", "elementor"),
2594
- description: __35("Every image needs a meaningful alt attribute for screen readers and image-search.", "elementor"),
2595
- fixHint: __35("Open the image's settings and add an Alt Text describing the image.", "elementor"),
2311
+ title: __29("Images alt text", "elementor"),
2312
+ description: __29("Every image needs a meaningful alt attribute for screen readers and image-search.", "elementor"),
2313
+ fixHint: __29("Open the image's settings and add an Alt Text describing the image.", "elementor"),
2596
2314
  categories: ["seo", "accessibility"],
2597
2315
  severity: "error",
2598
2316
  weight: 10,
2599
2317
  evaluate: (ctx) => {
2600
- if (!hasPageImages(ctx.elements.tree)) {
2601
- return { status: "skipped", reason: __35("No images", "elementor") };
2318
+ if (!hasAltTextImages(ctx.elements.tree)) {
2319
+ return { status: "skipped", reason: __29("No images", "elementor") };
2602
2320
  }
2321
+ const isReady = ctx.pageContext.ally_plugin_active;
2603
2322
  const violations = [];
2604
2323
  const failedWidgetIds = /* @__PURE__ */ new Set();
2605
2324
  let missingAltImageCount = 0;
2606
- walkImageLikeSources(ctx.elements.tree, ({ node, media }) => {
2325
+ walkAltTextImageSources(ctx.elements.tree, ({ node, media }) => {
2607
2326
  if (!isImageSourcePresent(media) || hasMeaningfulAlt(media, ctx.pageContext)) {
2608
2327
  return;
2609
2328
  }
@@ -2614,8 +2333,9 @@ var audit11 = {
2614
2333
  auditId: audit11.id,
2615
2334
  elementId: node.id,
2616
2335
  targetHint: "element-settings",
2617
- label: __35("Image is missing alt text.", "elementor"),
2618
- externalUrl: ctx.pageContext.ally_plugin_url
2336
+ label: __29("Image is missing alt text.", "elementor"),
2337
+ externalUrl: isReady ? ctx.pageContext.ally_scan_url : ctx.pageContext.ally_plugin_url,
2338
+ ctaLabel: __29("Fix with Ally", "elementor")
2619
2339
  });
2620
2340
  }
2621
2341
  });
@@ -2624,17 +2344,18 @@ var audit11 = {
2624
2344
  };
2625
2345
 
2626
2346
  // src/audits/images-inefficient-format.ts
2627
- import { __ as __36 } from "@wordpress/i18n";
2347
+ import { __ as __30 } from "@wordpress/i18n";
2628
2348
  var LEGACY_RASTER_MIME_TYPES = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/gif", "image/bmp", "image/tiff"]);
2629
2349
  var audit12 = {
2630
2350
  id: "audits/images-inefficient-format",
2631
- title: __36("Inefficient image format", "elementor"),
2632
- description: __36("Image isn't served in a modern, efficient format like WebP or AVIF.", "elementor"),
2633
- fixHint: __36("Convert the image to a modern format like WebP or AVIF, or enable image optimization.", "elementor"),
2351
+ title: __30("Inefficient image format", "elementor"),
2352
+ description: __30("Image isn't served in a modern, efficient format like WebP or AVIF.", "elementor"),
2353
+ fixHint: __30("Convert the image to a modern format like WebP or AVIF, or enable image optimization.", "elementor"),
2634
2354
  categories: ["performance"],
2635
2355
  severity: "info",
2636
2356
  weight: 1,
2637
2357
  evaluate: (ctx) => {
2358
+ const isReady = ctx.pageContext.image_optimization_plugin_active;
2638
2359
  const offendingElementIds = /* @__PURE__ */ new Set();
2639
2360
  let hasAnyImage = false;
2640
2361
  let inefficientImageFormatCount = 0;
@@ -2653,37 +2374,36 @@ var audit12 = {
2653
2374
  inefficientImageFormatCount++;
2654
2375
  offendingElementIds.add(node.id);
2655
2376
  };
2656
- walkImageLikeSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2657
- walkBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2658
- walkAtomicImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2659
- walkAtomicBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2377
+ walkAllImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2660
2378
  if (!hasAnyImage) {
2661
- return { status: "skipped", reason: __36("No images", "elementor") };
2379
+ return { status: "skipped", reason: __30("No images", "elementor") };
2662
2380
  }
2663
2381
  const violations = Array.from(offendingElementIds).map((elementId) => ({
2664
2382
  auditId: audit12.id,
2665
2383
  elementId,
2666
2384
  targetHint: "element-settings",
2667
- label: __36("Image isn't served in a modern format like WebP or AVIF.", "elementor"),
2668
- externalUrl: ctx.pageContext.image_optimization_plugin_url
2385
+ label: __30("Image isn't served in a modern format like WebP or AVIF.", "elementor"),
2386
+ externalUrl: isReady ? ctx.pageContext.image_optimization_settings_url : ctx.pageContext.image_optimization_plugin_url,
2387
+ ctaLabel: __30("Optimize all", "elementor")
2669
2388
  }));
2670
2389
  return violations.length === 0 ? { status: "pass" } : { status: "fail", violations, metadata: { inefficientImageFormatCount } };
2671
2390
  }
2672
2391
  };
2673
2392
 
2674
2393
  // src/audits/images-too-large.ts
2675
- import { __ as __37, sprintf as sprintf4 } from "@wordpress/i18n";
2394
+ import { __ as __31, sprintf as sprintf3 } from "@wordpress/i18n";
2676
2395
  var SIZE_THRESHOLD_BYTES = 500 * 1024;
2677
2396
  var BYTES_PER_KB = 1024;
2678
2397
  var audit13 = {
2679
2398
  id: "audits/images-too-large",
2680
- title: __37("Oversized images", "elementor"),
2681
- description: __37("Large image files slow down the page.", "elementor"),
2682
- fixHint: __37("Replace the image with a smaller version or enable image optimization.", "elementor"),
2399
+ title: __31("Oversized images", "elementor"),
2400
+ description: __31("Large image files slow down the page.", "elementor"),
2401
+ fixHint: __31("Replace the image with a smaller version or enable image optimization.", "elementor"),
2683
2402
  categories: ["performance"],
2684
2403
  severity: "warning",
2685
2404
  weight: 7,
2686
2405
  evaluate: (ctx) => {
2406
+ const isReady = ctx.pageContext.image_optimization_plugin_active;
2687
2407
  const widgetMaxKb = /* @__PURE__ */ new Map();
2688
2408
  let oversizedImageCount = 0;
2689
2409
  let hasAnyImage = false;
@@ -2704,44 +2424,42 @@ var audit13 = {
2704
2424
  const currentMax = widgetMaxKb.get(node.id) ?? 0;
2705
2425
  widgetMaxKb.set(node.id, Math.max(currentMax, kb));
2706
2426
  };
2707
- walkImageLikeSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2708
- walkBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2709
- walkAtomicImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2710
- walkAtomicBackgroundImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2427
+ walkAllImageSources(ctx.elements.tree, ({ node, media }) => evaluateSource(node, media));
2711
2428
  if (!hasAnyImage) {
2712
- return { status: "skipped", reason: __37("No images", "elementor") };
2429
+ return { status: "skipped", reason: __31("No images", "elementor") };
2713
2430
  }
2714
2431
  const violations = Array.from(widgetMaxKb.entries()).map(([elementId, kb]) => ({
2715
2432
  auditId: audit13.id,
2716
2433
  elementId,
2717
2434
  targetHint: "element-settings",
2718
- label: sprintf4(
2435
+ label: sprintf3(
2719
2436
  /* translators: %d is the image file size in kilobytes. */
2720
- __37("Image is %d KB (over 500 KB).", "elementor"),
2437
+ __31("Image is %d KB (over 500 KB).", "elementor"),
2721
2438
  kb
2722
2439
  ),
2723
- externalUrl: ctx.pageContext.image_optimization_plugin_url
2440
+ externalUrl: isReady ? ctx.pageContext.image_optimization_settings_url : ctx.pageContext.image_optimization_plugin_url,
2441
+ ctaLabel: __31("Optimize all", "elementor")
2724
2442
  }));
2725
2443
  return violations.length === 0 ? { status: "pass" } : { status: "fail", violations, metadata: { oversizedImageCount } };
2726
2444
  }
2727
2445
  };
2728
2446
 
2729
2447
  // src/audits/nested-boxed-containers.ts
2730
- import { __ as __38 } from "@wordpress/i18n";
2448
+ import { __ as __32 } from "@wordpress/i18n";
2731
2449
  function isBoxed(settings) {
2732
2450
  return settings.content_width === "boxed";
2733
2451
  }
2734
2452
  var audit14 = {
2735
2453
  id: "audits/nested-boxed-containers",
2736
- title: __38("Boxed container nested inside a boxed parent", "elementor"),
2737
- description: __38("An inner container does not need to be boxed when its parent already is.", "elementor"),
2738
- fixHint: __38("Change the inner container's content width to Full Width.", "elementor"),
2454
+ title: __32("Boxed container nested inside a boxed parent", "elementor"),
2455
+ description: __32("An inner container does not need to be boxed when its parent already is.", "elementor"),
2456
+ fixHint: __32("Change the inner container's content width to Full Width.", "elementor"),
2739
2457
  categories: ["best-practices", "performance"],
2740
2458
  severity: "info",
2741
2459
  weight: 5,
2742
2460
  evaluate: (ctx) => {
2743
2461
  if (ctx.elements.tree.length === 0) {
2744
- return { status: "skipped", reason: __38("No elements", "elementor") };
2462
+ return { status: "skipped", reason: __32("No elements", "elementor") };
2745
2463
  }
2746
2464
  const violations = [];
2747
2465
  walkElements(ctx.elements.tree, (node, parents) => {
@@ -2754,7 +2472,12 @@ var audit14 = {
2754
2472
  auditId: audit14.id,
2755
2473
  elementId: node.id,
2756
2474
  targetHint: "element-settings",
2757
- label: __38("Nested boxed container.", "elementor")
2475
+ label: __32("Nested boxed container.", "elementor"),
2476
+ angieFix: true,
2477
+ angiePrompt: __32(
2478
+ "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.",
2479
+ "elementor"
2480
+ )
2758
2481
  });
2759
2482
  }
2760
2483
  });
@@ -2763,12 +2486,12 @@ var audit14 = {
2763
2486
  };
2764
2487
 
2765
2488
  // src/audits/page-excerpt.ts
2766
- import { __ as __39 } from "@wordpress/i18n";
2489
+ import { __ as __33 } from "@wordpress/i18n";
2767
2490
  var audit15 = {
2768
2491
  id: "audits/page-excerpt",
2769
- title: __39("Page excerpt", "elementor"),
2770
- description: __39("A descriptive excerpt helps search engines and previews summarize the page.", "elementor"),
2771
- fixHint: __39("Open Page Settings and write a short excerpt.", "elementor"),
2492
+ title: __33("Page excerpt", "elementor"),
2493
+ description: __33("A descriptive excerpt helps search engines and previews summarize the page.", "elementor"),
2494
+ fixHint: __33("Open Page Settings and write a short excerpt.", "elementor"),
2772
2495
  categories: ["seo"],
2773
2496
  severity: "warning",
2774
2497
  weight: 5,
@@ -2781,7 +2504,7 @@ var audit15 = {
2781
2504
  violations: [
2782
2505
  {
2783
2506
  auditId: audit15.id,
2784
- label: __39("Page has no excerpt.", "elementor"),
2507
+ label: __33("Page has no excerpt.", "elementor"),
2785
2508
  targetHint: "page-settings",
2786
2509
  angieFix: true
2787
2510
  }
@@ -2791,12 +2514,12 @@ var audit15 = {
2791
2514
  };
2792
2515
 
2793
2516
  // src/audits/page-featured-image.ts
2794
- import { __ as __40 } from "@wordpress/i18n";
2517
+ import { __ as __34 } from "@wordpress/i18n";
2795
2518
  var audit16 = {
2796
2519
  id: "audits/page-featured-image",
2797
- title: __40("Page featured image", "elementor"),
2798
- description: __40("Featured images are used by social shares and many themes for hero visuals.", "elementor"),
2799
- fixHint: __40("Open Page Settings and set a featured image.", "elementor"),
2520
+ title: __34("Page featured image", "elementor"),
2521
+ description: __34("Featured images are used by social shares and many themes for hero visuals.", "elementor"),
2522
+ fixHint: __34("Open Page Settings and set a featured image.", "elementor"),
2800
2523
  categories: ["seo"],
2801
2524
  severity: "info",
2802
2525
  weight: 5,
@@ -2809,7 +2532,7 @@ var audit16 = {
2809
2532
  violations: [
2810
2533
  {
2811
2534
  auditId: audit16.id,
2812
- label: __40("No featured image set.", "elementor"),
2535
+ label: __34("No featured image set.", "elementor"),
2813
2536
  targetHint: "page-settings"
2814
2537
  }
2815
2538
  ]
@@ -2818,13 +2541,13 @@ var audit16 = {
2818
2541
  };
2819
2542
 
2820
2543
  // src/audits/page-title.ts
2821
- import { __ as __41 } from "@wordpress/i18n";
2544
+ import { __ as __35 } from "@wordpress/i18n";
2822
2545
  var MAX_TITLE_LENGTH = 60;
2823
2546
  var audit17 = {
2824
2547
  id: "audits/page-title",
2825
- title: __41("Page title", "elementor"),
2826
- description: __41("Pages need a clear title for SEO and screen-reader navigation.", "elementor"),
2827
- fixHint: __41("Open Page Settings and add a title.", "elementor"),
2548
+ title: __35("Page title", "elementor"),
2549
+ description: __35("Pages need a clear title for SEO and screen-reader navigation.", "elementor"),
2550
+ fixHint: __35("Open Page Settings and add a title.", "elementor"),
2828
2551
  categories: ["seo"],
2829
2552
  severity: "error",
2830
2553
  weight: 10,
@@ -2836,10 +2559,10 @@ var audit17 = {
2836
2559
  violations: [
2837
2560
  {
2838
2561
  auditId: audit17.id,
2839
- label: __41("Page has no title.", "elementor"),
2562
+ label: __35("Page has no title.", "elementor"),
2840
2563
  targetHint: "page-settings",
2841
2564
  angieFix: true,
2842
- angiePrompt: __41(
2565
+ angiePrompt: __35(
2843
2566
  "Based on this page's contents, generate a title for this page.",
2844
2567
  "elementor"
2845
2568
  )
@@ -2853,7 +2576,7 @@ var audit17 = {
2853
2576
  violations: [
2854
2577
  {
2855
2578
  auditId: audit17.id,
2856
- label: __41("Page title is too long.", "elementor"),
2579
+ label: __35("Page title is too long.", "elementor"),
2857
2580
  targetHint: "page-settings",
2858
2581
  angieFix: true
2859
2582
  }
@@ -2865,7 +2588,7 @@ var audit17 = {
2865
2588
  };
2866
2589
 
2867
2590
  // src/audits/prefer-global-colors.ts
2868
- import { __ as __42, sprintf as sprintf5 } from "@wordpress/i18n";
2591
+ import { __ as __36, sprintf as sprintf4 } from "@wordpress/i18n";
2869
2592
 
2870
2593
  // src/utils/collect-hardcoded-colors.ts
2871
2594
  var RESERVED_SETTING_KEYS = /* @__PURE__ */ new Set(["__globals__", "__dynamic__"]);
@@ -2933,18 +2656,18 @@ function findMatchingGlobalByValue(value, globals) {
2933
2656
  // src/audits/prefer-global-colors.ts
2934
2657
  var audit18 = {
2935
2658
  id: "audits/prefer-global-colors",
2936
- title: __42("Prefer global colors", "elementor"),
2937
- description: __42("Global colors make the design consistent and easy to update site-wide.", "elementor"),
2938
- fixHint: __42("Replace the hard-coded value with the matching global color.", "elementor"),
2659
+ title: __36("Prefer global colors", "elementor"),
2660
+ description: __36("Global colors make the design consistent and easy to update site-wide.", "elementor"),
2661
+ fixHint: __36("Replace the hard-coded value with the matching global color.", "elementor"),
2939
2662
  categories: ["best-practices"],
2940
2663
  severity: "info",
2941
2664
  weight: 3,
2942
2665
  evaluate: (ctx) => {
2943
2666
  if (ctx.elements.tree.length === 0) {
2944
- return { status: "skipped", reason: __42("No elements", "elementor") };
2667
+ return { status: "skipped", reason: __36("No elements", "elementor") };
2945
2668
  }
2946
2669
  if (ctx.kit.globals.colors.length === 0) {
2947
- return { status: "skipped", reason: __42("No global colors", "elementor") };
2670
+ return { status: "skipped", reason: __36("No global colors", "elementor") };
2948
2671
  }
2949
2672
  const violations = [];
2950
2673
  walkElements(ctx.elements.tree, (node) => {
@@ -2958,9 +2681,9 @@ var audit18 = {
2958
2681
  elementId: node.id,
2959
2682
  targetHint: "element-settings",
2960
2683
  angieFix: true,
2961
- label: sprintf5(
2684
+ label: sprintf4(
2962
2685
  /* translators: 1: hard-coded color value, 2: global color title. */
2963
- __42('replace "%1$s" with "%2$s" global color', "elementor"),
2686
+ __36('replace "%1$s" with "%2$s" global color', "elementor"),
2964
2687
  value,
2965
2688
  global.title
2966
2689
  )
@@ -2972,7 +2695,7 @@ var audit18 = {
2972
2695
  };
2973
2696
 
2974
2697
  // src/audits/prefer-global-fonts.ts
2975
- import { __ as __43, sprintf as sprintf6 } from "@wordpress/i18n";
2698
+ import { __ as __37, sprintf as sprintf5 } from "@wordpress/i18n";
2976
2699
 
2977
2700
  // src/utils/collect-hardcoded-fonts.ts
2978
2701
  var RESERVED_SETTING_KEYS2 = /* @__PURE__ */ new Set(["__globals__", "__dynamic__"]);
@@ -3032,18 +2755,18 @@ function collectHardcodedFonts(settings, pathPrefix = "") {
3032
2755
  // src/audits/prefer-global-fonts.ts
3033
2756
  var audit19 = {
3034
2757
  id: "audits/prefer-global-fonts",
3035
- title: __43("Prefer global fonts", "elementor"),
3036
- description: __43("Global fonts make the design consistent and easy to update site-wide.", "elementor"),
3037
- fixHint: __43("Replace the hard-coded value with the matching global font.", "elementor"),
2758
+ title: __37("Prefer global fonts", "elementor"),
2759
+ description: __37("Global fonts make the design consistent and easy to update site-wide.", "elementor"),
2760
+ fixHint: __37("Replace the hard-coded value with the matching global font.", "elementor"),
3038
2761
  categories: ["best-practices", "performance"],
3039
2762
  severity: "info",
3040
2763
  weight: 3,
3041
2764
  evaluate: (ctx) => {
3042
2765
  if (ctx.elements.tree.length === 0) {
3043
- return { status: "skipped", reason: __43("No elements", "elementor") };
2766
+ return { status: "skipped", reason: __37("No elements", "elementor") };
3044
2767
  }
3045
2768
  if (ctx.kit.globals.fonts.length === 0) {
3046
- return { status: "skipped", reason: __43("No global fonts", "elementor") };
2769
+ return { status: "skipped", reason: __37("No global fonts", "elementor") };
3047
2770
  }
3048
2771
  const violations = [];
3049
2772
  walkElements(ctx.elements.tree, (node) => {
@@ -3056,9 +2779,9 @@ var audit19 = {
3056
2779
  auditId: audit19.id,
3057
2780
  elementId: node.id,
3058
2781
  targetHint: "element-settings",
3059
- label: sprintf6(
2782
+ label: sprintf5(
3060
2783
  /* translators: %s: global font title. */
3061
- __43('replace hardcoded typography with "%s" global font', "elementor"),
2784
+ __37('replace hardcoded typography with "%s" global font', "elementor"),
3062
2785
  global.title
3063
2786
  )
3064
2787
  });
@@ -3069,15 +2792,15 @@ var audit19 = {
3069
2792
  };
3070
2793
 
3071
2794
  // src/audits/privacy-policy.ts
3072
- import { __ as __44 } from "@wordpress/i18n";
2795
+ import { __ as __38 } from "@wordpress/i18n";
3073
2796
  var audit20 = {
3074
2797
  id: "audits/privacy-policy",
3075
- title: __44("Privacy policy", "elementor"),
3076
- description: __44(
2798
+ title: __38("Privacy policy", "elementor"),
2799
+ description: __38(
3077
2800
  "A privacy policy page is required by privacy regulations such as GDPR (EU) and CCPA (US).",
3078
2801
  "elementor"
3079
2802
  ),
3080
- fixHint: __44(
2803
+ fixHint: __38(
3081
2804
  "Go to Settings > Privacy in the WordPress admin and assign a published privacy policy page.",
3082
2805
  "elementor"
3083
2806
  ),
@@ -3093,9 +2816,9 @@ var audit20 = {
3093
2816
  violations: [
3094
2817
  {
3095
2818
  auditId: audit20.id,
3096
- label: __44("No privacy policy page is set.", "elementor"),
2819
+ label: __38("No privacy policy page is set.", "elementor"),
3097
2820
  externalUrl: ctx.pageContext.privacy_settings_url,
3098
- ctaLabel: __44("Create", "elementor")
2821
+ ctaLabel: __38("Create", "elementor")
3099
2822
  }
3100
2823
  ]
3101
2824
  };
@@ -3103,15 +2826,15 @@ var audit20 = {
3103
2826
  };
3104
2827
 
3105
2828
  // src/audits/robots-noindex.ts
3106
- import { __ as __45 } from "@wordpress/i18n";
2829
+ import { __ as __39 } from "@wordpress/i18n";
3107
2830
  var audit21 = {
3108
2831
  id: "audits/robots-noindex",
3109
- title: __45("Search engine visibility", "elementor"),
3110
- description: __45(
2832
+ title: __39("Search engine visibility", "elementor"),
2833
+ description: __39(
3111
2834
  "When search engine visibility is checked in WordPress Settings -> Reading -> Search Engine Visibility, search engines are discouraged from indexing the website.",
3112
2835
  "elementor"
3113
2836
  ),
3114
- fixHint: __45(
2837
+ fixHint: __39(
3115
2838
  'Go to Settings \u2192 Reading in the WordPress admin and uncheck "Discourage search engines from indexing this site" when the website should be indexed.',
3116
2839
  "elementor"
3117
2840
  ),
@@ -3127,7 +2850,7 @@ var audit21 = {
3127
2850
  violations: [
3128
2851
  {
3129
2852
  auditId: audit21.id,
3130
- label: __45("Search engines are discouraged from indexing this website.", "elementor"),
2853
+ label: __39("Search engines are discouraged from indexing this website.", "elementor"),
3131
2854
  externalUrl: ctx.pageContext.reading_settings_url
3132
2855
  }
3133
2856
  ]
@@ -3136,15 +2859,15 @@ var audit21 = {
3136
2859
  };
3137
2860
 
3138
2861
  // src/audits/scan-for-cookies.ts
3139
- import { __ as __46 } from "@wordpress/i18n";
2862
+ import { __ as __40 } from "@wordpress/i18n";
3140
2863
  var audit22 = {
3141
2864
  id: "audits/scan-for-cookies",
3142
- title: __46("Scan for cookies", "elementor"),
3143
- description: __46(
2865
+ title: __40("Scan for cookies", "elementor"),
2866
+ description: __40(
3144
2867
  "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.",
3145
2868
  "elementor"
3146
2869
  ),
3147
- fixHint: __46("Run a Cookiez scan on this page to see which cookies need disclosure.", "elementor"),
2870
+ fixHint: __40("Run a Cookiez scan on this page to see which cookies need disclosure.", "elementor"),
3148
2871
  categories: ["compliance"],
3149
2872
  severity: "info",
3150
2873
  weight: 0,
@@ -3155,7 +2878,7 @@ var audit22 = {
3155
2878
  violations: [
3156
2879
  {
3157
2880
  auditId: audit22.id,
3158
- label: __46("This page has not been scanned for cookies yet.", "elementor"),
2881
+ label: __40("This page has not been scanned for cookies yet.", "elementor"),
3159
2882
  externalUrl: isReady ? ctx.pageContext.cookiez_scan_url : ctx.pageContext.cookiez_plugin_action_url
3160
2883
  }
3161
2884
  ]
@@ -3164,21 +2887,21 @@ var audit22 = {
3164
2887
  };
3165
2888
 
3166
2889
  // src/audits/sections-and-columns.ts
3167
- import { __ as __47 } from "@wordpress/i18n";
2890
+ import { __ as __41 } from "@wordpress/i18n";
3168
2891
  var audit23 = {
3169
2892
  id: "audits/sections-and-columns",
3170
- title: __47("Sections and columns", "elementor"),
3171
- description: __47(
2893
+ title: __41("Sections and columns", "elementor"),
2894
+ description: __41(
3172
2895
  "Sections and columns are legacy elements. Containers render fewer DOM nodes and are more flexible.",
3173
2896
  "elementor"
3174
2897
  ),
3175
- fixHint: __47("Use the Container Converter to replace each section/column with a container.", "elementor"),
2898
+ fixHint: __41("Use the Container Converter to replace each section/column with a container.", "elementor"),
3176
2899
  categories: ["best-practices", "performance"],
3177
2900
  severity: "warning",
3178
2901
  weight: 7,
3179
2902
  evaluate: (ctx) => {
3180
2903
  if (ctx.elements.tree.length === 0) {
3181
- return { status: "skipped", reason: __47("No elements", "elementor") };
2904
+ return { status: "skipped", reason: __41("No elements", "elementor") };
3182
2905
  }
3183
2906
  const violations = [];
3184
2907
  walkElements(ctx.elements.tree, (node) => {
@@ -3186,7 +2909,7 @@ var audit23 = {
3186
2909
  violations.push({
3187
2910
  auditId: audit23.id,
3188
2911
  elementId: node.id,
3189
- label: node.elType === "section" ? __47("Section element", "elementor") : __47("Column element", "elementor")
2912
+ label: node.elType === "section" ? __41("Section element", "elementor") : __41("Column element", "elementor")
3190
2913
  });
3191
2914
  }
3192
2915
  });
@@ -3195,15 +2918,15 @@ var audit23 = {
3195
2918
  };
3196
2919
 
3197
2920
  // src/audits/site-identity.ts
3198
- import { __ as __48 } from "@wordpress/i18n";
2921
+ import { __ as __42 } from "@wordpress/i18n";
3199
2922
  var audit24 = {
3200
2923
  id: "audits/site-identity",
3201
- title: __48("Site identity", "elementor"),
3202
- description: __48(
2924
+ title: __42("Site identity", "elementor"),
2925
+ description: __42(
3203
2926
  "Site name, description, logo, and favicon establish your brand and appear in search results and browser tabs.",
3204
2927
  "elementor"
3205
2928
  ),
3206
- fixHint: __48("Open Site Settings \u2192 Site Identity and complete all the missing fields.", "elementor"),
2929
+ fixHint: __42("Open Site Settings \u2192 Site Identity and complete all the missing fields.", "elementor"),
3207
2930
  categories: ["best-practices", "seo"],
3208
2931
  severity: "info",
3209
2932
  weight: 7,
@@ -3213,14 +2936,14 @@ var audit24 = {
3213
2936
  if (!identity.site_name_set) {
3214
2937
  violations.push({
3215
2938
  auditId: audit24.id,
3216
- label: __48("Site name is missing or still uses the default.", "elementor"),
2939
+ label: __42("Site name is missing or still uses the default.", "elementor"),
3217
2940
  targetHint: "site-identity-settings"
3218
2941
  });
3219
2942
  }
3220
2943
  if (!identity.site_description_set) {
3221
2944
  violations.push({
3222
2945
  auditId: audit24.id,
3223
- label: __48("Site description is missing or still uses the default.", "elementor"),
2946
+ label: __42("Site description is missing or still uses the default.", "elementor"),
3224
2947
  targetHint: "site-identity-settings",
3225
2948
  angieFix: true
3226
2949
  });
@@ -3228,14 +2951,14 @@ var audit24 = {
3228
2951
  if (!identity.site_logo_set) {
3229
2952
  violations.push({
3230
2953
  auditId: audit24.id,
3231
- label: __48("Site logo is not set.", "elementor"),
2954
+ label: __42("Site logo is not set.", "elementor"),
3232
2955
  targetHint: "site-identity-settings"
3233
2956
  });
3234
2957
  }
3235
2958
  if (!identity.site_favicon_set) {
3236
2959
  violations.push({
3237
2960
  auditId: audit24.id,
3238
- label: __48("Site favicon is not set.", "elementor"),
2961
+ label: __42("Site favicon is not set.", "elementor"),
3239
2962
  targetHint: "site-identity-settings"
3240
2963
  });
3241
2964
  }
@@ -3249,50 +2972,6 @@ var audit24 = {
3249
2972
  }
3250
2973
  };
3251
2974
 
3252
- // src/audits/too-many-widgets.ts
3253
- import { getWidgetsCache as getWidgetsCache3 } from "@elementor/editor-elements";
3254
- import { __ as __49 } from "@wordpress/i18n";
3255
- var WIDGET_COUNT_THRESHOLD = 100;
3256
- function isCountableWidget(node, widgetsCache) {
3257
- if (node.elType === "widget") {
3258
- return true;
3259
- }
3260
- return !!widgetsCache?.[node.elType]?.meta?.is_compound;
3261
- }
3262
- var audit25 = {
3263
- id: "audits/too-many-widgets",
3264
- title: __49("Too many widgets", "elementor"),
3265
- description: __49("Excessive DOM size caused by too many widgets degrades rendering performance.", "elementor"),
3266
- fixHint: __49("Reduce the number of widgets on the page by removing or combining elements.", "elementor"),
3267
- categories: ["best-practices", "performance"],
3268
- severity: "warning",
3269
- weight: 5,
3270
- evaluate: (ctx) => {
3271
- if (ctx.elements.tree.length === 0) {
3272
- return { status: "skipped", reason: __49("No elements", "elementor") };
3273
- }
3274
- const widgetsCache = getWidgetsCache3();
3275
- let widgetCount = 0;
3276
- walkElements(ctx.elements.tree, (node) => {
3277
- if (isCountableWidget(node, widgetsCache)) {
3278
- widgetCount++;
3279
- }
3280
- });
3281
- if (widgetCount <= WIDGET_COUNT_THRESHOLD) {
3282
- return { status: "pass" };
3283
- }
3284
- return {
3285
- status: "fail",
3286
- violations: [
3287
- {
3288
- auditId: audit25.id,
3289
- label: __49("Page has too many widgets.", "elementor")
3290
- }
3291
- ]
3292
- };
3293
- }
3294
- };
3295
-
3296
2975
  // src/register-audits.ts
3297
2976
  var AUDITS = [
3298
2977
  audit17,
@@ -3300,7 +2979,6 @@ var AUDITS = [
3300
2979
  audit16,
3301
2980
  audit10,
3302
2981
  audit7,
3303
- audit25,
3304
2982
  audit23,
3305
2983
  audit5,
3306
2984
  audit14,
@@ -3322,17 +3000,30 @@ var AUDITS = [
3322
3000
  audit8
3323
3001
  ];
3324
3002
  function registerAllAudits() {
3325
- for (const audit26 of AUDITS) {
3326
- registerAudit(audit26);
3003
+ for (const audit25 of AUDITS) {
3004
+ registerAudit(audit25);
3327
3005
  }
3328
3006
  }
3329
3007
 
3008
+ // src/sync-report-staleness.ts
3009
+ import { __privateListenTo as listenTo, commandEndEvent } from "@elementor/editor-v1-adapters";
3010
+ import { __dispatch as dispatch } from "@elementor/store";
3011
+ function syncReportStaleness() {
3012
+ listenTo(commandEndEvent("document/save/set-is-modified"), (e) => {
3013
+ const event = e;
3014
+ if (event.args?.status) {
3015
+ dispatch(slice.actions.reportStale());
3016
+ }
3017
+ });
3018
+ }
3019
+
3330
3020
  // src/init.ts
3331
3021
  function init() {
3332
3022
  registerSlice(slice);
3333
3023
  registerAllAudits();
3334
3024
  registerFloatingPanel(auditPanel.panel);
3335
3025
  registerAppBarAuditsToggle();
3026
+ syncReportStaleness();
3336
3027
  }
3337
3028
  export {
3338
3029
  init,