@maildeno/editor 0.4.5 → 0.4.7

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 (234) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/DatePicker-DIaGGAVI.js +5347 -0
  3. package/dist/DatePicker-DmMga5gQ.js +5347 -0
  4. package/dist/Dialog-GTfGtduh.js +6294 -0
  5. package/dist/HealthIndicator--ScSxVRA.js +1216 -0
  6. package/dist/HealthIndicator-BraAh2Aj.js +1217 -0
  7. package/dist/Icon-BYmOM482.js +2537 -0
  8. package/dist/Icon-c2SPT3-6.js +2577 -0
  9. package/dist/PreviewCanvas-C1-rP9-B.js +533 -0
  10. package/dist/PreviewCanvas-Nro7tNCn.js +533 -0
  11. package/dist/RichTextEditor-CrYhiAOo.js +12217 -0
  12. package/dist/RichTextEditor-D7Itx0fL.js +12218 -0
  13. package/dist/RowPreviewChildren-B66HbFdX.js +4 -0
  14. package/dist/RowPreviewChildren-DvrE9HTT.js +4 -0
  15. package/dist/{RowPreviewLegacyComponent-DVxrCJ14.js → RowPreviewLegacyComponent-CPqaWwsE.js} +1 -1
  16. package/dist/{RowPreviewRow-0pEQLmS4.js → RowPreviewRow-B91YPfoz.js} +1 -1
  17. package/dist/{RowPreviewRow-Eum1wh1W.js → RowPreviewRow-MaA-9ApX.js} +2 -2
  18. package/dist/SavedRowsPanel-CF-7R3q_.js +492 -0
  19. package/dist/SavedRowsPanel-TLOEvs3Z.js +491 -0
  20. package/dist/_plugin-vue_export-helper-Dj7HTbfw.js +8 -0
  21. package/dist/components/features/emailBuilder/EmailEditor.vue.d.ts +3 -3
  22. package/dist/composables/emailBuilder/transform/pipeline/optimize.d.ts +4 -4
  23. package/dist/editor.css +1 -1
  24. package/dist/element-CeRQDEn3.js +21396 -0
  25. package/dist/element.js +1 -1
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.js +8220 -41805
  28. package/dist/init.js +17 -16
  29. package/dist/mergeTagDefinitions-Cler19nv.js +27 -0
  30. package/dist/useEmailBuilder-CjJoxzTi.js +4893 -0
  31. package/dist/useEmailBuilder-gOc8otd8.js +4893 -0
  32. package/dist/useToast-DeNMwkP3.js +194 -0
  33. package/dist/{vue.runtime.esm-bundler-CsdyQceJ.js → vue.runtime.esm-bundler-D2AjRP90.js} +0 -1
  34. package/package.json +4 -2
  35. package/dist/adapters/index.d.ts +0 -38
  36. package/dist/baseStyles.d.ts +0 -13
  37. package/dist/blocks/canvasStyleHelpers.d.ts +0 -17
  38. package/dist/blocks/definitions/anchor.d.ts +0 -2
  39. package/dist/blocks/definitions/button.d.ts +0 -2
  40. package/dist/blocks/definitions/divider.d.ts +0 -2
  41. package/dist/blocks/definitions/heading.d.ts +0 -2
  42. package/dist/blocks/definitions/image.d.ts +0 -2
  43. package/dist/blocks/definitions/list.d.ts +0 -2
  44. package/dist/blocks/definitions/menu.d.ts +0 -2
  45. package/dist/blocks/definitions/paragraph.d.ts +0 -2
  46. package/dist/blocks/definitions/socials.d.ts +0 -2
  47. package/dist/blocks/definitions/spacer.d.ts +0 -2
  48. package/dist/blocks/definitions/video.d.ts +0 -2
  49. package/dist/blocks/index.d.ts +0 -8
  50. package/dist/brand.d.ts +0 -15
  51. package/dist/components/DesktopOnlyNotice.vue.d.ts +0 -20
  52. package/dist/components/features/emailBuilder/assistant/AssistantPanel.vue.d.ts +0 -37
  53. package/dist/components/features/emailBuilder/blocks/AnchorBlock.vue.d.ts +0 -3
  54. package/dist/components/features/emailBuilder/blocks/ButtonBlock.vue.d.ts +0 -3
  55. package/dist/components/features/emailBuilder/blocks/DividerBlock.vue.d.ts +0 -3
  56. package/dist/components/features/emailBuilder/blocks/HeadingBlock.vue.d.ts +0 -3
  57. package/dist/components/features/emailBuilder/blocks/ImageBlock.vue.d.ts +0 -3
  58. package/dist/components/features/emailBuilder/blocks/ListBlock.vue.d.ts +0 -3
  59. package/dist/components/features/emailBuilder/blocks/MenuBlock.vue.d.ts +0 -3
  60. package/dist/components/features/emailBuilder/blocks/ParagraphBlock.vue.d.ts +0 -3
  61. package/dist/components/features/emailBuilder/blocks/SocialsBlock.vue.d.ts +0 -3
  62. package/dist/components/features/emailBuilder/blocks/SpacerBlock.vue.d.ts +0 -3
  63. package/dist/components/features/emailBuilder/blocks/VideoBlock.vue.d.ts +0 -3
  64. package/dist/components/features/emailBuilder/canvas/Canvas.vue.d.ts +0 -3
  65. package/dist/components/features/emailBuilder/canvas/CanvasColumn.vue.d.ts +0 -49
  66. package/dist/components/features/emailBuilder/canvas/CanvasComponent.vue.d.ts +0 -56
  67. package/dist/components/features/emailBuilder/canvas/CanvasElement.vue.d.ts +0 -21
  68. package/dist/components/features/emailBuilder/canvas/CanvasRow.vue.d.ts +0 -49
  69. package/dist/components/features/emailBuilder/canvas/CanvasRowSpacer.vue.d.ts +0 -40
  70. package/dist/components/features/emailBuilder/health/HealthIndicator.vue.d.ts +0 -3
  71. package/dist/components/features/emailBuilder/layout/Header.vue.d.ts +0 -54
  72. package/dist/components/features/emailBuilder/layout/SendEmail.vue.d.ts +0 -21
  73. package/dist/components/features/emailBuilder/layout/SidebarLeft.vue.d.ts +0 -3
  74. package/dist/components/features/emailBuilder/layout/SidebarRight.vue.d.ts +0 -3
  75. package/dist/components/features/emailBuilder/panels/AnchorPanel.vue.d.ts +0 -3
  76. package/dist/components/features/emailBuilder/panels/ButtonPanel.vue.d.ts +0 -3
  77. package/dist/components/features/emailBuilder/panels/DividerPanel.vue.d.ts +0 -3
  78. package/dist/components/features/emailBuilder/panels/HeadingPanel.vue.d.ts +0 -3
  79. package/dist/components/features/emailBuilder/panels/ImagePanel.vue.d.ts +0 -3
  80. package/dist/components/features/emailBuilder/panels/ListPanel.vue.d.ts +0 -3
  81. package/dist/components/features/emailBuilder/panels/MenuPanel.vue.d.ts +0 -3
  82. package/dist/components/features/emailBuilder/panels/ParagraphPanel.vue.d.ts +0 -3
  83. package/dist/components/features/emailBuilder/panels/RowColumnPanel.vue.d.ts +0 -13
  84. package/dist/components/features/emailBuilder/panels/RowPanel.vue.d.ts +0 -13
  85. package/dist/components/features/emailBuilder/panels/RowSpacerPanel.vue.d.ts +0 -13
  86. package/dist/components/features/emailBuilder/panels/SocialsPanel.vue.d.ts +0 -3
  87. package/dist/components/features/emailBuilder/panels/SpacerPanel.vue.d.ts +0 -3
  88. package/dist/components/features/emailBuilder/panels/VideoPanel.vue.d.ts +0 -3
  89. package/dist/components/features/emailBuilder/panels/shared/DeviceTabs.vue.d.ts +0 -19
  90. package/dist/components/features/emailBuilder/panels/shared/LinkFieldMergeTags.vue.d.ts +0 -31
  91. package/dist/components/features/emailBuilder/panels/shared/OverrideBadge.vue.d.ts +0 -9
  92. package/dist/components/features/emailBuilder/panels/shared/PreviewBadge.vue.d.ts +0 -11
  93. package/dist/components/features/emailBuilder/panels/shared/TextFieldMergeTags.vue.d.ts +0 -25
  94. package/dist/components/features/emailBuilder/panels/ui/BoxStylingSection.vue.d.ts +0 -7
  95. package/dist/components/features/emailBuilder/panels/ui/PropertyAlignment.vue.d.ts +0 -30
  96. package/dist/components/features/emailBuilder/panels/ui/PropertyColor.vue.d.ts +0 -75
  97. package/dist/components/features/emailBuilder/panels/ui/PropertyGradientColor.vue.d.ts +0 -27
  98. package/dist/components/features/emailBuilder/panels/ui/PropertyNumberSlider.vue.d.ts +0 -74
  99. package/dist/components/features/emailBuilder/panels/ui/PropertySection.vue.d.ts +0 -17
  100. package/dist/components/features/emailBuilder/panels/ui/PropertySelect.vue.d.ts +0 -74
  101. package/dist/components/features/emailBuilder/panels/ui/PropertyTextStyle.vue.d.ts +0 -52
  102. package/dist/components/features/emailBuilder/panels/ui/PropertyTextStylePanel.vue.d.ts +0 -72
  103. package/dist/components/features/emailBuilder/panels/ui/PropertyToggle.vue.d.ts +0 -45
  104. package/dist/components/features/emailBuilder/panels/ui/PropertyVisibility.vue.d.ts +0 -32
  105. package/dist/components/features/emailBuilder/panels/ui/SpacingControl.vue.d.ts +0 -19
  106. package/dist/components/features/emailBuilder/panels/ui/color-picker/AlphaSlider.vue.d.ts +0 -28
  107. package/dist/components/features/emailBuilder/panels/ui/color-picker/ColorPickerPanel.vue.d.ts +0 -31
  108. package/dist/components/features/emailBuilder/panels/ui/color-picker/HueSlider.vue.d.ts +0 -19
  109. package/dist/components/features/emailBuilder/panels/ui/color-picker/RecentColors.vue.d.ts +0 -20
  110. package/dist/components/features/emailBuilder/panels/ui/color-picker/SVPanel.vue.d.ts +0 -39
  111. package/dist/components/features/emailBuilder/panels/ui/visibility-rule/Rule.vue.d.ts +0 -28
  112. package/dist/components/features/emailBuilder/preview/PreviewScreen.vue.d.ts +0 -7
  113. package/dist/components/features/emailBuilder/preview/ui/PreviewCanvas.vue.d.ts +0 -10
  114. package/dist/components/features/emailBuilder/preview/ui/PreviewCapabilityBadges.vue.d.ts +0 -12
  115. package/dist/components/features/emailBuilder/preview/ui/PreviewClientCard.vue.d.ts +0 -20
  116. package/dist/components/features/emailBuilder/preview/ui/PreviewClientList.vue.d.ts +0 -21
  117. package/dist/components/features/emailBuilder/preview/ui/PreviewClientPicker.vue.d.ts +0 -16
  118. package/dist/components/features/emailBuilder/preview/ui/PreviewComponent.vue.d.ts +0 -10
  119. package/dist/components/features/emailBuilder/preview/ui/PreviewDetailView.vue.d.ts +0 -41
  120. package/dist/components/features/emailBuilder/preview/ui/PreviewRendered.vue.d.ts +0 -8
  121. package/dist/components/features/emailBuilder/product/RowPreview.vue.d.ts +0 -8
  122. package/dist/components/features/emailBuilder/product/RowPreviewChildren.vue.d.ts +0 -14
  123. package/dist/components/features/emailBuilder/product/RowPreviewLegacyComponent.vue.d.ts +0 -14
  124. package/dist/components/features/emailBuilder/product/RowPreviewRow.vue.d.ts +0 -9
  125. package/dist/components/features/emailBuilder/product/SavedRowsPanel.vue.d.ts +0 -3
  126. package/dist/components/features/emailBuilder/product/SavedTemplatesPanel.vue.d.ts +0 -13
  127. package/dist/components/features/emailBuilder/product/VersionHistoryPanel.vue.d.ts +0 -14
  128. package/dist/components/features/emailBuilder/sidebar/SidebarBlockButton.vue.d.ts +0 -35
  129. package/dist/components/features/emailBuilder/tabs/GeneralTab.vue.d.ts +0 -3
  130. package/dist/components/features/emailBuilder/tabs/LayoutTab.vue.d.ts +0 -3
  131. package/dist/components/features/emailBuilder/tabs/content/ContentTab.vue.d.ts +0 -3
  132. package/dist/components/features/emailBuilder/tabs/content/LayersPanel.vue.d.ts +0 -7
  133. package/dist/components/features/emailBuilder/tabs/general/MergeTagsTab.vue.d.ts +0 -3
  134. package/dist/components/features/emailBuilder/tabs/general/StylesTab.vue.d.ts +0 -3
  135. package/dist/components/features/emailBuilder/tabs/general/VisibilityTab.vue.d.ts +0 -3
  136. package/dist/components/features/emailBuilder/tabs/general/VisibilityWrapESPTab.vue.d.ts +0 -3
  137. package/dist/components/features/emailBuilder/tabs/general/merge-tags/LinkTagTab.vue.d.ts +0 -3
  138. package/dist/components/features/emailBuilder/tabs/general/merge-tags/MergeTagTab.vue.d.ts +0 -3
  139. package/dist/components/features/emailBuilder/ui/LinkInputDialog.vue.d.ts +0 -17
  140. package/dist/components/features/emailBuilder/ui/canvas/CanvasRowDropZone.vue.d.ts +0 -12
  141. package/dist/components/features/emailBuilder/ui/canvas/DropZone.vue.d.ts +0 -10
  142. package/dist/components/features/emailBuilder/ui/canvas/Loader.vue.d.ts +0 -19
  143. package/dist/components/features/emailBuilder/ui/layers-panel/ComponentIcon.vue.d.ts +0 -7
  144. package/dist/components/features/emailBuilder/ui/layers-panel/LayerColumnChildren.vue.d.ts +0 -42
  145. package/dist/components/features/emailBuilder/ui/layers-panel/LayerDropLine.vue.d.ts +0 -8
  146. package/dist/components/features/emailBuilder/ui/renderers/AnchorRenderer.vue.d.ts +0 -10
  147. package/dist/components/features/emailBuilder/ui/renderers/DividerRenderer.vue.d.ts +0 -10
  148. package/dist/components/features/emailBuilder/ui/renderers/HeadingRenderer.vue.d.ts +0 -10
  149. package/dist/components/features/emailBuilder/ui/renderers/ImageRenderer.vue.d.ts +0 -10
  150. package/dist/components/features/emailBuilder/ui/renderers/ListRenderer.vue.d.ts +0 -10
  151. package/dist/components/features/emailBuilder/ui/renderers/MenuRenderer.vue.d.ts +0 -10
  152. package/dist/components/features/emailBuilder/ui/renderers/ParagraphRenderer.vue.d.ts +0 -10
  153. package/dist/components/features/emailBuilder/ui/renderers/SocialsRenderer.vue.d.ts +0 -10
  154. package/dist/components/features/emailBuilder/ui/renderers/SpacerRenderer.vue.d.ts +0 -10
  155. package/dist/components/features/emailBuilder/ui/renderers/VideoRenderer.vue.d.ts +0 -10
  156. package/dist/components/features/emailBuilder/ui/rich-text-editor/RichTextEditor.vue.d.ts +0 -12
  157. package/dist/components/features/emailBuilder/ui/rich-text-editor/pickers/MergeTagPicker.vue.d.ts +0 -18
  158. package/dist/components/features/emailBuilder/ui/rich-text-editor/pickers/RichTextColorPicker.vue.d.ts +0 -15
  159. package/dist/components/features/emailBuilder/ui/visibility/SupportBadge.vue.d.ts +0 -10
  160. package/dist/components/features/emailBuilder/ui/visibility/VisibilityPopover.vue.d.ts +0 -45
  161. package/dist/components/ui/ConfirmDialog.vue.d.ts +0 -3
  162. package/dist/components/ui/Icon.vue.d.ts +0 -33
  163. package/dist/components/ui/InfoDialog.vue.d.ts +0 -12
  164. package/dist/components/ui/Loading.vue.d.ts +0 -6
  165. package/dist/components/ui/ShadowSafeToast.vue.d.ts +0 -3
  166. package/dist/components/ui/primitives/Button.vue.d.ts +0 -23
  167. package/dist/components/ui/primitives/DatePicker.vue.d.ts +0 -16
  168. package/dist/components/ui/primitives/Dialog.vue.d.ts +0 -34
  169. package/dist/components/ui/primitives/InputText.vue.d.ts +0 -29
  170. package/dist/components/ui/primitives/Message.vue.d.ts +0 -20
  171. package/dist/components/ui/primitives/Select.vue.d.ts +0 -22
  172. package/dist/composables/emailBuilder/components/useComponentStyleEditor.d.ts +0 -72
  173. package/dist/composables/emailBuilder/components/useProductRowActions.d.ts +0 -3
  174. package/dist/composables/emailBuilder/components/useProductRows.d.ts +0 -59
  175. package/dist/composables/emailBuilder/components/useSystemRows.d.ts +0 -22
  176. package/dist/composables/emailBuilder/core/config/componentConfig.d.ts +0 -29
  177. package/dist/composables/emailBuilder/core/link-tags/useLinkTagInsert.d.ts +0 -13
  178. package/dist/composables/emailBuilder/core/merge-tags/mergeTagDefinitions.d.ts +0 -64
  179. package/dist/composables/emailBuilder/core/merge-tags/resolveMergeTags.d.ts +0 -27
  180. package/dist/composables/emailBuilder/core/ui/useLayerHoverScroll.d.ts +0 -20
  181. package/dist/composables/emailBuilder/core/ui/useLayoutDrag.d.ts +0 -85
  182. package/dist/composables/emailBuilder/core/ui/useRichTextEditors.d.ts +0 -6
  183. package/dist/composables/emailBuilder/core/ui/visibilityBadgeHelpers.d.ts +0 -22
  184. package/dist/composables/emailBuilder/core/useEmailBuilder.d.ts +0 -292
  185. package/dist/composables/emailBuilder/core/useEmailBuilderDefault.d.ts +0 -507
  186. package/dist/composables/emailBuilder/core/useEmailBuilderOperations.d.ts +0 -31
  187. package/dist/composables/emailBuilder/core/useEmailBuilderVisibility.d.ts +0 -31
  188. package/dist/composables/emailBuilder/export/actions/download.d.ts +0 -24
  189. package/dist/composables/emailBuilder/export/generators/html.d.ts +0 -3
  190. package/dist/composables/emailBuilder/export/generators/mjml.d.ts +0 -6
  191. package/dist/composables/emailBuilder/export/generators/react-email.d.ts +0 -61
  192. package/dist/composables/emailBuilder/export/helpers/generatorHelpers.d.ts +0 -53
  193. package/dist/composables/emailBuilder/export/logic/reactLogicWrapper.d.ts +0 -31
  194. package/dist/composables/emailBuilder/export/merge-tags/mergeTagMapper.d.ts +0 -42
  195. package/dist/composables/emailBuilder/export/merge-tags/reactMergeTagMapper.d.ts +0 -26
  196. package/dist/composables/emailBuilder/export/shared/containers.d.ts +0 -18
  197. package/dist/composables/emailBuilder/export/shared/styles.d.ts +0 -12
  198. package/dist/composables/emailBuilder/export/shared/tagIdentifier.d.ts +0 -1
  199. package/dist/composables/emailBuilder/export/targets/htmlExport.d.ts +0 -20
  200. package/dist/composables/emailBuilder/export/targets/jsonExport.d.ts +0 -28
  201. package/dist/composables/emailBuilder/export/targets/mjmlExport.d.ts +0 -19
  202. package/dist/composables/emailBuilder/export/targets/reactEmailExport.d.ts +0 -20
  203. package/dist/composables/emailBuilder/export/targets/templateSnapshot.d.ts +0 -24
  204. package/dist/composables/emailBuilder/export/transformers/espWrapper.d.ts +0 -2
  205. package/dist/composables/emailBuilder/export/transformers/formatting.d.ts +0 -3
  206. package/dist/composables/emailBuilder/export/transformers/minify.d.ts +0 -33
  207. package/dist/composables/emailBuilder/export/transformers/tagMapper.d.ts +0 -29
  208. package/dist/composables/emailBuilder/export/types/export.d.ts +0 -24
  209. package/dist/composables/emailBuilder/export/useEmailExportEngine.d.ts +0 -63
  210. package/dist/composables/emailBuilder/export/useEmailExportMobileStyles.d.ts +0 -3
  211. package/dist/composables/emailBuilder/export/useExportSettings.d.ts +0 -5
  212. package/dist/composables/emailBuilder/health/accessibilityChecker.d.ts +0 -64
  213. package/dist/composables/emailBuilder/health/healthIndicator.d.ts +0 -19
  214. package/dist/composables/emailBuilder/history/useEmailBuilderHistory.d.ts +0 -26
  215. package/dist/composables/emailBuilder/history/useHistory.d.ts +0 -16
  216. package/dist/composables/emailBuilder/preview/useClientPreview.d.ts +0 -127
  217. package/dist/composables/emailBuilder/transform/pipeline/hydrate.d.ts +0 -54
  218. package/dist/composables/emailBuilder/types/emailBuilder.d.ts +0 -79
  219. package/dist/composables/system/useColor.d.ts +0 -26
  220. package/dist/composables/system/useDeviceDetection.d.ts +0 -5
  221. package/dist/composables/system/useGoogleFonts.d.ts +0 -11
  222. package/dist/composables/system/useImageUpload.d.ts +0 -23
  223. package/dist/composables/system/useSavedRowsPanel.d.ts +0 -35
  224. package/dist/composables/ui/useConfirm.d.ts +0 -33
  225. package/dist/composables/ui/useInfoDialog.d.ts +0 -22
  226. package/dist/composables/ui/useTeleportTarget.d.ts +0 -49
  227. package/dist/composables/ui/useToast.d.ts +0 -32
  228. package/dist/devStyleMirror.d.ts +0 -20
  229. package/dist/documentScopedStyles.d.ts +0 -39
  230. package/dist/element-Dq6bmt2l.js +0 -48454
  231. package/dist/lightDom.d.ts +0 -30
  232. package/dist/shadowStyles.d.ts +0 -8
  233. package/dist/utils/generateId.d.ts +0 -1
  234. package/dist/utils/shadowDom.d.ts +0 -30
