@useinsider/guido 3.9.0-beta.7791f66 → 3.9.0-beta.f67c6c8

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 (79) hide show
  1. package/dist/@types/config/schemas.js +70 -66
  2. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue.js +8 -8
  3. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue2.js +15 -12
  4. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue.js +5 -5
  5. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue2.js +16 -15
  6. package/dist/components/organisms/unsubscribe/UnsubscribePageSelection.vue.js +5 -5
  7. package/dist/components/organisms/unsubscribe/UnsubscribePageSelection.vue2.js +42 -37
  8. package/dist/components/organisms/unsubscribe/UnsubscribeTypeSelection.vue.js +3 -3
  9. package/dist/components/organisms/unsubscribe/UnsubscribeTypeSelection.vue2.js +40 -37
  10. package/dist/composables/useHtmlCompiler.js +13 -10
  11. package/dist/composables/usePreviewInteractionGuard.js +17 -0
  12. package/dist/composables/usePreviewMode.js +15 -14
  13. package/dist/composables/useRecommendationPreview.js +111 -0
  14. package/dist/composables/useSave.js +14 -14
  15. package/dist/composables/validators/useUnsubscribeBlockValidator.js +26 -17
  16. package/dist/config/compiler/htmlCompilerRules.js +38 -10
  17. package/dist/config/compiler/recommendationCompilerRules.js +84 -109
  18. package/dist/config/compiler/utils/recommendationCompilerUtils.js +110 -98
  19. package/dist/config/migrator/recommendation/htmlBuilder.js +53 -52
  20. package/dist/config/migrator/recommendation/settingsMapper.js +38 -33
  21. package/dist/enums/html-validator.js +6 -5
  22. package/dist/extensions/Blocks/Items/block.js +48 -29
  23. package/dist/extensions/Blocks/Items/utils/nodeConfigUtils.js +62 -45
  24. package/dist/extensions/Blocks/Recommendation/block.js +64 -42
  25. package/dist/extensions/Blocks/Recommendation/canvasPreview.css.js +16 -0
  26. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +41 -32
  27. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +461 -363
  28. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +99 -87
  29. package/dist/extensions/Blocks/Recommendation/controls/main/pricePlacement.js +133 -0
  30. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +160 -158
  31. package/dist/extensions/Blocks/Recommendation/extension.js +30 -29
  32. package/dist/extensions/Blocks/Recommendation/iconsRegistry.js +21 -7
  33. package/dist/extensions/Blocks/Recommendation/recommendation.css.js +64 -4
  34. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +3 -1
  35. package/dist/extensions/Blocks/Recommendation/templates/grid/elementRenderer.js +130 -96
  36. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +43 -54
  37. package/dist/extensions/Blocks/Recommendation/templates/index.js +17 -14
  38. package/dist/extensions/Blocks/Recommendation/templates/list/elementRenderer.js +86 -80
  39. package/dist/extensions/Blocks/Recommendation/templates/list/template.js +31 -40
  40. package/dist/extensions/Blocks/Recommendation/templates/utils.js +155 -102
  41. package/dist/extensions/Blocks/Unsubscribe/block.js +40 -37
  42. package/dist/extensions/Blocks/Unsubscribe/control.js +19 -16
  43. package/dist/extensions/Blocks/controlFactories.js +76 -57
  44. package/dist/guido.css +1 -1
  45. package/dist/src/@types/config/schemas.d.ts +16 -0
  46. package/dist/src/composables/useConfig.d.ts +4 -0
  47. package/dist/src/composables/usePreviewInteractionGuard.d.ts +3 -0
  48. package/dist/src/composables/useRecommendationPreview.d.ts +10 -0
  49. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +1 -0
  50. package/dist/src/config/migrator/recommendation/settingsMapper.d.ts +1 -1
  51. package/dist/src/extensions/Blocks/CouponBlock/controls/index.d.ts +1 -1
  52. package/dist/src/extensions/Blocks/Items/block.d.ts +8 -0
  53. package/dist/src/extensions/Blocks/Items/controls/index.d.ts +3 -3
  54. package/dist/src/extensions/Blocks/Items/utils/nodeConfigUtils.d.ts +1 -1
  55. package/dist/src/extensions/Blocks/Recommendation/block.d.ts +10 -0
  56. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +29 -3
  57. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +1 -1
  58. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +3 -1
  59. package/dist/src/extensions/Blocks/Recommendation/controls/main/pricePlacement.d.ts +59 -0
  60. package/dist/src/extensions/Blocks/Recommendation/controls/name/index.d.ts +1 -1
  61. package/dist/src/extensions/Blocks/Recommendation/controls/oldPrice/index.d.ts +1 -1
  62. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusDiscount/index.d.ts +1 -1
  63. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusPrice/index.d.ts +1 -1
  64. package/dist/src/extensions/Blocks/Recommendation/controls/price/index.d.ts +1 -1
  65. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +2 -0
  66. package/dist/src/extensions/Blocks/Recommendation/templates/grid/elementRenderer.d.ts +13 -1
  67. package/dist/src/extensions/Blocks/Recommendation/templates/grid/template.d.ts +4 -4
  68. package/dist/src/extensions/Blocks/Recommendation/templates/index.d.ts +1 -1
  69. package/dist/src/extensions/Blocks/Recommendation/templates/list/elementRenderer.d.ts +10 -1
  70. package/dist/src/extensions/Blocks/Recommendation/templates/list/template.d.ts +3 -3
  71. package/dist/src/extensions/Blocks/Recommendation/templates/utils.d.ts +51 -1
  72. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +15 -0
  73. package/dist/src/extensions/Blocks/controlFactories.d.ts +10 -1
  74. package/dist/src/stores/config.d.ts +36 -0
  75. package/dist/src/stores/unsubscribe.d.ts +11 -1
  76. package/dist/src/utils/ampErrorFilter.d.ts +1 -0
  77. package/dist/stores/unsubscribe.js +8 -7
  78. package/dist/utils/ampErrorFilter.js +6 -5
  79. package/package.json +1 -1
