@useinsider/guido 3.6.1-beta.03230b7 → 3.7.0-beta.509616e

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 (46) hide show
  1. package/README.md +14 -0
  2. package/dist/components/Guido.vue.js +1 -1
  3. package/dist/components/Guido.vue2.js +75 -72
  4. package/dist/components/organisms/header/EditorActions.vue.js +1 -1
  5. package/dist/components/organisms/header/EditorActions.vue2.js +6 -6
  6. package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
  7. package/dist/components/organisms/header/LeftSlot.vue2.js +9 -9
  8. package/dist/composables/useActionsApi.js +16 -15
  9. package/dist/composables/useHtmlCompiler.js +40 -22
  10. package/dist/composables/useRecommendation.js +34 -33
  11. package/dist/enums/toaster.js +1 -1
  12. package/dist/extensions/Blocks/Recommendation/block.js +35 -16
  13. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +127 -123
  14. package/dist/extensions/Blocks/Recommendation/controls/main/layoutOrientation.js +12 -11
  15. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +46 -46
  16. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +13 -9
  17. package/dist/extensions/Blocks/Recommendation/templates/grid/elementRenderer.js +35 -29
  18. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +28 -28
  19. package/dist/extensions/Blocks/Recommendation/templates/list/elementRenderer.js +8 -8
  20. package/dist/extensions/Blocks/Recommendation/templates/utils.js +56 -49
  21. package/dist/extensions/Blocks/Recommendation/useRecommendationBlockWarning.js +16 -0
  22. package/dist/extensions/Blocks/Recommendation/utils/partnerCustomizations.js +21 -0
  23. package/dist/extensions/Blocks/Recommendation/utils/priceFormatter.js +17 -17
  24. package/dist/extensions/Blocks/Recommendation/utils/recommendationBlockCount.js +9 -0
  25. package/dist/guido.css +1 -1
  26. package/dist/src/components/Guido.vue.d.ts +1 -0
  27. package/dist/src/composables/useHtmlCompiler.d.ts +1 -0
  28. package/dist/src/enums/toaster.d.ts +2 -1
  29. package/dist/src/extensions/Blocks/Recommendation/block.d.ts +13 -0
  30. package/dist/src/extensions/Blocks/Recommendation/block.test.d.ts +1 -0
  31. package/dist/src/extensions/Blocks/Recommendation/templates/grid/elementRenderer.d.ts +7 -0
  32. package/dist/src/extensions/Blocks/Recommendation/templates/index.d.ts +1 -1
  33. package/dist/src/extensions/Blocks/Recommendation/templates/utils.d.ts +8 -0
  34. package/dist/src/extensions/Blocks/Recommendation/templates/utils.test.d.ts +1 -0
  35. package/dist/src/extensions/Blocks/Recommendation/useRecommendationBlockWarning.d.ts +12 -0
  36. package/dist/src/extensions/Blocks/Recommendation/utils/partnerCustomizations.d.ts +7 -0
  37. package/dist/src/extensions/Blocks/Recommendation/utils/priceFormatter.test.d.ts +1 -0
  38. package/dist/src/extensions/Blocks/Recommendation/utils/recommendationBlockCount.d.ts +28 -0
  39. package/dist/src/extensions/Blocks/Recommendation/utils/recommendationBlockCount.test.d.ts +1 -0
  40. package/dist/src/utils/ampErrorFilter.d.ts +2 -0
  41. package/dist/src/utils/ampErrorFilter.test.d.ts +1 -0
  42. package/dist/src/utils/templatePreparation.d.ts +16 -0
  43. package/dist/src/utils/templatePreparation.test.d.ts +1 -0
  44. package/dist/utils/ampErrorFilter.js +8 -0
  45. package/dist/utils/templatePreparation.js +59 -58
  46. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
- import { DEFAULT_PRODUCTS_PER_ROW as L } from "../../constants/layout.js";
2
- import { getDefaultProducts as E, DEFAULTS as I, createBlockTemplate as _, DEFAULT_CARD_COMPOSITION as S, spacer as b, buildElementRenderer as A, DEFAULT_CARD_VISIBILITY as f } from "../utils.js";
3
- import { ATTRIBUTE_CELL_CLASS as w, DEFAULT_CELL_PADDING as D, gridElementRenderer as C } from "./elementRenderer.js";
4
- const O = `
1
+ import { DEFAULT_PRODUCTS_PER_ROW as m } from "../../constants/layout.js";
2
+ import { getDefaultProducts as I, DEFAULTS as b, createBlockTemplate as E, DEFAULT_CARD_COMPOSITION as L, spacer as S, buildElementRenderer as _, DEFAULT_CARD_VISIBILITY as f } from "../utils.js";
3
+ import { buildFillerCell as w, gridElementRenderer as A } from "./elementRenderer.js";
4
+ const D = `
5
5
  <tr class="recommendation-product-row">
