@useinsider/guido 3.8.1 → 3.8.2-beta.61c2971

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 (66) hide show
  1. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue.js +8 -8
  2. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue2.js +15 -12
  3. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue.js +5 -5
  4. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue2.js +16 -15
  5. package/dist/components/organisms/unsubscribe/UnsubscribePageSelection.vue.js +5 -5
  6. package/dist/components/organisms/unsubscribe/UnsubscribePageSelection.vue2.js +42 -37
  7. package/dist/components/organisms/unsubscribe/UnsubscribeTypeSelection.vue.js +3 -3
  8. package/dist/components/organisms/unsubscribe/UnsubscribeTypeSelection.vue2.js +40 -37
  9. package/dist/composables/useHtmlCompiler.js +13 -10
  10. package/dist/composables/useHtmlValidator.js +180 -133
  11. package/dist/composables/usePreviewInteractionGuard.js +17 -0
  12. package/dist/composables/useSave.js +14 -14
  13. package/dist/composables/validators/useUnsubscribeBlockValidator.js +26 -17
  14. package/dist/config/compiler/htmlCompilerRules.js +38 -10
  15. package/dist/config/compiler/recommendationCompilerRules.js +83 -108
  16. package/dist/config/compiler/utils/recommendationCompilerUtils.js +58 -57
  17. package/dist/config/compiler/utils/recommendationIgnoreUtils.js +15 -0
  18. package/dist/config/migrator/recommendation/extractors.js +44 -22
  19. package/dist/config/migrator/recommendation/htmlBuilder.js +175 -169
  20. package/dist/config/migrator/recommendationMigrator.js +30 -31
  21. package/dist/extensions/Blocks/Recommendation/block.js +4 -1
  22. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +26 -20
  23. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +176 -145
  24. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +58 -57
  25. package/dist/extensions/Blocks/Recommendation/controls/main/layoutOrientation.js +43 -31
  26. package/dist/extensions/Blocks/Recommendation/controls/main/productCount.js +3 -2
  27. package/dist/extensions/Blocks/Recommendation/controls/main/productLayout.js +55 -45
  28. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +60 -53
  29. package/dist/extensions/Blocks/Recommendation/controls/syncInfoMessage.js +7 -6
  30. package/dist/extensions/Blocks/Recommendation/extension.js +3 -2
  31. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +3 -2
  32. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +3 -2
  33. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +13 -12
  34. package/dist/extensions/Blocks/Recommendation/templates/index.js +7 -6
  35. package/dist/extensions/Blocks/Recommendation/templates/utils.js +1 -1
  36. package/dist/extensions/Blocks/Unsubscribe/block.js +40 -37
  37. package/dist/extensions/Blocks/Unsubscribe/control.js +19 -16
  38. package/dist/extensions/Blocks/controlFactories.js +76 -57
  39. package/dist/guido.css +1 -1
  40. package/dist/src/composables/useHtmlValidator.d.ts +27 -0
  41. package/dist/src/composables/useHtmlValidator.test.d.ts +1 -0
  42. package/dist/src/composables/usePreviewInteractionGuard.d.ts +3 -0
  43. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +1 -0
  44. package/dist/src/config/compiler/utils/recommendationIgnoreUtils.d.ts +17 -0
  45. package/dist/src/config/compiler/utils/recommendationIgnoreUtils.test.d.ts +1 -0
  46. package/dist/src/config/migrator/recommendation/extractors.d.ts +15 -0
  47. package/dist/src/config/migrator/recommendation/htmlBuilder.d.ts +8 -0
  48. package/dist/src/extensions/Blocks/CouponBlock/controls/index.d.ts +1 -1
  49. package/dist/src/extensions/Blocks/Items/controls/index.d.ts +3 -3
  50. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  51. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +21 -0
  52. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +7 -0
  53. package/dist/src/extensions/Blocks/Recommendation/controls/main/layoutOrientation.d.ts +5 -0
  54. package/dist/src/extensions/Blocks/Recommendation/controls/main/productLayout.d.ts +6 -0
  55. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +9 -0
  56. package/dist/src/extensions/Blocks/Recommendation/controls/name/index.d.ts +1 -1
  57. package/dist/src/extensions/Blocks/Recommendation/controls/oldPrice/index.d.ts +1 -1
  58. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusDiscount/index.d.ts +1 -1
  59. package/dist/src/extensions/Blocks/Recommendation/controls/omnibusPrice/index.d.ts +1 -1
  60. package/dist/src/extensions/Blocks/Recommendation/controls/price/index.d.ts +1 -1
  61. package/dist/src/extensions/Blocks/controlFactories.d.ts +10 -1
  62. package/dist/src/stores/unsubscribe.d.ts +11 -1
  63. package/dist/src/utils/ampErrorFilter.d.ts +1 -0
  64. package/dist/stores/unsubscribe.js +8 -7
  65. package/dist/utils/ampErrorFilter.js +6 -5
  66. package/package.json +1 -1
