@elementor/editor-audits 4.4.0-1121 → 4.4.0-1123

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -937,7 +937,7 @@ function SubpageHeader({ title, onBack, backLabel, icon }) {
937
937
  import * as React12 from "react";
938
938
  import { getElementIcon, getElementTitle } from "@elementor/editor-elements";
939
939
  import { useFloatingPanelZIndex as useFloatingPanelZIndex2 } from "@elementor/editor-floating-panels";
940
- import { CheckIcon, ChevronDownIcon as ChevronDownIcon2, HelpIcon, InfoCircleIcon, WandIcon as WandIcon2 } from "@elementor/icons";
940
+ import { CheckIcon, ChevronDownIcon as ChevronDownIcon2, HelpIcon, InfoCircleIcon } from "@elementor/icons";
941
941
  import { Alert, Box as Box7, Collapse as Collapse2, IconButton as IconButton5, Tooltip as Tooltip3, Typography as Typography6 } from "@elementor/ui";
942
942
  import { __ as __10 } from "@wordpress/i18n";
943
943
 
@@ -1003,9 +1003,9 @@ function onKeyboardClick(callback) {
1003
1003
  import * as React8 from "react";
1004
1004
  import { isAngieAvailable, sendPromptToAngie } from "@elementor/editor-mcp";
1005
1005
  import { AngieIcon } from "@elementor/icons";
1006
- import { IconButton as IconButton4, Tooltip as Tooltip2 } from "@elementor/ui";
1006
+ import { Button as Button3, IconButton as IconButton4, Tooltip as Tooltip2 } from "@elementor/ui";
1007
1007
  import { __ as __9 } from "@wordpress/i18n";
1008
- function FixViolationWithAngie({ prompt, panelZIndex }) {
1008
+ function FixViolationWithAngie({ prompt, panelZIndex, variant = "icon" }) {
1009
1009
  const label = __9("Fix with Angie", "elementor");
1010
1010
  const href = `#angie-prompt=${encodeURIComponent(prompt)}`;
1011
1011
  const handleClick = (event) => {
@@ -1024,6 +1024,21 @@ function FixViolationWithAngie({ prompt, panelZIndex }) {
1024
1024
  })
1025
1025
  );
1026
1026
  };
1027
+ if (variant === "button") {
1028
+ return /* @__PURE__ */ React8.createElement(
1029
+ Button3,
1030
+ {
1031
+ component: "a",
1032
+ href,
1033
+ size: "small",
1034
+ color: "secondary",
1035
+ variant: "outlined",
1036
+ startIcon: /* @__PURE__ */ React8.createElement(AngieIcon, { fontSize: "tiny", "aria-hidden": true }),
1037
+ onClick: handleClick
1038
+ },
1039
+ label
1040
+ );
1041
+ }
1027
1042
  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" })));
1028
1043
  }
1029
1044
 
@@ -1035,15 +1050,17 @@ var SEVERITY_CONFIG = {
1035
1050
  warning: { Icon: AlertCircleIcon, color: "warning" },
1036
1051
  info: { Icon: BulbIcon, color: "info" }
1037
1052
  };
1053
+ var INFO_ICON_VERTICAL_NUDGE = "translateY(-2px)";
1038
1054
  function SeverityIcon({ severity }) {
1039
1055
  const { Icon, color } = SEVERITY_CONFIG[severity];
1040
- return /* @__PURE__ */ React9.createElement(Icon, { fontSize: "small", color });
1056
+ const sx = "info" === severity ? { transform: INFO_ICON_VERTICAL_NUDGE } : void 0;
1057
+ return /* @__PURE__ */ React9.createElement(Icon, { fontSize: "small", color, sx });
1041
1058
  }
1042
1059
 
1043
1060
  // src/components/violation-cta-button.tsx
1044
1061
  import * as React10 from "react";
1045
1062
  import { WandIcon } from "@elementor/icons";
1046
- import { Button as Button3 } from "@elementor/ui";
1063
+ import { Button as Button4 } from "@elementor/ui";
1047
1064
  function ViolationCtaButton({ ctaLabel, externalUrl, variant = "outlined", withIcon = false }) {
1048
1065
  const handleClick = (event) => {
1049
1066
  event.stopPropagation();
@@ -1051,7 +1068,7 @@ function ViolationCtaButton({ ctaLabel, externalUrl, variant = "outlined", withI
1051
1068
  window.open(externalUrl, "_blank", "noopener");
1052
1069
  };
1053
1070
  return /* @__PURE__ */ React10.createElement(
1054
- Button3,
1071
+ Button4,
1055
1072
  {
1056
1073
  variant,
1057
1074
  color: "secondary",
@@ -1098,31 +1115,49 @@ function StatusIndicator({ audit: audit26, violations }) {
1098
1115
  }
1099
1116
  return /* @__PURE__ */ React12.createElement(CheckIcon, { fontSize: "small", color: "success" });
1100
1117
  }
1101
- function GuidanceAction({ violation }) {
1118
+ function GuidanceAction({
1119
+ violation,
1120
+ sharedAngiePrompt,
1121
+ panelZIndex
1122
+ }) {
1102
1123
  const hasPrimaryCta = !!(violation.ctaLabel && violation.externalUrl);
1103
- if (!hasPrimaryCta) {
1124
+ if (!hasPrimaryCta && !sharedAngiePrompt) {
1104
1125
  return null;
1105
1126
  }
1106
- return /* @__PURE__ */ React12.createElement(Box7, { display: "flex", alignItems: "center", gap: 1, mt: 1 }, /* @__PURE__ */ React12.createElement(
1127
+ return /* @__PURE__ */ React12.createElement(Box7, { display: "flex", alignItems: "center", gap: 1, mt: 1 }, hasPrimaryCta && /* @__PURE__ */ React12.createElement(
1107
1128
  ViolationCtaButton,
1108
1129
  {
1109
1130
  ctaLabel: violation.ctaLabel,
1110
1131
  externalUrl: violation.externalUrl,
1111
1132
  withIcon: true
1112
1133
  }
1113
- ), violation.secondaryCtaLabel && violation.secondaryCtaUrl && /* @__PURE__ */ React12.createElement(
1134
+ ), hasPrimaryCta && violation.secondaryCtaLabel && violation.secondaryCtaUrl && /* @__PURE__ */ React12.createElement(
1114
1135
  ViolationCtaButton,
1115
1136
  {
1116
1137
  ctaLabel: violation.secondaryCtaLabel,
1117
1138
  externalUrl: violation.secondaryCtaUrl,
1118
1139
  variant: "text"
1119
1140
  }
1120
- ));
1141
+ ), sharedAngiePrompt && /* @__PURE__ */ React12.createElement(FixViolationWithAngie, { prompt: sharedAngiePrompt, panelZIndex, variant: "button" }));
1142
+ }
1143
+ function getSharedAngiePrompt(violations) {
1144
+ if (!violations || violations.length < 2) {
1145
+ return void 0;
1146
+ }
1147
+ const [first, ...rest] = violations;
1148
+ if (!first.angieFix || !first.angiePrompt) {
1149
+ return void 0;
1150
+ }
1151
+ const allShareTheSameFix = rest.every(
1152
+ (violation) => violation.angieFix && violation.angiePrompt === first.angiePrompt
1153
+ );
1154
+ return allShareTheSameFix ? first.angiePrompt : void 0;
1121
1155
  }
1122
1156
  function ViolationRow({ audit: audit26, expanded, onToggleExpand, skipReason, violations }) {
1123
1157
  const primaryViolation = violations?.[0];
1124
1158
  const isGuidanceFocusable = !!primaryViolation;
1125
1159
  const panelZIndex = useFloatingPanelZIndex2(AUDIT_PANEL_ID);
1160
+ const sharedAngiePrompt = getSharedAngiePrompt(violations);
1126
1161
  const handleGuidanceClick = () => {
1127
1162
  if (isGuidanceFocusable && primaryViolation) {
1128
1163
  focusViolation(primaryViolation);
@@ -1172,11 +1207,20 @@ function ViolationRow({ audit: audit26, expanded, onToggleExpand, skipReason, vi
1172
1207
  onKeyDown: isGuidanceFocusable ? onKeyboardClick(handleGuidanceClick) : void 0
1173
1208
  },
1174
1209
  /* @__PURE__ */ React12.createElement(Typography6, { variant: "caption", component: "p", color: "text.secondary" }, audit26.description),
1175
- primaryViolation && /* @__PURE__ */ React12.createElement(GuidanceAction, { violation: primaryViolation })
1210
+ primaryViolation && /* @__PURE__ */ React12.createElement(
1211
+ GuidanceAction,
1212
+ {
1213
+ violation: primaryViolation,
1214
+ sharedAngiePrompt,
1215
+ panelZIndex
1216
+ }
1217
+ )
1176
1218
  ))), violations && violations.length > 0 && /* @__PURE__ */ React12.createElement(Box7, { role: "list", sx: { paddingBlockEnd: 1 } }, violations.map((violation, idx) => {
1177
1219
  const widgetIcon = violation.elementId ? getElementIcon(violation.elementId) : null;
1178
1220
  const elementTitle = violation.elementId ? getElementTitle(violation.elementId) : null;
1179
- const rowLabel = elementTitle ? `${elementTitle} - ${violation.label}` : violation.label;
1221
+ const displayLabel = elementTitle ?? violation.label;
1222
+ const angiePromptContext = elementTitle ? `${elementTitle} - ${violation.label}` : violation.label;
1223
+ const showsOwnFixAction = !!violation.angieFix && !sharedAngiePrompt;
1180
1224
  return /* @__PURE__ */ React12.createElement(
1181
1225
  Box7,
1182
1226
  {
@@ -1193,18 +1237,28 @@ function ViolationRow({ audit: audit26, expanded, onToggleExpand, skipReason, vi
1193
1237
  paddingInline: 1,
1194
1238
  borderRadius: 1,
1195
1239
  cursor: "pointer",
1196
- "&:hover": { bgcolor: "action.hover" }
1240
+ "&:hover": { bgcolor: "action.hover" },
1241
+ "&:hover .violation-row-fix-action, &:focus-visible .violation-row-fix-action": {
1242
+ opacity: 1
1243
+ }
1197
1244
  }
1198
1245
  },
1199
1246
  /* @__PURE__ */ React12.createElement(ViolationIcon, { violation, widgetIcon }),
1200
- /* @__PURE__ */ React12.createElement(Box7, { sx: { flex: 1 } }, /* @__PURE__ */ React12.createElement(Typography6, { variant: "caption" }, rowLabel), violation.detail && /* @__PURE__ */ React12.createElement(Typography6, { variant: "caption", color: "text.secondary" }, violation.detail)),
1201
- violation.angieFix ? /* @__PURE__ */ React12.createElement(
1202
- FixViolationWithAngie,
1247
+ /* @__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)),
1248
+ showsOwnFixAction && /* @__PURE__ */ React12.createElement(
1249
+ Box7,
1203
1250
  {
1204
- prompt: violation.angiePrompt ?? buildAngiePrompt(rowLabel),
1205
- panelZIndex
1206
- }
1207
- ) : /* @__PURE__ */ React12.createElement(WandIcon2, { fontSize: "tiny", "aria-hidden": true })
1251
+ className: "violation-row-fix-action",
1252
+ sx: { opacity: 0, transition: "opacity .15s" }
1253
+ },
1254
+ /* @__PURE__ */ React12.createElement(
1255
+ FixViolationWithAngie,
1256
+ {
1257
+ prompt: violation.angiePrompt ?? buildAngiePrompt(angiePromptContext),
1258
+ panelZIndex
1259
+ }
1260
+ )
1261
+ )
1208
1262
  );
1209
1263
  }))));
1210
1264
  }
@@ -1406,7 +1460,7 @@ function findAuditRun(report, auditId) {
1406
1460
 
1407
1461
  // src/components/promotion-card.tsx
1408
1462
  import * as React15 from "react";
1409
- import { Box as Box10, Button as Button4, Typography as Typography8 } from "@elementor/ui";
1463
+ import { Box as Box10, Button as Button5, Typography as Typography8 } from "@elementor/ui";
1410
1464
  function PromotionCard({ ctaDisabled, ctaLabel, icon: Icon, onCtaClick, subtitle, title }) {
1411
1465
  return /* @__PURE__ */ React15.createElement(
1412
1466
  Box10,
@@ -1424,7 +1478,7 @@ function PromotionCard({ ctaDisabled, ctaLabel, icon: Icon, onCtaClick, subtitle
1424
1478
  },
1425
1479
  /* @__PURE__ */ React15.createElement(Icon, { fontSize: "small", color: "action" }),
1426
1480
  /* @__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)),
1427
- /* @__PURE__ */ React15.createElement(Button4, { variant: "outlined", color: "secondary", size: "small", disabled: ctaDisabled, onClick: onCtaClick }, ctaLabel)
1481
+ /* @__PURE__ */ React15.createElement(Button5, { variant: "outlined", color: "secondary", size: "small", disabled: ctaDisabled, onClick: onCtaClick }, ctaLabel)
1428
1482
  );
1429
1483
  }
1430
1484
 
@@ -1527,7 +1581,7 @@ function ReportShell({ report }) {
1527
1581
  import * as React20 from "react";
1528
1582
  import { useFloatingPanelZIndex as useFloatingPanelZIndex3 } from "@elementor/editor-floating-panels";
1529
1583
  import { ReloadIcon } from "@elementor/icons";
1530
- import { Badge as Badge2, Button as Button5, Tooltip as Tooltip4 } from "@elementor/ui";
1584
+ import { Badge as Badge2, Button as Button6, Tooltip as Tooltip4 } from "@elementor/ui";
1531
1585
  import { __ as __16 } from "@wordpress/i18n";
1532
1586
  function RescanButton({ hasScanned, isStale, disabled, onClick }) {
1533
1587
  const panelZIndex = useFloatingPanelZIndex3(AUDIT_PANEL_ID);
@@ -1539,7 +1593,7 @@ function RescanButton({ hasScanned, isStale, disabled, onClick }) {
1539
1593
  PopperProps: { sx: { zIndex: panelZIndex } }
1540
1594
  },
1541
1595
  /* @__PURE__ */ React20.createElement(Badge2, { variant: "dot", color: "warning", invisible: !isStale, overlap: "rectangular" }, /* @__PURE__ */ React20.createElement(
1542
- Button5,
1596
+ Button6,
1543
1597
  {
1544
1598
  variant: "contained",
1545
1599
  size: "small",
@@ -2252,6 +2306,7 @@ var audit11 = {
2252
2306
  if (!hasPageImages(ctx.elements.tree)) {
2253
2307
  return { status: "skipped", reason: __29("No images", "elementor") };
2254
2308
  }
2309
+ const isReady = ctx.pageContext.ally_plugin_active;
2255
2310
  const violations = [];
2256
2311
  const failedWidgetIds = /* @__PURE__ */ new Set();
2257
2312
  let missingAltImageCount = 0;
@@ -2267,7 +2322,7 @@ var audit11 = {
2267
2322
  elementId: node.id,
2268
2323
  targetHint: "element-settings",
2269
2324
  label: __29("Image is missing alt text.", "elementor"),
2270
- externalUrl: ctx.pageContext.ally_plugin_url,
2325
+ externalUrl: isReady ? ctx.pageContext.ally_scan_url : ctx.pageContext.ally_plugin_url,
2271
2326
  ctaLabel: __29("Fix with Ally", "elementor")
2272
2327
  });
2273
2328
  }
@@ -2288,6 +2343,7 @@ var audit12 = {
2288
2343
  severity: "info",
2289
2344
  weight: 1,
2290
2345
  evaluate: (ctx) => {
2346
+ const isReady = ctx.pageContext.image_optimization_plugin_active;
2291
2347
  const offendingElementIds = /* @__PURE__ */ new Set();
2292
2348
  let hasAnyImage = false;
2293
2349
  let inefficientImageFormatCount = 0;
@@ -2318,7 +2374,7 @@ var audit12 = {
2318
2374
  elementId,
2319
2375
  targetHint: "element-settings",
2320
2376
  label: __30("Image isn't served in a modern format like WebP or AVIF.", "elementor"),
2321
- externalUrl: ctx.pageContext.image_optimization_plugin_url,
2377
+ externalUrl: isReady ? ctx.pageContext.image_optimization_settings_url : ctx.pageContext.image_optimization_plugin_url,
2322
2378
  ctaLabel: __30("Optimize all", "elementor")
2323
2379
  }));
2324
2380
  return violations.length === 0 ? { status: "pass" } : { status: "fail", violations, metadata: { inefficientImageFormatCount } };
@@ -2338,6 +2394,7 @@ var audit13 = {
2338
2394
  severity: "warning",
2339
2395
  weight: 7,
2340
2396
  evaluate: (ctx) => {
2397
+ const isReady = ctx.pageContext.image_optimization_plugin_active;
2341
2398
  const widgetMaxKb = /* @__PURE__ */ new Map();
2342
2399
  let oversizedImageCount = 0;
2343
2400
  let hasAnyImage = false;
@@ -2374,7 +2431,7 @@ var audit13 = {
2374
2431
  __31("Image is %d KB (over 500 KB).", "elementor"),
2375
2432
  kb
2376
2433
  ),
2377
- externalUrl: ctx.pageContext.image_optimization_plugin_url,
2434
+ externalUrl: isReady ? ctx.pageContext.image_optimization_settings_url : ctx.pageContext.image_optimization_plugin_url,
2378
2435
  ctaLabel: __31("Optimize all", "elementor")
2379
2436
  }));
2380
2437
  return violations.length === 0 ? { status: "pass" } : { status: "fail", violations, metadata: { oversizedImageCount } };
@@ -2409,7 +2466,12 @@ var audit14 = {
2409
2466
  auditId: audit14.id,
2410
2467
  elementId: node.id,
2411
2468
  targetHint: "element-settings",
2412
- label: __32("Nested boxed container.", "elementor")
2469
+ label: __32("Nested boxed container.", "elementor"),
2470
+ angieFix: true,
2471
+ angiePrompt: __32(
2472
+ "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.",
2473
+ "elementor"
2474
+ )
2413
2475
  });
2414
2476
  }
2415
2477
  });