6
6
  <td>
7
7
  <table
@@ -18,7 +18,7 @@ const O = `
18
18
  </table>
19
19
  </td>
20
20
  </tr>
21
- `, g = `
21
+ `, C = `
22
22
  <tr
23
23
  class="recommendation-attribute-row"
24
24
  data-attribute-type="{-{-ATTR_TYPE-}-}"
@@ -27,38 +27,38 @@ const O = `
27
27
  {-{-CELLS-}-}
28
28
  </tr>
29
29
  `;
30
- function P(t, e, o, r = S, n = {}) {
31
- const c = (100 / e).toFixed(2), i = e - t.length, l = `<td class="${w}" style="padding: ${D};" width="${c}%"></td>`, d = i > 0 ? l.repeat(i) : "", a = A(o, r, n);
32
- return r.filter((s) => a[s]).map((s) => {
33
- const T = f[s] ?? !0, u = T ? "" : 'style="display: none;"', R = t.map((m) => a[s](m).replace("<td", `<td width="${c}%"`)).join("");
34
- return g.replace("{-{-ATTR_TYPE-}-}", s).replace("{-{-VISIBILITY-}-}", T ? "1" : "0").replace("{-{-DISPLAY_STYLE-}-}", u).replace("{-{-CELLS-}-}", R + d);
30
+ function O(t, e, n, r = L, c = {}) {
31
+ const a = (100 / e).toFixed(2), s = e - t.length, l = s > 0 ? w(a).repeat(s) : "", i = _(n, r, c);
32
+ return r.filter((o) => i[o]).map((o) => {
33
+ const p = f[o] ?? !0, u = p ? "" : 'style="display: none;"', T = t.map((R) => i[o](R).replace("<td", `<td width="${a}%"`)).join("");
34
+ return C.replace("{-{-ATTR_TYPE-}-}", o).replace("{-{-VISIBILITY-}-}", p ? "1" : "0").replace("{-{-DISPLAY_STYLE-}-}", u).replace("{-{-CELLS-}-}", T + l);
35
35
  }).join("");
36
36
  }
37
- function U(t, e, o, r, n = {}) {
38
- const c = [];
37
+ function g(t, e, n, r, c = {}) {
38
+ const a = [];
39
39
  for (let l = 0; l < t.length; l += e)
40
- c.push(t.slice(l, l + e));
41
- return c.map((l, d) => {
42
- const a = P(
40
+ a.push(t.slice(l, l + e));
41
+ return a.map((l, i) => {
42
+ const d = O(
43
43
  l,
44
44
  e,
45
- o,
45
+ n,
46
46
  r,
47
- n
48
- ), p = O.replace("{-{-ATTRIBUTE_ROWS-}-}", a);
49
- return d > 0 ? b + p : p;
47
+ c
48
+ ), o = D.replace("{-{-ATTRIBUTE_ROWS-}-}", d);
49
+ return i > 0 ? S + o : o;
50
50
  }).join("");
51
51
  }
52
- function h(t, e, o, r = {}) {
53
- return U(t, e, C, o, r);
52
+ function P(t, e, n, r = {}) {
53
+ return g(t, e, A, n, r);
54
54
  }
55
- function F(t) {
56
- const e = t ? `ins-recommendation-v3-block-${t}` : void 0, o = _("grid", e), r = E(), n = h(r, L);
57
- return o.replace("{-{-TITLE-}-}", I.TITLE).replace("{-{-PRODUCT_ROWS-}-}", n).replace("{-{-MOBILE_PRODUCT_ROWS-}-}", "");
55
+ function B(t) {
56
+ const e = t ? `ins-recommendation-v3-block-${t}` : void 0, n = E("grid", e), r = I(), c = P(r, m);
57
+ return n.replace("{-{-TITLE-}-}", b.TITLE).replace("{-{-PRODUCT_ROWS-}-}", c).replace("{-{-MOBILE_PRODUCT_ROWS-}-}", "");
58
58
  }
59
59
  export {
60
- F as getDefaultTemplate,
61
- P as prepareGridAttributeRows,
62
- U as prepareGridProductRows,
63
- h as prepareProductRows
60
+ B as getDefaultTemplate,
61
+ O as prepareGridAttributeRows,
62
+ g as prepareGridProductRows,
63
+ P as prepareProductRows
64
64
  };
@@ -14,10 +14,10 @@ function b() {
14
14
  thousandSeparator: e.thousandSeparator
15
15
  };
16
16
  }
17
- function s(t, e = "price") {
18
- const n = b(), a = t[e], r = (a == null ? void 0 : a[n.code]) ?? Object.values(a ?? {})[0] ?? 0;
17
+ function r(t, e = "price") {
18
+ const n = b(), s = t[e], a = (s == null ? void 0 : s[n.code]) ?? Object.values(s ?? {})[0] ?? 0;
19
19
  return R({
20
- price: r,
20
+ price: a,
21
21
  currency: n
22
22
  });
23
23
  }
@@ -69,7 +69,7 @@ const I = {
69
69
  esd-extension-block-id="${o.PRICE}"
70
70
  align="left">
71
71
  <p contenteditable="false" style="font-size: 16px; color: #333333; font-weight: bold; margin: 0;">
72
- <strong>${s(t, "price")}</strong>
72
+ <strong>${r(t, "price")}</strong>
73
73
  </p>
74
74
  </td>
75
75
  </tr>
@@ -85,8 +85,8 @@ const I = {
85
85
  align="left">
86
86
  <p
87
87
  contenteditable="false"
88
- style="font-size: 14px; color: #999999; text-decoration: line-through; margin: 0;">
89
- <strong>${s(t, "original_price")}</strong>
88
+ style="font-size: 14px; color: #999999; margin: 0;">
89
+ <s><strong>${r(t, "original_price")}</strong></s>
90
90
  </p>
91
91
  </td>
92
92
  </tr>
@@ -104,7 +104,7 @@ const I = {
104
104
  align="left">
105
105
  <p contenteditable="false" style="font-size: 12px; color: #666666; margin: 0;">
106
106
  <span class="omnibus-text-before">Lowest 30-day price: </span>
107
- <span class="omnibus-price-value">${s(t, "original_price")}</span>
107
+ <span class="omnibus-price-value">${r(t, "original_price")}</span>
108
108
  <span class="omnibus-text-after"></span>
109
109
  </p>
110
110
  </td>
@@ -115,7 +115,7 @@ const I = {
115
115
  */
116
116
  [g]: (t) => {
117
117
  var i, l;
118
- const e = b(), n = ((i = t.original_price) == null ? void 0 : i[e.code]) ?? Object.values(t.original_price ?? {})[0] ?? 0, a = ((l = t.price) == null ? void 0 : l[e.code]) ?? Object.values(t.price ?? {})[0] ?? 0, r = n > 0 ? Math.round((n - a) / n * 100) : 0, m = r > 0 ? `-${r}%` : "0%";
118
+ const e = b(), n = ((i = t.original_price) == null ? void 0 : i[e.code]) ?? Object.values(t.original_price ?? {})[0] ?? 0, s = ((l = t.price) == null ? void 0 : l[e.code]) ?? Object.values(t.price ?? {})[0] ?? 0, a = n > 0 ? Math.round((n - s) / n * 100) : 0, m = a > 0 ? `-${a}%` : "0%";
119
119
  return `
