@useinsider/guido 3.13.0 → 3.13.1-beta.a1e3c5f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -134,7 +134,7 @@ const config: GuidoConfigInput = {
134
134
  // Optional: Editor settings
135
135
  editor?: {
136
136
  locale?: string, // Default: 'en'
137
- translationsPath?: string, // Default: 'window.trans.en'
137
+ translationsPath?: string, // Default: first locale registered on window.trans
138
138
  migrationDate?: number, // Stripo migration timestamp
139
139
  emailHeader?: {
140
140
  senderName?: string,
@@ -1,4 +1,7 @@
1
1
  import { ProductType as a, MessageType as o } from "./schemas.js";
2
+ const i = {
3
+ translationsPath: "window.trans[Object.keys(window.trans)[0]]"
4
+ };
2
5
  a.EMAIL;
3
6
  o.PROMOTIONAL;
4
7
  const e = [
@@ -33,12 +36,13 @@ const e = [
33
36
  "vikingsankara",
34
37
  "stripoeditorlivetest"
35
38
  ];
36
- function n(t) {
39
+ function s(t) {
37
40
  return e.includes(t);
38
41
  }
39
42
  export {
43
+ i as DEFAULT_EDITOR,
40
44
  o as MessageType,
41
45
  a as ProductType,
42
46
  e as TEST_PARTNERS,
43
- n as isTestPartner
47
+ s as isTestPartner
44
48
  };
@@ -1,5 +1,5 @@
1
- import { ModuleFolderDefaults as y } from "../../enums/defaults.js";
2
- import { object as a, number as n, optional as e, string as t, picklist as r, pipe as p, minLength as b, custom as h, boolean as o, array as c, record as k, fallback as C, literal as i, looseObject as g, variant as R, union as f, unknown as s, lazy as T } from "../../node_modules/valibot/dist/index.js";
1
+ import { ModuleFolderDefaults as f } from "../../enums/defaults.js";
2
+ import { object as a, number as n, optional as e, string as t, picklist as r, pipe as p, minLength as b, custom as h, boolean as o, array as c, record as k, fallback as C, literal as i, looseObject as g, variant as R, union as y, unknown as s, lazy as T } from "../../node_modules/valibot/dist/index.js";
3
3
  const d = {
4
4
  /** Promotional/marketing emails */
5
5
  PROMOTIONAL: 1,
@@ -12,7 +12,7 @@ const d = {
12
12
  ARCHITECT: 49,
13
13
  /** Unsubscribe page builder */
14
14
  UNSUBSCRIBE_PAGES: 97
15
- }, A = a({
15
+ }, I = a({
16
16
  /** Unique identifier for the template being edited */
17
17
  templateId: p(
18
18
  t(),
@@ -25,7 +25,7 @@ const d = {
25
25
  ),
26
26
  /** Optional variation ID for A/B testing */
27
27
  variationId: e(t())
28
- }), I = a({
28
+ }), A = a({
29
29
  /** Fallback font name (e.g., "Georgia") */
30
30
  name: t(),
31
31
  /** Fallback font family (e.g., "serif" or "sans-serif") */
@@ -53,7 +53,7 @@ const d = {
53
53
  /** Display name for the current user */
54
54
  username: e(t(), "Guido User"),
55
55
  /** Fallback font settings from partner settings — used to match backend size calculation */
56
- fallbackFont: e(I)
56
+ fallbackFont: e(A)
57
57
  }), v = a({
58
58
  /** Display text for the dynamic content */
59
59
  text: t(),
@@ -83,7 +83,7 @@ const d = {
83
83
  /** Block ID (matches the dictionary key and the legacy HTML element id) */
84
84
  id: e(n()),
85
85
  /** Decimal places for price display (legacy data may use string or number) */
86
- decimalCount: e(f([t(), n()])),
86
+ decimalCount: e(y([t(), n()])),
87
87
  /** Pinned product IDs (empty array when filter-driven) */
88
88
  productIds: e(c(s())),
89
89
  /** Whether the block requested live products at send time */
@@ -117,7 +117,7 @@ const d = {
117
117
  /** Block type marker used by some legacy variants */
118
118
  blockType: e(t()),
119
119
  /** Size variant marker (legacy data may use string or number) */
120
- size: e(f([t(), n()])),
120
+ size: e(y([t(), n()])),
121
121
  /** Vertical responsiveness flag (legacy size=1 variants) */
122
122
  verticalResponsiveness: e(o()),
123
123
  /** Legacy "Move to next line" price placement toggle (cardPricePlacement.js) */
@@ -175,9 +175,9 @@ const d = {
175
175
  /** Email header settings */
176
176
  emailHeader: e(N, { senderName: "", subject: "" }),
177
177
  /** Folder name for user-saved modules (used by Stripo plugin panel for path construction) */
178
- savedModulesFolderName: e(t(), y.SAVED_MODULES),
178
+ savedModulesFolderName: e(t(), f.SAVED_MODULES),
179
179
  /** Folder name for default/prebuilt modules (used by Stripo plugin panel for path construction) */
180
- defaultModulesFolderName: e(t(), y.DEFAULT_MODULES)
180
+ defaultModulesFolderName: e(t(), f.DEFAULT_MODULES)
181
181
  }), x = a({
182
182
  /** Whether to show the header bar */
183
183
  showHeader: e(o(), !0),
@@ -203,7 +203,9 @@ const d = {
203
203
  /** Enable autosave (3-min interval + tab-hide). User toggles on/off from the header. */
204
204
  autosave: e(o(), !1),
205
205
  /** Enable pre-save domain-allowlist validation (opt-in; Guido self-serves the block toaster) */
206
- allowlistEnabled: e(o(), !1)
206
+ allowlistEnabled: e(o(), !1),
207
+ /** Enable persistent link tracking-id assignment for the click heatmap (opt-in) */
208
+ linkTrackingIds: e(o(), !1)
207
209
  }), U = r([
208
210
  "amp-accordion",
209
211
  "amp-carousel",
@@ -299,7 +301,7 @@ const d = {
299
301
  }), K = a({
300
302
  // Required sections
301
303
  /** Identity configuration (required) */
302
- identity: A,
304
+ identity: I,
303
305
  /** Partner configuration (required) */
304
306
  partner: L,
305
307
  // Optional sections (with defaults)
@@ -331,10 +333,10 @@ export {
331
333
  v as DynamicContentSchema,
332
334
  O as EditorSchema,
333
335
  N as EmailHeaderSchema,
334
- I as FallbackFontSchema,
336
+ A as FallbackFontSchema,
335
337
  F as FeaturesSchema,
336
338
  K as GuidoConfigSchema,
337
- A as IdentitySchema,
339
+ I as IdentitySchema,
338
340
  P as LegacyRecommendationConfigSchema,
339
341
  d as MessageType,
340
342
  L as PartnerSchema,
@@ -12,7 +12,7 @@ var t = function() {
12
12
  n,
13
13
  !1,
14
14
  null,
15
- "5079a1c8"
15
+ "81958b3d"
16
16
  );
17
17
  const l = s.exports;
18
18
  export {
@@ -1,49 +1,50 @@
1
- import { defineComponent as q, defineAsyncComponent as M, ref as U, computed as _, watch as B, onMounted as K, onUnmounted as V } from "vue";
2
- import { useCortexBlueprintBridge as j } from "../composables/useCortexBlueprintBridge.js";
3
- import { provideGuidoActions as J } from "../composables/useGuidoActions.js";
4
- import { useGuidoStateBridge as Q } from "../composables/useGuidoStateBridge.js";
5
- import { usePartner as X } from "../composables/usePartner.js";
6
- import { useStripo as Y } from "../composables/useStripo.js";
7
- import { useTimerClone as Z } from "../composables/useTimerClone.js";
8
- import { migrate as I } from "../config/migrator/index.js";
1
+ import { defineComponent as K, defineAsyncComponent as M, ref as O, computed as U, watch as _, onMounted as V, onUnmounted as j } from "vue";
2
+ import { useCortexBlueprintBridge as J } from "../composables/useCortexBlueprintBridge.js";
3
+ import { provideGuidoActions as Q } from "../composables/useGuidoActions.js";
4
+ import { useGuidoStateBridge as X } from "../composables/useGuidoStateBridge.js";
5
+ import { usePartner as Y } from "../composables/usePartner.js";
6
+ import { useStripo as Z } from "../composables/useStripo.js";
7
+ import { useTimerClone as ee } from "../composables/useTimerClone.js";
8
+ import { migrate as B } from "../config/migrator/index.js";
9
9
  import { ModuleFolderDefaults as N } from "../enums/defaults.js";
10
- import { RIBBON_SELECTOR as ee } from "../enums/onboarding.js";
11
- import { useRecommendationExtensionStore as te } from "../extensions/Blocks/Recommendation/store/recommendation.js";
12
- import oe from "./organisms/AutoSaveController.vue.js";
13
- import ne from "./organisms/base/Toaster.vue.js";
14
- import re from "./organisms/extensions/recommendation/FilterSelectionDrawer.vue.js";
15
- import se from "./organisms/header/HeaderWrapper.vue.js";
16
- import ie from "./organisms/LoadingWrapper.vue.js";
17
- import ae from "./organisms/save-as-template/SaveAsTemplateDrawer.vue.js";
18
- import ce from "./organisms/unsubscribe/UnsubscribeWrapper.vue.js";
19
- import { useStripoApi as de } from "../services/stripoApi.js";
20
- import { useConfigStore as me } from "../stores/config.js";
21
- import { useDynamicContentStore as le } from "../stores/dynamic-content.js";
22
- import { useEditorStore as O } from "../stores/editor.js";
23
- import { usePreviewStore as ue } from "../stores/preview.js";
24
- import { useTemplateStore as pe } from "../stores/template.js";
25
- import { useUnsubscribeStore as fe } from "../stores/unsubscribe.js";
26
- const xe = /* @__PURE__ */ q({
10
+ import { RIBBON_SELECTOR as te } from "../enums/onboarding.js";
11
+ import { useRecommendationExtensionStore as oe } from "../extensions/Blocks/Recommendation/store/recommendation.js";
12
+ import ne from "./organisms/AutoSaveController.vue.js";
13
+ import re from "./organisms/base/Toaster.vue.js";
14
+ import se from "./organisms/extensions/recommendation/FilterSelectionDrawer.vue.js";
15
+ import ie from "./organisms/header/HeaderWrapper.vue.js";
16
+ import ae from "./organisms/LoadingWrapper.vue.js";
17
+ import ce from "./organisms/save-as-template/SaveAsTemplateDrawer.vue.js";
18
+ import de from "./organisms/unsubscribe/UnsubscribeWrapper.vue.js";
19
+ import { useStripoApi as me } from "../services/stripoApi.js";
20
+ import { useConfigStore as le } from "../stores/config.js";
21
+ import { useDynamicContentStore as ue } from "../stores/dynamic-content.js";
22
+ import { useEditorStore as P } from "../stores/editor.js";
23
+ import { usePreviewStore as pe } from "../stores/preview.js";
24
+ import { useTemplateStore as fe } from "../stores/template.js";
25
+ import { useUnsubscribeStore as ye } from "../stores/unsubscribe.js";
26
+ import { readTrackSeq as ve } from "../utils/linkTrackingIds.js";
27
+ const $e = /* @__PURE__ */ K({
27
28
  __name: "Guido",
28
29
  props: {
29
30
  config: null,
30
31
  ready: { type: Boolean, default: !0 }
31
32
  },
32
33
  emits: ["dynamic-content:open", "back", "save:start", "save:complete", "on-change", "ready", "onboarding:finished", "test-email:click"],
33
- setup(P, { expose: A, emit: n }) {
34
- const i = P, W = M(
34
+ setup(A, { expose: W, emit: r }) {
35
+ const i = A, x = M(
35
36
  () => import("./organisms/email-preview/PreviewContainer.vue.js")
36
- ), x = M(
37
+ ), G = M(
37
38
  () => import("./organisms/onboarding/OnboardingWrapper.vue.js")
38
- ), g = U(), l = U(), u = le(), S = fe(), o = me();
39
+ ), g = O(), l = O(), u = ue(), S = ye(), o = le();
39
40
  i.ready && o.init(i.config);
40
- const d = O(), G = ue(), m = _(() => d.hasChanges), { isTestPartner: H } = X(), h = () => {
41
+ const c = P(), H = pe(), m = U(() => c.hasChanges), { isTestPartner: $ } = Y(), h = () => {
41
42
  var t;
42
43
  return (t = g.value) == null ? void 0 : t.handleSave(!0);
43
44
  }, b = (t) => {
44
- d.loadingStatus = t;
45
+ c.loadingStatus = t;
45
46
  };
46
- j(), Q();
47
+ J(), X();
47
48
  const p = () => {
48
49
  var t, e;
49
50
  return {
@@ -54,78 +55,78 @@ const xe = /* @__PURE__ */ q({
54
55
  savedModulesFolderName: ((t = o.editor) == null ? void 0 : t.savedModulesFolderName) || N.SAVED_MODULES,
55
56
  defaultModulesFolderName: ((e = o.editor) == null ? void 0 : e.defaultModulesFolderName) || N.DEFAULT_MODULES
56
57
  };
57
- }, { initPlugin: E, preloadRuntime: w } = Y(), { getDefaultTemplate: C } = de(), { cloneTimersOnSave: D, hasTimerBlocks: T } = Z(), $ = _(() => {
58
+ }, { initPlugin: E, preloadRuntime: w } = Z(), { getDefaultTemplate: C } = me(), { cloneTimersOnSave: D, hasTimerBlocks: k } = ee(), z = U(() => {
58
59
  var t;
59
60
  return !((t = o.ui) != null && t.showHeader);
60
61
  });
61
- J({
62
+ Q({
62
63
  onBack: () => {
63
- console.debug("guido:back"), n("back");
64
+ console.debug("guido:back"), r("back");
64
65
  },
65
66
  onSaveStart: () => {
66
- console.debug("guido:save:start"), n("save:start");
67
+ console.debug("guido:save:start"), r("save:start");
67
68
  },
68
69
  onSaveComplete: (t) => {
69
70
  const e = { ...t, metadata: p() };
70
- console.debug("guido:save:complete", e), n("save:complete", e);
71
+ console.debug("guido:save:complete", e), r("save:complete", e);
71
72
  },
72
73
  onTestEmailClick: () => {
73
- console.debug("guido:test-email:click"), n("test-email:click");
74
+ console.debug("guido:test-email:click"), r("test-email:click");
74
75
  }
75
76
  });
76
- const L = (t) => {
77
+ const T = (t) => {
77
78
  console.debug("dynamic-content:close", t), u.setSelectedDynamicContent(t), document.dispatchEvent(new CustomEvent("dynamic-content:close", { detail: t }));
78
- }, R = () => {
79
+ }, F = () => {
79
80
  console.debug("dynamic-content:close", "Without Data"), document.dispatchEvent(new CustomEvent("dynamic-content:close", { detail: { text: "", value: "" } }));
80
81
  };
81
- B(() => m.value, () => {
82
- n("on-change", m.value);
82
+ _(() => m.value, () => {
83
+ r("on-change", m.value);
83
84
  });
84
85
  const f = (t) => {
85
- const e = t, { attribute: s, position: c } = e.detail;
86
- console.debug("dynamic-content:open", e.detail), n("dynamic-content:open", s, c);
86
+ const e = t, { attribute: s, position: d } = e.detail;
87
+ console.debug("dynamic-content:open", e.detail), r("dynamic-content:open", s, d);
87
88
  };
88
89
  let a = null;
89
90
  const y = () => {
90
91
  var e;
91
- const t = document.querySelector(ee);
92
+ const t = document.querySelector(te);
92
93
  (e = l.value) == null || e.style.setProperty("--ribbon-offset", `${(t == null ? void 0 : t.offsetHeight) ?? 0}px`);
93
94
  }, v = async () => {
94
95
  var t;
95
96
  try {
96
- o.initialized || o.init(i.config), d.templateId = o.templateId;
97
- const e = o.template, s = (e == null ? void 0 : e.html) || "", c = (e == null ? void 0 : e.css) || "", k = (e == null ? void 0 : e.preselectedDynamicContent) || [];
97
+ o.initialized || o.init(i.config), c.templateId = o.templateId;
98
+ const e = o.template, s = (e == null ? void 0 : e.html) || "", d = (e == null ? void 0 : e.css) || "", I = (e == null ? void 0 : e.preselectedDynamicContent) || [];
98
99
  S.selectedUnsubscribePages = (e == null ? void 0 : e.selectedUnsubscribePages) || [];
99
- const F = ((t = e == null ? void 0 : e.migration) == null ? void 0 : t.recommendationConfigs) ?? {};
100
- let r = {
101
- html: s && await I(s, F),
102
- css: c
100
+ const L = ((t = e == null ? void 0 : e.migration) == null ? void 0 : t.recommendationConfigs) ?? {}, R = { linkTrackingIds: o.isFeatureEnabled("linkTrackingIds") };
101
+ let n = {
102
+ html: s && await B(s, L, R),
103
+ css: d
103
104
  };
104
- r.html || (r = await C(), r.html = await I(r.html, F)), T(r.html) && (r.html = await D(r.html));
105
- const z = {
106
- preselectedDynamicContentList: k,
105
+ n.html || (n = await C(), n.html = await B(n.html, L, R)), k(n.html) && (n.html = await D(n.html)), c.trackIdSessionFloor = ve(n.html);
106
+ const q = {
107
+ preselectedDynamicContentList: I,
107
108
  onReady: () => {
108
- console.debug("guido:ready"), n("ready");
109
+ console.debug("guido:ready"), r("ready");
109
110
  }
110
111
  };
111
- await E(r, p(), z), u.selectedDynamicContentList = k;
112
+ await E(n, p(), q), u.selectedDynamicContentList = I;
112
113
  } catch (e) {
113
114
  console.error("Failed to initialize Stripo editor:", e);
114
115
  }
115
116
  };
116
- return K(async () => {
117
+ return V(async () => {
117
118
  var e;
118
- console.debug("Guido says happy coding 🎉"), console.debug("🚗 Ka-Chow"), te().$reset(), O().$reset(), pe().$reset(), y();
119
+ console.debug("Guido says happy coding 🎉"), console.debug("🚗 Ka-Chow"), oe().$reset(), P().$reset(), fe().$reset(), y();
119
120
  const t = (e = l.value) == null ? void 0 : e.parentElement;
120
121
  if (t && (a = new ResizeObserver(y), a.observe(t)), w(), i.ready)
121
122
  await v();
122
123
  else {
123
- const s = B(() => i.ready, (c) => {
124
- c && (s(), v());
124
+ const s = _(() => i.ready, (d) => {
125
+ d && (s(), v());
125
126
  });
126
127
  }
127
128
  document.addEventListener("dynamic-content:open", f);
128
- }), V(() => {
129
+ }), j(() => {
129
130
  a == null || a.disconnect(), document.removeEventListener("dynamic-content:open", f);
130
131
  try {
131
132
  window.UIEditor.removeEditor();
@@ -133,17 +134,17 @@ const xe = /* @__PURE__ */ q({
133
134
  console.debug("Failed to remove Stripo editor: No editor found");
134
135
  }
135
136
  o.reset();
136
- }), A({
137
+ }), W({
137
138
  dynamicContent: {
138
- insert: L,
139
- close: R
139
+ insert: T,
140
+ close: F
140
141
  },
141
142
  hasChanges: m,
142
143
  saveSilent: h,
143
144
  setLoading: b
144
- }), { __sfc: !0, PreviewContainer: W, OnboardingWrapper: x, headerWrapperRef: g, wrapperRef: l, dynamicContentStore: u, unsubscribeStore: S, props: i, configStore: o, editorStore: d, previewStore: G, hasChanges: m, isTestPartner: H, saveSilent: h, setLoading: b, emit: n, buildMetadata: p, initPlugin: E, preloadRuntime: w, getDefaultTemplate: C, cloneTimersOnSave: D, hasTimerBlocks: T, noHeader: $, insertDynamicContent: L, closeDynamicContent: R, handleDynamicContentOpen: f, ribbonObserver: a, updateRibbonOffset: y, startEditor: v, AutoSaveController: oe, Toaster: ne, FilterSelectionDrawer: re, HeaderWrapper: se, LoadingWrapper: ie, SaveAsTemplateDrawer: ae, UnsubscribeWrapper: ce };
145
+ }), { __sfc: !0, PreviewContainer: x, OnboardingWrapper: G, headerWrapperRef: g, wrapperRef: l, dynamicContentStore: u, unsubscribeStore: S, props: i, configStore: o, editorStore: c, previewStore: H, hasChanges: m, isTestPartner: $, saveSilent: h, setLoading: b, emit: r, buildMetadata: p, initPlugin: E, preloadRuntime: w, getDefaultTemplate: C, cloneTimersOnSave: D, hasTimerBlocks: k, noHeader: z, insertDynamicContent: T, closeDynamicContent: F, handleDynamicContentOpen: f, ribbonObserver: a, updateRibbonOffset: y, startEditor: v, AutoSaveController: ne, Toaster: re, FilterSelectionDrawer: se, HeaderWrapper: ie, LoadingWrapper: ae, SaveAsTemplateDrawer: ce, UnsubscribeWrapper: de };
145
146
  }
146
147
  });
147
148
  export {
148
- xe as default
149
+ $e as default
149
150
  };
@@ -1,18 +1,20 @@
1
+ import "../@types/config/schemas.js";
2
+ import { DEFAULT_EDITOR } from "../@types/config/defaults.js";
1
3
  import memoize from "../node_modules/lodash-es/memoize.js";
2
4
  import { ref } from "vue";
3
5
  import { useConfig } from "./useConfig.js";
4
6
  const useTranslations = () => {
5
7
  var o, e;
6
- const { config } = useConfig(), translationsPath = ((e = (o = config.value) == null ? void 0 : o.editor) == null ? void 0 : e.translationsPath) || "window.trans.en", translations = ref(eval(translationsPath)), trans = (n, t) => {
7
- const r = translations.value[n];
8
- return r ? t ? Object.entries(t).reduce(
8
+ const { config } = useConfig(), translationsPath = ((e = (o = config.value) == null ? void 0 : o.editor) == null ? void 0 : e.translationsPath) || DEFAULT_EDITOR.translationsPath, translations = ref(eval(translationsPath)), trans = (t, n) => {
9
+ const r = translations.value[t];
10
+ return r ? n ? Object.entries(n).reduce(
9
11
  (s, [i, a]) => s.replace(new RegExp(`{${i}}`, "g"), String(a)),
10
12
  r
11
- ) : r : (console.warn(`Translation key not found: ${n}`), n);
13
+ ) : r : (console.warn(`Translation key not found: ${t}`), t);
12
14
  };
13
15
  return memoize(
14
16
  trans,
15
- (n, t) => !t || Object.keys(t).length === 0 ? n : JSON.stringify({ key: n, params: t })
17
+ (t, n) => !n || Object.keys(n).length === 0 ? t : JSON.stringify({ key: t, params: n })
16
18
  );
17
19
  };
18
20
  export {
@@ -1,4 +1,4 @@
1
- const c = `
1
+ const r = `
2
2
  <!--[if gte mso 9]>
3
3
  <xml>
4
4
  <o:OfficeDocumentSettings>
@@ -6,15 +6,21 @@ const c = `
6
6
  <o:PixelsPerInch>96</o:PixelsPerInch>
7
7
  </o:OfficeDocumentSettings>
8
8
  </xml>
9
- <![endif]-->`, s = {
9
+ <![endif]-->`, n = {
10
10
  xmlns: "http://www.w3.org/1999/xhtml",
11
11
  "xmlns:o": "urn:schemas-microsoft-com:office:office"
12
- }, r = [
12
+ }, p = `
13
+ <style type="text/css">
14
+ ul, ol { padding: 0px 0px 0px 40px; }
15
+ li p { mso-margin-bottom-alt: 15px; }
16
+ .es-text-ltr ul, .es-text-ltr ol { padding: 0px 0px 0px 40px; }
17
+ .es-text-rtl ol, .es-text-rtl ul { padding: 0px 40px 0px 0px; }
18
+ </style>`, l = [
13
19
  {
14
20
  id: "append-office-xml",
15
21
  description: "Appending office XML into the head if it does not exist.",
16
22
  type: "custom",
17
- processor: (e) => e.includes("<o:OfficeDocumentSettings>") ? e : e.replace("</head>", `${c}</head>`),
23
+ processor: (e) => e.includes("<o:OfficeDocumentSettings>") ? e : e.replace("</head>", `${r}</head>`),
18
24
  priority: 70
19
25
  },
20
26
  {
@@ -23,14 +29,21 @@ const c = `
23
29
  type: "custom",
24
30
  processor: (e) => {
25
31
  let t = e;
26
- return Object.entries(s).forEach(([o, i]) => {
27
- const n = new RegExp(`${o}="[^"]*"`);
28
- t.search(n) === -1 && (t = t.replace("<html", `<html ${o}="${i}"`));
32
+ return Object.entries(n).forEach(([o, i]) => {
33
+ const s = new RegExp(`${o}="[^"]*"`);
34
+ t.search(s) === -1 && (t = t.replace("<html", `<html ${o}="${i}"`));
29
35
  }), t;
30
36
  },
31
37
  priority: 71
38
+ },
39
+ {
40
+ id: "append-outlook-list-css",
41
+ description: "Appending Outlook list rules into the head if the base CSS does not already carry them.",
42
+ type: "custom",
43
+ processor: (e) => e.includes("mso-margin-bottom-alt") ? e : e.replace("</head>", `${p}</head>`),
44
+ priority: 73
32
45
  }
33
46
  ];
34
47
  export {
35
- r as outlookCompilerRules
48
+ l as outlookCompilerRules
36
49
  };
@@ -1,14 +1,15 @@
1
- import { migrateCheckbox as t } from "./checkboxMigrator.js";
2
- import { migrateCouponBlock as i } from "./couponBlockMigrator.js";
1
+ import { migrateCheckbox as i } from "./checkboxMigrator.js";
2
+ import { migrateCouponBlock as a } from "./couponBlockMigrator.js";
3
3
  import { migrateItemsBlock as e } from "./itemsBlockMigrator.js";
4
- import { migrateRadioButton as a } from "./radioButtonMigrator.js";
5
- import { migrateRecommendation as c } from "./recommendationMigrator.js";
6
- import { migrateSocialIcons as g } from "./socialIconMigrator.js";
7
- import { migrateUnsubscribe as n } from "./unsubscribeMigrator.js";
8
- const k = async (o, r = {}) => {
9
- let m = o;
10
- return m = t(m), m = a(m), m = await n(m), m = i(m), m = c(m, r), m = e(m), m = g(m), m;
4
+ import { migrateRadioButton as c } from "./radioButtonMigrator.js";
5
+ import { migrateRecommendation as g } from "./recommendationMigrator.js";
6
+ import { migrateSocialIcons as n } from "./socialIconMigrator.js";
7
+ import { migrateTrackIds as p } from "./trackIdMigrator.js";
8
+ import { migrateUnsubscribe as f } from "./unsubscribeMigrator.js";
9
+ const x = async (m, o = {}, t = {}) => {
10
+ let r = m;
11
+ return r = i(r), r = c(r), r = await f(r), r = a(r), r = g(r, o), r = e(r), r = n(r), t.linkTrackingIds && (r = p(r)), r;
11
12
  };
12
13
  export {
13
- k as migrate
14
+ x as migrate
14
15
  };
@@ -0,0 +1,11 @@
1
+ import { assignTrackingIds as t } from "../../utils/linkTrackingIds.js";
2
+ function e(r) {
3
+ try {
4
+ return t(r).html;
5
+ } catch (o) {
6
+ return console.error("TrackIdMigrator failed:", o), r;
7
+ }
8
+ }
9
+ export {
10
+ e as migrateTrackIds
11
+ };
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-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{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-6e711fd7]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-5079a1c8]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-5079a1c8]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-5079a1c8]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .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-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{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-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom: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-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{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-6e711fd7]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-81958b3d]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-81958b3d]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-81958b3d]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .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-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{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-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom:110px!important}
package/dist/library.js CHANGED
@@ -1,14 +1,19 @@
1
1
  import { default as a } from "./components/Guido.vue.js";
2
- import { MessageType as f, ProductType as t } from "./@types/config/schemas.js";
2
+ import { MessageType as T, ProductType as f } from "./@types/config/schemas.js";
3
3
  import "./@types/config/defaults.js";
4
- import { getValidationErrors as s, isValidConfig as d, parseConfig as g, parseConfigSafe as n, validateConfig as l } from "./@types/config/validator.js";
4
+ import { getValidationErrors as t, isValidConfig as p, parseConfig as d, parseConfigSafe as g, validateConfig as n } from "./@types/config/validator.js";
5
+ import { TRACK_ID_ATTRIBUTE as m, TRACK_SEQ_ATTRIBUTE as l, assignTrackingIds as x, readTrackSeq as A } from "./utils/linkTrackingIds.js";
5
6
  export {
6
7
  a as Guido,
7
- f as MessageType,
8
- t as ProductType,
9
- s as getValidationErrors,
10
- d as isValidConfig,
11
- g as parseConfig,
12
- n as parseConfigSafe,
13
- l as validateConfig
8
+ T as MessageType,
9
+ f as ProductType,
10
+ m as TRACK_ID_ATTRIBUTE,
11
+ l as TRACK_SEQ_ATTRIBUTE,
12
+ x as assignTrackingIds,
13
+ t as getValidationErrors,
14
+ p as isValidConfig,
15
+ d as parseConfig,
16
+ g as parseConfigSafe,
17
+ A as readTrackSeq,
18
+ n as validateConfig
14
19
  };
@@ -385,6 +385,8 @@ export declare const FeaturesSchema: v.ObjectSchema<{
385
385
  readonly autosave: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
386
386
  /** Enable pre-save domain-allowlist validation (opt-in; Guido self-serves the block toaster) */
387
387
  readonly allowlistEnabled: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
388
+ /** Enable persistent link tracking-id assignment for the click heatmap (opt-in) */
389
+ readonly linkTrackingIds: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
388
390
  }, undefined>;
389
391
  /**
390
392
  * Default block types available in Stripo
@@ -774,6 +776,8 @@ export declare const GuidoConfigSchema: v.ObjectSchema<{
774
776
  readonly autosave: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
775
777
  /** Enable pre-save domain-allowlist validation (opt-in; Guido self-serves the block toaster) */
776
778
  readonly allowlistEnabled: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
779
+ /** Enable persistent link tracking-id assignment for the click heatmap (opt-in) */
780
+ readonly linkTrackingIds: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
777
781
  }, undefined>, {}>;
778
782
  /** Block configuration */
779
783
  readonly blocks: v.OptionalSchema<v.ObjectSchema<{
@@ -97,6 +97,7 @@ export declare const useConfig: () => {
97
97
  liquidSyntax: boolean;
98
98
  autosave: boolean;
99
99
  allowlistEnabled: boolean;
100
+ linkTrackingIds: boolean;
100
101
  };
101
102
  blocks: {
102
103
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -229,6 +230,7 @@ export declare const useConfig: () => {
229
230
  liquidSyntax: boolean;
230
231
  autosave: boolean;
231
232
  allowlistEnabled: boolean;
233
+ linkTrackingIds: boolean;
232
234
  } | null>;
233
235
  blocks: import("vue").ComputedRef<{
234
236
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1,9 +1,4 @@
1
1
  import type { ModuleAddData, ModuleModifier } from '@@/Types/stripo';
2
- /**
3
- * Host-registered translation key for the "some attributes could not be
4
- * converted" toaster. Guido resolves it against the host's `window.trans.en`;
5
- * the copy is registered host-side.
6
- */
7
2
  export declare const UNRESOLVED_DYNAMIC_CONTENT_ON_DROP_KEY = "dynamic-content.unresolved-attributes-on-drop";
8
3
  /**
9
4
  * Repairs label-form dynamic-content tokens in saved modules as they are dropped
@@ -1,2 +1,5 @@
1
1
  import type { LegacyRecommendationConfig } from '@@/Types/config';
2
- export declare const migrate: (html: string, recommendationConfigs?: Record<string, LegacyRecommendationConfig>) => Promise<string>;
2
+ export interface MigrateOptions {
3
+ linkTrackingIds?: boolean;
4
+ }
5
+ export declare const migrate: (html: string, recommendationConfigs?: Record<string, LegacyRecommendationConfig>, options?: MigrateOptions) => Promise<string>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Backfills persistent link tracking ids (SD-141414) at load so legacy
3
+ * templates, library imports and externally edited HTML enter the editor
4
+ * already tagged — before Stripo sees the document (no dirty flag, no undo).
5
+ */
6
+ export declare function migrateTrackIds(html: string): string;
@@ -1,5 +1,7 @@
1
1
  export { default as Guido } from './components/Guido.vue';
2
2
  export type { GuidoConfig, GuidoConfigInput, IdentityConfig, PartnerConfig, TemplateConfig, TemplateMigrationConfig, LegacyRecommendationConfig, EditorConfig, UIConfig, FeaturesConfig, BlocksConfig, CompilerConfig, DynamicContent, EmailHeader, DefaultBlockType, CustomBlockType, ValidationResult, ValidationError, } from './@types/config';
3
3
  export { validateConfig, parseConfig, parseConfigSafe, isValidConfig, getValidationErrors, MessageType, ProductType, } from './@types/config';
4
+ export { assignTrackingIds, readTrackSeq, TRACK_ID_ATTRIBUTE, TRACK_SEQ_ATTRIBUTE, } from './utils/linkTrackingIds';
5
+ export type { TrackingIdResult } from './utils/linkTrackingIds';
4
6
  export type { StripoEventType } from './@types/events';
5
7
  export type { PositionData, DynamicContentEvent, MergeTagClickEvent, MergeTag, BaseDynamicContent, DynamicContentNode, DynamicContentList, TooltipOptions, TextValueObject, L10n, } from './@types/generic';
@@ -102,6 +102,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
102
102
  liquidSyntax: boolean;
103
103
  autosave: boolean;
104
104
  allowlistEnabled: boolean;
105
+ linkTrackingIds: boolean;
105
106
  };
106
107
  blocks: {
107
108
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -240,6 +241,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
240
241
  liquidSyntax: boolean;
241
242
  autosave: boolean;
242
243
  allowlistEnabled: boolean;
244
+ linkTrackingIds: boolean;
243
245
  };
244
246
  blocks: {
245
247
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -378,6 +380,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
378
380
  liquidSyntax: boolean;
379
381
  autosave: boolean;
380
382
  allowlistEnabled: boolean;
383
+ linkTrackingIds: boolean;
381
384
  };
382
385
  blocks: {
383
386
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -516,6 +519,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
516
519
  liquidSyntax: boolean;
517
520
  autosave: boolean;
518
521
  allowlistEnabled: boolean;
522
+ linkTrackingIds: boolean;
519
523
  };
520
524
  blocks: {
521
525
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -654,6 +658,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
654
658
  liquidSyntax: boolean;
655
659
  autosave: boolean;
656
660
  allowlistEnabled: boolean;
661
+ linkTrackingIds: boolean;
657
662
  };
658
663
  blocks: {
659
664
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -792,6 +797,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
792
797
  liquidSyntax: boolean;
793
798
  autosave: boolean;
794
799
  allowlistEnabled: boolean;
800
+ linkTrackingIds: boolean;
795
801
  };
796
802
  blocks: {
797
803
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -930,6 +936,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
930
936
  liquidSyntax: boolean;
931
937
  autosave: boolean;
932
938
  allowlistEnabled: boolean;
939
+ linkTrackingIds: boolean;
933
940
  };
934
941
  blocks: {
935
942
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1068,6 +1075,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1068
1075
  liquidSyntax: boolean;
1069
1076
  autosave: boolean;
1070
1077
  allowlistEnabled: boolean;
1078
+ linkTrackingIds: boolean;
1071
1079
  };
1072
1080
  blocks: {
1073
1081
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1206,6 +1214,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1206
1214
  liquidSyntax: boolean;
1207
1215
  autosave: boolean;
1208
1216
  allowlistEnabled: boolean;
1217
+ linkTrackingIds: boolean;
1209
1218
  };
1210
1219
  blocks: {
1211
1220
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1344,6 +1353,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1344
1353
  liquidSyntax: boolean;
1345
1354
  autosave: boolean;
1346
1355
  allowlistEnabled: boolean;
1356
+ linkTrackingIds: boolean;
1347
1357
  };
1348
1358
  blocks: {
1349
1359
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1482,6 +1492,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1482
1492
  liquidSyntax: boolean;
1483
1493
  autosave: boolean;
1484
1494
  allowlistEnabled: boolean;
1495
+ linkTrackingIds: boolean;
1485
1496
  };
1486
1497
  blocks: {
1487
1498
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1620,6 +1631,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1620
1631
  liquidSyntax: boolean;
1621
1632
  autosave: boolean;
1622
1633
  allowlistEnabled: boolean;
1634
+ linkTrackingIds: boolean;
1623
1635
  };
1624
1636
  blocks: {
1625
1637
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1758,6 +1770,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1758
1770
  liquidSyntax: boolean;
1759
1771
  autosave: boolean;
1760
1772
  allowlistEnabled: boolean;
1773
+ linkTrackingIds: boolean;
1761
1774
  };
1762
1775
  blocks: {
1763
1776
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1896,6 +1909,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1896
1909
  liquidSyntax: boolean;
1897
1910
  autosave: boolean;
1898
1911
  allowlistEnabled: boolean;
1912
+ linkTrackingIds: boolean;
1899
1913
  };
1900
1914
  blocks: {
1901
1915
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2034,6 +2048,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2034
2048
  liquidSyntax: boolean;
2035
2049
  autosave: boolean;
2036
2050
  allowlistEnabled: boolean;
2051
+ linkTrackingIds: boolean;
2037
2052
  };
2038
2053
  blocks: {
2039
2054
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2172,6 +2187,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2172
2187
  liquidSyntax: boolean;
2173
2188
  autosave: boolean;
2174
2189
  allowlistEnabled: boolean;
2190
+ linkTrackingIds: boolean;
2175
2191
  };
2176
2192
  blocks: {
2177
2193
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2310,6 +2326,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2310
2326
  liquidSyntax: boolean;
2311
2327
  autosave: boolean;
2312
2328
  allowlistEnabled: boolean;
2329
+ linkTrackingIds: boolean;
2313
2330
  };
2314
2331
  blocks: {
2315
2332
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2450,6 +2467,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2450
2467
  liquidSyntax: boolean;
2451
2468
  autosave: boolean;
2452
2469
  allowlistEnabled: boolean;
2470
+ linkTrackingIds: boolean;
2453
2471
  };
2454
2472
  blocks: {
2455
2473
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -9,6 +9,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
9
9
  isStripoInitialized: boolean;
10
10
  templateId: string;
11
11
  syncModulesEnabled: boolean;
12
+ trackIdSessionFloor: number;
12
13
  }, {
13
14
  isEditorToolbarVisible: (state: {
14
15
  loadingStatus: boolean;
@@ -21,6 +22,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
21
22
  isStripoInitialized: boolean;
22
23
  templateId: string;
23
24
  syncModulesEnabled: boolean;
25
+ trackIdSessionFloor: number;
24
26
  } & import("pinia").PiniaCustomStateProperties<{
25
27
  loadingStatus: boolean;
26
28
  isCodeEditorOpen: boolean;
@@ -32,6 +34,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
32
34
  isStripoInitialized: boolean;
33
35
  templateId: string;
34
36
  syncModulesEnabled: boolean;
37
+ trackIdSessionFloor: number;
35
38
  }>) => boolean;
36
39
  isUndoButtonDisabled: (state: {
37
40
  loadingStatus: boolean;
@@ -44,6 +47,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
44
47
  isStripoInitialized: boolean;
45
48
  templateId: string;
46
49
  syncModulesEnabled: boolean;
50
+ trackIdSessionFloor: number;
47
51
  } & import("pinia").PiniaCustomStateProperties<{
48
52
  loadingStatus: boolean;
49
53
  isCodeEditorOpen: boolean;
@@ -55,6 +59,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
55
59
  isStripoInitialized: boolean;
56
60
  templateId: string;
57
61
  syncModulesEnabled: boolean;
62
+ trackIdSessionFloor: number;
58
63
  }>) => boolean;
59
64
  isRedoButtonDisabled: (state: {
60
65
  loadingStatus: boolean;
@@ -67,6 +72,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
67
72
  isStripoInitialized: boolean;
68
73
  templateId: string;
69
74
  syncModulesEnabled: boolean;
75
+ trackIdSessionFloor: number;
70
76
  } & import("pinia").PiniaCustomStateProperties<{
71
77
  loadingStatus: boolean;
72
78
  isCodeEditorOpen: boolean;
@@ -78,6 +84,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
78
84
  isStripoInitialized: boolean;
79
85
  templateId: string;
80
86
  syncModulesEnabled: boolean;
87
+ trackIdSessionFloor: number;
81
88
  }>) => boolean;
82
89
  isCodeEditorButtonDisabled: (state: {
83
90
  loadingStatus: boolean;
@@ -90,6 +97,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
90
97
  isStripoInitialized: boolean;
91
98
  templateId: string;
92
99
  syncModulesEnabled: boolean;
100
+ trackIdSessionFloor: number;
93
101
  } & import("pinia").PiniaCustomStateProperties<{
94
102
  loadingStatus: boolean;
95
103
  isCodeEditorOpen: boolean;
@@ -101,6 +109,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
101
109
  isStripoInitialized: boolean;
102
110
  templateId: string;
103
111
  syncModulesEnabled: boolean;
112
+ trackIdSessionFloor: number;
104
113
  }>) => boolean;
105
114
  isPreviewButtonDisabled: (state: {
106
115
  loadingStatus: boolean;
@@ -113,6 +122,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
113
122
  isStripoInitialized: boolean;
114
123
  templateId: string;
115
124
  syncModulesEnabled: boolean;
125
+ trackIdSessionFloor: number;
116
126
  } & import("pinia").PiniaCustomStateProperties<{
117
127
  loadingStatus: boolean;
118
128
  isCodeEditorOpen: boolean;
@@ -124,6 +134,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
124
134
  isStripoInitialized: boolean;
125
135
  templateId: string;
126
136
  syncModulesEnabled: boolean;
137
+ trackIdSessionFloor: number;
127
138
  }>) => boolean;
128
139
  isViewOptionsDisabled: (state: {
129
140
  loadingStatus: boolean;
@@ -136,6 +147,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
136
147
  isStripoInitialized: boolean;
137
148
  templateId: string;
138
149
  syncModulesEnabled: boolean;
150
+ trackIdSessionFloor: number;
139
151
  } & import("pinia").PiniaCustomStateProperties<{
140
152
  loadingStatus: boolean;
141
153
  isCodeEditorOpen: boolean;
@@ -147,6 +159,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
147
159
  isStripoInitialized: boolean;
148
160
  templateId: string;
149
161
  syncModulesEnabled: boolean;
162
+ trackIdSessionFloor: number;
150
163
  }>) => boolean;
151
164
  isVersionHistoryButtonDisabled: (state: {
152
165
  loadingStatus: boolean;
@@ -159,6 +172,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
159
172
  isStripoInitialized: boolean;
160
173
  templateId: string;
161
174
  syncModulesEnabled: boolean;
175
+ trackIdSessionFloor: number;
162
176
  } & import("pinia").PiniaCustomStateProperties<{
163
177
  loadingStatus: boolean;
164
178
  isCodeEditorOpen: boolean;
@@ -170,6 +184,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
170
184
  isStripoInitialized: boolean;
171
185
  templateId: string;
172
186
  syncModulesEnabled: boolean;
187
+ trackIdSessionFloor: number;
173
188
  }>) => boolean;
174
189
  isExportButtonDisabled: (state: {
175
190
  loadingStatus: boolean;
@@ -182,6 +197,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
182
197
  isStripoInitialized: boolean;
183
198
  templateId: string;
184
199
  syncModulesEnabled: boolean;
200
+ trackIdSessionFloor: number;
185
201
  } & import("pinia").PiniaCustomStateProperties<{
186
202
  loadingStatus: boolean;
187
203
  isCodeEditorOpen: boolean;
@@ -193,6 +209,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
193
209
  isStripoInitialized: boolean;
194
210
  templateId: string;
195
211
  syncModulesEnabled: boolean;
212
+ trackIdSessionFloor: number;
196
213
  }>) => boolean;
197
214
  isSaveAsButtonDisabled: (state: {
198
215
  loadingStatus: boolean;
@@ -205,6 +222,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
205
222
  isStripoInitialized: boolean;
206
223
  templateId: string;
207
224
  syncModulesEnabled: boolean;
225
+ trackIdSessionFloor: number;
208
226
  } & import("pinia").PiniaCustomStateProperties<{
209
227
  loadingStatus: boolean;
210
228
  isCodeEditorOpen: boolean;
@@ -216,6 +234,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
216
234
  isStripoInitialized: boolean;
217
235
  templateId: string;
218
236
  syncModulesEnabled: boolean;
237
+ trackIdSessionFloor: number;
219
238
  }>) => boolean;
220
239
  isTestButtonDisabled: (state: {
221
240
  loadingStatus: boolean;
@@ -228,6 +247,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
228
247
  isStripoInitialized: boolean;
229
248
  templateId: string;
230
249
  syncModulesEnabled: boolean;
250
+ trackIdSessionFloor: number;
231
251
  } & import("pinia").PiniaCustomStateProperties<{
232
252
  loadingStatus: boolean;
233
253
  isCodeEditorOpen: boolean;
@@ -239,6 +259,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
239
259
  isStripoInitialized: boolean;
240
260
  templateId: string;
241
261
  syncModulesEnabled: boolean;
262
+ trackIdSessionFloor: number;
242
263
  }>) => boolean;
243
264
  isSaveButtonDisabled: (state: {
244
265
  loadingStatus: boolean;
@@ -251,6 +272,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
251
272
  isStripoInitialized: boolean;
252
273
  templateId: string;
253
274
  syncModulesEnabled: boolean;
275
+ trackIdSessionFloor: number;
254
276
  } & import("pinia").PiniaCustomStateProperties<{
255
277
  loadingStatus: boolean;
256
278
  isCodeEditorOpen: boolean;
@@ -262,6 +284,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
262
284
  isStripoInitialized: boolean;
263
285
  templateId: string;
264
286
  syncModulesEnabled: boolean;
287
+ trackIdSessionFloor: number;
265
288
  }>) => boolean;
266
289
  isInSaveableState: (state: {
267
290
  loadingStatus: boolean;
@@ -274,6 +297,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
274
297
  isStripoInitialized: boolean;
275
298
  templateId: string;
276
299
  syncModulesEnabled: boolean;
300
+ trackIdSessionFloor: number;
277
301
  } & import("pinia").PiniaCustomStateProperties<{
278
302
  loadingStatus: boolean;
279
303
  isCodeEditorOpen: boolean;
@@ -285,5 +309,6 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
285
309
  isStripoInitialized: boolean;
286
310
  templateId: string;
287
311
  syncModulesEnabled: boolean;
312
+ trackIdSessionFloor: number;
288
313
  }>) => boolean;
289
314
  }, {}>;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Persistent link tracking ids for the click heatmap (SD-141414).
3
+ * Pure string transforms — no DOM parse/serialize round-trip, safe for both
4
+ * Stripo raw HTML and hand-authored HTML-editor documents. Shared with email-fe.
5
+ */
6
+ export declare const TRACK_ID_ATTRIBUTE = "data-ins-track-id";
7
+ export declare const TRACK_SEQ_ATTRIBUTE = "data-ins-track-seq";
8
+ export interface TrackingIdResult {
9
+ html: string;
10
+ changed: boolean;
11
+ nextSeq: number;
12
+ }
13
+ /**
14
+ * Reads the persisted id high-water mark: <body> attr, then <html> attr,
15
+ * then the leading comment fallback. Returns 0 when absent.
16
+ */
17
+ export declare function readTrackSeq(html: string): number;
18
+ /**
19
+ * Ensures every <a> opening tag carries a unique positive-integer id and the
20
+ * document records the id high-water mark. Ids are minted above
21
+ * max(persisted seq, max id in document, floor) so deleted ids are never
22
+ * reused — even after a version restore or paste-over (floor = session floor).
23
+ * Idempotent: a document already normalized returns changed=false.
24
+ */
25
+ export declare function assignTrackingIds(html: string, floor?: number): TrackingIdResult;
@@ -10,7 +10,10 @@ const o = e("guidoEditor", {
10
10
  hasChanges: !1,
11
11
  isStripoInitialized: !1,
12
12
  templateId: "",
13
- syncModulesEnabled: !1
13
+ syncModulesEnabled: !1,
14
+ // In-session id high-water mark; floors tracking-id minting so ids
15
+ // deleted by a version restore / paste-over are never reused.
16
+ trackIdSessionFloor: 0
14
17
  }),
15
18
  getters: {
16
19
  // Middle Slot
@@ -0,0 +1,44 @@
1
+ const E = "data-ins-track-id", g = "data-ins-track-seq", N = /<a\b[^>]*>/gi, $ = /\bdata-ins-track-id\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i, R = /\bdata-ins-track-seq\s*=\s*(?:"(\d+)"|'(\d+)'|(\d+))/i, f = /<body\b[^>]*>/i, b = /<html\b[^>]*>/i, A = /<!--\s*ins-track-seq:(\d+)\s*-->/i, k = /^[1-9][0-9]{0,14}$/;
2
+ function S(e) {
3
+ const s = Number(e);
4
+ return Number.isSafeInteger(s) && s > 0 ? s : 0;
5
+ }
6
+ function x(e) {
7
+ var i, a, c;
8
+ const n = [(i = f.exec(e)) == null ? void 0 : i[0], (a = b.exec(e)) == null ? void 0 : a[0]].reduce(
9
+ (T, o) => T ?? (o ? R.exec(o) : null),
10
+ null
11
+ ), t = (n == null ? void 0 : n[1]) ?? (n == null ? void 0 : n[2]) ?? (n == null ? void 0 : n[3]) ?? ((c = A.exec(e)) == null ? void 0 : c[1]) ?? "";
12
+ return S(t);
13
+ }
14
+ function I(e, s) {
15
+ const n = `${g}="${s}"`, t = f.exec(e) ?? b.exec(e);
16
+ if (t) {
17
+ const [i] = t, a = R.exec(i), c = a ? i.replace(a[0], n) : i.replace(/\/?>$/, (T) => ` ${n}${T}`);
18
+ return e.slice(0, t.index) + c + e.slice(t.index + i.length);
19
+ }
20
+ return A.test(e) ? e.replace(A, `<!--ins-track-seq:${s}-->`) : `<!--ins-track-seq:${s}-->${e}`;
21
+ }
22
+ function p(e, s, n) {
23
+ const t = `${E}="${n}"`;
24
+ return s ? e.replace(s[0], t) : e.replace(/^<a\b/i, (i) => `${i} ${t}`);
25
+ }
26
+ function q(e, s = 0) {
27
+ const n = /* @__PURE__ */ new Set();
28
+ let t = 0;
29
+ const i = [...e.matchAll(N)].map((r) => {
30
+ const [_] = r, u = $.exec(_), l = u ? u[1] ?? u[2] ?? u[3] ?? "" : "";
31
+ let d = null;
32
+ return k.test(l) && !n.has(Number(l)) && (d = Number(l), n.add(d), t = Math.max(t, d)), { index: r.index, tag: _, attrMatch: u, id: d };
33
+ });
34
+ let a = Math.max(x(e), t, s), c = "", T = 0, o = !1;
35
+ return i.forEach((r) => {
36
+ c += e.slice(T, r.index), r.id === null ? (a += 1, c += p(r.tag, r.attrMatch, a), o = !0) : c += r.tag, T = r.index + r.tag.length;
37
+ }), c += e.slice(T), a > 0 && x(c) !== a && (c = I(c, a), o = !0), { html: c, changed: o, nextSeq: a };
38
+ }
39
+ export {
40
+ E as TRACK_ID_ATTRIBUTE,
41
+ g as TRACK_SEQ_ATTRIBUTE,
42
+ q as assignTrackingIds,
43
+ x as readTrackSeq
44
+ };
@@ -1,62 +1,65 @@
1
- import { useActionsApi as P } from "../composables/useActionsApi.js";
2
- import { useHtmlCompiler as w } from "../composables/useHtmlCompiler.js";
3
- import { DEFAULT_CURRENCY as l, DEFAULT_NODE_CONFIG as i } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
4
- import { useRecommendationExtensionStore as b } from "../extensions/Blocks/Recommendation/store/recommendation.js";
5
- import { mapLegacyStrategy as E } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMap.js";
6
- import { DATA_ATTRIBUTES as h } from "../extensions/Blocks/Unsubscribe/utils/constants.js";
7
- import { parsePageList as H } from "../extensions/Blocks/Unsubscribe/utils/utils.js";
8
- import { useDynamicContentStore as U } from "../stores/dynamic-content.js";
9
- import { useUnsubscribeStore as F } from "../stores/unsubscribe.js";
10
- function L(s) {
11
- const o = new DOMParser().parseFromString(s, "text/html").querySelectorAll(`[${h.PAGE_LIST}]`), t = [];
12
- return o.forEach((a) => {
13
- const n = a.getAttribute(h.PAGE_LIST);
14
- n && t.push(...H(n));
1
+ import { useActionsApi as k } from "../composables/useActionsApi.js";
2
+ import { useConfig as U } from "../composables/useConfig.js";
3
+ import { useHtmlCompiler as x } from "../composables/useHtmlCompiler.js";
4
+ import { DEFAULT_CURRENCY as d, DEFAULT_NODE_CONFIG as s } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
5
+ import { useRecommendationExtensionStore as T } from "../extensions/Blocks/Recommendation/store/recommendation.js";
6
+ import { mapLegacyStrategy as L } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMap.js";
7
+ import { DATA_ATTRIBUTES as C } from "../extensions/Blocks/Unsubscribe/utils/constants.js";
8
+ import { parsePageList as R } from "../extensions/Blocks/Unsubscribe/utils/utils.js";
9
+ import { useDynamicContentStore as N } from "../stores/dynamic-content.js";
10
+ import { useEditorStore as z } from "../stores/editor.js";
11
+ import { useUnsubscribeStore as _ } from "../stores/unsubscribe.js";
12
+ import { assignTrackingIds as v } from "./linkTrackingIds.js";
13
+ function O(c) {
14
+ const n = new DOMParser().parseFromString(c, "text/html").querySelectorAll(`[${C.PAGE_LIST}]`), t = [];
15
+ return n.forEach((a) => {
16
+ const r = a.getAttribute(C.PAGE_LIST);
17
+ r && t.push(...R(r));
15
18
  }), [...new Set(t)];
16
19
  }
17
- async function R(s) {
18
- const m = new DOMParser().parseFromString(s, "text/html").querySelectorAll(".recommendation-block-v2");
20
+ async function M(c) {
21
+ const m = new DOMParser().parseFromString(c, "text/html").querySelectorAll(".recommendation-block-v2");
19
22
  if (m.length === 0)
20
23
  return;
21
- const o = b();
24
+ const n = T();
22
25
  m.forEach((t) => {
23
- var c, p, g, f, r;
24
- const a = t.getAttribute("recommendation-id"), n = a ? Number(a) : NaN;
25
- if (!Number.isFinite(n))
26
+ var l, g, f, i, S;
27
+ const a = t.getAttribute("recommendation-id"), r = a ? Number(a) : NaN;
28
+ if (!Number.isFinite(r))
26
29
  return;
27
- const d = t.getAttribute("esd-ext-config");
28
- if (!d)
30
+ const p = t.getAttribute("esd-ext-config");
31
+ if (!p)
29
32
  return;
30
33
  let e;
31
34
  try {
32
- e = JSON.parse(d);
35
+ e = JSON.parse(p);
33
36
  } catch {
34
37
  return;
35
38
  }
36
39
  if (!e || typeof e != "object" || Array.isArray(e))
37
40
  return;
38
- const y = {
41
+ const o = {
39
42
  // Normalize legacy strategy keys (e.g. `mostViewed` → `mostPopular`) so
40
43
  // already-saved blocks that never re-run the migrator don't emit a
41
44
  // strategy-less campaign URL. `||` also catches '' / undefined (SD-144882).
42
- strategy: E(e.strategy) || i.strategy,
43
- language: e.language ?? i.language,
44
- size: e.size ?? i.size,
45
+ strategy: L(e.strategy) || s.strategy,
46
+ language: e.language ?? s.language,
47
+ size: e.size ?? s.size,
45
48
  // Spread the default arrays so each block gets a fresh reference
46
49
  // instead of sharing the singleton in DEFAULT_NODE_CONFIG.
47
- productIds: e.productIds ?? [...i.productIds],
48
- filters: e.filters ?? [...i.filters],
49
- shuffleProducts: e.shuffleProducts ?? i.shuffleProducts,
50
- currencyCode: ((c = e.currency) == null ? void 0 : c.code) ?? l.code,
51
- currencyAlignment: ((p = e.currency) == null ? void 0 : p.alignment) ?? l.alignment,
52
- currencyDecimalCount: ((g = e.currency) == null ? void 0 : g.decimalCount) ?? l.decimalCount,
53
- currencyDecimalSeparator: ((f = e.currency) == null ? void 0 : f.decimalSeparator) ?? l.decimalSeparator,
54
- currencyThousandSeparator: ((r = e.currency) == null ? void 0 : r.thousandSeparator) ?? l.thousandSeparator
50
+ productIds: e.productIds ?? [...s.productIds],
51
+ filters: e.filters ?? [...s.filters],
52
+ shuffleProducts: e.shuffleProducts ?? s.shuffleProducts,
53
+ currencyCode: ((l = e.currency) == null ? void 0 : l.code) ?? d.code,
54
+ currencyAlignment: ((g = e.currency) == null ? void 0 : g.alignment) ?? d.alignment,
55
+ currencyDecimalCount: ((f = e.currency) == null ? void 0 : f.decimalCount) ?? d.decimalCount,
56
+ currencyDecimalSeparator: ((i = e.currency) == null ? void 0 : i.decimalSeparator) ?? d.decimalSeparator,
57
+ currencyThousandSeparator: ((S = e.currency) == null ? void 0 : S.thousandSeparator) ?? d.thousandSeparator
55
58
  };
56
- o.seedBlockUrlConfig(n, y);
59
+ n.seedBlockUrlConfig(r, o);
57
60
  });
58
61
  try {
59
- await o.fetchRecommendationCreateData();
62
+ await n.fetchRecommendationCreateData();
60
63
  } catch (t) {
61
64
  console.warn(
62
65
  "Recommendation reference data pre-load failed; validator will skip the availability check.",
@@ -64,43 +67,51 @@ async function R(s) {
64
67
  );
65
68
  }
66
69
  }
67
- const G = () => {
68
- const s = U(), u = F(), { getCompiledEmail: m, getTemplateData: o } = P(), { compileHtml: t, compileAmpHtml: a } = w();
70
+ const Z = () => {
71
+ const c = N(), u = _(), { getCompiledEmail: m, getTemplateData: n, updateHtmlAndCss: t } = k(), { compileHtml: a, compileAmpHtml: r } = x(), { isFeatureEnabled: p } = U();
69
72
  return {
70
73
  prepareTemplateDetails: async () => {
71
- const { html: d, ampHtml: e = "", ampErrors: y = [] } = await m({
74
+ let { html: o, css: l, syncModulesIds: g = [] } = await n();
75
+ if (p("linkTrackingIds"))
76
+ try {
77
+ const h = z(), { html: A, changed: H, nextSeq: I } = v(o, h.trackIdSessionFloor);
78
+ H && (t(A, l), o = A), h.trackIdSessionFloor = I;
79
+ } catch (h) {
80
+ console.error("Tracking-id assignment failed, saving without new ids:", h);
81
+ }
82
+ const { html: f, ampHtml: i = "", ampErrors: S = [] } = await m({
72
83
  minimize: !0,
73
84
  resetDataSavedFlag: !1
74
- }), { html: c, css: p, syncModulesIds: g = [] } = await o();
75
- u.selectedUnsubscribePages.length && await u.fetchTemplates(), await R(c);
76
- const { compiledHtml: f, stats: r, appliedRules: A } = t(d), C = e && a(e).compiledHtml, T = s.getSelectedDynamicContentList, D = b(), S = L(c);
85
+ });
86
+ u.selectedUnsubscribePages.length && await u.fetchTemplates(), await M(o);
87
+ const { compiledHtml: w, stats: y, appliedRules: D } = a(f), E = i && r(i).compiledHtml, P = c.getSelectedDynamicContentList, F = T(), b = O(o);
77
88
  return console.debug("HTML Compilation Stats:", {
78
- originalSize: r.originalSize,
79
- compiledSize: r.compiledSize,
80
- reduction: `${r.reductionPercentage.toFixed(2)}%`,
81
- appliedRules: A,
82
- executionTime: `${r.executionTime.toFixed(2)}ms`
89
+ originalSize: y.originalSize,
90
+ compiledSize: y.compiledSize,
91
+ reduction: `${y.reductionPercentage.toFixed(2)}%`,
92
+ appliedRules: D,
93
+ executionTime: `${y.executionTime.toFixed(2)}ms`
83
94
  }), {
84
- dynamicContentList: T,
85
- compiledHtml: f,
86
- rawHtml: c,
87
- css: p,
88
- ampHtml: C,
89
- ampErrors: y,
95
+ dynamicContentList: P,
96
+ compiledHtml: w,
97
+ rawHtml: o,
98
+ css: l,
99
+ ampHtml: E,
100
+ ampErrors: S,
90
101
  modules: g.map(Number),
91
102
  recommendation: {
92
- campaignUrls: D.recommendationCampaignUrls,
103
+ campaignUrls: F.recommendationCampaignUrls,
93
104
  configs: {}
94
105
  },
95
106
  unsubscribe: {
96
- status: S.length > 0,
97
- config: S
107
+ status: b.length > 0,
108
+ config: b
98
109
  }
99
110
  };
100
111
  }
101
112
  };
102
113
  };
103
114
  export {
104
- L as getHtmlReferencedUnsubscribePages,
105
- G as useTemplatePreparation
115
+ O as getHtmlReferencedUnsubscribePages,
116
+ Z as useTemplatePreparation
106
117
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useinsider/guido",
3
- "version": "3.13.0",
3
+ "version": "3.13.1-beta.a1e3c5f",
4
4
  "description": "Guido is a Vue + TypeScript wrapper for Email Plugin. Easily embed the email editor in your Vue applications.",
5
5
  "main": "./dist/guido.umd.cjs",
6
6
  "module": "./dist/library.js",