@@ -1,19 +1,20 @@
1
1
  var S = Object.defineProperty;
2
- var L = (c, o, e) => o in c ? S(c, o, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[o] = e;
3
- var a = (c, o, e) => L(c, typeof o != "symbol" ? o + "" : o, e);
4
- import { useToaster as f } from "../../../composables/useToaster.js";
2
+ var f = (c, o, e) => o in c ? S(c, o, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[o] = e;
3
+ var a = (c, o, e) => f(c, typeof o != "symbol" ? o + "" : o, e);
4
+ import { useToaster as L } from "../../../composables/useToaster.js";
5
5
  import { ToasterTypeOptions as A } from "../../../enums/toaster.js";
6
6
  import { PAGE_TYPES as h } from "../../../enums/unsubscribe.js";
7
- import { useUnsubscribeStore as u } from "../../../stores/unsubscribe.js";
8
- import { Block as y, BlockCompositionType as T, ContextActionType as k, ModificationDescription as d, BlockType as I } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
9
- import { getDefaultTemplate as v } from "./template.js";
7
+ import { useEditorStore as y } from "../../../stores/editor.js";
8
+ import { useUnsubscribeStore as l } from "../../../stores/unsubscribe.js";
9
+ import { Block as T, BlockCompositionType as I, ContextActionType as B, ModificationDescription as d, BlockType as v } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
10
+ import { getDefaultTemplate as C } from "./template.js";
10
11
  import { UNSUBSCRIBE_BLOCK_SELECTOR as m, DATA_ATTRIBUTES as n, UNSUBSCRIBE_EVENTS as b } from "./utils/constants.js";
11
- import { parsePageList as B } from "./utils/utils.js";
12
- const C = "unsubscribe-block", E = 'a[data-unsubscribe-link="true"]', N = "{{ins-unsubscribe-link}}", U = "https://academy.insiderone.com/docs/adding-unsubscribe-pages-into-emails", R = "Removing the unsubscribe text leaves an empty block, but an active unsubscribe link is required. Undo your last action to restore the text, or delete the unsubscribe block entirely.", D = {
12
+ import { parsePageList as _ } from "./utils/utils.js";
13
+ const N = "unsubscribe-block", E = 'a[data-unsubscribe-link="true"]', U = "{{ins-unsubscribe-link}}", R = "https://academy.insiderone.com/docs/adding-unsubscribe-pages-into-emails", D = "Removing the unsubscribe text leaves an empty block, but an active unsubscribe link is required. Undo your last action to restore the text, or delete the unsubscribe block entirely.", w = {
13
14
  [h.GLOBAL_UNSUBSCRIBE]: "{{ins-global-unsubscribe-link}}",
14
15
  [h.SUBSCRIPTION_PREFERENCE_CENTER]: "{{ins-preferences-unsubscribe-link}}"
15
- }, w = 3;
16
- class H extends y {
16
+ }, P = 3;
17
+ class W extends T {
17
18
  constructor() {
18
19
  super();
19
20
  a(this, "selectEventListener", null);
@@ -29,13 +30,13 @@ class H extends y {
29
30
  a(this, "guardAttemptsByBlockId", /* @__PURE__ */ new Map());
30
31
  }
31
32
  getId() {
32
- return C;
33
+ return N;
33
34
  }
34
35
  getIcon() {
35
36
  return "unsubscribe-icon";
36
37
  }
37
38
  getBlockCompositionType() {
38
- return T.BLOCK;
39
+ return I.BLOCK;
39
40
  }
40
41
  getName() {
41
42
  return this.api.translate("Unsubscribe Block");
@@ -44,15 +45,16 @@ class H extends y {
44
45
  return this.api.translate("Unsubscribe Block Description");
45
46
  }
46
47
  getTemplate() {
47
- return v();
48
+ return C();
48
49
  }
49
50
  getContextActionsIds() {
50
- return [k.MOVE, k.REMOVE];
51
+ return [B.MOVE, B.REMOVE];
51
52
  }
52
53
  onSelect(e) {
53
- this.currentNode = e;
54
+ if (this.currentNode = e, y().isPreviewModeOpen)
55
+ return;
54
56
  const t = this._getOrAssignBlockId(e);
55
- t && this.linkStateByBlockId.set(t, this._hasUnsubscribeLink(e)), this._setupSelectEventListener(), this._setupCancelEventListener(), !("getAttribute" in e && e.getAttribute("data-migration")) && (this._resetStoreState(), this._loadBlockState(e), this._checkExistingBlocks(), this._openDrawer());
57
+ t && this.linkStateByBlockId.set(t, this._hasUnsubscribeLink(e)), this._setupSelectEventListener(), this._setupCancelEventListener(t), !("getAttribute" in e && e.getAttribute("data-migration")) && (this._resetStoreState(), t && (l().pendingBlockId = t), this._loadBlockState(e), this._checkExistingBlocks(), this._openDrawer());
56
58
  }
57
59
  onCreated(e) {
58
60
  this._ensureLeadingTextGuard(e);
@@ -107,9 +109,9 @@ class H extends y {
107
109
  const g = this._getOrAssignBlockId(e);
108
110
  if (!g)
109
111
  return;
110
- const _ = this.guardAttemptsByBlockId.get(g) ?? 0;
111
- if (!(_ >= w)) {
112
- this.guardAttemptsByBlockId.set(g, _ + 1);
112
+ const k = this.guardAttemptsByBlockId.get(g) ?? 0;
113
+ if (!(k >= P)) {
114
+ this.guardAttemptsByBlockId.set(g, k + 1);
113
115
  try {
114
116
  this.api.getDocumentModifier().modifyHtml(r).prepend(" ").apply(new d("Ensure unsubscribe link text guard"));
115
117
  } catch (p) {
@@ -154,8 +156,8 @@ class H extends y {
154
156
  const t = this.api.getDocumentRoot();
155
157
  t && "querySelectorAll" in t && t.querySelectorAll(m).forEach((r) => {
156
158
  if ("getAttribute" in r) {
157
- const i = r.getAttribute(n.BLOCK_ID), l = i ? parseInt(i) : 0;
158
- l > e && (e = l);
159
+ const i = r.getAttribute(n.BLOCK_ID), u = i ? parseInt(i) : 0;
160
+ u > e && (e = u);
159
161
  }
160
162
  });
161
163
  } catch {
@@ -164,14 +166,14 @@ class H extends y {
164
166
  }
165
167
  _warnLinkRemoved() {
166
168
  try {
167
- const { showToaster: e } = f();
169
+ const { showToaster: e } = L();
168
170
  e({
169
171
  type: A.Warning,
170
- message: this.api.translate(R),
172
+ message: this.api.translate(D),
171
173
  actionButton: {
172
174
  text: this.api.translate("Visit Academy"),
173
175
  onClick: () => {
174
- window.open(U, "_blank", "noopener,noreferrer");
176
+ window.open(R, "_blank", "noopener,noreferrer");
175
177
  }
176
178
  }
177
179
  });
@@ -188,9 +190,10 @@ class H extends y {
188
190
  _removeSelectEventListener() {
189
191
  this.selectEventListener && (document.removeEventListener(b.SELECT, this.selectEventListener), this.selectEventListener = null);
190
192
  }
191
- _setupCancelEventListener() {
192
- this._removeCancelEventListener(), this.cancelEventListener = () => {
193
- this._handleCancel();
193
+ _setupCancelEventListener(e) {
194
+ this._removeCancelEventListener(), this.cancelEventListener = (t) => {
195
+ const { detail: s } = t;
196
+ ((s == null ? void 0 : s.blockId) ?? null) === e && this._handleCancel();
194
197
  }, document.addEventListener(b.CANCEL, this.cancelEventListener);
195
198
  }
196
199
  _removeCancelEventListener() {
@@ -200,7 +203,7 @@ class H extends y {
200
203
  try {
201
204
  if (!this.currentNode)
202
205
  return;
203
- this.api.getDocumentModifier().modifyHtml(this.currentNode).replaceWith(`<${I.EMPTY_CONTAINER}/>`).apply(new d("Removed unsubscribe block due to cancel"));
206
+ this.api.getDocumentModifier().modifyHtml(this.currentNode).replaceWith(`<${v.EMPTY_CONTAINER}/>`).apply(new d("Removed unsubscribe block due to cancel"));
204
207
  } catch (e) {
205
208
  console.warn("[UnsubscribeBlock] Failed to remove unsubscribe block:", e);
206
209
  }
@@ -218,19 +221,19 @@ class H extends y {
218
221
  this.api.getDocumentModifier().modifyHtml(s).setAttribute("href", r).apply(new d(`Updated unsubscribe link to ${r}`)), this.api.getDocumentModifier().modifyHtml(this.currentNode).setAttribute(n.PAGE_TYPE, e.toString()).setAttribute(n.PAGE_LIST, t).apply(new d("Updated unsubscribe block metadata"));
219
222
  }
220
223
  _getMergeTag(e) {
221
- return D[e] ?? N;
224
+ return w[e] ?? U;
222
225
  }
223
226
  _openDrawer() {
224
227
  if (!(this.currentNode && this.currentNode.getAttribute("data-unsubscribe-page-type")))
225
228
  try {
226
- const e = u();
229
+ const e = l();
227
230
  e.typeSelectionDrawerStatus = !0;
228
231
  } catch (e) {
229
232
  console.error("[UnsubscribeBlock] Failed to open drawer:", e);
230
233
  }
231
234
  }
232
235
  _checkExistingBlocks() {
233
- const e = u();
236
+ const e = l();
234
237
  e.isGlobalUnsubscribeDisabled = !1, e.isSubscriptionPreferencesCenterDisabled = !1, this.api.getDocumentRoot().querySelectorAll(m).forEach((s) => {
235
238
  if ("getAttribute" in s) {
236
239
  const r = s.getAttribute(n.PAGE_TYPE);
@@ -247,11 +250,11 @@ class H extends y {
247
250
  const t = e.getAttribute(n.PAGE_TYPE), s = e.getAttribute(n.PAGE_LIST);
248
251
  if (!t || !s)
249
252
  return;
250
- const r = u(), i = Number(t), l = B(s);
251
- await r.fetchTemplates(), r.setCollectionWithoutAutoSelection(i), r.loadSelectedTemplates(l);
253
+ const r = l(), i = Number(t), u = _(s);
254
+ await r.fetchTemplates(), r.setCollectionWithoutAutoSelection(i), r.loadSelectedTemplates(u);
252
255
  }
253
256
  _resetStoreState() {
254
- u().$reset();
257
+ l().$reset();
255
258
  }
256
259
  _removeBlockTemplatesFromStore(e) {
257
260
  if (!("getAttribute" in e))
@@ -259,11 +262,11 @@ class H extends y {
259
262
  const t = e.getAttribute(n.PAGE_LIST);
260
263
  if (!t)
261
264
  return;
262
- const s = u(), r = B(t);
265
+ const s = l(), r = _(t);
263
266
  s.removeUnsubscribePages(r);
264
267
  }
265
268
  }
266
269
  export {
267
- C as UNSUBSCRIBE_BLOCK_ID,
268
- H as UnsubscribeBlock
270
+ N as UNSUBSCRIBE_BLOCK_ID,
271
+ W as UnsubscribeBlock
269
272
  };
@@ -1,18 +1,19 @@
1
1
  var T = Object.defineProperty;
2
- var p = (o, s, e) => s in o ? T(o, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : o[s] = e;
3
- var c = (o, s, e) => p(o, typeof s != "symbol" ? s + "" : s, e);
2
+ var p = (a, s, e) => s in a ? T(a, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[s] = e;
3
+ var c = (a, s, e) => p(a, typeof s != "symbol" ? s + "" : s, e);
4
+ import { useEditorStore as _ } from "../../../stores/editor.js";
4
5
  import { useUnsubscribeStore as l } from "../../../stores/unsubscribe.js";
5
- import { Control as _, UEAttr as i, UIElementType as n } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
+ import { Control as g, UEAttr as i, UIElementType as n } from "../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
6
7
  import { DATA_ATTRIBUTES as d } from "./utils/constants.js";
7
8
  import { parsePageList as h } from "./utils/utils.js";
8
- const g = "ui-elements-unsubscribe", r = {
9
+ const v = "ui-elements-unsubscribe", r = {
9
10
  SELECT_BUTTON: "selectTemplateButton",
10
11
  PREV_BUTTON: "prevButton",
11
12
  NEXT_BUTTON: "nextButton",
12
13
  COUNTER_TEXT: "counterText",
13
14
  PREVIEW_IMAGE: "previewImage"
14
15
  };
15
- class B extends _ {
16
+ class w extends g {
16
17
  constructor() {
17
18
  super(...arguments);
18
19
  c(this, "currentPreviewIndex", 0);
@@ -22,7 +23,7 @@ class B extends _ {
22
23
  c(this, "lastParsedAttribute");
23
24
  }
24
25
  getId() {
25
- return g;
26
+ return v;
26
27
  }
27
28
  getTemplate() {
28
29
  return `
@@ -59,10 +60,12 @@ class B extends _ {
59
60
  this.api.onValueChanged(r.NEXT_BUTTON, () => this._onNextClick())
60
61
  );
61
62
  }
62
- _onButtonClick() {
63
+ async _onButtonClick() {
63
64
  try {
65
+ if (_().isPreviewModeOpen)
66
+ return;
64
67
  const e = l();
65
- this._reseedSelectionFromBlock(), e.activeType = e.getSelectedCollection[this.currentPreviewIndex], e.pageSelectionUpdateStatus = !0, e.pageSelectionDrawerStatus = !0;
68
+ await e.fetchTemplates(!0), this._reseedSelectionFromBlock(), e.activeType = e.getSelectedCollection[this.currentPreviewIndex], e.pageSelectionUpdateStatus = !0, e.pageSelectionDrawerStatus = !0;
66
69
  } catch (e) {
67
70
  console.error("[UnsubscribeControl] Failed to open drawer:", e);
68
71
  }
@@ -71,8 +74,8 @@ class B extends _ {
71
74
  const e = l();
72
75
  if (e.$reset(), !this.currentNode || !("getAttribute" in this.currentNode))
73
76
  return;
74
- const t = this.currentNode.getAttribute(d.PAGE_TYPE), a = this.currentNode.getAttribute(d.PAGE_LIST);
75
- !t || !a || (e.setCollectionWithoutAutoSelection(Number(t)), e.loadSelectedTemplates(h(a)));
77
+ const t = this.currentNode.getAttribute(d.PAGE_TYPE), o = this.currentNode.getAttribute(d.PAGE_LIST);
78
+ !t || !o || (e.setCollectionWithoutAutoSelection(Number(t)), e.loadSelectedTemplates(h(o)));
76
79
  }
77
80
  _onPrevClick() {
78
81
  this.currentPreviewIndex > 0 && (this.currentPreviewIndex--, this._updatePreview(), this._updateCounter());
@@ -100,8 +103,8 @@ class B extends _ {
100
103
  }
101
104
  }
102
105
  _updateCounter() {
103
- const e = this.currentPreviewIndex + 1, t = this.totalTemplates, a = this.currentPreviewIndex === 0, u = this.currentPreviewIndex >= t - 1;
104
- this.api.setUIEAttribute(r.PREV_BUTTON, i.BUTTON.disabled, a), this.api.setUIEAttribute(r.NEXT_BUTTON, i.BUTTON.disabled, u), this.api.setUIEAttribute(
106
+ const e = this.currentPreviewIndex + 1, t = this.totalTemplates, o = this.currentPreviewIndex === 0, u = this.currentPreviewIndex >= t - 1;
107
+ this.api.setUIEAttribute(r.PREV_BUTTON, i.BUTTON.disabled, o), this.api.setUIEAttribute(r.NEXT_BUTTON, i.BUTTON.disabled, u), this.api.setUIEAttribute(
105
108
  r.COUNTER_TEXT,
106
109
  i.LABEL.text,
107
110
  `${this.api.translate("Showing")} ${e} ${this.api.translate("of")} ${t}`
@@ -142,8 +145,8 @@ class B extends _ {
142
145
  </${n.LABEL}>
143
146
  `;
144
147
  }
145
- _getIconButton(e, t, a) {
146
- const u = a ? `${i.BUTTON.disabled}="true"` : "";
148
+ _getIconButton(e, t, o) {
149
+ const u = o ? `${i.BUTTON.disabled}="true"` : "";
147
150
  return `
148
151
  <${n.BUTTON}
149
152
  id="${e}"
@@ -172,6 +175,6 @@ class B extends _ {
172
175
  }
173
176
  }
174
177
  export {
175
- g as UNSUBSCRIBE_CONTROL_ID,
176
- B as UnsubscribeControl
178
+ v as UNSUBSCRIBE_CONTROL_ID,
179
+ w as UnsubscribeControl
177
180
  };
@@ -1,6 +1,6 @@
1
- import { TextAlignBuiltInControl as a, TextColorBuiltInControl as g, TextSizeBuiltInControl as x, TextStyleBuiltInControl as f, TextFontFamilyBuiltInControl as C, ButtonBackgroundColorBuiltInControl as T, TextPaddingsBuiltInControl as B, ImageSizeBuiltInControl as I, ImageMarginsBuiltInControl as y, ButtonAlignBuiltInControl as m, ButtonBorderBuiltInControl as S, ButtonBorderRadiusBuiltInControl as A, ButtonColorBuiltInControl as b, ButtonFontFamilyBuiltInControl as N, ButtonMarginsBuiltInControl as q, ButtonPaddingsBuiltInControl as $, ButtonTextBuiltInControl as k, ButtonTextSizeBuiltInControl as p, ButtonTextStyleAndFontColorBuiltInControl as F, ButtonFitToContainerBuiltInControl as h, TextLineSpacingBuiltInControl as M } from "../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
- const z = "buttonTextStyleForm";
3
- function P(o, n, r) {
1
+ import { TextAlignBuiltInControl as a, TextColorBuiltInControl as g, TextSizeBuiltInControl as f, TextStyleBuiltInControl as x, TextFontFamilyBuiltInControl as C, ButtonBackgroundColorBuiltInControl as T, TextPaddingsBuiltInControl as B, ImageSizeBuiltInControl as m, ImageMarginsBuiltInControl as y, ButtonAlignBuiltInControl as I, ButtonBorderBuiltInControl as S, ButtonBorderRadiusBuiltInControl as A, ButtonColorBuiltInControl as b, ButtonFontFamilyBuiltInControl as N, ButtonMarginsBuiltInControl as q, ButtonPaddingsBuiltInControl as $, ButtonTextBuiltInControl as k, ButtonTextSizeBuiltInControl as p, ButtonTextStyleAndFontColorBuiltInControl as h, ButtonFitToContainerBuiltInControl as F, TextLineSpacingBuiltInControl as M } from "../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
+ const R = "buttonTextStyleForm", _ = "backgroundColor", E = /* @__PURE__ */ new Set(["", "transparent", "rgba(0, 0, 0, 0)"]);
3
+ function z(o, n, r) {
4
4
  return class extends a {
5
5
  getId() {
6
6
  return o;
@@ -13,7 +13,7 @@ function P(o, n, r) {
13
13
  }
14
14
  };
15
15
  }
16
- function _(o, n, r) {
16
+ function v(o, n, r) {
17
17
  return class extends g {
18
18
  getId() {
19
19
  return o;
@@ -26,8 +26,8 @@ function _(o, n, r) {
26
26
  }
27
27
  };
28
28
  }
29
- function v(o, n, r) {
30
- return class extends x {
29
+ function P(o, n, r) {
30
+ return class extends f {
31
31
  getId() {
32
32
  return o;
33
33
  }
@@ -39,8 +39,8 @@ function v(o, n, r) {
39
39
  }
40
40
  };
41
41
  }
42
- function L(o, n, r) {
43
- return class extends f {
42
+ function D(o, n, r) {
43
+ return class extends x {
44
44
  getId() {
45
45
  return o;
46
46
  }
@@ -52,7 +52,7 @@ function L(o, n, r) {
52
52
  }
53
53
  };
54
54
  }
55
- function R(o, n, r) {
55
+ function K(o, n, r) {
56
56
  return class extends C {
57
57
  getId() {
58
58
  return o;
@@ -65,7 +65,7 @@ function R(o, n, r) {
65
65
  }
66
66
  };
67
67
  }
68
- function O(o, n, r) {
68
+ function L(o, n, r) {
69
69
  return class extends T {
70
70
  getId() {
71
71
  return o;
@@ -76,9 +76,28 @@ function O(o, n, r) {
76
76
  const t = e.closest(r);
77
77
  return t ? t.querySelectorAll(`[esd-extension-block-id="${n}"]`) : [];
78
78
  }
79
+ /**
80
+ * Mirrors the picked background onto each target cell as an INLINE style.
81
+ *
82
+ * Stripo's built-in applies the background as a stylesheet rule, not inline. On
83
+ * regeneration we re-bake the captured color inline (cellBgStyleAttr); inline then
84
+ * outranks Stripo's rule in the cascade, so a later re-edit silently has no effect
85
+ * (SD-143023). Writing the inline here keeps it the single source of truth and in
86
+ * sync on every edit. Merged into the parent control's single patch (no extra apply).
87
+ */
88
+ getAdditionalModifications(e) {
89
+ const t = this.getTargetNodes(e);
90
+ if (!t.length)
91
+ return;
92
+ const s = this.api.getValues()[_], i = !s || E.has(s), c = this.api.getDocumentModifier();
93
+ return t.forEach((u) => {
94
+ const l = c.modifyHtml(u);
95
+ i ? l.removeStyle("background-color") : l.setStyle("background-color", s);
96
+ }), c;
97
+ }
79
98
  };
80
99
  }
81
- function Y(o, n, r) {
100
+ function U(o, n, r) {
82
101
  return class extends B {
83
102
  getId() {
84
103
  return o;
@@ -91,7 +110,7 @@ function Y(o, n, r) {
91
110
  }
92
111
  };
93
112
  }
94
- function w(o, n, r) {
113
+ function Y(o, n, r) {
95
114
  return class extends M {
96
115
  getId() {
97
116
  return o;
@@ -101,8 +120,8 @@ function w(o, n, r) {
101
120
  }
102
121
  };
103
122
  }
104
- function D(o, n, r = ".ins-recommendation-product-container") {
105
- return class extends m {
123
+ function w(o, n, r = ".ins-recommendation-product-container") {
124
+ return class extends I {
106
125
  getId() {
107
126
  return o;
108
127
  }
@@ -112,7 +131,7 @@ function D(o, n, r = ".ins-recommendation-product-container") {
112
131
  }
113
132
  };
114
133
  }
115
- function H(o, n, r = ".ins-recommendation-product-container") {
134
+ function G(o, n, r = ".ins-recommendation-product-container") {
116
135
  return class extends b {
117
136
  getId() {
118
137
  return o;
@@ -123,7 +142,7 @@ function H(o, n, r = ".ins-recommendation-product-container") {
123
142
  }
124
143
  };
125
144
  }
126
- function K(o, n, r = ".ins-recommendation-product-container") {
145
+ function H(o, n, r = ".ins-recommendation-product-container") {
127
146
  return class extends S {
128
147
  getId() {
129
148
  return o;
@@ -134,7 +153,7 @@ function K(o, n, r = ".ins-recommendation-product-container") {
134
153
  }
135
154
  };
136
155
  }
137
- function U(o, n, r = ".ins-recommendation-product-container") {
156
+ function V(o, n, r = ".ins-recommendation-product-container") {
138
157
  return class extends A {
139
158
  getId() {
140
159
  return o;
@@ -145,7 +164,7 @@ function U(o, n, r = ".ins-recommendation-product-container") {
145
164
  }
146
165
  };
147
166
  }
148
- function V(o, n, r = ".ins-recommendation-product-container") {
167
+ function W(o, n, r = ".ins-recommendation-product-container") {
149
168
  return class extends N {
150
169
  getId() {
151
170
  return o;
@@ -156,7 +175,7 @@ function V(o, n, r = ".ins-recommendation-product-container") {
156
175
  }
157
176
  };
158
177
  }
159
- function W(o, n, r = ".ins-recommendation-product-container") {
178
+ function X(o, n, r = ".ins-recommendation-product-container") {
160
179
  return class extends q {
161
180
  getId() {
162
181
  return o;
@@ -167,7 +186,7 @@ function W(o, n, r = ".ins-recommendation-product-container") {
167
186
  }
168
187
  };
169
188
  }
170
- function X(o, n, r = ".ins-recommendation-product-container") {
189
+ function j(o, n, r = ".ins-recommendation-product-container") {
171
190
  return class extends $ {
172
191
  getId() {
173
192
  return o;
@@ -178,7 +197,7 @@ function X(o, n, r = ".ins-recommendation-product-container") {
178
197
  }
179
198
  };
180
199
  }
181
- function j(o, n, r = ".ins-recommendation-product-container") {
200
+ function J(o, n, r = ".ins-recommendation-product-container") {
182
201
  return class extends k {
183
202
  getId() {
184
203
  return o;
@@ -189,7 +208,7 @@ function j(o, n, r = ".ins-recommendation-product-container") {
189
208
  }
190
209
  };
191
210
  }
192
- function G(o, n, r = ".ins-recommendation-product-container") {
211
+ function Q(o, n, r = ".ins-recommendation-product-container") {
193
212
  return class extends p {
194
213
  getId() {
195
214
  return o;
@@ -200,8 +219,8 @@ function G(o, n, r = ".ins-recommendation-product-container") {
200
219
  }
201
220
  };
202
221
  }
203
- function J(o, n, r) {
204
- return class extends F {
222
+ function Z(o, n, r) {
223
+ return class extends h {
205
224
  getId() {
206
225
  return o;
207
226
  }
@@ -223,21 +242,21 @@ function J(o, n, r) {
223
242
  const t = e.closest(r);
224
243
  if (!t)
225
244
  return;
226
- const i = t.querySelectorAll(`[esd-extension-block-id="${n}"] a.es-button`);
227
- if (!i.length)
245
+ const s = t.querySelectorAll(`[esd-extension-block-id="${n}"] a.es-button`);
246
+ if (!s.length)
228
247
  return;
229
- const s = this.api.getValues()[z];
230
- if (!s)
248
+ const i = this.api.getValues()[R];
249
+ if (!i)
231
250
  return;
232
- const u = s.bold ? "bold" : "normal", c = s.italic ? "italic" : "normal", l = this.api.getDocumentModifier();
233
- return i.forEach((d) => {
234
- l.modifyHtml(d).setStyle("font-weight", u).setStyle("font-style", c);
251
+ const c = i.bold ? "bold" : "normal", u = i.italic ? "italic" : "normal", l = this.api.getDocumentModifier();
252
+ return s.forEach((d) => {
253
+ l.modifyHtml(d).setStyle("font-weight", c).setStyle("font-style", u);
235
254
  }), l;
236
255
  }
237
256
  };
238
257
  }
239
- function Q(o, n, r) {
240
- return class extends h {
258
+ function tt(o, n, r) {
259
+ return class extends F {
241
260
  getId() {
242
261
  return o;
243
262
  }
@@ -247,8 +266,8 @@ function Q(o, n, r) {
247
266
  }
248
267
  };
249
268
  }
250
- function Z(o, n, r) {
251
- return class extends I {
269
+ function et(o, n, r) {
270
+ return class extends m {
252
271
  getId() {
253
272
  return o;
254
273
  }
@@ -262,7 +281,7 @@ function Z(o, n, r) {
262
281
  }
263
282
  };
264
283
  }
265
- function tt(o, n, r) {
284
+ function nt(o, n, r) {
266
285
  return class extends y {
267
286
  getId() {
268
287
  return o;
@@ -278,25 +297,25 @@ function tt(o, n, r) {
278
297
  };
279
298
  }
280
299
  export {
281
- D as createButtonAlignControl,
282
- K as createButtonBorderControl,
283
- U as createButtonBorderRadiusControl,
284
- H as createButtonColorControl,
285
- Q as createButtonFitToContainerControl,
286
- V as createButtonFontFamilyControl,
287
- W as createButtonMarginsControl,
288
- X as createButtonPaddingsControl,
289
- j as createButtonTextControl,
290
- G as createButtonTextSizeControl,
291
- J as createButtonTextStyleAndFontColorControl,
292
- tt as createImageMarginsControl,
293
- Z as createImageSizeControl,
294
- Y as createPaddingsControl,
295
- P as createTextAlignControl,
296
- O as createTextBackgroundColorControl,
297
- _ as createTextColorControl,
298
- R as createTextFontFamilyControl,
299
- w as createTextLineSpacingControl,
300
- v as createTextSizeControl,
301
- L as createTextStyleControl
300
+ w as createButtonAlignControl,
301
+ H as createButtonBorderControl,
302
+ V as createButtonBorderRadiusControl,
303
+ G as createButtonColorControl,
304
+ tt as createButtonFitToContainerControl,
305
+ W as createButtonFontFamilyControl,
306
+ X as createButtonMarginsControl,
307
+ j as createButtonPaddingsControl,
308
+ J as createButtonTextControl,
309
+ Q as createButtonTextSizeControl,
310
+ Z as createButtonTextStyleAndFontColorControl,
311
+ nt as createImageMarginsControl,
312
+ et as createImageSizeControl,
313
+ U as createPaddingsControl,
314
+ z as createTextAlignControl,
315
+ L as createTextBackgroundColorControl,
316
+ v as createTextColorControl,
317
+ K as createTextFontFamilyControl,
318
+ Y as createTextLineSpacingControl,
319
+ P as createTextSizeControl,
320
+ D as createTextStyleControl
302
321
  };
package/dist/guido.css CHANGED
@@ -1 +1 @@
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-f6a8cb4c]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-f6a8cb4c]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-f6a8cb4c]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-f6a8cb4c]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-f6a8cb4c]{object-fit:cover;transform:scale(1)}[data-v-18e1220a] .guido__verion-history-view-option-selection-desktop svg,[data-v-18e1220a] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-18e1220a] .in-segments-wrapper__button_selected,[data-v-18e1220a] .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-669fe7cf]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-669fe7cf]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-669fe7cf]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-669fe7cf]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-669fe7cf]{object-fit:cover;transform:scale(1)}[data-v-7a305bb2] .guido__verion-history-view-option-selection-desktop svg,[data-v-7a305bb2] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-7a305bb2] .in-segments-wrapper__button_selected,[data-v-7a305bb2] .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-06afaecb]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-06afaecb]{min-height:504px}.iframe-wrapper[data-v-cbafc185]{width:258px}.iframe-scaled[data-v-cbafc185]{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,4 +1,31 @@
1
1
  import type { DynamicContent } from '@@/Types/generic';
2
+ import type { FiltersResponse } from '@@/Types/recommendation';
3
+ /**
4
+ * Parses a `{{recoId_index_attributeName}}` variable (already stripped of the
5
+ * braces/whitespace) into its recommendation id and attribute name. Returns
6
+ * null for anything that isn't a well-formed recommendation variable.
7
+ */
8
+ export declare function parseRecommendationVariable(field: string): {
9
+ recoId: string;
10
+ attribute: string;
11
+ } | null;
12
+ /**
13
+ * Builds the set of attribute forms valid inside partner-authored
14
+ * `{{recoId_index_attribute}}` variables: the built-in compiler attributes plus
15
+ * each account attribute in its canonical token form — bare for
16
+ * `defaultAttribute`, `product_attribute.<name>` for `productAttribute`. Using
17
+ * `resolveProductAttrValue` (the same producer the block builder uses) keeps the
18
+ * validator's notion of "valid form" identical to what is actually emitted.
19
+ */
20
+ export declare function buildPartnerAttributeNameSet(filterList: FiltersResponse): Set<string>;
21
+ /**
22
+ * Returns the `{{...}}` custom fields in the HTML that are invalid. Partner
23
+ * attributes in `ins-skip-compile` blocks (recoId ∈ ignoredIds) are checked
24
+ * against `ignoredAttributeNames`; Guido-generated reco variables and known merge
25
+ * tags pass. An unrecognized dotted token is ignored: it was never captured
26
+ * before, so flagging it would false-positive on merge tags like `{{user.name}}`.
27
+ */
28
+ export declare function findInvalidCustomFields(html: string, validCustomFields: string[], recommendationBlockIds: string[], ignoredIds: Set<string>, ignoredAttributeNames: Set<string> | null): string[];
2
29
  export declare const useHtmlValidator: () => {
3
30
  validateHtml: (html: string, customFields: DynamicContent[], isOnSaveValidation?: boolean) => Promise<boolean>;
4
31
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare const usePreviewInteractionGuard: () => {
2
+ blockPreviewInteractions: (iframe: HTMLIFrameElement) => void;
3
+ };
@@ -1,3 +1,4 @@
1
1
  export declare const useUnsubscribeBlockValidator: () => {
2
2
  validateUnsubscribeBlockUniqueness: (html: string) => boolean;
3
+ validateUnsubscribeBlockHasTemplate: (html: string) => boolean;
3
4
  };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Returns true when a recommendation block has opted out of Guido's
3
+ * product-data → template-variable substitution. Partners signal opt-out by
4
+ * adding the `ins-skip-compile` class on the `.recommendation-block-v2`
5
+ * element. Only the variable substitution is skipped — campaign URL building,
6
+ * hidden-row removal, Outlook image widths, discount conditions, text-trim CSS
7
+ * and general HTML optimizations still apply. See `CSS_CLASS_SKIP_COMPILE` for
8
+ * the full scope.
9
+ */
10
+ export declare function isIgnoredRecommendationBlock(block: Element): boolean;
11
+ /**
12
+ * DOM-parses the HTML once and returns the set of `recommendation-id`
13
+ * values that belong to ignored blocks. Use this in regex/string-based
14
+ * compiler rules where the rule processes flat matches and only has the
15
+ * extracted block ID rather than a DOM Element reference.
16
+ */
17
+ export declare function getIgnoredRecommendationBlockIds(html: string): Set<string>;