@@ -1,16 +1,16 @@
1
- import { defineComponent as v, ref as b, computed as f, watch as _ } from "vue";
1
+ import { defineComponent as _, ref as b, computed as f, watch as v } from "vue";
2
2
  import U from "../../wrappers/WpDrawer.vue.js";
3
- import { useToaster as N } from "../../../composables/useToaster.js";
4
- import { useTranslations as w } from "../../../composables/useTranslations.js";
3
+ import { useToaster as w } from "../../../composables/useToaster.js";
4
+ import { useTranslations as N } from "../../../composables/useTranslations.js";
5
5
  import { ACADEMY_LINKS as D } from "../../../enums/academy.js";
6
- import { ToasterTypeOptions as P } from "../../../enums/toaster.js";
7
- import { PAGE_TYPES as l, getTypeTranslations as R, UNSUBSCRIBE_PAGES_LINK as G } from "../../../enums/unsubscribe.js";
8
- import { useUnsubscribeStore as I } from "../../../stores/unsubscribe.js";
6
+ import { ToasterTypeOptions as I } from "../../../enums/toaster.js";
7
+ import { PAGE_TYPES as l, getTypeTranslations as R, UNSUBSCRIBE_PAGES_LINK as P } from "../../../enums/unsubscribe.js";
8
+ import { useUnsubscribeStore as G } from "../../../stores/unsubscribe.js";
9
9
  import { InSegments as L } from "@useinsider/design-system-vue";