@@ -0,0 +1,1216 @@
1
+ import { C as F, L as _, N as be, P as ye, T as ke, X as W, f as X, g as r, gt as A, h as O, l as H, mt as g, n as V, st as D, u as N, v as T, vt as Ie, y as b, yt as k, z as J } from "./vue.runtime.esm-bundler-D2AjRP90.js";
2
+ import { H as Le, U as Ee, V as Ce, W as Ae } from "./element-CeRQDEn3.js";
3
+ import { n as we } from "./useEmailBuilder-gOc8otd8.js";
4
+ var Se = 60, Te = 40, Re = 500, Ne = 80, Ge = 60, $e = 80, ne = 398, Y = 30, re = 140, oe = 117, Me = 105, Oe = {
5
+ heading: 317,
6
+ paragraph: 337,
7
+ list: 250,
8
+ menu: 250,
9
+ button: 488,
10
+ anchor: 394,
11
+ image: 281,
12
+ video: 320,
13
+ divider: 461,
14
+ socials: 149,
15
+ spacer: 80
16
+ }, Be = 54, Ue = 229, He = 450, De = 120, xe = 5, Ke = 0.92, Pe = 1.08, Fe = /* @__PURE__ */ new Set([
17
+ "heading",
18
+ "paragraph",
19
+ "list",
20
+ "menu",
21
+ "button",
22
+ "anchor"
23
+ ]), We = /* @__PURE__ */ new Set(["image", "video"]);
24
+ function le(t) {
25
+ return t.componentType ?? t.type;
26
+ }
27
+ function Ve(t) {
28
+ const e = t.props ?? {};
29
+ return le(t) === "menu" ? (e.items ?? []).map((s) => s.label ?? "").join(" ") : e.content ?? e.text ?? e.label ?? "";
30
+ }
31
+ function Ye(t) {
32
+ return (t.props?.items ?? []).length;
33
+ }
34
+ function je(t) {
35
+ const e = (t.props?.platforms ?? []).filter((i) => i.enabled !== !1), s = e.reduce((i, n) => i + (n.link?.length ?? 0), 0), a = e.reduce((i, n) => i + (n.icon?.length ?? 0), 0);
36
+ return {
37
+ count: e.length,
38
+ linkBytes: s,
39
+ srcBytes: a
40
+ };
41
+ }
42
+ function ze(t) {
43
+ const e = t.props?.mobile;
44
+ return !e || typeof e != "object" ? !1 : Object.values(e).some((s) => s != null);
45
+ }
46
+ function de(t, e, s) {
47
+ if (!(s >= xe))
48
+ for (const a of t) {
49
+ const i = a.type;
50
+ if (i === "row") {
51
+ e.estimatedBytes += ne;
52
+ const n = a.backgroundColor && a.backgroundColor !== "transparent", c = a.backgroundGradient?.useGradient;
53
+ (n || c) && (e.estimatedBytes += Y);
54
+ const o = a.columns ?? [], d = (a.gap ?? 0) > 0;
55
+ o.forEach((p, u) => {
56
+ e.estimatedBytes += re, (p.backgroundColor && p.backgroundColor !== "transparent" || p.backgroundGradient?.useGradient) && (e.estimatedBytes += Y), d && u < o.length - 1 && (e.estimatedBytes += oe), de(p.children ?? p.components ?? [], e, s + 1);
57
+ });
58
+ continue;
59
+ }
60
+ if (i === "row-spacer") {
61
+ e.estimatedBytes += 80;
62
+ continue;
63
+ }
64
+ Xe(a, e);
65
+ }
66
+ }
67
+ function Xe(t, e) {
68
+ const s = le(t);
69
+ if (e.estimatedBytes += Oe[s] ?? 337, ze(t) && (e.estimatedBytes += De), t.props?.fontFamily && e.usedFonts.add(t.props.fontFamily), t.props?.mobile?.fontFamily && e.usedFonts.add(t.props.mobile.fontFamily), Fe.has(s)) {
70
+ e.textCompCount++;
71
+ const a = Ve(t);
72
+ e.textChars += a.length, e.estimatedBytes += new TextEncoder().encode(a).length, s === "menu" && (e.estimatedBytes += Ye(t) * Be), (s === "button" || s === "anchor") && (e.estimatedBytes += (t.props?.link ?? "").length), s === "button" && t.props?.icon && (e.estimatedBytes += He, e.estimatedBytes += (t.props.icon ?? "").length, e.estimatedBytes += (t.props.iconAlt ?? "").length, e.buttonIconCount++);
73
+ } else if (We.has(s))
74
+ e.imageCompCount++, e.imageWeight += Re, e.estimatedBytes += (t.props?.src ?? "").length;
75
+ else if (s === "socials") {
76
+ const { count: a, linkBytes: i, srcBytes: n } = je(t);
77
+ e.socialIconCount += a, e.imageWeight += a * Ne, e.estimatedBytes += a * Ue + i + n;
78
+ }
79
+ }
80
+ function Je(t) {
81
+ return { metrics: O(() => {
82
+ const e = {
83
+ textChars: 0,
84
+ imageWeight: 0,
85
+ estimatedBytes: 1520,
86
+ usedFonts: /* @__PURE__ */ new Set(),
87
+ textCompCount: 0,
88
+ imageCompCount: 0,
89
+ socialIconCount: 0,
90
+ buttonIconCount: 0
91
+ }, s = t.value ?? [];
92
+ for (const v of s) {
93
+ if (!v.columns) {
94
+ e.estimatedBytes += 80;
95
+ continue;
96
+ }
97
+ e.estimatedBytes += ne;
98
+ const I = v.backgroundColor && v.backgroundColor !== "transparent", y = v.backgroundGradient?.useGradient;
99
+ (I || y) && (e.estimatedBytes += Y);
100
+ const E = v.columns ?? [], w = (v.gap ?? 0) > 0;
101
+ E.forEach((C, U) => {
102
+ e.estimatedBytes += re, (C.backgroundColor && C.backgroundColor !== "transparent" || C.backgroundGradient?.useGradient) && (e.estimatedBytes += Y), w && U < E.length - 1 && (e.estimatedBytes += oe), de(C.children ?? C.components ?? [], e, 0);
103
+ });
104
+ }
105
+ e.estimatedBytes += e.usedFonts.size * Me;
106
+ const a = e.textChars + e.imageWeight, i = a === 0 ? 100 : Math.round(e.textChars / a * 100), n = 100 - i, c = Math.round(e.estimatedBytes / 1024 * 10) / 10, o = Math.round(e.estimatedBytes * Ke / 1024 * 10) / 10, d = Math.round(e.estimatedBytes * Pe / 1024 * 10) / 10;
107
+ let p;
108
+ i >= Se ? p = "good" : i >= Te ? p = "warn" : p = "bad";
109
+ let u;
110
+ d <= Ge ? u = "good" : d <= $e ? u = "warn" : u = "bad";
111
+ const L = p === "good" ? `Good ratio (${i}% text). Email clients will render this reliably.` : p === "warn" ? `Low text ratio (${i}%). Spam filters prefer >= 60% text. Add more copy.` : `Poor ratio (${i}% text). Heavy image emails are often blocked or sent to spam.`, S = u === "good" ? `Estimated ${o}–${d} KB — well within Gmail's 102 KB limit.` : u === "warn" ? `Estimated ${o}–${d} KB — approaching Gmail's 102 KB clip threshold.` : `Estimated ${o}–${d} KB — Gmail will clip this email at ~102 KB.`;
112
+ return {
113
+ textChars: e.textChars,
114
+ textCompCount: e.textCompCount,
115
+ imageCompCount: e.imageCompCount,
116
+ socialIconCount: e.socialIconCount,
117
+ buttonIconCount: e.buttonIconCount,
118
+ textRatio: i,
119
+ imageRatio: n,
120
+ estimatedKB: c,
121
+ estimatedLow: o,
122
+ estimatedHigh: d,
123
+ ratioStatus: p,
124
+ kbStatus: u,
125
+ ratioTip: L,
126
+ kbTip: S,
127
+ isEmpty: a === 0 && e.estimatedBytes <= 1570
128
+ };
129
+ }) };
130
+ }
131
+ var qe = {
132
+ IMG_ALT_MISSING: {
133
+ id: "IMG_ALT_MISSING",
134
+ name: "Image missing alt text",
135
+ severity: "critical",
136
+ info: "WCAG 1.1.1 (Level A). Screen readers announce the file name when alt is absent, which is meaningless to recipients using assistive tech."
137
+ },
138
+ IMG_ALT_DECORATIVE: {
139
+ id: "IMG_ALT_DECORATIVE",
140
+ name: "Decorative image should use empty alt",
141
+ severity: "serious",
142
+ info: 'WCAG 1.1.1. Purely decorative images should have alt="" so screen readers skip them rather than announce a filename.'
143
+ },
144
+ IMG_ALT_REDUNDANT: {
145
+ id: "IMG_ALT_REDUNDANT",
146
+ name: "Alt text starts with redundant phrasing",
147
+ severity: "serious",
148
+ info: 'Phrases like "image of" or "picture of" are added by the screen reader automatically — including them causes duplicated announcements.'
149
+ },
150
+ LINK_EMPTY: {
151
+ id: "LINK_EMPTY",
152
+ name: "Link has no accessible text",
153
+ severity: "critical",
154
+ info: 'WCAG 2.4.4 (Level A). A link with no readable label is announced as "link" with no destination, leaving AT users unable to act on it.'
155
+ },
156
+ LINK_VAGUE: {
157
+ id: "LINK_VAGUE",
158
+ name: "Link text is not descriptive",
159
+ severity: "serious",
160
+ info: 'WCAG 2.4.4. Phrases like "click here" or "read more" lose meaning when read out of context — screen readers often list links separately.'
161
+ },
162
+ LINK_NO_HREF: {
163
+ id: "LINK_NO_HREF",
164
+ name: "Link is missing a destination URL",
165
+ severity: "critical",
166
+ info: "WCAG 2.4.4. A link without href is not focusable or actionable for keyboard / AT users."
167
+ },
168
+ LINK_DUPLICATE_TEXT_DIFFERENT_URL: {
169
+ id: "LINK_DUPLICATE_TEXT_DIFFERENT_URL",
170
+ name: "Same link text points to different URLs",
171
+ severity: "serious",
172
+ info: "WCAG 2.4.4. Repeated link text should identify the destination. When the same link text (ignoring case and trailing punctuation) points to different URLs, screen reader users navigating a link list may not be able to distinguish the destinations."
173
+ },
174
+ BUTTON_EMPTY: {
175
+ id: "BUTTON_EMPTY",
176
+ name: "Button has no accessible label",
177
+ severity: "critical",
178
+ info: "WCAG 4.1.2 (Level A). Interactive elements must expose a name to assistive technology."
179
+ },
180
+ BUTTON_ICON_NO_ALT: {
181
+ id: "BUTTON_ICON_NO_ALT",
182
+ name: "Icon missing alt text",
183
+ severity: "serious",
184
+ info: "WCAG 1.1.1. When a clickable element's icon adds meaning, its alt becomes part of the element's accessible name. Leave alt empty only if the icon is purely decorative."
185
+ },
186
+ CONTRAST_FAIL_NORMAL: {
187
+ id: "CONTRAST_FAIL_NORMAL",
188
+ name: "Text contrast below 4.5:1",
189
+ severity: "critical",
190
+ info: "WCAG 1.4.3 (Level AA). Normal text needs at least 4.5:1 contrast against its background to be readable for users with low vision."
191
+ },
192
+ CONTRAST_FAIL_LARGE: {
193
+ id: "CONTRAST_FAIL_LARGE",
194
+ name: "Large text contrast below 3:1",
195
+ severity: "critical",
196
+ info: "WCAG 1.4.3 (Level AA). Large text (≥18pt or ≥14pt bold) needs at least 3:1 contrast."
197
+ },
198
+ HEADING_HIERARCHY: {
199
+ id: "HEADING_HIERARCHY",
200
+ name: "Heading levels skip a step",
201
+ severity: "serious",
202
+ info: "WCAG 1.3.1 (Level A). Skipping from H1 to H3 confuses screen reader users who navigate by heading level."
203
+ },
204
+ HEADING_MISSING: {
205
+ id: "HEADING_MISSING",
206
+ name: "No heading present",
207
+ severity: "serious",
208
+ info: "WCAG 2.4.6. Long content benefits from at least one heading so AT users can orient themselves and skim."
209
+ },
210
+ FONT_TOO_SMALL: {
211
+ id: "FONT_TOO_SMALL",
212
+ name: "Font size below 12px",
213
+ severity: "serious",
214
+ info: "Below 12px, body copy becomes hard to read for users with low vision and may force pinch-zoom on mobile."
215
+ },
216
+ SOCIAL_ICON_NO_LABEL: {
217
+ id: "SOCIAL_ICON_NO_LABEL",
218
+ name: "Social icon missing accessible name",
219
+ severity: "serious",
220
+ info: "WCAG 4.1.2. Each social link must announce its platform — alt text or aria-label, not just an icon."
221
+ },
222
+ LANG_MISSING: {
223
+ id: "LANG_MISSING",
224
+ name: "Email language not specified",
225
+ severity: "critical",
226
+ info: "WCAG 3.1.1 (Level A). Without lang, screen readers may pronounce content using the wrong voice."
227
+ },
228
+ LINK_MALFORMED: {
229
+ id: "LINK_MALFORMED",
230
+ name: "Link URL is malformed",
231
+ severity: "critical",
232
+ info: "WCAG 2.4.4. URLs without a scheme like `https://` (e.g. `app.com`) are rendered inconsistently across email clients — some treat them as plain text, breaking the link entirely."
233
+ },
234
+ LINK_INSECURE_HTTP: {
235
+ id: "LINK_INSECURE_HTTP",
236
+ name: "Link uses insecure HTTP",
237
+ severity: "serious",
238
+ info: "Modern email clients flag or block `http://` links. Use `https://` so the link works reliably and recipients aren't warned about insecure destinations."
239
+ },
240
+ LINK_DANGEROUS_SCHEME: {
241
+ id: "LINK_DANGEROUS_SCHEME",
242
+ name: "Link uses a blocked URL scheme",
243
+ severity: "critical",
244
+ info: "Schemes like `javascript:`, `data:`, `vbscript:`, and `file:` are stripped by every major email client. The link will be inert when delivered."
245
+ },
246
+ LINK_MERGETAG_UNGUARDED: {
247
+ id: "LINK_MERGETAG_UNGUARDED",
248
+ name: "Link URL contains a merge tag without a fallback",
249
+ severity: "serious",
250
+ info: "WCAG 2.4.4. If the tag resolves to empty for a recipient, the URL becomes broken (e.g. `https://example.com/?id=`). Add a pipe-default such as `{{ user_id|'guest' }}`."
251
+ },
252
+ IMG_SRC_MISSING: {
253
+ id: "IMG_SRC_MISSING",
254
+ name: "Image source URL is missing",
255
+ severity: "critical",
256
+ info: "Without a src, the image cannot load and recipients only see the alt text — or nothing if alt is also empty."
257
+ },
258
+ IMG_SRC_MALFORMED: {
259
+ id: "IMG_SRC_MALFORMED",
260
+ name: "Image source URL is malformed",
261
+ severity: "critical",
262
+ info: "Image src must be an absolute URL with a scheme (http:// or https://). Relative paths and bare hostnames don't load in email clients."
263
+ },
264
+ IMG_SRC_INSECURE: {
265
+ id: "IMG_SRC_INSECURE",
266
+ name: "Image source uses insecure HTTP",
267
+ severity: "critical",
268
+ info: "Most modern email clients (Gmail, Apple Mail, Outlook 365) refuse to load http:// images by default — recipients will see a broken-image placeholder. Switch to https://."
269
+ },
270
+ IMG_SRC_DATAURI: {
271
+ id: "IMG_SRC_DATAURI",
272
+ name: "Image source uses a data: URI",
273
+ severity: "critical",
274
+ info: "Most email clients strip or refuse data: URIs to prevent phishing. Host the image on an HTTPS URL instead."
275
+ },
276
+ IMG_SRC_MERGETAG_UNGUARDED: {
277
+ id: "IMG_SRC_MERGETAG_UNGUARDED",
278
+ name: "Image source contains a merge tag without a fallback",
279
+ severity: "serious",
280
+ info: "If the tag resolves to empty for a recipient, the image won't load. Add a pipe-default like `{{ avatar_url|'https://example.com/default-avatar.png' }}`."
281
+ },
282
+ PREHEADER_MISSING: {
283
+ id: "PREHEADER_MISSING",
284
+ name: "Preheader text not set",
285
+ severity: "serious",
286
+ info: "Preheader text is the first content screen readers and inbox previews announce after the subject line. Without one, recipients see whatever raw content the email starts with — usually a 'View in browser' link or a CTA, which is unhelpful for both AT users and open rates."
287
+ },
288
+ MERGETAG_IN_ALT: {
289
+ id: "MERGETAG_IN_ALT",
290
+ name: "Image alt depends on a merge tag without a fallback",
291
+ severity: "serious",
292
+ info: "WCAG 1.1.1. If the recipient has no value for the tag, the alt becomes empty and screen readers will skip the image silently. Use a pipe-default like {{ name|'Profile photo' }} to guarantee a description."
293
+ },
294
+ MERGETAG_LINK_LABEL: {
295
+ id: "MERGETAG_LINK_LABEL",
296
+ name: "Link text is only a merge tag without a fallback",
297
+ severity: "critical",
298
+ info: "WCAG 2.4.4. If the tag resolves to empty for some recipients, the link will have no readable label at all. Add surrounding text or a pipe-default such as {{ cta|'View offer' }}."
299
+ },
300
+ MERGETAG_RESOLVES_EMPTY: {
301
+ id: "MERGETAG_RESOLVES_EMPTY",
302
+ name: "Merge tag resolves to empty in preview",
303
+ severity: "serious",
304
+ info: "With preview active, this tag's value is empty — meaning some recipients will see nothing where this content should appear. Set a preview value or add a pipe-default to the tag."
305
+ }
306
+ }, ue = "maildeno:a11y:ignoredIssues";
307
+ function Qe() {
308
+ if (typeof window > "u" || !window.localStorage) return /* @__PURE__ */ new Set();
309
+ try {
310
+ const t = window.localStorage.getItem(ue);
311
+ if (!t) return /* @__PURE__ */ new Set();
312
+ const e = JSON.parse(t);
313
+ return new Set(Array.isArray(e) ? e : []);
314
+ } catch {
315
+ return /* @__PURE__ */ new Set();
316
+ }
317
+ }
318
+ function q(t) {
319
+ if (!(typeof window > "u" || !window.localStorage))
320
+ try {
321
+ window.localStorage.setItem(ue, JSON.stringify([...t]));
322
+ } catch {
323
+ }
324
+ }
325
+ var Ze = 5;
326
+ function Q(t) {
327
+ return t.componentType ?? t.type;
328
+ }
329
+ function M(t) {
330
+ return t.id ? String(t.id) : t._uid ? String(t._uid) : `${Q(t) ?? "unknown"}:no-id`;
331
+ }
332
+ function ce(t, e, s, a) {
333
+ return e?._id ? `${t}::${s}_${e._id}` : `${t}::${s}${a}`;
334
+ }
335
+ function pe(t) {
336
+ return t.replace(/<[^>]+>/g, " ").replace(/&nbsp;/gi, " ").replace(/&amp;/gi, "&").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&#39;/gi, "'").replace(/&apos;/gi, "'").replace(/&#(\d+);/g, (e, s) => {
337
+ const a = parseInt(s, 10);
338
+ if (a === 160 || a < 32) return " ";
339
+ try {
340
+ return String.fromCharCode(a);
341
+ } catch {
342
+ return " ";
343
+ }
344
+ }).replace(/&#x([0-9a-f]+);/gi, (e, s) => {
345
+ const a = parseInt(s, 16);
346
+ if (a === 160 || a < 32) return " ";
347
+ try {
348
+ return String.fromCharCode(a);
349
+ } catch {
350
+ return " ";
351
+ }
352
+ }).replace(/\s+/g, " ").trim();
353
+ }
354
+ function se(t, e) {
355
+ const s = t.props ?? {}, a = s.text ?? s.label ?? s.content ?? s.alt ?? "", i = pe(String(a));
356
+ return i ? i.length > 30 ? i.slice(0, 30) + "…" : i : e;
357
+ }
358
+ function j(t, e, s, a = "merge") {
359
+ if (!s.previewActive) return se(t, e);
360
+ const i = t.props ?? {}, n = i.text ?? i.label ?? i.content ?? i.alt ?? "", c = pe(B(String(n), s, a));
361
+ return c ? c.length > 30 ? c.slice(0, 30) + "…" : c : se(t, e);
362
+ }
363
+ function B(t, e, s) {
364
+ if (!t) return "";
365
+ const a = s === "link" ? e.linkTagContext : e.mergeTagContext;
366
+ try {
367
+ return Ae(t, a, e.previewActive);
368
+ } catch {
369
+ return String(t);
370
+ }
371
+ }
372
+ function x(t) {
373
+ if (!t) return [];
374
+ try {
375
+ const e = Ee(t), s = [];
376
+ return e.forEach((a, i) => {
377
+ (!a || !a.trim()) && s.push(i);
378
+ }), s;
379
+ } catch {
380
+ return [];
381
+ }
382
+ }
383
+ function fe(t) {
384
+ if (!t) return !1;
385
+ try {
386
+ return Le(t).length === 0 ? !1 : String(t).replace(/\{\{[^}]*\}\}/g, " ").trim().length === 0;
387
+ } catch {
388
+ return !1;
389
+ }
390
+ }
391
+ function ae(t) {
392
+ if (!t) return null;
393
+ const e = t.trim().toLowerCase();
394
+ if (e === "transparent") return null;
395
+ if (e.startsWith("#")) {
396
+ let a = e.slice(1);
397
+ if (a.length === 3 && (a = a.split("").map((o) => o + o).join("")), a.length !== 6) return null;
398
+ const i = parseInt(a.slice(0, 2), 16), n = parseInt(a.slice(2, 4), 16), c = parseInt(a.slice(4, 6), 16);
399
+ return [
400
+ i,
401
+ n,
402
+ c
403
+ ].some(Number.isNaN) ? null : [
404
+ i,
405
+ n,
406
+ c
407
+ ];
408
+ }
409
+ const s = e.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
410
+ return s ? [
411
+ +s[1],
412
+ +s[2],
413
+ +s[3]
414
+ ] : null;
415
+ }
416
+ function ie([t, e, s]) {
417
+ const a = [
418
+ t,
419
+ e,
420
+ s
421
+ ].map((i) => {
422
+ const n = i / 255;
423
+ return n <= 0.03928 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4);
424
+ });
425
+ return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2];
426
+ }
427
+ function et(t, e) {
428
+ const s = ae(t), a = ae(e);
429
+ if (!s || !a) return null;
430
+ const i = ie(s), n = ie(a), [c, o] = i > n ? [i, n] : [n, i];
431
+ return (c + 0.05) / (o + 0.05);
432
+ }
433
+ function tt(t, e, s) {
434
+ if (t.props?.backgroundColor && t.props.backgroundColor !== "transparent") return t.props.backgroundColor;
435
+ for (let a = e.length - 1; a >= 0; a--) {
436
+ const i = e[a].backgroundColor;
437
+ if (i && i !== "transparent") return i;
438
+ }
439
+ return s;
440
+ }
441
+ function st(t, e, s) {
442
+ const a = (t.props ?? {}).backgroundGradient;
443
+ if (a?.useGradient && a.gradient?.colors?.length >= 2) {
444
+ const i = [...a.gradient.colors].sort((n, c) => (n.position ?? 0) - (c.position ?? 0));
445
+ return [{
446
+ color: i[0].color,
447
+ isGradientEndpoint: !0
448
+ }, {
449
+ color: i[i.length - 1].color,
450
+ isGradientEndpoint: !0
451
+ }];
452
+ }
453
+ return [{
454
+ color: tt(t, e, s),
455
+ isGradientEndpoint: !1
456
+ }];
457
+ }
458
+ function P(t, e, s, a, i, n) {
459
+ const c = t.props?.color ?? t.props?.textColor;
460
+ if (!c) return;
461
+ const o = st(t, a.parentChain, a.bodyBg), d = o.length > 1, p = o.map((C) => ({
462
+ bg: C.color,
463
+ ratio: et(c, C.color)
464
+ })).filter((C) => C.ratio !== null);
465
+ if (p.length === 0) return;
466
+ const u = p.reduce((C, U) => C.ratio < U.ratio ? C : U), L = parseInt((t.props?.fontSize ?? "16").toString(), 10), S = (t.props?.fontWeight ?? "").toString().match(/bold|[6-9]00/), v = L >= 18 || L >= 14 && !!S, I = v ? 3 : 4.5, y = v ? "CONTRAST_FAIL_LARGE" : "CONTRAST_FAIL_NORMAL", E = `"${n}"`, w = d ? " (worst point of gradient)" : "";
467
+ u.ratio < I ? h(a, f(y, e, `${i} ${E} text contrast is ${u.ratio.toFixed(2)}:1${w} — needs at least ${I}:1.`, "failed", s)) : h(a, f(y, e, `${i} ${E} contrast (${u.ratio.toFixed(2)}:1${w}) meets WCAG AA.`, "passed", s));
468
+ }
469
+ function at(t, e, s, a, i, n) {
470
+ const c = (t.props?.iconAlt ?? "").toString().trim(), o = B((t.props?.text ?? t.props?.label ?? t.props?.content ?? "").toString(), a, "link").trim(), d = o.length > 0;
471
+ if (!c) {
472
+ d ? h(a, f("BUTTON_ICON_NO_ALT", e, `${i} "${n}" icon is decorative (aria-hidden); text "${o.length > 30 ? o.slice(0, 30) + "…" : o}" provides the label.`, "passed", s)) : h(a, f("BUTTON_ICON_NO_ALT", e, `${i} "${n}" has only an icon (no text) but no alt — assistive tech cannot describe what this does.`, "failed", s));
473
+ return;
474
+ }
475
+ const p = c.toLowerCase();
476
+ p === "icon" || p === "image" || p === "img" ? h(a, f("BUTTON_ICON_NO_ALT", e, `${i} "${n}" icon alt is "${c}" — non-descriptive. Either describe what the icon represents (e.g. "TikTok logo") or leave alt empty for a decorative icon.`, "failed", s)) : h(a, f("BUTTON_ICON_NO_ALT", e, `${i} "${n}" icon has descriptive alt ("${c}").`, "passed", s));
477
+ }
478
+ var it = /* @__PURE__ */ new Set([
479
+ "http:",
480
+ "https:",
481
+ "mailto:",
482
+ "tel:",
483
+ "sms:"
484
+ ]), nt = [
485
+ "javascript:",
486
+ "data:",
487
+ "vbscript:",
488
+ "file:"
489
+ ];
490
+ function rt(t) {
491
+ try {
492
+ const e = new URL(t);
493
+ return it.has(e.protocol);
494
+ } catch {
495
+ return !1;
496
+ }
497
+ }
498
+ var ot = {
499
+ link: {
500
+ malformed: "LINK_MALFORMED",
501
+ insecure: "LINK_INSECURE_HTTP",
502
+ dangerous: "LINK_DANGEROUS_SCHEME",
503
+ unguarded: "LINK_MERGETAG_UNGUARDED",
504
+ insecureSeverity: "serious",
505
+ insecureMsg: (t, e) => `${t} "${e}" uses http:// — switch to https:// to avoid client warnings and improve deliverability.`,
506
+ malformedMsg: (t, e, s) => `${t} "${e}" URL "${s}" is not a valid absolute URL. Add the scheme (e.g. https://).`,
507
+ dangerousMsg: (t, e, s) => `${t} "${e}" uses ${s} scheme — email clients will strip this link.`,
508
+ unguardedMsg: (t, e, s) => `${t} "${e}" URL uses merge tag${s.includes(",") ? "s" : ""} { ${s} } without a fallback.`,
509
+ passedMsg: (t, e) => `${t} "${e}" URL is well-formed.`
510
+ },
511
+ src: {
512
+ malformed: "IMG_SRC_MALFORMED",
513
+ insecure: "IMG_SRC_INSECURE",
514
+ dangerous: "IMG_SRC_DATAURI",
515
+ unguarded: "IMG_SRC_MERGETAG_UNGUARDED",
516
+ insecureSeverity: "critical",
517
+ insecureMsg: (t, e) => `${t} "${e}" source uses http:// — most modern email clients refuse to load insecure images by default. Switch to https://.`,
518
+ malformedMsg: (t, e, s) => `${t} "${e}" source "${s}" is not a valid absolute URL. The image will not load.`,
519
+ dangerousMsg: (t, e, s) => `${t} "${e}" source uses ${s} scheme — email clients will block this image.`,
520
+ unguardedMsg: (t, e, s) => `${t} "${e}" source uses merge tag${s.includes(",") ? "s" : ""} { ${s} } without a fallback. The image will fail to load if the tag is empty.`,
521
+ passedMsg: (t, e) => `${t} "${e}" source URL is well-formed.`
522
+ }
523
+ };
524
+ function $(t, e, s, a, i, n, c) {
525
+ if (!t) return;
526
+ const o = t.trim();
527
+ if (!o) return;
528
+ const d = ot[c], p = x(o);
529
+ p.length > 0 && h(a, f(d.unguarded, e, d.unguardedMsg(i, n, p.join(", ")), "failed", s));
530
+ const u = B(o, a, "link").trim(), L = u.toLowerCase(), S = nt.find((v) => L.startsWith(v));
531
+ if (S) {
532
+ h(a, f(d.dangerous, e, d.dangerousMsg(i, n, S.replace(":", "")), "failed", s));
533
+ return;
534
+ }
535
+ if (!rt(u)) {
536
+ h(a, f(d.malformed, e, d.malformedMsg(i, n, u.length > 40 ? u.slice(0, 40) + "…" : u), "failed", s));
537
+ return;
538
+ }
539
+ try {
540
+ const v = new URL(u);
541
+ if (v.protocol === "http:") {
542
+ const I = v.hostname.toLowerCase();
543
+ if (!(I === "localhost" || I === "127.0.0.1" || I === "::1" || I.endsWith(".local"))) {
544
+ h(a, f(d.insecure, e, d.insecureMsg(i, n), "failed", s));
545
+ return;
546
+ }
547
+ }
548
+ } catch {
549
+ }
550
+ h(a, f(d.malformed, e, d.passedMsg(i, n), "passed", s));
551
+ }
552
+ function he(t, e, s, a, i, n) {
553
+ $(t, e, s, a, i, n, "link");
554
+ }
555
+ var ge = [
556
+ "click here",
557
+ "click",
558
+ "here",
559
+ "read more",
560
+ "more",
561
+ "learn more",
562
+ "this",
563
+ "this link",
564
+ "link",
565
+ "go here",
566
+ "go to",
567
+ "see here",
568
+ "check this out",
569
+ "check it out",
570
+ "find out more",
571
+ "details",
572
+ "info",
573
+ "information"
574
+ ];
575
+ function Z(t) {
576
+ return t.toLowerCase().replace(/[!.?…]+$/, "").trim();
577
+ }
578
+ var lt = [
579
+ "image of",
580
+ "picture of",
581
+ "photo of",
582
+ "graphic of",
583
+ "image:",
584
+ "img:"
585
+ ];
586
+ function f(t, e, s, a, i) {
587
+ const n = qe[t];
588
+ return {
589
+ id: `${t}:${e}`,
590
+ ruleId: t,
591
+ severity: n.severity,
592
+ status: a,
593
+ rule: n.name,
594
+ message: s,
595
+ info: n.info,
596
+ componentRef: i
597
+ };
598
+ }
599
+ function h(t, e) {
600
+ t.ignored.has(e.id) && (e.status = "ignored"), t.issues.push(e);
601
+ }
602
+ function dt(t, e) {
603
+ const s = M(t), a = {
604
+ type: "image",
605
+ id: s
606
+ }, i = t.props?.alt, n = B(i, e, "link");
607
+ if (t.props?.decorative === !0) {
608
+ n && n.length > 0 ? h(e, f("IMG_ALT_DECORATIVE", s, "Image is marked decorative but has alt text — set alt to an empty string.", "failed", a)) : h(e, f("IMG_ALT_DECORATIVE", s, "Decorative image correctly uses empty alt — screen readers will skip it.", "passed", a));
609
+ return;
610
+ }
611
+ const c = x(i);
612
+ if (c.length > 0 && h(e, f("MERGETAG_IN_ALT", s, `Alt text uses merge tag${c.length === 1 ? "" : "s"} { ${c.join(", ")} } without a fallback. Add a pipe-default like {{ ${c[0]}|'…' }}.`, "failed", a)), !n || n.trim().length === 0) h(e, f("IMG_ALT_MISSING", s, "Image has no alt text. Add a short description of what the image conveys.", "failed", a));
613
+ else {
614
+ const u = n.toLowerCase().trim(), L = lt.find((S) => u.startsWith(S));
615
+ L ? h(e, f("IMG_ALT_REDUNDANT", s, `Alt text starts with "${L}" — remove it; screen readers already announce "image".`, "failed", a)) : h(e, f("IMG_ALT_MISSING", s, `Image has descriptive alt text ("${n.length > 40 ? n.slice(0, 40) + "…" : n}").`, "passed", a));
616
+ }
617
+ const o = t.props?.src, d = n && n.trim() ? n.trim() : "Image", p = d.length > 30 ? d.slice(0, 30) + "…" : d;
618
+ if (!o || !o.toString().trim() ? h(e, f("IMG_SRC_MISSING", s, `Image "${p}" has no source URL set.`, "failed", a)) : $(o.toString(), s, a, e, "Image", p, "src"), t.props?.enabled === !0) {
619
+ const u = t.props?.link;
620
+ !u || !u.toString().trim() ? h(e, f("LINK_NO_HREF", s, `Image "${p}" is set as a clickable link but has no destination URL.`, "failed", a)) : $(u.toString(), s, a, e, "Image", p, "link");
621
+ }
622
+ }
623
+ function ut(t, e) {
624
+ const s = M(t), a = {
625
+ type: "video",
626
+ id: s
627
+ }, i = t.props?.alt, n = B(i, e, "link"), c = n && n.trim() ? n.trim() : "Video", o = c.length > 30 ? c.slice(0, 30) + "…" : c;
628
+ !n || n.trim().length === 0 ? h(e, f("IMG_ALT_MISSING", s, "Video has no alt text. Add a short description of the video's purpose.", "failed", a)) : h(e, f("IMG_ALT_MISSING", s, `Video has descriptive alt text ("${o}").`, "passed", a));
629
+ const d = t.props?.src;
630
+ d && d.toString().trim() && $(d.toString(), s, a, e, "Video", o, "src");
631
+ const p = t.props?.coverImage;
632
+ p && p.toString().trim() && $(p.toString(), s, a, e, "Video poster", o, "src");
633
+ const u = t.props?.fallbackLink;
634
+ !u || !u.toString().trim() ? h(e, f("LINK_NO_HREF", s, `Video "${o}" has no fallback link — recipients in clients that can't play <video> have no way to reach the content.`, "failed", a)) : $(u.toString(), s, a, e, "Video", o, "link");
635
+ }
636
+ function ct(t, e) {
637
+ const s = M(t), a = {
638
+ type: "button",
639
+ id: s
640
+ }, i = (t.props?.label ?? t.props?.text ?? t.props?.content ?? "").toString(), n = B(i, e, "link").trim(), c = j(t, "Button", e, "link"), o = t.props?.link;
641
+ if (n && o?.toString().trim() && e.links.push({
642
+ text: n,
643
+ url: o.toString().trim(),
644
+ componentId: s,
645
+ componentRef: a
646
+ }), !i.trim()) h(e, f("BUTTON_EMPTY", s, "Button has no visible label. Add text describing the action.", "failed", a));
647
+ else if (fe(i) && x(i).length > 0) {
648
+ const d = x(i);
649
+ h(e, f("MERGETAG_LINK_LABEL", s, `Button label is only the merge tag${d.length === 1 ? "" : "s"} { ${d.join(", ")} } with no fallback. If the tag is empty for a recipient, the button has no label.`, "failed", a));
650
+ } else e.previewActive && !n ? h(e, f("MERGETAG_RESOLVES_EMPTY", s, "Button label resolves to empty in preview. Set a preview value for the tag, or add a pipe-default.", "failed", a)) : h(e, f("BUTTON_EMPTY", s, `Button "${c}" has a clear label.`, "passed", a));
651
+ o ? (h(e, f("LINK_NO_HREF", s, `Button "${c}" has a destination URL.`, "passed", a)), he(o, s, a, e, "Button", c)) : h(e, f("LINK_NO_HREF", s, `Button "${c}" has no destination URL set.`, "failed", a)), t.props?.icon && at(t, s, a, e, "Button", c), P(t, s, a, e, "Button", c);
652
+ }
653
+ function pt(t, e) {
654
+ const s = M(t), a = {
655
+ type: "anchor",
656
+ id: s
657
+ }, i = (t.props?.content ?? t.props?.text ?? t.props?.label ?? "").toString(), n = B(i, e, "link").trim(), c = t.props?.link, o = j(t, "Link", e, "link");
658
+ if (n && c?.toString().trim() && e.links.push({
659
+ text: n,
660
+ url: c.toString().trim(),
661
+ componentId: s,
662
+ componentRef: a
663
+ }), !i.trim()) {
664
+ h(e, f("LINK_EMPTY", s, "Link has no readable text.", "failed", a));
665
+ return;
666
+ }
667
+ if (fe(i) && x(i).length > 0) {
668
+ const u = x(i);
669
+ h(e, f("MERGETAG_LINK_LABEL", s, `Link text is only the merge tag${u.length === 1 ? "" : "s"} { ${u.join(", ")} } with no fallback. If empty, the link will have no readable label.`, "failed", a));
670
+ } else e.previewActive && !n && h(e, f("MERGETAG_RESOLVES_EMPTY", s, "Link text resolves to empty in preview. Set a preview value or add a pipe-default to the tag.", "failed", a));
671
+ const d = n || i, p = Z(d);
672
+ ge.includes(p) ? h(e, f("LINK_VAGUE", s, `Link text "${d}" is not descriptive on its own. Use text that describes the destination.`, "failed", a)) : d && h(e, f("LINK_VAGUE", s, `Link "${o}" has descriptive text.`, "passed", a)), c ? (h(e, f("LINK_NO_HREF", s, `Link "${o}" has an href set.`, "passed", a)), he(c, s, a, e, "Link", o)) : h(e, f("LINK_NO_HREF", s, `Link "${o}" has no href set.`, "failed", a)), P(t, s, a, e, "Link", o);
673
+ }
674
+ function ft(t, e) {
675
+ const s = M(t), a = {
676
+ type: "heading",
677
+ id: s
678
+ }, i = parseInt((t.props?.level ?? t.props?.tag ?? "h2").toString().replace(/\D/g, ""), 10), n = j(t, `H${Number.isFinite(i) ? i : "?"}`, e, "merge");
679
+ if (!Number.isFinite(i)) return;
680
+ const c = e.headingLevels[e.headingLevels.length - 1];
681
+ c === void 0 ? i !== 1 ? h(e, f("HEADING_HIERARCHY", s, `First heading is H${i} ("${n}"). Start the outline with H1 so screen readers know where the page begins.`, "failed", a)) : h(e, f("HEADING_HIERARCHY", s, `Document starts with H1 ("${n}").`, "passed", a)) : i > c + 1 ? h(e, f("HEADING_HIERARCHY", s, `Heading "${n}" jumps from H${c} to H${i}. Use H${c + 1} instead.`, "failed", a)) : h(e, f("HEADING_HIERARCHY", s, `Heading "${n}" (H${i}) follows correctly from H${c}.`, "passed", a)), e.headingLevels.push(i), P(t, s, a, e, `H${i}`, n);
682
+ }
683
+ function ht(t, e) {
684
+ const s = M(t), a = {
685
+ type: "paragraph",
686
+ id: s
687
+ }, i = Q(t) === "list" ? "List" : "Text", n = j(t, i, e, "merge"), c = t.props?.fontSize;
688
+ if (c) {
689
+ const o = parseInt(c.toString(), 10);
690
+ Number.isFinite(o) && (o < 12 ? h(e, f("FONT_TOO_SMALL", s, `${i} "${n}" font size is ${o}px — below the 12px minimum for comfortable reading.`, "failed", a)) : h(e, f("FONT_TOO_SMALL", s, `${i} "${n}" font size (${o}px) is readable.`, "passed", a)));
691
+ }
692
+ P(t, s, a, e, i, n);
693
+ }
694
+ function gt(t, e) {
695
+ const s = M(t), a = (t.props?.platforms ?? []).filter((n) => n.enabled !== !1);
696
+ if (a.length === 0) return;
697
+ const i = a.filter((n) => !(n.name?.toString().trim() || n.alt?.toString().trim() || n.label?.toString().trim() || n.platform?.toString().trim()));
698
+ i.length > 0 ? h(e, f("SOCIAL_ICON_NO_LABEL", s, `${i.length} of ${a.length} social icons missing an accessible name. Set the platform name so screen readers can announce it.`, "failed", {
699
+ type: "socials",
700
+ id: s
701
+ })) : h(e, f("SOCIAL_ICON_NO_LABEL", s, `All ${a.length} social icons have accessible names.`, "passed", {
702
+ type: "socials",
703
+ id: s
704
+ })), a.forEach((n, c) => {
705
+ const o = (n.name || n.alt || n.label || n.platform || `#${c + 1}`).toString(), d = ce(s, n, "p", c), p = {
706
+ type: "socials",
707
+ id: d
708
+ }, u = n.link;
709
+ !u || !u.toString().trim() ? h(e, f("LINK_NO_HREF", d, `Social icon "${o}" is enabled but has no link.`, "failed", p)) : $(u.toString(), d, p, e, "Social", o, "link");
710
+ const L = n.icon;
711
+ L && L.toString().trim() && $(L.toString(), d, p, e, "Social icon", o, "src");
712
+ });
713
+ }
714
+ function mt(t, e) {
715
+ const s = M(t), a = {
716
+ type: "menu",
717
+ id: s
718
+ }, i = (t.props?.items ?? []).filter((n) => n.enabled !== !1);
719
+ P(t, s, a, e, "Menu", `Menu (${i.length} item${i.length === 1 ? "" : "s"})`), i.length !== 0 && i.forEach((n, c) => {
720
+ const o = (n.label ?? "").toString(), d = o.trim() || `item #${c + 1}`, p = d.length > 30 ? d.slice(0, 30) + "…" : d, u = ce(s, n, "i", c), L = {
721
+ type: "menu-item",
722
+ id: u
723
+ };
724
+ if (o.trim() || h(e, f("LINK_EMPTY", u, `Menu item #${c + 1} has no readable text.`, "failed", L)), o.trim()) {
725
+ const I = Z(o);
726
+ ge.includes(I) && h(e, f("LINK_VAGUE", u, `Menu item "${p}" is not descriptive on its own.`, "failed", L));
727
+ }
728
+ const S = n.link, v = o.trim();
729
+ v && S?.toString().trim() && e.links.push({
730
+ text: v,
731
+ url: S.toString().trim(),
732
+ componentId: u,
733
+ componentRef: L
734
+ }), !S || !S.toString().trim() ? h(e, f("LINK_NO_HREF", u, `Menu item "${p}" has no link set.`, "failed", L)) : $(S.toString(), u, L, e, "Menu item", p, "link");
735
+ });
736
+ }
737
+ function me(t, e, s) {
738
+ if (!(s >= Ze))
739
+ for (const a of t) {
740
+ const i = a.type;
741
+ if (i === "row") {
742
+ e.parentChain.push({ backgroundColor: a.backgroundColor }), (a.columns ?? []).forEach((n) => {
743
+ e.parentChain.push({ backgroundColor: n.backgroundColor }), me(n.children ?? n.components ?? [], e, s + 1), e.parentChain.pop();
744
+ }), e.parentChain.pop();
745
+ continue;
746
+ }
747
+ if (i !== "row-spacer")
748
+ switch (Q(a)) {
749
+ case "image":
750
+ dt(a, e);
751
+ break;
752
+ case "video":
753
+ ut(a, e);
754
+ break;
755
+ case "button":
756
+ ct(a, e), e.textComponentCount++;
757
+ break;
758
+ case "anchor":
759
+ pt(a, e), e.textComponentCount++;
760
+ break;
761
+ case "heading":
762
+ ft(a, e), e.textComponentCount++;
763
+ break;
764
+ case "paragraph":
765
+ case "list":
766
+ ht(a, e), e.textComponentCount++;
767
+ break;
768
+ case "menu":
769
+ mt(a, e), e.textComponentCount++;
770
+ break;
771
+ case "socials":
772
+ gt(a, e);
773
+ }
774
+ }
775
+ }
776
+ function vt(t) {
777
+ const e = t.slice(0, 3).map((a) => a.length > 40 ? a.slice(0, 40) + "…" : a), s = t.length - e.length;
778
+ return s > 0 ? `${e.join(", ")}, +${s} more` : e.join(", ");
779
+ }
780
+ function _t(t) {
781
+ const e = /* @__PURE__ */ new Map();
782
+ for (const s of t.links) {
783
+ const a = Z(s.text), i = e.get(a);
784
+ i ? i.push(s) : e.set(a, [s]);
785
+ }
786
+ e.forEach((s) => {
787
+ const a = [...new Set(s.map((n) => n.url))], i = a.length <= 1;
788
+ for (const n of s) i ? h(t, f("LINK_DUPLICATE_TEXT_DIFFERENT_URL", n.componentId, s.length > 1 ? `Link text "${n.text}" appears ${s.length} times in this email and always points to the same destination.` : `Link text "${n.text}" is unique in this email.`, "passed", n.componentRef)) : h(t, f("LINK_DUPLICATE_TEXT_DIFFERENT_URL", n.componentId, `Link text "${n.text}" is used for ${a.length} different URLs in this email (${vt(a)}). Screen reader users scanning a link list won't be able to tell these destinations apart by text alone.`, "failed", n.componentRef));
789
+ });
790
+ }
791
+ function bt(t, e) {
792
+ const s = t?.lang ?? t?.language ?? t?.locale;
793
+ !s || !s.toString().trim() ? h(e, f("LANG_MISSING", "document", 'Email language is not set. Add a lang attribute (e.g. "en") so screen readers use the correct pronunciation.', "failed", { type: "document" })) : h(e, f("LANG_MISSING", "document", `Email language is set ("${s}").`, "passed", { type: "document" }));
794
+ const a = (t?.preheader ?? t?.preheaderText ?? t?.previewText ?? "").toString().trim();
795
+ if (!a) h(e, f("PREHEADER_MISSING", "document", "Preheader text is not set. Add a short summary that previews the email's content.", "failed", { type: "document" }));
796
+ else {
797
+ const i = a.length;
798
+ h(e, f("PREHEADER_MISSING", "document", `Preheader is set ("${i > 50 ? a.slice(0, 50) + "…" : a}", ${i} char${i === 1 ? "" : "s"}).`, "passed", { type: "document" }));
799
+ }
800
+ e.textComponentCount >= 3 && e.headingLevels.length === 0 ? h(e, f("HEADING_MISSING", "document", "No headings used. Add at least one to help screen reader users navigate.", "failed", { type: "document" })) : e.headingLevels.length > 0 && h(e, f("HEADING_MISSING", "document", `${e.headingLevels.length} heading${e.headingLevels.length === 1 ? "" : "s"} used for structure.`, "passed", { type: "document" }));
801
+ }
802
+ function yt(t, e = {}) {
803
+ const s = D(Qe()), a = (d, p) => d === void 0 ? p : d && typeof d == "object" && "value" in d ? d.value : d, i = O(() => {
804
+ const d = {
805
+ issues: [],
806
+ ignored: s.value,
807
+ parentChain: [],
808
+ bodyBg: a(e.bodyBg, "#ffffff") ?? "#ffffff",
809
+ headingLevels: [],
810
+ textComponentCount: 0,
811
+ links: [],
812
+ mergeTagContext: a(e.mergeTagContext, {}),
813
+ linkTagContext: a(e.linkTagContext, {}),
814
+ previewActive: a(e.previewActive, !1)
815
+ }, p = t.value ?? [];
816
+ for (const w of p)
817
+ w.columns && (d.parentChain.push({ backgroundColor: w.backgroundColor }), (w.columns ?? []).forEach((C) => {
818
+ d.parentChain.push({ backgroundColor: C.backgroundColor }), me(C.children ?? C.components ?? [], d, 0), d.parentChain.pop();
819
+ }), d.parentChain.pop());
820
+ _t(d), bt(a(e.emailMeta, void 0), d);
821
+ const u = d.issues.filter((w) => w.status === "failed"), L = d.issues.filter((w) => w.status === "passed"), S = d.issues.filter((w) => w.status === "ignored"), v = u.filter((w) => w.severity === "critical").length, I = u.filter((w) => w.severity === "serious").length;
822
+ let y;
823
+ v > 0 ? y = "bad" : I > 0 ? y = "warn" : y = "good";
824
+ let E;
825
+ return y === "good" ? E = u.length === 0 ? "No accessibility issues" : "Looks good" : y === "warn" ? E = `${I} serious issue${I === 1 ? "" : "s"}` : E = `${v} critical issue${v === 1 ? "" : "s"}`, {
826
+ failed: u,
827
+ passed: L,
828
+ ignored: S,
829
+ failedCount: u.length,
830
+ passedCount: L.length,
831
+ ignoredCount: S.length,
832
+ criticalCount: v,
833
+ seriousCount: I,
834
+ status: y,
835
+ summaryMessage: E
836
+ };
837
+ });
838
+ function n(d) {
839
+ const p = new Set(s.value);
840
+ p.add(d), s.value = p, q(p);
841
+ }
842
+ function c(d) {
843
+ const p = new Set(s.value);
844
+ p.delete(d), s.value = p, q(p);
845
+ }
846
+ function o() {
847
+ s.value = /* @__PURE__ */ new Set(), q(s.value);
848
+ }
849
+ return {
850
+ summary: i,
851
+ ignoreIssue: n,
852
+ restoreIssue: c,
853
+ clearIgnored: o
854
+ };
855
+ }
856
+ var kt = ["aria-expanded"], It = { class: "panel-header" }, Lt = { class: "panel-header-right" }, Et = ["aria-label", "aria-pressed"], Ct = ["aria-expanded"], At = { class: "metric-summary" }, wt = { class: "metric-left" }, St = { class: "metric-icon" }, Tt = {
857
+ width: "12",
858
+ height: "12",
859
+ viewBox: "0 0 12 12",
860
+ fill: "none"
861
+ }, Rt = ["fill"], Nt = ["fill"], Gt = { class: "metric-right" }, $t = { class: "mini-bar" }, Mt = {
862
+ key: 0,
863
+ class: "metric-detail"
864
+ }, Ot = { class: "detail-tip" }, Bt = { class: "detail-stats" }, Ut = { class: "stat-item" }, Ht = { class: "stat-value" }, Dt = { class: "stat-item" }, xt = { class: "stat-value" }, Kt = { class: "stat-item" }, Pt = { class: "stat-value" }, Ft = {
865
+ key: 0,
866
+ class: "stat-item"
867
+ }, Wt = { class: "stat-value" }, Vt = {
868
+ key: 1,
869
+ class: "stat-item"
870
+ }, Yt = { class: "stat-value" }, jt = { class: "stat-item" }, zt = { class: "stat-value" }, Xt = ["aria-expanded"], Jt = { class: "metric-summary" }, qt = { class: "metric-left" }, Qt = { class: "metric-icon" }, Zt = {
871
+ width: "12",
872
+ height: "12",
873
+ viewBox: "0 0 12 12",
874
+ fill: "none"
875
+ }, es = ["stroke"], ts = { class: "metric-right" }, ss = {
876
+ key: 0,
877
+ class: "pulse-dot pulse-dot--bad"
878
+ }, as = {
879
+ key: 0,
880
+ class: "metric-detail"
881
+ }, is = { class: "detail-tip" }, ns = { class: "format-note" }, rs = { class: "format-row format-row--html" }, os = { class: "format-info" }, ls = { class: "format-range" }, ds = ["aria-expanded"], us = { class: "metric-summary" }, cs = { class: "metric-left" }, ps = { class: "metric-icon" }, fs = {
882
+ width: "12",
883
+ height: "12",
884
+ viewBox: "0 0 12 12",
885
+ fill: "none"
886
+ }, hs = ["fill"], gs = ["stroke"], ms = { class: "metric-right" }, vs = {
887
+ key: 0,
888
+ class: "pulse-dot pulse-dot--bad"
889
+ }, _s = {
890
+ class: "a11y-tabs",
891
+ role: "tablist",
892
+ "aria-label": "Accessibility issue status"
893
+ }, bs = ["aria-selected"], ys = { class: "a11y-tab-count a11y-tab-count--failed" }, ks = ["aria-selected"], Is = { class: "a11y-tab-count a11y-tab-count--passed" }, Ls = ["aria-selected"], Es = { class: "a11y-tab-count a11y-tab-count--ignored" }, Cs = {
894
+ key: 0,
895
+ class: "a11y-list",
896
+ role: "tabpanel"
897
+ }, As = {
898
+ key: 0,
899
+ class: "a11y-empty"
900
+ }, ws = { class: "a11y-item-header" }, Ss = { class: "a11y-rule" }, Ts = ["onClick", "aria-label"], Rs = { class: "a11y-message" }, Ns = { class: "a11y-info" }, Gs = {
901
+ key: 1,
902
+ class: "a11y-list",
903
+ role: "tabpanel"
904
+ }, $s = {
905
+ key: 0,
906
+ class: "a11y-empty"
907
+ }, Ms = { class: "a11y-item-header" }, Os = { class: "a11y-rule" }, Bs = { class: "a11y-message" }, Us = {
908
+ key: 2,
909
+ class: "a11y-list",
910
+ role: "tabpanel"
911
+ }, Hs = {
912
+ key: 0,
913
+ class: "a11y-empty"
914
+ }, Ds = { class: "a11y-item-header" }, xs = { class: "a11y-rule" }, Ks = ["onClick", "aria-label"], Ps = { class: "a11y-message" }, Fs = /* @__PURE__ */ ke({
915
+ __name: "HealthIndicator",
916
+ setup(t) {
917
+ const { rows: e, canvasStyles: s, mergeTagPreviewContext: a, mergeTagPreviewActive: i, linkTagPreviewContext: n, linkTagPreviewActive: c } = we(), { metrics: o } = Je(e), d = O(() => !!(i?.value || c?.value)), p = O(() => ({
918
+ lang: s?.value?.language,
919
+ preheader: s?.value?.preheaderText
920
+ })), { summary: u, ignoreIssue: L, restoreIssue: S } = yt(e, {
921
+ bodyBg: O(() => s?.value?.bodyBackgroundColor ?? "#ffffff"),
922
+ emailMeta: p,
923
+ mergeTagContext: a,
924
+ linkTagContext: n,
925
+ previewActive: d
926
+ }), v = D(!1), I = D(!1), y = D(null), E = D("failed"), w = D(null), C = O(() => o.value.ratioStatus === "bad" || o.value.kbStatus === "bad" || u.value.status === "bad" ? "bad" : o.value.ratioStatus === "warn" || o.value.kbStatus === "warn" || u.value.status === "warn" ? "warn" : "good"), U = O(() => C.value === "bad" ? "Needs attention" : C.value === "warn" ? "Review suggested" : "Looks healthy");
927
+ function K(R) {
928
+ return R === "good" ? "#22c55e" : R === "warn" ? "#f59e0b" : "#ef4444";
929
+ }
930
+ function ve() {
931
+ v.value = !v.value, v.value || (y.value = null, I.value = !1);
932
+ }
933
+ function ee() {
934
+ v.value = !1, y.value = null, I.value = !1;
935
+ }
936
+ function G(R) {
937
+ y.value = y.value === R ? null : R;
938
+ }
939
+ function z(R) {
940
+ return R.id;
941
+ }
942
+ function te(R) {
943
+ R.key === "Escape" && ee();
944
+ }
945
+ return ye(() => window.addEventListener("keydown", te)), be(() => window.removeEventListener("keydown", te)), (R, l) => g(o).isEmpty ? T("", !0) : (_(), b("div", {
946
+ key: 0,
947
+ class: "relative",
948
+ ref_key: "rootRef",
949
+ ref: w
950
+ }, [
951
+ r("button", {
952
+ class: A(["health-orb", [`health-orb--${C.value}`, { "health-orb--active": v.value }]]),
953
+ onClick: ve,
954
+ "aria-label": "Email health indicator",
955
+ "aria-expanded": v.value
956
+ }, [...l[14] || (l[14] = [
957
+ r("span", { class: "orb-ring orb-ring--outer" }, null, -1),
958
+ r("span", { class: "orb-ring orb-ring--inner" }, null, -1),
959
+ r("span", { class: "orb-core" }, null, -1)
960
+ ])], 10, kt),
961
+ F(V, { name: "popover" }, {
962
+ default: W(() => [v.value ? (_(), b("div", {
963
+ key: 0,
964
+ class: A(["health-panel", { "health-panel--expanded": I.value }]),
965
+ role: "dialog",
966
+ "aria-label": "Email health details"
967
+ }, [
968
+ r("div", It, [l[16] || (l[16] = r("span", { class: "panel-title" }, "Email Health", -1)), r("div", Lt, [r("span", { class: A(["panel-status-badge", `status-badge--${C.value}`]) }, k(U.value), 3), r("button", {
969
+ type: "button",
970
+ class: A(["panel-expand-btn", { "panel-expand-btn--active": I.value }]),
971
+ onClick: l[0] || (l[0] = N((m) => I.value = !I.value, ["stop"])),
972
+ "aria-label": I.value ? "Collapse panel" : "Expand panel for more detail",
973
+ "aria-pressed": I.value,
974
+ title: "Toggle wide view"
975
+ }, [...l[15] || (l[15] = [r("svg", {
976
+ width: "12",
977
+ height: "12",
978
+ viewBox: "0 0 12 12",
979
+ fill: "none",
980
+ xmlns: "http://www.w3.org/2000/svg"
981
+ }, [r("path", {
982
+ d: "M1 6h10M8.5 3.5L11 6l-2.5 2.5M3.5 3.5L1 6l2.5 2.5",
983
+ stroke: "currentColor",
984
+ "stroke-width": "1.4",
985
+ "stroke-linecap": "round",
986
+ "stroke-linejoin": "round"
987
+ })], -1)])], 10, Et)])]),
988
+ r("div", {
989
+ class: A(["metric-card", [`metric-card--${g(o).ratioStatus}`, { "metric-card--expanded": y.value === "ratio" }]]),
990
+ onClick: l[1] || (l[1] = (m) => G("ratio")),
991
+ tabindex: "0",
992
+ onKeydown: [l[2] || (l[2] = H((m) => G("ratio"), ["enter"])), l[3] || (l[3] = H(N((m) => G("ratio"), ["prevent"]), ["space"]))],
993
+ role: "button",
994
+ "aria-expanded": y.value === "ratio"
995
+ }, [r("div", At, [r("div", wt, [r("span", St, [(_(), b("svg", Tt, [r("rect", {
996
+ x: "0.5",
997
+ y: "0.5",
998
+ width: "4",
999
+ height: "11",
1000
+ rx: "1",
1001
+ fill: K(g(o).ratioStatus),
1002
+ opacity: "0.9"
1003
+ }, null, 8, Rt), r("rect", {
1004
+ x: "6.5",
1005
+ y: "3.5",
1006
+ width: "5",
1007
+ height: "8",
1008
+ rx: "1",
1009
+ fill: K(g(o).ratioStatus),
1010
+ opacity: "0.4"
1011
+ }, null, 8, Nt)]))]), l[17] || (l[17] = r("span", { class: "metric-label" }, "Text : Image", -1))]), r("div", Gt, [
1012
+ r("div", $t, [r("div", {
1013
+ class: A(["mini-bar-fill", `mini-bar-fill--${g(o).ratioStatus}`]),
1014
+ style: Ie({ width: `${g(o).textRatio}%` })
1015
+ }, null, 6)]),
1016
+ r("span", { class: A(["metric-value", `metric-value--${g(o).ratioStatus}`]) }, k(g(o).textRatio) + "% ", 3),
1017
+ (_(), b("svg", {
1018
+ class: A(["chevron", { "chevron--open": y.value === "ratio" }]),
1019
+ width: "12",
1020
+ height: "12",
1021
+ viewBox: "0 0 12 12",
1022
+ fill: "none"
1023
+ }, [...l[18] || (l[18] = [r("path", {
1024
+ d: "M3 4.5L6 7.5L9 4.5",
1025
+ stroke: "currentColor",
1026
+ "stroke-width": "1.5",
1027
+ "stroke-linecap": "round",
1028
+ "stroke-linejoin": "round"
1029
+ }, null, -1)])], 2))
1030
+ ])]), F(V, { name: "expand" }, {
1031
+ default: W(() => [y.value === "ratio" ? (_(), b("div", Mt, [
1032
+ r("p", Ot, k(g(o).ratioTip), 1),
1033
+ r("div", Bt, [
1034
+ r("div", Ut, [l[19] || (l[19] = r("span", { class: "stat-label" }, "Text chars", -1)), r("span", Ht, k(g(o).textChars.toLocaleString()), 1)]),
1035
+ r("div", Dt, [l[20] || (l[20] = r("span", { class: "stat-label" }, "Text weight", -1)), r("span", xt, k(g(o).textRatio) + "%", 1)]),
1036
+ r("div", Kt, [l[21] || (l[21] = r("span", { class: "stat-label" }, "Images", -1)), r("span", Pt, k(g(o).imageCompCount), 1)]),
1037
+ g(o).socialIconCount > 0 ? (_(), b("div", Ft, [l[22] || (l[22] = r("span", { class: "stat-label" }, "Social icons", -1)), r("span", Wt, k(g(o).socialIconCount), 1)])) : T("", !0),
1038
+ g(o).buttonIconCount > 0 ? (_(), b("div", Vt, [l[23] || (l[23] = r("span", { class: "stat-label" }, "Button icons", -1)), r("span", Yt, k(g(o).buttonIconCount), 1)])) : T("", !0),
1039
+ r("div", jt, [l[24] || (l[24] = r("span", { class: "stat-label" }, "Image weight", -1)), r("span", zt, k(g(o).imageRatio) + "%", 1)])
1040
+ ]),
1041
+ l[25] || (l[25] = r("div", { class: "detail-thresholds" }, [
1042
+ r("span", { class: "threshold threshold--good" }, "≥60% text = good"),
1043
+ r("span", { class: "threshold threshold--warn" }, "40–59% = caution"),
1044
+ r("span", { class: "threshold threshold--bad" }, "<40% = spam risk")
1045
+ ], -1))
1046
+ ])) : T("", !0)]),
1047
+ _: 1
1048
+ })], 42, Ct),
1049
+ r("div", {
1050
+ class: A(["metric-card", [`metric-card--${g(o).kbStatus}`, { "metric-card--expanded": y.value === "kb" }]]),
1051
+ onClick: l[4] || (l[4] = (m) => G("kb")),
1052
+ tabindex: "0",
1053
+ onKeydown: [l[5] || (l[5] = H((m) => G("kb"), ["enter"])), l[6] || (l[6] = H(N((m) => G("kb"), ["prevent"]), ["space"]))],
1054
+ role: "button",
1055
+ "aria-expanded": y.value === "kb"
1056
+ }, [r("div", Jt, [r("div", qt, [r("span", Qt, [(_(), b("svg", Zt, [r("path", {
1057
+ d: "M2 10V2l3 2.5L8 1l2 3v6H2z",
1058
+ stroke: K(g(o).kbStatus),
1059
+ "stroke-width": "1.2",
1060
+ "stroke-linejoin": "round",
1061
+ fill: "none"
1062
+ }, null, 8, es)]))]), l[26] || (l[26] = r("span", { class: "metric-label" }, "HTML Size", -1))]), r("div", ts, [
1063
+ g(o).kbStatus === "bad" ? (_(), b("span", ss)) : (_(), b("span", {
1064
+ key: 1,
1065
+ class: A(["status-dot", `status-dot--${g(o).kbStatus}`])
1066
+ }, null, 2)),
1067
+ r("span", { class: A(["metric-value", `metric-value--${g(o).kbStatus}`]) }, " ~" + k(g(o).estimatedLow) + "–" + k(g(o).estimatedHigh) + " KB ", 3),
1068
+ (_(), b("svg", {
1069
+ class: A(["chevron", { "chevron--open": y.value === "kb" }]),
1070
+ width: "12",
1071
+ height: "12",
1072
+ viewBox: "0 0 12 12",
1073
+ fill: "none"
1074
+ }, [...l[27] || (l[27] = [r("path", {
1075
+ d: "M3 4.5L6 7.5L9 4.5",
1076
+ stroke: "currentColor",
1077
+ "stroke-width": "1.5",
1078
+ "stroke-linecap": "round",
1079
+ "stroke-linejoin": "round"
1080
+ }, null, -1)])], 2))
1081
+ ])]), F(V, { name: "expand" }, {
1082
+ default: W(() => [y.value === "kb" ? (_(), b("div", as, [
1083
+ r("p", is, k(g(o).kbTip), 1),
1084
+ r("div", ns, [
1085
+ r("div", rs, [l[29] || (l[29] = r("span", { class: "format-badge" }, "HTML", -1)), r("div", os, [r("span", ls, k(g(o).estimatedLow) + "–" + k(g(o).estimatedHigh) + " KB estimated", 1), l[28] || (l[28] = r("span", { class: "format-caveat" }, " Prettier formatting adds ~20–30% on export (no minify option). Actual exported file may be larger. ", -1))])]),
1086
+ l[30] || (l[30] = r("div", { class: "format-row format-row--other" }, [r("span", { class: "format-badge format-badge--muted" }, "JSX"), r("div", { class: "format-info" }, [r("span", { class: "format-range format-range--muted" }, "Size unpredictable"), r("span", { class: "format-caveat" }, "React Email renders its own HTML — final size depends on its transformer output.")])], -1)),
1087
+ l[31] || (l[31] = r("div", { class: "format-row format-row--other" }, [r("span", { class: "format-badge format-badge--muted" }, "MJML"), r("div", { class: "format-info" }, [r("span", { class: "format-range format-range--muted" }, "Size unpredictable"), r("span", { class: "format-caveat" }, "MJML compiles to HTML with its own markup; final size varies.")])], -1))
1088
+ ]),
1089
+ l[32] || (l[32] = r("div", { class: "detail-thresholds" }, [
1090
+ r("span", { class: "threshold threshold--good" }, "<60 KB safe"),
1091
+ r("span", { class: "threshold threshold--warn" }, "60–80 KB caution"),
1092
+ r("span", { class: "threshold threshold--bad" }, "102+ KB Gmail clips")
1093
+ ], -1))
1094
+ ])) : T("", !0)]),
1095
+ _: 1
1096
+ })], 42, Xt),
1097
+ r("div", {
1098
+ class: A(["metric-card", [`metric-card--${g(u).status}`, { "metric-card--expanded": y.value === "a11y" }]]),
1099
+ onClick: l[11] || (l[11] = (m) => G("a11y")),
1100
+ tabindex: "0",
1101
+ onKeydown: [l[12] || (l[12] = H((m) => G("a11y"), ["enter"])), l[13] || (l[13] = H(N((m) => G("a11y"), ["prevent"]), ["space"]))],
1102
+ role: "button",
1103
+ "aria-expanded": y.value === "a11y"
1104
+ }, [r("div", us, [r("div", cs, [r("span", ps, [(_(), b("svg", fs, [r("circle", {
1105
+ cx: "6",
1106
+ cy: "3",
1107
+ r: "1.4",
1108
+ fill: K(g(u).status)
1109
+ }, null, 8, hs), r("path", {
1110
+ d: "M3 6h6M6 6v4M4.2 10l1.8-2.5L7.8 10",
1111
+ stroke: K(g(u).status),
1112
+ "stroke-width": "1.2",
1113
+ "stroke-linecap": "round",
1114
+ "stroke-linejoin": "round",
1115
+ fill: "none"
1116
+ }, null, 8, gs)]))]), l[33] || (l[33] = r("span", { class: "metric-label" }, "Accessibility", -1))]), r("div", ms, [
1117
+ g(u).status === "bad" ? (_(), b("span", vs)) : (_(), b("span", {
1118
+ key: 1,
1119
+ class: A(["status-dot", `status-dot--${g(u).status}`])
1120
+ }, null, 2)),
1121
+ r("span", { class: A(["metric-value", `metric-value--${g(u).status}`]) }, k(g(u).summaryMessage), 3),
1122
+ (_(), b("svg", {
1123
+ class: A(["chevron", { "chevron--open": y.value === "a11y" }]),
1124
+ width: "12",
1125
+ height: "12",
1126
+ viewBox: "0 0 12 12",
1127
+ fill: "none"
1128
+ }, [...l[34] || (l[34] = [r("path", {
1129
+ d: "M3 4.5L6 7.5L9 4.5",
1130
+ stroke: "currentColor",
1131
+ "stroke-width": "1.5",
1132
+ "stroke-linecap": "round",
1133
+ "stroke-linejoin": "round"
1134
+ }, null, -1)])], 2))
1135
+ ])]), F(V, { name: "expand" }, {
1136
+ default: W(() => [y.value === "a11y" ? (_(), b("div", {
1137
+ key: 0,
1138
+ class: "metric-detail a11y-detail",
1139
+ onClick: l[10] || (l[10] = N(() => {
1140
+ }, ["stop"]))
1141
+ }, [
1142
+ r("div", _s, [
1143
+ r("button", {
1144
+ type: "button",
1145
+ class: A(["a11y-tab a11y-tab--failed", { "a11y-tab--active": E.value === "failed" }]),
1146
+ role: "tab",
1147
+ "aria-selected": E.value === "failed",
1148
+ onClick: l[7] || (l[7] = N((m) => E.value = "failed", ["stop"]))
1149
+ }, [l[35] || (l[35] = r("span", { class: "a11y-tab-label" }, "Failed", -1)), r("span", ys, k(g(u).failedCount), 1)], 10, bs),
1150
+ r("button", {
1151
+ type: "button",
1152
+ class: A(["a11y-tab a11y-tab--passed", { "a11y-tab--active": E.value === "passed" }]),
1153
+ role: "tab",
1154
+ "aria-selected": E.value === "passed",
1155
+ onClick: l[8] || (l[8] = N((m) => E.value = "passed", ["stop"]))
1156
+ }, [l[36] || (l[36] = r("span", { class: "a11y-tab-label" }, "Passed", -1)), r("span", Is, k(g(u).passedCount), 1)], 10, ks),
1157
+ r("button", {
1158
+ type: "button",
1159
+ class: A(["a11y-tab a11y-tab--ignored", { "a11y-tab--active": E.value === "ignored" }]),
1160
+ role: "tab",
1161
+ "aria-selected": E.value === "ignored",
1162
+ onClick: l[9] || (l[9] = N((m) => E.value = "ignored", ["stop"]))
1163
+ }, [l[37] || (l[37] = r("span", { class: "a11y-tab-label" }, "Ignored", -1)), r("span", Es, k(g(u).ignoredCount), 1)], 10, Ls)
1164
+ ]),
1165
+ E.value === "failed" ? (_(), b("div", Cs, [g(u).failed.length === 0 ? (_(), b("p", As, " No failing checks. Your email looks accessible. ")) : T("", !0), (_(!0), b(X, null, J(g(u).failed, (m) => (_(), b("div", {
1166
+ key: z(m),
1167
+ class: A(["a11y-item", `a11y-item--${m.severity}`])
1168
+ }, [
1169
+ r("div", ws, [
1170
+ r("span", { class: A(["a11y-severity", `a11y-severity--${m.severity}`]) }, k(m.severity), 3),
1171
+ r("span", Ss, k(m.rule), 1),
1172
+ r("button", {
1173
+ type: "button",
1174
+ class: "a11y-action",
1175
+ onClick: N((_e) => g(L)(m.id), ["stop"]),
1176
+ "aria-label": `Ignore: ${m.rule}`
1177
+ }, " Ignore ", 8, Ts)
1178
+ ]),
1179
+ r("p", Rs, k(m.message), 1),
1180
+ r("p", Ns, k(m.info), 1)
1181
+ ], 2))), 128))])) : T("", !0),
1182
+ E.value === "passed" ? (_(), b("div", Gs, [g(u).passed.length === 0 ? (_(), b("p", $s, " No checks have passed yet — start adding content to see results. ")) : T("", !0), (_(!0), b(X, null, J(g(u).passed, (m) => (_(), b("div", {
1183
+ key: z(m),
1184
+ class: "a11y-item a11y-item--passed"
1185
+ }, [r("div", Ms, [l[38] || (l[38] = r("span", { class: "a11y-severity a11y-severity--passed" }, "passed", -1)), r("span", Os, k(m.rule), 1)]), r("p", Bs, k(m.message), 1)]))), 128))])) : T("", !0),
1186
+ E.value === "ignored" ? (_(), b("div", Us, [g(u).ignored.length === 0 ? (_(), b("p", Hs, ' Nothing ignored. Use "Ignore" on a failing check to suppress it here. ')) : T("", !0), (_(!0), b(X, null, J(g(u).ignored, (m) => (_(), b("div", {
1187
+ key: z(m),
1188
+ class: "a11y-item a11y-item--ignored"
1189
+ }, [r("div", Ds, [
1190
+ l[39] || (l[39] = r("span", { class: "a11y-severity a11y-severity--ignored" }, "ignored", -1)),
1191
+ r("span", xs, k(m.rule), 1),
1192
+ r("button", {
1193
+ type: "button",
1194
+ class: "a11y-action a11y-action--restore",
1195
+ onClick: N((_e) => g(S)(m.id), ["stop"]),
1196
+ "aria-label": `Restore: ${m.rule}`
1197
+ }, " Restore ", 8, Ks)
1198
+ ]), r("p", Ps, k(m.message), 1)]))), 128))])) : T("", !0)
1199
+ ])) : T("", !0)]),
1200
+ _: 1
1201
+ })], 42, ds),
1202
+ l[40] || (l[40] = r("p", { class: "panel-footer" }, " Estimates based on template structure · ±8–10% variance ", -1))
1203
+ ], 2)) : T("", !0)]),
1204
+ _: 1
1205
+ }),
1206
+ v.value ? (_(), b("div", {
1207
+ key: 0,
1208
+ class: "click-outside",
1209
+ onClick: ee
1210
+ })) : T("", !0)
1211
+ ], 512));
1212
+ }
1213
+ }), js = /* @__PURE__ */ Ce(Fs, [["__scopeId", "data-v-e50d82a8"]]);
1214
+ export {
1215
+ js as default
1216
+ };