120
120
  <tr>
121
121
  <td
@@ -1,60 +1,66 @@
1
- import { ATTR_CUSTOM_PREFIX as u, ATTR_PRODUCT_IMAGE as m, ATTR_PRODUCT_NAME as b, ATTR_PRODUCT_OLD_PRICE as T, ATTR_PRODUCT_PRICE as _, ATTR_PRODUCT_OMNIBUS_PRICE as g, ATTR_PRODUCT_OMNIBUS_DISCOUNT as C, ATTR_PRODUCT_BUTTON as f } from "../constants/selectors.js";
2
- function U(t) {
1
+ import { ATTR_CUSTOM_PREFIX as d, ATTR_PRODUCT_IMAGE as u, ATTR_PRODUCT_NAME as p, ATTR_PRODUCT_OLD_PRICE as m, ATTR_PRODUCT_PRICE as b, ATTR_PRODUCT_OMNIBUS_PRICE as T, ATTR_PRODUCT_OMNIBUS_DISCOUNT as _, ATTR_PRODUCT_BUTTON as g } from "../constants/selectors.js";
2
+ function y(t) {
3
3
  return t.replace(/_/g, " ").replace(/\b\w/g, (e) => e.toUpperCase());
4
4
  }
5
- function R(t, e) {
5
+ function U(t) {
6
+ const e = Array.isArray(t) ? t[t.length - 1] : t;
7
+ if (typeof e == "string")
8
+ return e;
9
+ if (typeof e == "number")
10
+ return String(e);
11
+ }
12
+ function C(t, e) {
6
13
  const n = Object.values(e).find((r) => r.attributeName === t);
7
14
  return (n == null ? void 0 : n.type) === "defaultAttribute";
8
15
  }
9
- function y(t, e) {
10
- return R(t, e) ? t : `product_attribute.${t}`;
16
+ function h(t, e) {
17
+ return C(t, e) ? t : `product_attribute.${t}`;
11
18
  }
12
19
  const P = Symbol("customCellHtml");
13
- function S(t, e, n = {}) {
20
+ function L(t, e, n = {}) {
14
21
  const r = t[P];
15
22
  if (!r)
16
23
  return { ...t };
17
- const l = { ...t };
18
- return e.filter((o) => o.startsWith(u) && !l[o]).forEach((o) => {
19
- const s = o.substring(u.length), a = U(s), D = y(s, n), O = R(s, n);
20
- l[o] = (d) => {
21
- var p;
22
- const i = O ? d[s] : (p = d.product_attributes) == null ? void 0 : p[s];
23
- let c = a;
24
- return typeof i == "string" ? c = i : typeof i == "number" && (c = String(i)), r(D, c);
24
+ const i = { ...t };
25
+ return e.filter((o) => o.startsWith(d) && !i[o]).forEach((o) => {
26
+ const s = o.substring(d.length), l = y(s), A = h(s, n), R = C(s, n);
27
+ i[o] = (c) => {
28
+ var a;
29
+ const D = R ? c[s] : (a = c.product_attributes) == null ? void 0 : a[s], O = U(D) ?? l;
30
+ return r(A, O);
25
31
  };
26
- }), l;
32
+ }), i;
27
33
  }
28
- const L = {
34
+ const M = {
29
35
  TITLE: "You May Also Like!"
30
- }, M = [
36
+ }, $ = [
37
+ u,
38
+ p,
31
39
  m,
32
40
  b,
33
41
  T,
34
42
  _,
35
- g,
36
- C,
37
- f
38
- ], $ = {
39
- [m]: !0,
43
+ g
44
+ ], w = {
45
+ [u]: !0,
46
+ [p]: !0,
40
47
  [b]: !0,
41
- [_]: !0,
42
- [T]: !0,
43
- [g]: !1,
44
- [C]: !1,
45
- [f]: !0
46
- }, h = `
48
+ [m]: !0,
49
+ [T]: !1,
50
+ [_]: !1,
51
+ [g]: !0
52
+ }, I = `
47
53
  <tr>
48
54
  <td class="spacer" style="height: 10px;"></td>
49
55
  </tr>
50
- `, A = "https://email-static.useinsider.com/stripo/modules/email-recommendation-v3/assets/images/image-placeholder.png";
51
- function w(t) {
52
- return !t || typeof t != "string" || t.trim() === "" ? A : t.startsWith("http://") ? t.replace("http://", "https://") : t;
56
+ `, f = "https://email-static.useinsider.com/stripo/modules/email-recommendation-v3/assets/images/image-placeholder.png";
57
+ function N(t) {
58
+ return !t || typeof t != "string" || t.trim() === "" ? f : t.startsWith("http://") ? t.replace("http://", "https://") : t;
53
59
  }
54
- function I(t) {
60
+ function E(t) {
55
61
  return {
56
62
  name: "Product Name",
57
- image_url: A,
63
+ image_url: f,
58
64
  price: { USD: 18 },
59
65
  original_price: { USD: 20 },
60
66
  discount: { USD: 2 },
@@ -66,13 +72,13 @@ function I(t) {
66
72
  category: []
67
73
  };
68
74
  }
69
- function N(t = 6) {
75
+ function k(t = 6) {
70
76
  return Array.from(
71
77
  { length: t },
72
- (e, n) => I(String(n + 1))
78
+ (e, n) => E(String(n + 1))
73
79
  );
74
80
  }
75
- function k(t = "grid", e) {
81
+ function x(t = "grid", e) {
76
82
  const n = t === "list" ? `
77
83
  data-layout="list"` : "", r = e ? ` ${e}` : "";
78
84
  return `
@@ -110,7 +116,7 @@ function k(t = "grid", e) {
110
116
  </table>
111
117
  </td>
112
118
  </tr>
113
- ${h}
119
+ ${I}
114
120
  <tr>
115
121
  <td>
116
122
  <table
@@ -150,16 +156,17 @@ function k(t = "grid", e) {
150
156
  }
151
157
  export {
152
158
  P as CUSTOM_CELL_HTML,
153
- L as DEFAULTS,
154
- M as DEFAULT_CARD_COMPOSITION,
155
- $ as DEFAULT_CARD_VISIBILITY,
156
- A as PLACEHOLDER_IMAGE,
157
- S as buildElementRenderer,
158
- k as createBlockTemplate,
159
- N as getDefaultProducts,
160
- R as isDefaultAttribute,
161
- y as resolveProductAttrValue,
162
- w as sanitizeImageUrl,
163
- h as spacer,
164
- U as toDisplayName
159
+ M as DEFAULTS,
160
+ $ as DEFAULT_CARD_COMPOSITION,
161
+ w as DEFAULT_CARD_VISIBILITY,
162
+ f as PLACEHOLDER_IMAGE,
163
+ L as buildElementRenderer,
164
+ x as createBlockTemplate,
165
+ k as getDefaultProducts,
166
+ C as isDefaultAttribute,
167
+ h as resolveProductAttrValue,
168
+ N as sanitizeImageUrl,
169
+ I as spacer,
170
+ y as toDisplayName,
171
+ U as toDisplayableAttributeValue
165
172
  };
@@ -0,0 +1,16 @@
1
+ import { useToaster as r } from "../../../composables/useToaster.js";
2
+ import { useTranslations as s } from "../../../composables/useTranslations.js";
3
+ import { ToasterTypeOptions as n } from "../../../enums/toaster.js";
4
+ import { countLiveRecommendationBlocks as i } from "./utils/recommendationBlockCount.js";
5
+ const a = "newsletter.multiple-reco-blocks-warning", f = () => {
6
+ const { showToaster: o } = r(), t = s();
7
+ return { warnIfMultipleBlocks: (e) => {
8
+ i(e) > 1 && o({
9
+ type: n.Dark,
10
+ message: t(a)
11
+ });
12
+ } };
13
+ };
14
+ export {
15
+ f as useRecommendationBlockWarning
16
+ };
@@ -0,0 +1,21 @@
1
+ const i = [
2
+ "ozonebg",
3
+ "ozonehr",
4
+ "ozonero",
5
+ "ozoneinfo",
6
+ "babybg",
7
+ "ozongr",
8
+ "iboodat",
9
+ "iboodbe",
10
+ "iboodde",
11
+ "iboodfr",
12
+ "iboodnl",
13
+ "iboodpl"
14
+ ], r = ["interencheres", "interencherespreprod"], d = ["lodenfrey", "lodenfreyuat"], c = "manualMerchandising";
15
+ function s(e, n) {
16
+ const o = {};
17
+ return i.includes(e) && (o.excludePurchaseDay = "30", o.userId = "{user_id}"), r.includes(e) && (o.hp = "1"), d.includes(e) && n === c && (o.includeOutOfStockItems = "true"), o;
18
+ }
19
+ export {
20
+ s as getPartnerRecommendationParams
21
+ };
@@ -1,29 +1,29 @@
1
- function d(a, t) {
2
- const { thousandSeparator: s, decimalSeparator: e, decimalCount: r } = t, u = Math.abs(a).toFixed(r), [c, o] = u.split("."), n = c.replace(
1
+ function d(c, t) {
2
+ const { thousandSeparator: i, decimalSeparator: e, decimalCount: r } = t, [u, o = ""] = String(c).split("."), n = o.slice(0, r).padEnd(r, "0"), a = u.replace(
3
3
  /\B(?=(\d{3})+(?!\d))/g,
4
- s
5
- ), i = a < 0 ? "-" : "";
6
- return r > 0 && o ? `${i}${n}${e}${o}` : `${i}${n}`;
4
+ i
5
+ );
6
+ return r > 0 && n ? `${a}${e}${n}` : a;
7
7
  }
8
- function m(a) {
9
- const { price: t, currency: s } = a, {
8
+ function f(c) {
9
+ const { price: t, currency: i } = c, {
10
10
  symbol: e,
11
11
  alignment: r,
12
12
  thousandSeparator: u,
13
- decimalSeparator: c,
14
- decimalCount: o
15
- } = s;
13
+ decimalSeparator: o,
14
+ decimalCount: n
15
+ } = i;
16
16
  if (typeof t != "number" || Number.isNaN(t)) {
17
- const i = "0".repeat(o), $ = `0${c}${i}`;
18
- return r === "before" ? `${e} ${$}` : `${$} ${e}`;
17
+ const s = "0".repeat(n), m = `0${o}${s}`;
18
+ return r === "before" ? `${e} ${m}` : `${m} ${e}`;
19
19
  }
20
- const n = d(t, {
20
+ const a = d(t, {
21
21
  thousandSeparator: u,
22
- decimalSeparator: c,
23
- decimalCount: o
22
+ decimalSeparator: o,
23
+ decimalCount: n
24
24
  });
25
- return r === "before" ? `${e} ${n}` : `${n} ${e}`;
25
+ return r === "before" ? `${e} ${a}` : `${a} ${e}`;
26
26
  }
27
27
  export {
28
- m as formatPrice
28
+ f as formatPrice
29
29
  };
@@ -0,0 +1,9 @@
1
+ const r = ".recommendation-block-v2", t = ".recommendation-product-row";
2
+ function n(o) {
3
+ return Array.from(o.querySelectorAll(r)).filter((e) => e.querySelector(t)).length;
4
+ }
5
+ export {
6
+ r as RECOMMENDATION_BLOCK_SELECTOR,
7
+ t as RECOMMENDATION_PRODUCT_ROW_SELECTOR,
8
+ n as countLiveRecommendationBlocks
9
+ };
package/dist/guido.css CHANGED
@@ -1 +1 @@
1
- .gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-cd76c125] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-195ab6d4]{position:relative;display:inline-block}.new-tag[data-v-195ab6d4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-195ab6d4] .guido__view-option-selection-desktop svg,[data-v-195ab6d4] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-195ab6d4] .in-segments-wrapper__button_selected,[data-v-195ab6d4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-195ab6d4] .in-tooltip-wrapper__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-64c52560]{gap:8px}.version-history__toolbar[data-v-64c52560]{gap:4px}.view-options-wrapper[data-v-d405ca59]{position:relative;display:inline-block}.new-tag[data-v-d405ca59]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-d405ca59] .guido__verion-history-view-option-selection-desktop svg,[data-v-d405ca59] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-d405ca59] .in-segments-wrapper__button_selected,[data-v-d405ca59] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-d405ca59] .in-tooltip-wrapper__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-4e2a4adb]{gap:4px}.header-wrapper[data-v-5c02dcc7]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-b293a2b0]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-b293a2b0]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-b293a2b0]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-df672485]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-df672485]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-df672485]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-df672485]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-df672485]{object-fit:cover;transform:scale(1)}[data-v-43c617a7] .guido__verion-history-view-option-selection-desktop svg,[data-v-43c617a7] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-43c617a7] .in-segments-wrapper__button_selected,[data-v-43c617a7] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-988f8da6]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-988f8da6]{min-height:504px}.iframe-wrapper[data-v-e0424e99]{width:258px}.iframe-scaled[data-v-e0424e99]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-7419ae06] .vueperslides__bullets,[data-v-796d193b] .vueperslides__bullets{pointer-events:none!important}[data-v-796d193b] .vueperslides__parallax-wrapper{height:110px!important}[data-v-cadfc82d] .vueperslides__bullets{pointer-events:none!important}[data-v-cadfc82d] .vueperslides__parallax-wrapper{height:110px!important}
1
+ .gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-195ab6d4]{position:relative;display:inline-block}.new-tag[data-v-195ab6d4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-195ab6d4] .guido__view-option-selection-desktop svg,[data-v-195ab6d4] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-195ab6d4] .in-segments-wrapper__button_selected,[data-v-195ab6d4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-195ab6d4] .in-tooltip-wrapper__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-64c52560]{gap:8px}.version-history__toolbar[data-v-64c52560]{gap:4px}.view-options-wrapper[data-v-d405ca59]{position:relative;display:inline-block}.new-tag[data-v-d405ca59]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-d405ca59] .guido__verion-history-view-option-selection-desktop svg,[data-v-d405ca59] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-d405ca59] .in-segments-wrapper__button_selected,[data-v-d405ca59] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-d405ca59] .in-tooltip-wrapper__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-82128f7d]{gap:4px}.header-wrapper[data-v-5c02dcc7]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-50dac6de]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-50dac6de]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-50dac6de]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-df672485]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-df672485]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-df672485]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-df672485]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-df672485]{object-fit:cover;transform:scale(1)}[data-v-43c617a7] .guido__verion-history-view-option-selection-desktop svg,[data-v-43c617a7] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-43c617a7] .in-segments-wrapper__button_selected,[data-v-43c617a7] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-988f8da6]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-988f8da6]{min-height:504px}.iframe-wrapper[data-v-e0424e99]{width:258px}.iframe-scaled[data-v-e0424e99]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-7419ae06] .vueperslides__bullets,[data-v-796d193b] .vueperslides__bullets{pointer-events:none!important}[data-v-796d193b] .vueperslides__parallax-wrapper{height:110px!important}[data-v-cadfc82d] .vueperslides__bullets{pointer-events:none!important}[data-v-cadfc82d] .vueperslides__parallax-wrapper{height:110px!important}
@@ -12,6 +12,7 @@ declare const _default: import("vue").DefineComponent<__VLS_TypePropsToOption<__
12
12
  };
13
13
  hasChanges: import("vue").ComputedRef<boolean>;
14
14
  saveSilent: () => Promise<Omit<SavedTemplateDetails, "metadata" | "silent"> | undefined> | undefined;
15
+ setLoading: (value: boolean) => void;
15
16
  }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {
16
17
  "dynamic-content:open": (detail: {
17
18
  text: string;
@@ -1,4 +1,5 @@
1
1
  import type { CompilationResult } from '@@/Types/html-compiler';
2
2
  export declare const useHtmlCompiler: () => {
3
3
  compileHtml: (html: string) => CompilationResult;
4
+ compileAmpHtml: (amp: string) => CompilationResult;
4
5
  };
@@ -1,5 +1,6 @@
1
1
  export declare enum ToasterTypeOptions {
2
2
  Success = "success",
3
3
  Warning = "warning",
4
- Alert = "alert"
4
+ Alert = "alert",
5
+ Dark = "dark"
5
6
  }
@@ -55,6 +55,19 @@ export declare class RecommendationBlock extends Block {
55
55
  * @param node - The block node being deleted
56
56
  */
57
57
  onDelete(node: ImmutableHtmlNode): void;
58
+ /**
59
+ * Warns (dark advisory toaster) when the design holds more than one live
60
+ * recommendation block. Triggered from the user-add paths only (fresh drop
61
+ * and duplicate) — never from document-load/migration, so opening a template
62
+ * that already has multiple blocks does not fire it. `onCreated` also runs
63
+ * for a reco block that arrives inside a saved structure (SD-143028), so this
64
+ * covers that case too — no separate `module_dropped` handling is needed.
65
+ *
66
+ * Counting lives in `useRecommendationBlockWarning`, whose DOM-based logic
67
+ * ignores the empty shells deletions leave behind (SD-143028). Wrapped in
68
+ * try/catch because the document root may be unavailable during initial load.
69
+ */
70
+ private _warnIfMultipleBlocks;
58
71
  /**
59
72
  * Generates the next unique recommendation ID by scanning all existing blocks
60
73
  * in the document and finding the maximum existing ID + 1.
@@ -17,4 +17,11 @@ export declare const DEFAULT_CELL_PADDING = "0 5px";
17
17
  * Used by controls to reliably select direct children for spacing adjustments
18
18
  */
19
19
  export declare const ATTRIBUTE_CELL_CLASS = "attribute-cell";
20
+ /**
21
+ * Builds an empty filler cell used to pad incomplete rows (productsPerRow >
22
+ * number of products). Without fillers, table-layout:fixed redistributes the
23
+ * width among fewer cells and stretches them. Shared by the template and the
24
+ * card-composition control so both render identical filler markup.
25
+ */
26
+ export declare function buildFillerCell(columnWidth: string, padding?: string): string;
20
27
  export declare const gridElementRenderer: ElementRenderer;
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import type { RecommendationProduct } from '@@/Types/recommendation';
8
8
  import { type Orientation, type PrepareProductRowsOptions } from './utils';
9
- export { DEFAULTS, DEFAULT_CARD_COMPOSITION, DEFAULT_CARD_VISIBILITY, getDefaultProducts, spacer, sanitizeImageUrl, createBlockTemplate, type Orientation, type PrepareProductRowsOptions, type ElementRenderer, type ProductCardGetter, } from './utils';
9
+ export { DEFAULTS, DEFAULT_CARD_COMPOSITION, DEFAULT_CARD_VISIBILITY, getDefaultProducts, spacer, sanitizeImageUrl, createBlockTemplate, toDisplayableAttributeValue, type Orientation, type PrepareProductRowsOptions, type ElementRenderer, type ProductCardGetter, } from './utils';
10
10
  /**
11
11
  * Unified function to prepare product rows for any layout.
12
12
  * Delegates to the appropriate layout-specific implementation.
@@ -5,6 +5,14 @@ import { ATTR_PRODUCT_IMAGE, ATTR_PRODUCT_NAME, ATTR_PRODUCT_PRICE, ATTR_PRODUCT
5
5
  * e.g., "rating_star" → "Rating Star"
6
6
  */
7
7
  export declare function toDisplayName(attrName: string): string;
8
+ /**
9
+ * Reduces a raw product-attribute value to a displayable scalar string.
10
+ * Array-type attributes (filter types `Strings`/`Numbers`) are substituted with
11
+ * their last item by the email renderer at send time, so we mirror that here to
12
+ * keep the editor preview consistent with what is actually sent.
13
+ * Returns undefined when there is no displayable string/number.
14
+ */
15
+ export declare function toDisplayableAttributeValue(value: unknown): string | undefined;
8
16
  /**
9
17
  * Checks whether an attribute is a default (top-level) product attribute
10
18
  * vs a custom product attribute (nested under `product_attributes`).
@@ -0,0 +1,12 @@
1
+ import type { QueryableRoot } from './utils/recommendationBlockCount';
2
+ /**
3
+ * Advisory shown when a design ends up with more than one recommendation block,
4
+ * since multiple blocks can slow campaign delivery. Driven from the block
5
+ * lifecycle (`onCreated` for direct drop / duplicate, counting the editor's
6
+ * document root). `onCreated` also fires for a reco block that arrives inside a
7
+ * saved structure (SD-143028), so that case is covered without a separate
8
+ * `module_dropped` re-check.
9
+ */
10
+ export declare const useRecommendationBlockWarning: () => {
11
+ warnIfMultipleBlocks: (root: QueryableRoot) => void;
12
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Returns the extra recommendation feed query params for an account, if any.
3
+ * Accounts without customizations get an empty object.
4
+ * @param partnerName Account subdomain (`config.partner.name`)
5
+ * @param strategy Recommendation strategy key of the block
6
+ */
7
+ export declare function getPartnerRecommendationParams(partnerName: string, strategy: string): Record<string, string>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Counts *live* recommendation blocks in a document.
3
+ *
4
+ * Deleting a recommendation block strips its inner content but leaves an empty
5
+ * `.recommendation-block-v2` container shell behind — present in every document
6
+ * representation the editor exposes (`getDocumentRoot`, `getDocumentRootHtmlNode`
7
+ * and even `getTemplateData`). A raw class count therefore over-reports after a
8
+ * delete→add cycle (SD-143028). The product row (`.recommendation-product-row`)
9
+ * is present on every live block from drop time (the default template ships
10
+ * product rows, both grid and list layouts) and is removed from a deleted
11
+ * block's leftover shell, so filtering by it cleanly separates live blocks from
12
+ * ghosts without reading any store.
13
+ */
14
+ export declare const RECOMMENDATION_BLOCK_SELECTOR = ".recommendation-block-v2";
15
+ export declare const RECOMMENDATION_PRODUCT_ROW_SELECTOR = ".recommendation-product-row";
16
+ interface QueryableNode {
17
+ querySelector(selector: string): unknown;
18
+ }
19
+ export interface QueryableRoot {
20
+ querySelectorAll(selector: string): Iterable<QueryableNode>;
21
+ }
22
+ /**
23
+ * Counts the live recommendation blocks reachable from `root`. Works on the
24
+ * editor's `ImmutableHtmlNode` document root (the `onCreated` drop / duplicate
25
+ * paths) and on any DOM-like root exposing `querySelectorAll`.
26
+ */
27
+ export declare function countLiveRecommendationBlocks(root: QueryableRoot): number;
28
+ export {};
@@ -0,0 +1,2 @@
1
+ export declare const AMP_ERROR_WHITELISTED_TOKENS: string[];
2
+ export declare const filterAmpErrors: (ampErrors: string[]) => string[];
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,20 @@
1
1
  import type { SavedTemplateDetails } from '@@/Types/stripo';
2
+ /**
3
+ * Collects every unsubscribe page id referenced by the unsubscribe blocks
4
+ * present in the email HTML, deduped.
5
+ *
6
+ * The email's unsubscribe link is variation-scoped on the backend, which
7
+ * resolves the page to render from the page ids the FE persists for that
8
+ * variation. Those ids must reflect the blocks actually in the email — not a
9
+ * UI-only selection store — otherwise a block that entered the editor by any
10
+ * path other than the page-selection UI (e.g. a dropped saved module) sends an
11
+ * empty association and the backend falls back to the default page.
12
+ *
13
+ * Read from raw editor HTML: the `data-unsubscribe-page-list` attribute lives
14
+ * on the block node there (the save-time sync-module extractor relies on the
15
+ * same source), so it is reliably present regardless of how the block was added.
16
+ */
17
+ export declare function getHtmlReferencedUnsubscribePages(html: string): number[];
2
18
  export declare const useTemplatePreparation: () => {
3
19
  prepareTemplateDetails: () => Promise<Omit<SavedTemplateDetails, "metadata">>;
4
20
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import { MERGE_TAGS as t } from "../enums/unsubscribe.js";
2
+ const E = Object.values(t), c = (r) => r.filter(
3
+ (e) => !E.some((o) => e.includes(o))
4
+ );
5
+ export {
6
+ E as AMP_ERROR_WHITELISTED_TOKENS,
7
+ c as filterAmpErrors
8
+ };