10
- const W = /* @__PURE__ */ v({
10
+ const W = /* @__PURE__ */ _({
11
11
  __name: "UnsubscribeTypeSelection",
12
- setup(O) {
13
- const t = w(), { showToaster: d } = N(), e = I(), n = b(!1), i = b(!0), o = b(l.GLOBAL_UNSUBSCRIBE), T = t(
12
+ setup(k) {
13
+ const t = N(), { showToaster: d } = w(), e = G(), n = b(!1), o = b(!0), r = b(l.GLOBAL_UNSUBSCRIBE), T = t(
14
14
  "unsubscription-preference.select-unsubscribe-page-description",
15
15
  { action: `<a href="${D.GLOBAL_UNSUBSCRIBE}" target="_blank">${t("ds-rules.visit-academy")}</a>` }
16
16
  ), y = f(() => [
@@ -31,7 +31,7 @@ const W = /* @__PURE__ */ v({
31
31
  disable: e.isSubscriptionPreferencesCenterDisabled,
32
32
  tooltipText: e.isSubscriptionPreferencesCenterDisabled ? t("unsubscription-preference.pref-center-added") : ""
33
33
  }
34
- ]), S = f(
34
+ ]), g = f(
35
35
  () => e.isGlobalUnsubscribeDisabled && e.isSubscriptionPreferencesCenterDisabled
36
36
  ), C = f(() => ({
37
37
  primaryButton: {
@@ -41,8 +41,8 @@ const W = /* @__PURE__ */ v({
41
41
  n.value ? "unsubscription-preference.selecting-type" : "products.select-and-continue"
42
42
  ),
43
43
  loadingStatus: n.value,
44
- disabledStatus: S.value,
45
- skeletonStatus: i.value,
44
+ disabledStatus: g.value,
45
+ skeletonStatus: o.value,
46
46
  skeletonSizing: { width: 168, height: 40 }
47
47
  },
48
48
  cancelOrBackButton: {
@@ -50,59 +50,62 @@ const W = /* @__PURE__ */ v({
50
50
  type: "secondary",
51
51
  labelText: t("products.cancel"),
52
52
  disabledStatus: n.value,
53
- skeletonStatus: i.value,
53
+ skeletonStatus: o.value,
54
54
  skeletonSizing: { width: 78, height: 40 }
55
55
  }
56
- })), m = (s) => {
57
- e.typeSelectionDrawerStatus = !1, document.dispatchEvent(new CustomEvent("unsubscribe:cancel")), s && setTimeout(() => {
56
+ })), S = (s) => {
57
+ e.typeSelectionDrawerStatus = !1, document.dispatchEvent(new CustomEvent("unsubscribe:cancel", {
58
+ detail: { blockId: e.pendingBlockId }
59
+ })), s && setTimeout(() => {
58
60
  s();
59
61
  }, 500);
60
62
  }, E = (s) => {
61
- o.value = s;
62
- }, g = () => {
63
- const s = e.hasTemplatesByCollectionType, r = [];
64
- if (Object.entries(s).forEach(([c, p]) => {
65
- p || r.push(Number(c));
66
- }), r.length === 0)
63
+ r.value = s;
64
+ }, m = () => {
65
+ const s = e.hasTemplatesByCollectionType, a = [];
66
+ if (Object.entries(s).forEach(([i, p]) => {
67
+ p || a.push(Number(i));
68
+ }), a.length === 0)
67
69
  return !0;
68
- const B = R(), a = r.map((c) => B[c]);
70
+ const h = R(), c = a.map((i) => h[i]);
69
71
  let u = "";
70
- if (r.length === 1)
72
+ if (a.length === 1)
71
73
  u = t("unsubscription-preference.create-page-to-continue", {
72
- page: a[0]
74
+ page: c[0]
73
75
  });
74
- else if (r.length === 2)
76
+ else if (a.length === 2)
75
77
  u = t("unsubscription-preference.create-pages-to-continue", {
76
- page1: a[0],
77
- page2: a[1]
78
+ page1: c[0],
79
+ page2: c[1]
78
80
  });
79
81
  else {
80
- const c = a.pop() ?? "", p = a.join(", ");
82
+ const i = c.pop() ?? "", p = c.join(", ");
81
83
  u = t("unsubscription-preference.create-multiple-pages-to-continue", {
82
84
  pages: p,
83
- lastPage: c
85
+ lastPage: i
84
86
  });
85
87
  }
86
88
  return d({
87
- type: P.Warning,
89
+ type: I.Warning,
88
90
  message: u,
89
91
  actionButton: {
90
92
  text: "Go to Unsubscribe Pages",
91
93
  onClick: () => {
92
- window.location.href = G;
94
+ const i = new URL(P, window.location.origin).href;
95
+ window.open(i, "_blank", "noopener");
93
96
  }
94
97
  }
95
98
  }), !1;
96
- }, h = async () => {
97
- if (n.value = !0, await e.fetchTemplates(), e.setCollection(o.value), !g()) {
98
- n.value = !1, m();
99
+ }, B = async () => {
100
+ if (n.value = !0, await e.fetchTemplates(!0), e.setCollection(r.value), !m()) {
101
+ n.value = !1, S();
99
102
  return;
100
103
  }
101
104
  n.value = !1, e.typeSelectionDrawerStatus = !1, e.pageSelectionDrawerStatus = !0;
102
105
  };
103
- return _(() => e.typeSelectionDrawerStatus, (s) => {
104
- s && (i.value = !0, o.value = l.GLOBAL_UNSUBSCRIBE, e.isGlobalUnsubscribeDisabled && (o.value = e.isSubscriptionPreferencesCenterDisabled ? 0 : l.SUBSCRIPTION_PREFERENCE_CENTER), i.value = !1);
105
- }), { __sfc: !0, trans: t, showToaster: d, unsubscribeStore: e, isSelecting: n, skeletonStatus: i, selectedCollection: o, descriptionText: T, segmentList: y, hasNoSelectableType: S, footerButtonGroupOptions: C, closeModal: m, changeCollection: E, validateTemplates: g, selectCollection: h, WpDrawer: U, InSegments: L };
106
+ return v(() => e.typeSelectionDrawerStatus, (s) => {
107
+ s && (o.value = !0, r.value = l.GLOBAL_UNSUBSCRIBE, e.isGlobalUnsubscribeDisabled && (r.value = e.isSubscriptionPreferencesCenterDisabled ? 0 : l.SUBSCRIPTION_PREFERENCE_CENTER), o.value = !1);
108
+ }), { __sfc: !0, trans: t, showToaster: d, unsubscribeStore: e, isSelecting: n, skeletonStatus: o, selectedCollection: r, descriptionText: T, segmentList: y, hasNoSelectableType: g, footerButtonGroupOptions: C, closeModal: S, changeCollection: E, validateTemplates: m, selectCollection: B, WpDrawer: U, InSegments: L };
106
109
  }
107
110
  });
108
111
  export {
@@ -26,24 +26,27 @@ const y = /* @__PURE__ */ new Set([
26
26
  "format-comment-braces",
27
27
  "add-universal-link-flags",
28
28
  // Coupon placeholder replacement (only present when liquidSyntax is enabled).
29
- "liquid-coupon-code"
29
+ "liquid-coupon-code",
30
+ // Editor-only attribute strip — AMP4EMAIL has no `contenteditable`, so leaving
31
+ // it in narrows/breaks rendering. Pure attribute removal, emits nothing.
32
+ "remove-contenteditable"
30
33
  ]), M = () => {
31
- var t, m, r;
32
- const { compiler: e, isFeatureEnabled: a, partner: n } = k(), p = ((t = e.value) == null ? void 0 : t.customRules) || [], l = [
33
- ...!!((m = e.value) != null && m.ignoreDefaultRules) ? [] : R,
34
+ var i, m, r;
35
+ const { compiler: o, isFeatureEnabled: n, partner: a } = k(), p = ((i = o.value) == null ? void 0 : i.customRules) || [], l = [
36
+ ...!!((m = o.value) != null && m.ignoreDefaultRules) ? [] : R,
34
37
  ...v,
35
38
  ...x,
36
39
  ...g,
37
40
  ...b,
38
41
  ...H,
39
- ...a("liquidSyntax") ? C : [],
40
- ...p.map((o, f) => ({
41
- ...o,
42
- priority: o.priority + 1e3 + f
42
+ ...n("liquidSyntax") ? C : [],
43
+ ...p.map((e, f) => ({
44
+ ...e,
45
+ priority: e.priority + 1e3 + f
43
46
  // Ensure additional rules run after default rules
44
47
  }))
45
- ], u = s(l), c = l.filter((o) => y.has(o.id)), d = s(c), i = (r = n.value) == null ? void 0 : r.fallbackFont;
46
- return { compileHtml: (o) => u.compile(o, void 0, i), compileAmpHtml: (o) => d.compile(o, void 0, i) };
48
+ ], u = s(l), c = l.filter((e) => y.has(e.id)), d = s(c), t = (r = a.value) == null ? void 0 : r.fallbackFont;
49
+ return { compileHtml: (e) => u.compile(e, void 0, t), compileAmpHtml: (e) => d.compile(e, void 0, t) };
47
50
  };
48
51
  export {
49
52
  M as useHtmlCompiler
@@ -0,0 +1,17 @@
1
+ const o = ["click", "auxclick", "submit"], c = (t) => {
2
+ t.preventDefault(), t.stopPropagation();
3
+ }, a = () => ({ blockPreviewInteractions: (r) => {
4
+ try {
5
+ const e = r.contentDocument;
6
+ if (!e)
7
+ return;
8
+ o.forEach((n) => {
9
+ e.addEventListener(n, c, !0);
10
+ });
11
+ } catch (e) {
12
+ console.warn("[usePreviewInteractionGuard] Could not guard preview iframe:", e);
13
+ }
14
+ } });
15
+ export {
16
+ a as usePreviewInteractionGuard
17
+ };
@@ -1,28 +1,29 @@
1
- import { useEditorStore as d } from "../stores/editor.js";
2
- import { usePreviewStore as n } from "../stores/preview.js";
3
- import { useActionsApi as p } from "./useActionsApi.js";
4
- import { useCodeEditorApi as c } from "./useCodeEditorApi.js";
5
- import { useHtmlCompiler as u } from "./useHtmlCompiler.js";
6
- const g = () => {
7
- const t = d(), e = n(), { closeCodeEditor: i } = c(), { getPreviewData: a, getCompiledEmail: s } = p(), { compileHtml: m } = u(), r = () => {
1
+ import { useEditorStore as n } from "../stores/editor.js";
2
+ import { usePreviewStore as p } from "../stores/preview.js";
3
+ import { useActionsApi as c } from "./useActionsApi.js";
4
+ import { useCodeEditorApi as u } from "./useCodeEditorApi.js";
5
+ import { useHtmlCompiler as v } from "./useHtmlCompiler.js";
6
+ import { useRecommendationPreview as w } from "./useRecommendationPreview.js";
7
+ const C = () => {
8
+ const t = n(), e = p(), { closeCodeEditor: a } = u(), { getPreviewData: m, getCompiledEmail: s } = c(), { compileHtml: l } = v(), { substituteRecommendationPreview: r } = w(), i = () => {
8
9
  t.isPreviewModeOpen = !1, e.$reset();
9
10
  };
10
11
  return {
11
- closePreviewMode: r,
12
+ closePreviewMode: i,
12
13
  openPreviewMode: () => {
13
- t.isCodeEditorOpen && i(), t.isPreviewModeOpen = !0;
14
+ t.isCodeEditorOpen && a(), t.isPreviewModeOpen = !0;
14
15
  },
15
16
  loadPreviewData: async () => {
16
17
  if (!(t.loadingStatus || e.isLoaded)) {
17
18
  t.loadingStatus = !0;
18
19
  try {
19
- const [o, l] = await Promise.all([
20
- a(),
20
+ const [o, d] = await Promise.all([
21
+ m(),
21
22
  s({ minimize: !0, resetDataSavedFlag: !1 })
22
23
  ]);
23
- e.templateHtml = o.html || "", e.ampHtml = o.ampHtml || "", e.ampErrors = o.ampErrors || [], e.setEmailFormat(o.ampHtml ? "AMP" : "html"), e.emailSizeKB = m(l.html).estimatedSizeKB, e.isLoaded = !0;
24
+ e.templateHtml = r(o.html || ""), e.ampHtml = r(o.ampHtml || ""), e.ampErrors = o.ampErrors || [], e.setEmailFormat(o.ampHtml ? "AMP" : "html"), e.emailSizeKB = l(d.html).estimatedSizeKB, e.isLoaded = !0;
24
25
  } catch (o) {
25
- console.error("Failed to load preview data:", o), r();
26
+ console.error("Failed to load preview data:", o), i();
26
27
  } finally {
27
28
  t.loadingStatus = !1;
28
29
  }
@@ -31,5 +32,5 @@ const g = () => {
31
32
  };
32
33
  };
33
34
  export {
34
- g as usePreviewMode
35
+ C as usePreviewMode
35
36
  };
@@ -0,0 +1,111 @@
1
+ import { BLOCK_ROOT_SELECTOR as S, CURRENCY_ATTR as p, CONTAINER_SELECTOR as O } from "../extensions/Blocks/Recommendation/constants/selectors.js";
2
+ import { useRecommendationExtensionStore as h } from "../extensions/Blocks/Recommendation/store/recommendation.js";
3
+ import { formatPrice as E } from "../extensions/Blocks/Recommendation/utils/priceFormatter.js";
4
+ const f = `${S}[recommendation-id]`, C = ".product-old-price", R = /\{\{(?:reco_)?(\d+_\d+_[a-zA-Z0-9_.]+)\}\}/g, T = new RegExp(
5
+ "\\{%\\s*if\\s+([\\s\\S]+?)\\s*%\\}([\\s\\S]*?)(?:\\{%\\s*else\\s*%\\}([\\s\\S]*?))?\\{%\\s*endif\\s*%\\}",
6
+ "g"
7
+ );
8
+ function b(e) {
9
+ return e.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
10
+ }
11
+ function _(e) {
12
+ var s;
13
+ const n = (s = e.replace(/[\u0000-\u0020]+/g, "").toLowerCase().match(/^([a-z][a-z0-9+.-]*):/)) == null ? void 0 : s[1];
14
+ return n && n !== "http" && n !== "https" && n !== "mailto" ? "" : e;
15
+ }
16
+ function N(e) {
17
+ const t = e.getAttribute(p.CURRENCY) || "USD", n = e.getAttribute(p.DECIMAL_SEPARATOR) || ".", s = e.getAttribute(p.THOUSAND_SEPARATOR) || ",";
18
+ return {
19
+ code: t,
20
+ symbol: e.getAttribute(p.SYMBOL) || t,
21
+ alignment: e.getAttribute(p.ALIGNMENT) === "0" ? "before" : "after",
22
+ decimalCount: parseInt(e.getAttribute(p.DECIMAL_COUNT) || "2") || 2,
23
+ decimalSeparator: n,
24
+ thousandSeparator: s
25
+ };
26
+ }
27
+ function g(e, t) {
28
+ return (e == null ? void 0 : e[t]) ?? Object.values(e ?? {})[0] ?? 0;
29
+ }
30
+ function L(e, t) {
31
+ const n = g(e.original_price, t), s = g(e.price, t), o = n > 0 ? Math.round((n - s) / n * 100) : 0;
32
+ return o > 0 ? `${o}%` : "0%";
33
+ }
34
+ function P(e, t, n, s, o, a) {
35
+ s.forEach((i, c) => {
36
+ const r = (m) => `${n}_${c}_${m}`, u = E({ price: g(i.price, o.code), currency: o }), l = E({ price: g(i.original_price, o.code), currency: o }), d = L(i, o.code);
37
+ e.set(r("name"), i.name ?? ""), e.set(r("image_url"), _(i.image_url ?? "")), e.set(r("url"), _(i.url ?? "")), e.set(r("item_id"), i.item_id ?? ""), e.set(r("currency"), ""), e.set(r("price"), u), e.set(r("original_price"), l), e.set(r("discount"), d), e.set(r("omnibus_price"), l), e.set(r("omnibus_discount"), d), a && u === l && t.add(`${n}_${c}`), Object.entries(i.product_attributes ?? {}).forEach(([m, A]) => {
38
+ e.set(r(`product_attribute.${m}`), String(A)), e.set(r(m), String(A));
39
+ });
40
+ });
41
+ }
42
+ function $(e, t) {
43
+ const n = e.match(/^(.+?)\s*(!=|==)\s*"([^"]*)"$/);
44
+ if (!n)
45
+ return !0;
46
+ const [, s, o, a] = n, i = t.get(s.trim()) ?? "";
47
+ return o === "!=" ? i !== a : i === a;
48
+ }
49
+ function z(e, t) {
50
+ return e.replace(
51
+ T,
52
+ (n, s, o, a) => s.split(/\s+and\s+/).every((c) => $(c.trim(), t)) ? o : a ?? ""
53
+ );
54
+ }
55
+ function w(e) {
56
+ let t = e;
57
+ for (; t.children.length === 1; )
58
+ [t] = t.children;
59
+ return t;
60
+ }
61
+ function D(e, t) {
62
+ var i;
63
+ if (!t.size)
64
+ return e;
65
+ const n = e.replaceAll("{%", "<!--{%").replaceAll("%}", "%}-->"), s = new DOMParser().parseFromString(n, "text/html");
66
+ s.querySelectorAll(f).forEach((c) => {
67
+ const r = Number(c.getAttribute("recommendation-id"));
68
+ if (!Number.isFinite(r))
69
+ return;
70
+ const u = c.querySelectorAll(O);
71
+ (u.length ? Array.from(u) : [c]).forEach((d) => {
72
+ d.querySelectorAll(C).forEach((m, A) => {
73
+ t.has(`${r}_${A}`) && (w(m).textContent = "");
74
+ });
75
+ });
76
+ });
77
+ const o = (i = e.match(/<!DOCTYPE[^>]*>/i)) == null ? void 0 : i[0];
78
+ return ((o ? `${o}
79
+ ` : "") + s.documentElement.outerHTML).replaceAll("<!--{%", "{%").replaceAll("%}-->", "%}").replaceAll("&lt;!--{%", "{%").replaceAll("%}--&gt;", "%}");
80
+ }
81
+ function I(e) {
82
+ if (!e || !e.includes("recommendation-block-v2"))
83
+ return e;
84
+ const t = h(), n = new DOMParser().parseFromString(e, "text/html"), s = Array.from(n.querySelectorAll(f)), o = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Set();
85
+ if (s.forEach((c) => {
86
+ var l;
87
+ const r = Number(c.getAttribute("recommendation-id"));
88
+ if (!Number.isFinite(r))
89
+ return;
90
+ const u = ((l = t.blockStates[r]) == null ? void 0 : l.recommendationProducts) ?? [];
91
+ u.length && P(
92
+ o,
93
+ a,
94
+ r,
95
+ u,
96
+ N(c),
97
+ c.getAttribute("hide-price") === "true"
98
+ );
99
+ }), !o.size)
100
+ return e;
101
+ let i = D(e, a);
102
+ return i = i.replace(
103
+ R,
104
+ (c, r) => o.has(r) ? b(o.get(r)) : c
105
+ ), i = z(i, o), i;
106
+ }
107
+ const q = () => ({ substituteRecommendationPreview: I });
108
+ export {
109
+ I as substituteRecommendationPreview,
110
+ q as useRecommendationPreview
111
+ };
@@ -1,32 +1,32 @@
1
- import { useActionsApi as x } from "./useActionsApi.js";
1
+ import { useActionsApi as H } from "./useActionsApi.js";
2
2
  import { useConfig as y } from "./useConfig.js";
3
- import { useSaveStart as w, useSaveComplete as H } from "./useGuidoActions.js";
3
+ import { useSaveStart as w, useSaveComplete as k } from "./useGuidoActions.js";
4
4
  import { useSyncModuleExtractor as q } from "./useSyncModuleExtractor.js";
5
5
  import { useStripoApi as C } from "../services/stripoApi.js";
6
6
  import { useTemplatePreparation as E } from "../utils/templatePreparation.js";
7
- import { useHtmlValidator as k } from "./useHtmlValidator.js";
7
+ import { useHtmlValidator as B } from "./useHtmlValidator.js";
8
8
  import { useCouponBlockValidator as L } from "./validators/useCouponBlockValidator.js";
9
9
  import { useLiquidValidator as P } from "./validators/useLiquidValidator.js";
10
- import { useUnsubscribeBlockValidator as B } from "./validators/useUnsubscribeBlockValidator.js";
11
- const J = () => {
12
- const o = w(), s = H(), { validateHtml: r } = k(), { validateLiquidSyntax: n } = P(), { validateCouponBlockTags: l } = L(), { validateUnsubscribeBlockUniqueness: d } = B(), { callbacks: i, isFeatureEnabled: u } = y(), { extractSyncModuleData: c } = q(), { setSyncModuleUnsubscriptionPages: m } = C(), { editorSave: p } = x();
13
- return { save: async (f = !1, v = !1) => {
10
+ import { useUnsubscribeBlockValidator as U } from "./validators/useUnsubscribeBlockValidator.js";
11
+ const K = () => {
12
+ const o = w(), s = k(), { validateHtml: r } = B(), { validateLiquidSyntax: l } = P(), { validateCouponBlockTags: n } = L(), { validateUnsubscribeBlockUniqueness: d, validateUnsubscribeBlockHasTemplate: c } = U(), { callbacks: i, isFeatureEnabled: u } = y(), { extractSyncModuleData: m } = q(), { setSyncModuleUnsubscriptionPages: p } = C(), { editorSave: v } = H();
13
+ return { save: async (f = !1, S = !1) => {
14
14
  var a;
15
15
  o();
16
- const { prepareTemplateDetails: S } = E(), t = await S();
17
- if (!l(t.compiledHtml) || !d(t.compiledHtml))
16
+ const { prepareTemplateDetails: V } = E(), t = await V();
17
+ if (!n(t.compiledHtml) || !d(t.compiledHtml) || !c(t.compiledHtml))
18
18
  return;
19
19
  if (u("liquidSyntax")) {
20
- if (!await n(t.compiledHtml))
20
+ if (!await l(t.compiledHtml))
21
21
  return;
22
22
  } else if (!await r(t.compiledHtml, t.dynamicContentList, !0))
23
23
  return;
24
- if ((a = i.value) != null && a.externalValidation && !await i.value.externalValidation(t) || !await p())
24
+ if ((a = i.value) != null && a.externalValidation && !await i.value.externalValidation(t) || !await v())
25
25
  return;
26
- const { unsubscribePayload: V, stripoModules: b } = c(t.rawHtml);
27
- return await m(V), t.modules = b, f || s({ ...t, silent: v }), t;
26
+ const { unsubscribePayload: b, stripoModules: x } = m(t.rawHtml);
27
+ return await p(b), t.modules = x, f || s({ ...t, silent: S }), t;
28
28
  } };
29
29
  };
30
30
  export {
31
- J as useSave
31
+ K as useSave
32
32
  };
@@ -1,32 +1,41 @@
1
1
  import { ToasterTypeOptions as i } from "../../enums/toaster.js";
2
- import { PAGE_TYPES as n } from "../../enums/unsubscribe.js";
3
- import { UNSUBSCRIBE_BLOCK_SELECTOR as u, DATA_ATTRIBUTES as l } from "../../extensions/Blocks/Unsubscribe/utils/constants.js";
4
- import { useToaster as T } from "../useToaster.js";
2
+ import { PAGE_TYPES as u } from "../../enums/unsubscribe.js";
3
+ import { UNSUBSCRIBE_BLOCK_SELECTOR as l, DATA_ATTRIBUTES as a } from "../../extensions/Blocks/Unsubscribe/utils/constants.js";
4
+ import { useToaster as m } from "../useToaster.js";
5
5
  import { useTranslations as E } from "../useTranslations.js";
6
- const m = [
6
+ const T = [
7
7
  {
8
- pageType: n.GLOBAL_UNSUBSCRIBE,
8
+ pageType: u.GLOBAL_UNSUBSCRIBE,
9
9
  messageKey: "unsubscription-preference.duplicate-global-unsub"
10
10
  },
11
11
  {
12
- pageType: n.SUBSCRIPTION_PREFERENCE_CENTER,
12
+ pageType: u.SUBSCRIPTION_PREFERENCE_CENTER,
13
13
  messageKey: "unsubscription-preference.duplicate-pref-center"
14
14
  }
15
- ], g = "unsubscription-preference.duplicate-both", y = () => {
16
- const { showToaster: o } = T(), a = E();
17
- return { validateUnsubscribeBlockUniqueness: (c) => {
18
- const p = new DOMParser().parseFromString(c, "text/html"), t = /* @__PURE__ */ new Map();
19
- p.querySelectorAll(u).forEach((e) => {
20
- const r = Number(e.getAttribute(l.PAGE_TYPE));
21
- t.set(r, (t.get(r) ?? 0) + 1);
15
+ ], g = "unsubscription-preference.duplicate-both", b = "unsubscription-preference.select-page-to-continue", P = () => {
16
+ const { showToaster: c } = m(), p = E();
17
+ return { validateUnsubscribeBlockUniqueness: (n) => {
18
+ const o = new DOMParser().parseFromString(n, "text/html"), s = /* @__PURE__ */ new Map();
19
+ o.querySelectorAll(l).forEach((e) => {
20
+ const r = Number(e.getAttribute(a.PAGE_TYPE));
21
+ s.set(r, (s.get(r) ?? 0) + 1);
22
22
  });
23
- const s = m.filter((e) => (t.get(e.pageType) ?? 0) > 1).map((e) => e.messageKey);
24
- return s.length ? (o({
23
+ const t = T.filter((e) => (s.get(e.pageType) ?? 0) > 1).map((e) => e.messageKey);
24
+ return t.length ? (c({
25
25
  type: i.Alert,
26
- message: a(s.length > 1 ? g : s[0])
26
+ message: p(t.length > 1 ? g : t[0])
27
+ }), !1) : !0;
28
+ }, validateUnsubscribeBlockHasTemplate: (n) => {
29
+ const o = new DOMParser().parseFromString(n, "text/html");
30
+ return Array.from(o.querySelectorAll(l)).some((t) => {
31
+ const e = t.getAttribute(a.PAGE_TYPE), r = t.getAttribute(a.PAGE_LIST);
32
+ return !e || !r;
33
+ }) ? (c({
34
+ type: i.Warning,
35
+ message: p(b)
27
36
  }), !1) : !0;
28
37
  } };
29
38
  };
30
39
  export {
31
- y as useUnsubscribeBlockValidator
40
+ P as useUnsubscribeBlockValidator
32
41
  };
@@ -1,5 +1,5 @@
1
- import { stripFontTags as n } from "../../utils/stripFontTags.js";
2
- const l = [
1
+ import { stripFontTags as p } from "../../utils/stripFontTags.js";
2
+ const d = [
3
3
  {
4
4
  id: "fix-url-encoding-start",
5
5
  description: "Replace {%22 with %7B%22 to fix URL encoding",
@@ -58,7 +58,7 @@ const l = [
58
58
  id: "strip-font-tags",
59
59
  description: 'Unwrap browser-translate-injected <font dir="auto"> tags, keeping inner content; other <font> elements are left intact.',
60
60
  type: "custom",
61
- processor: (e) => n(e),
61
+ processor: (e) => p(e),
62
62
  priority: 15
63
63
  },
64
64
  {
@@ -99,7 +99,7 @@ const l = [
99
99
  description: "Adding MSO Conditions",
100
100
  type: "custom",
101
101
  processor: (e) => {
102
- const i = [
102
+ const t = [
103
103
  `<!--[if gte mso 9]>
104
104
  <style>sup {
105
105
  font-size: 100% !important;
@@ -119,13 +119,13 @@ const l = [
119
119
  <w:DontUseAdvancedTypographyReadingMail></w:DontUseAdvancedTypographyReadingMail>
120
120
  </w:WordDocument>
121
121
  </xml><![endif]-->`
122
- ], s = /<head>([\S\s]*)<\/head>/, o = new RegExp(s);
122
+ ], i = /<head>([\S\s]*)<\/head>/, o = new RegExp(i);
123
123
  if (!e.match(o))
124
124
  return e;
125
- let t = e;
126
- return i.forEach((r) => {
127
- t = t.replace("</head>", `${r}</head>`);
128
- }), t;
125
+ let s = e;
126
+ return t.forEach((r) => {
127
+ s = s.replace("</head>", `${r}</head>`);
128
+ }), s;
129
129
  },
130
130
  priority: 30
131
131
  },
@@ -146,8 +146,36 @@ const l = [
146
146
  replacement: "email-static.useinsider.com",
147
147
  replaceAll: !0,
148
148
  priority: 41
149
+ },
150
+ {
151
+ id: "convert-rgb-to-hex",
152
+ // SD-142395: rgb()/6-digit hex are verbose; hex/3-digit shorthand is byte-identical
153
+ // to render. Shrinks the whole email to stay under Gmail's ~102KB clip threshold.
154
+ // Leaves rgba()/8-digit hex (alpha) untouched.
155
+ description: "Convert rgb() colors to hex and collapse 6-digit hex to shorthand",
156
+ type: "custom",
157
+ processor: (e) => {
158
+ const t = (i) => i.toString(16).padStart(2, "0");
159
+ return e.replace(/rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/gi, (i, o, n, s) => {
160
+ const [r, a, c] = [Number(o), Number(n), Number(s)];
161
+ return r > 255 || a > 255 || c > 255 ? i : `#${t(r)}${t(a)}${t(c)}`;
162
+ }).replace(/#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3\b/gi, "#$1$2$3");
163
+ },
164
+ priority: 44
165
+ },
166
+ {
167
+ id: "remove-contenteditable",
168
+ // SD-142395: contenteditable is a Stripo editor flag with zero effect in a
169
+ // delivered email. Stripping it globally shaves bytes off every text block,
170
+ // helping the whole email stay under Gmail's ~102KB clip threshold.
171
+ description: "Strip editor-only contenteditable attributes from compiled output",
172
+ type: "regex",
173
+ pattern: '\\s+contenteditable="[^"]*"',
174
+ replacement: "",
175
+ flags: "g",
176
+ priority: 45
149
177
  }
150
178
  ];
151
179
  export {
152
- l as defaultHtmlCompilerRules
180
+ d as defaultHtmlCompilerRules
153
181
  };