@useinsider/guido 3.11.0-beta.ad6af17 → 3.11.0-beta.c237b25

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.
@@ -1,5 +1,5 @@
1
1
  import { ModuleFolderDefaults as S } from "../../enums/defaults.js";
2
- import { object as a, number as s, optional as e, string as t, picklist as l, pipe as p, minLength as b, custom as h, boolean as o, array as c, record as k, literal as i, looseObject as y, variant as g, union as f, unknown as n } from "../../node_modules/valibot/dist/index.js";
2
+ import { object as a, number as n, optional as e, string as t, picklist as l, pipe as p, minLength as b, custom as h, boolean as o, array as c, record as k, literal as i, looseObject as g, variant as R, union as f, unknown as s } 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
- }, R = a({
15
+ }, C = a({
16
16
  /** Unique identifier for the template being edited */
17
17
  templateId: p(
18
18
  t(),
@@ -30,7 +30,7 @@ const d = {
30
30
  name: t(),
31
31
  /** Fallback font family (e.g., "serif" or "sans-serif") */
32
32
  family: t()
33
- }), C = a({
33
+ }), A = a({
34
34
  /** Partner/organization name (required) */
35
35
  name: p(
36
36
  t(),
@@ -54,7 +54,7 @@ const d = {
54
54
  username: e(t(), "Guido User"),
55
55
  /** Fallback font settings from partner settings — used to match backend size calculation */
56
56
  fallbackFont: e(T)
57
- }), A = a({
57
+ }), y = a({
58
58
  /** Display text for the dynamic content */
59
59
  text: t(),
60
60
  /** Template variable value (e.g., {{username}}) */
@@ -68,35 +68,35 @@ const d = {
68
68
  value: t()
69
69
  })
70
70
  )
71
- }), I = y({
71
+ }), I = g({
72
72
  /** Block ID (matches the dictionary key and the legacy HTML element id) */
73
- id: e(s()),
73
+ id: e(n()),
74
74
  /** Decimal places for price display (legacy data may use string or number) */
75
- decimalCount: e(f([t(), s()])),
75
+ decimalCount: e(f([t(), n()])),
76
76
  /** Pinned product IDs (empty array when filter-driven) */
77
- productIds: e(c(n())),
77
+ productIds: e(c(s())),
78
78
  /** Whether the block requested live products at send time */
79
79
  sendProductRequestFlag: e(o()),
80
80
  /** Whether to randomize product order */
81
81
  shuffleProducts: e(o()),
82
82
  /** Filter rules driving product selection */
83
- filters: e(c(n())),
83
+ filters: e(c(s())),
84
84
  /** Currency code (e.g. 'EUR') — sometimes absent in legacy data */
85
85
  currency: e(t()),
86
86
  /** Currency display settings (separators, alignment, decimals) */
87
- currencySettings: e(n()),
87
+ currencySettings: e(s()),
88
88
  /** Locale (e.g. 'nl_NL') */
89
89
  language: e(t()),
90
90
  /** Recommendation strategy key (e.g. 'newArrivals') */
91
91
  strategy: e(t()),
92
92
  /** Snapshot of products as rendered by the legacy block */
93
- recommendedProducts: e(c(n())),
93
+ recommendedProducts: e(c(s())),
94
94
  /** Number of product cards per row */
95
- cardsInRow: e(s()),
95
+ cardsInRow: e(n()),
96
96
  /** Mobile-only padding (right) */
97
- mobileRightPadding: e(s()),
97
+ mobileRightPadding: e(n()),
98
98
  /** Mobile-only padding (left) */
99
- mobileLeftPadding: e(s()),
99
+ mobileLeftPadding: e(n()),
100
100
  /** Disable responsive scaling */
101
101
  unresponsive: e(o()),
102
102
  /** Layout orientation ('vertical' | 'horizontal') */
@@ -106,14 +106,14 @@ const d = {
106
106
  /** Block type marker used by some legacy variants */
107
107
  blockType: e(t()),
108
108
  /** Size variant marker (legacy data may use string or number) */
109
- size: e(f([t(), s()])),
109
+ size: e(f([t(), n()])),
110
110
  /** Vertical responsiveness flag (legacy size=1 variants) */
111
111
  verticalResponsiveness: e(o()),
112
112
  /** Legacy "Move to next line" price placement toggle (cardPricePlacement.js) */
113
113
  isPriceMovedToNextLine: e(o()),
114
114
  /** Legacy "Hide if same as discounted" / delete-price-for-zero-sale toggle */
115
115
  isPriceDeletedForZeroSale: e(o())
116
- }), L = a({
116
+ }), v = a({
117
117
  /**
118
118
  * Legacy recommendation block configs keyed by block ID.
119
119
  * Pass this when loading a template authored with the v1
@@ -124,25 +124,34 @@ const d = {
124
124
  k(t(), I),
125
125
  {}
126
126
  )
127
- }), P = a({
127
+ }), L = a({
128
128
  /** Initial HTML content */
129
129
  html: e(t(), ""),
130
130
  /** Initial CSS content */
131
131
  css: e(t(), ""),
132
132
  /** Preselected dynamic content items */
133
133
  preselectedDynamicContent: e(
134
- c(A),
134
+ c(y),
135
+ []
136
+ ),
137
+ /**
138
+ * Full set of dynamic-content items the account offers (label + token).
139
+ * Used as the label→token map that repairs label-form placeholders
140
+ * (e.g. `{{ Phone Number }}`) in dropped saved modules.
141
+ */
142
+ availableDynamicContent: e(
143
+ c(y),
135
144
  []
136
145
  ),
137
146
  /** Valid custom field attribute names from the partner's categorized fields */
138
147
  customFieldAttributes: e(c(t()), []),
139
148
  /** Selected unsubscribe page IDs */
140
- selectedUnsubscribePages: e(c(s()), []),
149
+ selectedUnsubscribePages: e(c(n()), []),
141
150
  /** Force recreate template in Stripo storage (use true when updating externally modified templates) */
142
151
  forceRecreate: e(o(), !1),
143
152
  /** Migration-only inputs (legacy block configs) */
144
- migration: e(L, {})
145
- }), v = a({
153
+ migration: e(v, {})
154
+ }), P = a({
146
155
  /** Sender display name */
147
156
  senderName: e(t(), ""),
148
157
  /** Email subject line */
@@ -153,9 +162,9 @@ const d = {
153
162
  /** Path to translations object */
154
163
  translationsPath: e(t(), "window.trans[Object.keys(window.trans)[0]]"),
155
164
  /** Migration date for template compatibility */
156
- migrationDate: e(s(), 1759696858),
165
+ migrationDate: e(n(), 1759696858),
157
166
  /** Email header settings */
158
- emailHeader: e(v, { senderName: "", subject: "" }),
167
+ emailHeader: e(P, { senderName: "", subject: "" }),
159
168
  /** Folder name for user-saved modules (used by Stripo plugin panel for path construction) */
160
169
  savedModulesFolderName: e(t(), S.SAVED_MODULES),
161
170
  /** Folder name for default/prebuilt modules (used by Stripo plugin panel for path construction) */
@@ -184,7 +193,7 @@ const d = {
184
193
  liquidSyntax: e(o(), !1),
185
194
  /** Enable autosave (3-min interval + tab-hide). User toggles on/off from the header. */
186
195
  autosave: e(o(), !1)
187
- }), N = l([
196
+ }), D = l([
188
197
  "amp-accordion",
189
198
  "amp-carousel",
190
199
  "amp-form-controls",
@@ -198,7 +207,7 @@ const d = {
198
207
  "text-block",
199
208
  "timer-block",
200
209
  "video-block"
201
- ]), D = l([
210
+ ]), N = l([
202
211
  "dynamic-content",
203
212
  "checkbox-block",
204
213
  "radio-button-block",
@@ -209,12 +218,12 @@ const d = {
209
218
  ]), x = a({
210
219
  /** Default blocks to exclude from the editor */
211
220
  excludeDefaults: e(
212
- c(N),
221
+ c(D),
213
222
  []
214
223
  ),
215
224
  /** Custom blocks to include in the editor */
216
225
  includeCustoms: e(
217
- c(D),
226
+ c(N),
218
227
  []
219
228
  )
220
229
  }), m = a({
@@ -223,7 +232,7 @@ const d = {
223
232
  /** Human-readable description */
224
233
  description: e(t()),
225
234
  /** Priority for rule ordering (lower = earlier) */
226
- priority: s()
235
+ priority: n()
227
236
  }), F = a({
228
237
  ...m.entries,
229
238
  type: i("replace"),
@@ -255,7 +264,7 @@ const d = {
255
264
  (u) => typeof u == "function",
256
265
  "processor must be a function"
257
266
  )
258
- }), H = g("type", [
267
+ }), H = R("type", [
259
268
  F,
260
269
  U,
261
270
  B,
@@ -279,12 +288,12 @@ const d = {
279
288
  }), V = a({
280
289
  // Required sections
281
290
  /** Identity configuration (required) */
282
- identity: R,
291
+ identity: C,
283
292
  /** Partner configuration (required) */
284
- partner: C,
293
+ partner: A,
285
294
  // Optional sections (with defaults)
286
295
  /** Template content and presets */
287
- template: e(P, {}),
296
+ template: e(L, {}),
288
297
  /** Editor settings */
289
298
  editor: e(E, {}),
290
299
  /** UI configuration */
@@ -303,24 +312,24 @@ export {
303
312
  q as CallbacksSchema,
304
313
  H as CompilerRuleSchema,
305
314
  j as CompilerSchema,
306
- D as CustomBlockTypeSchema,
315
+ N as CustomBlockTypeSchema,
307
316
  w as CustomRuleSchema,
308
- N as DefaultBlockTypeSchema,
309
- A as DynamicContentSchema,
317
+ D as DefaultBlockTypeSchema,
318
+ y as DynamicContentSchema,
310
319
  E as EditorSchema,
311
- v as EmailHeaderSchema,
320
+ P as EmailHeaderSchema,
312
321
  T as FallbackFontSchema,
313
322
  O as FeaturesSchema,
314
323
  V as GuidoConfigSchema,
315
- R as IdentitySchema,
324
+ C as IdentitySchema,
316
325
  I as LegacyRecommendationConfigSchema,
317
326
  d as MessageType,
318
- C as PartnerSchema,
327
+ A as PartnerSchema,
319
328
  r as ProductType,
320
329
  U as RegexRuleSchema,
321
330
  B as RemoveRuleSchema,
322
331
  F as ReplaceRuleSchema,
323
- L as TemplateMigrationSchema,
324
- P as TemplateSchema,
332
+ v as TemplateMigrationSchema,
333
+ L as TemplateSchema,
325
334
  M as UISchema
326
335
  };
@@ -0,0 +1,36 @@
1
+ import { useConfig as D } from "./useConfig.js";
2
+ import { useToaster as T } from "./useToaster.js";
3
+ import { useTranslations as g } from "./useTranslations.js";
4
+ import { ToasterTypeOptions as C } from "../enums/toaster.js";
5
+ import { buildDynamicContentLookup as h, convertModuleDynamicContent as O } from "../utils/dynamicContentConverter.js";
6
+ const M = "dynamic-content.unresolved-attributes-on-drop", A = () => {
7
+ const { template: i, isFeatureEnabled: p } = D(), { showToaster: a } = T(), m = g();
8
+ return { handleModuleAdd: (n, e) => {
9
+ var s, c, l;
10
+ try {
11
+ if ((n == null ? void 0 : n.action) !== "ADD" && (n == null ? void 0 : n.action) !== "COPY")
12
+ return;
13
+ const o = h(((s = i.value) == null ? void 0 : s.availableDynamicContent) ?? []);
14
+ if (o.size === 0)
15
+ return;
16
+ const t = (c = e == null ? void 0 : e.getTargetNode) == null ? void 0 : c.call(e), r = (l = t == null ? void 0 : t.getOuterHTML) == null ? void 0 : l.call(t);
17
+ if (!t || typeof r != "string")
18
+ return;
19
+ const { html: u, unresolved: y } = O(
20
+ r,
21
+ o,
22
+ p("liquidSyntax")
23
+ );
24
+ u !== r && e.modifyHtml(t).replaceWith(u), y.length > 0 && a({
25
+ type: C.Warning,
26
+ message: m(M)
27
+ });
28
+ } catch (o) {
29
+ console.error("Failed to repair dynamic content on module add:", o);
30
+ }
31
+ } };
32
+ };
33
+ export {
34
+ M as UNRESOLVED_DYNAMIC_CONTENT_ON_DROP_KEY,
35
+ A as useModuleDynamicContentRepair
36
+ };
@@ -1,44 +1,45 @@
1
- import { useActionsApi as U } from "./useActionsApi.js";
2
- import { useBlocksConfig as R } from "./useBlocksConfig.js";
3
- import { useConfig as x } from "./useConfig.js";
4
- import { useCustomInterfaceAppearance as O } from "./useCustomInterfaceAppearance.js";
5
- import { useFullStoryBridge as q } from "./useFullStoryBridge.js";
6
- import { useStripoEventHandler as H } from "./useStripoEventHandler.js";
7
- import { useStripoNotifications as N } from "./useStripoNotifications.js";
8
- import { useToaster as z } from "./useToaster.js";
9
- import { localePatch as L } from "../config/i18n/index.js";
10
- import { en as h } from "../config/i18n/en/index.js";
11
- import { ToasterTypeOptions as $ } from "../enums/toaster.js";
12
- import { useStripoApi as G } from "../services/stripoApi.js";
13
- import J from "../static/styles/customEditorStyle.css.js";
14
- import { useEditorStore as E } from "../stores/editor.js";
15
- import { dynamicContentToMergeTags as X } from "../utils/genericUtil.js";
16
- import j from "../package.json.js";
17
- const fo = (C, l) => {
18
- const { features: c, template: w, isFeatureEnabled: u } = x(), { handleError: m, showToaster: b } = z(), { getToken: T, getCustomFonts: k, getSyncModulesStatus: B } = G(), { handleEvent: F } = H(), { getStripoBlocksConfig: v } = R(), { getStripoNotifications: V } = N(), _ = async (s, i = [], r = !1) => {
19
- var g, S, y;
20
- const o = E(), { html: p, css: a } = s, { baseBlocks: t, extensions: d } = await v(), f = ((g = c.value) == null ? void 0 : g.displayConditions) ?? !0, M = ((S = c.value) == null ? void 0 : S.modulesDisabled) ?? !1, P = ((y = w.value) == null ? void 0 : y.forceRecreate) ?? !1;
1
+ import { useActionsApi as x } from "./useActionsApi.js";
2
+ import { useBlocksConfig as O } from "./useBlocksConfig.js";
3
+ import { useConfig as q } from "./useConfig.js";
4
+ import { useCustomInterfaceAppearance as H } from "./useCustomInterfaceAppearance.js";
5
+ import { useFullStoryBridge as N } from "./useFullStoryBridge.js";
6
+ import { useModuleDynamicContentRepair as z } from "./useModuleDynamicContentRepair.js";
7
+ import { useStripoEventHandler as L } from "./useStripoEventHandler.js";
8
+ import { useStripoNotifications as $ } from "./useStripoNotifications.js";
9
+ import { useToaster as G } from "./useToaster.js";
10
+ import { localePatch as J } from "../config/i18n/index.js";
11
+ import { en as C } from "../config/i18n/en/index.js";
12
+ import { ToasterTypeOptions as X } from "../enums/toaster.js";
13
+ import { useStripoApi as j } from "../services/stripoApi.js";
14
+ import K from "../static/styles/customEditorStyle.css.js";
15
+ import { useEditorStore as w } from "../stores/editor.js";
16
+ import { buildMergeTagEntries as Q } from "../utils/genericUtil.js";
17
+ import W from "../package.json.js";
18
+ const ye = (b, l) => {
19
+ const { features: c, template: u, isFeatureEnabled: m } = q(), { handleError: p, showToaster: k } = G(), { getToken: v, getCustomFonts: T, getSyncModulesStatus: B } = j(), { handleEvent: F } = L(), { handleModuleAdd: M } = z(), { getStripoBlocksConfig: A } = O(), { getStripoNotifications: V } = $(), _ = async (s, i = [], r = !1) => {
20
+ var S, y, h, E;
21
+ const e = w(), { html: f, css: a } = s, { baseBlocks: t, extensions: d } = await A(), g = ((S = c.value) == null ? void 0 : S.displayConditions) ?? !0, P = ((y = c.value) == null ? void 0 : y.modulesDisabled) ?? !1, I = ((h = u.value) == null ? void 0 : h.forceRecreate) ?? !1;
21
22
  await window.UIEditor.initEditor(
22
23
  document.querySelector("#guido-editor"),
23
24
  {
24
- metadata: C,
25
- html: p,
25
+ metadata: b,
26
+ html: f,
26
27
  css: a,
27
- forceRecreate: P,
28
+ forceRecreate: I,
28
29
  locale: "en",
29
30
  undoButtonSelector: "#guido__undo-button",
30
31
  redoButtonSelector: "#guido__redo-button",
31
32
  mobileViewButtonSelector: ".guido__view-option-selection-mobile",
32
33
  desktopViewButtonSelector: ".guido__view-option-selection-desktop",
33
34
  codeEditorButtonSelector: "#guido__code-button",
34
- customAppearanceMergetags: !u("liquidSyntax"),
35
+ customAppearanceMergetags: !m("liquidSyntax"),
35
36
  customAppearanceMergetagsBorderColor: "#f1f3fe",
36
37
  customAppearanceMergetagsBackgroundColor: "#f1f3fe",
37
- customViewStyles: J,
38
- conditionsEnabled: f,
39
- customConditionsEnabled: f,
38
+ customViewStyles: K,
39
+ conditionsEnabled: g,
40
+ customConditionsEnabled: g,
40
41
  enableXSSSecurity: !0,
41
- modulesDisabled: M,
42
+ modulesDisabled: P,
42
43
  syncModulesEnabled: r,
43
44
  messageSettingsEnabled: !0,
44
45
  displayGmailAnnotations: !0,
@@ -56,43 +57,45 @@ const fo = (C, l) => {
56
57
  },
57
58
  mergeTags: [
58
59
  {
59
- entries: X(
60
+ entries: Q(
60
61
  l.preselectedDynamicContentList,
61
- u("liquidSyntax")
62
+ ((E = u.value) == null ? void 0 : E.availableDynamicContent) ?? [],
63
+ m("liquidSyntax")
62
64
  )
63
65
  }
64
66
  ],
65
- async onTokenRefreshRequest(e) {
67
+ async onTokenRefreshRequest(o) {
66
68
  try {
67
- const n = await T();
68
- e(n);
69
+ const n = await v();
70
+ o(n);
69
71
  } catch (n) {
70
- m(n, "Failed to refresh token");
72
+ p(n, "Failed to refresh token");
71
73
  }
72
74
  },
73
75
  onTemplateLoaded() {
74
76
  try {
75
- const { importCss: e } = O(), { activateCustomViewStyles: n, updateTimerInClonedTemplate: D } = U(), { injectFullStory: I } = q();
76
- e(), n(), I(), D(), l.onReady(), o.isStripoInitialized = !0, o.loadingStatus = !1, setTimeout(() => {
77
- o.hasChanges = !1;
77
+ const { importCss: o } = H(), { activateCustomViewStyles: n, updateTimerInClonedTemplate: R } = x(), { injectFullStory: U } = N();
78
+ o(), n(), U(), R(), l.onReady(), e.isStripoInitialized = !0, e.loadingStatus = !1, setTimeout(() => {
79
+ e.hasChanges = !1;
78
80
  }, 1e3);
79
- } catch (e) {
80
- m(e, "Failed to load custom interface appearance");
81
+ } catch (o) {
82
+ p(o, "Failed to load custom interface appearance");
81
83
  }
82
84
  },
83
- onCodeEditorVisibilityChanged(e) {
84
- o.isCodeEditorOpen = e;
85
+ onCodeEditorVisibilityChanged(o) {
86
+ e.isCodeEditorOpen = o;
85
87
  },
86
- onEditorVisualModeChanged(e) {
87
- o.editorVisualMode = e.toLowerCase();
88
+ onEditorVisualModeChanged(o) {
89
+ e.editorVisualMode = o.toLowerCase();
88
90
  },
89
- onVersionHistoryVisibilityChanged(e) {
90
- o.isVersionHistoryOpen = e;
91
+ onVersionHistoryVisibilityChanged(o) {
92
+ e.isVersionHistoryOpen = o;
91
93
  },
92
94
  onDataChanged() {
93
- o.hasChanges = !0;
95
+ e.hasChanges = !0;
94
96
  },
95
97
  onEvent: F,
98
+ onModuleAdd: M,
96
99
  notifications: V(),
97
100
  ignoreClickOutsideSelectors: [
98
101
  "#guido-dynamic-content-modal",
@@ -100,16 +103,16 @@ const fo = (C, l) => {
100
103
  ".in-drawer__container"
101
104
  ],
102
105
  extensions: d,
103
- localePatch: L
106
+ localePatch: J
104
107
  }
105
108
  );
106
- }, A = (s) => new Promise((i, r) => {
109
+ }, D = (s) => new Promise((i, r) => {
107
110
  var d;
108
111
  if (document.getElementById("UiEditorScript")) {
109
112
  Promise.resolve(s()).then(i, r);
110
113
  return;
111
114
  }
112
- const o = j.guido, a = `https://email-static.useinsider.com/guido/${(d = o == null ? void 0 : o.stripo) == null ? void 0 : d.version}/UIEditor.js`, t = document.createElement("script");
115
+ const e = W.guido, a = `https://email-static.useinsider.com/guido/${(d = e == null ? void 0 : e.stripo) == null ? void 0 : d.version}/UIEditor.js`, t = document.createElement("script");
113
116
  t.id = "UiEditorScript", t.type = "module", t.src = a, t.onload = () => {
114
117
  Promise.resolve(s()).then(i, r);
115
118
  }, t.onerror = () => {
@@ -117,21 +120,21 @@ const fo = (C, l) => {
117
120
  }, document.body.appendChild(t);
118
121
  });
119
122
  return { initPlugin: async (s) => {
120
- const i = E();
123
+ const i = w();
121
124
  try {
122
- await A(async () => {
123
- const [r, o] = await Promise.all([
124
- k(),
125
+ await D(async () => {
126
+ const [r, e] = await Promise.all([
127
+ T(),
125
128
  B()
126
129
  ]);
127
- i.syncModulesEnabled = o, await _(s, r, o);
130
+ i.syncModulesEnabled = e, await _(s, r, e);
128
131
  });
129
132
  } catch {
130
- i.loadingStatus = !1, b({
131
- type: $.Error,
132
- message: h["An error has occurred. Please report the error code to support."],
133
+ i.loadingStatus = !1, k({
134
+ type: X.Error,
135
+ message: C["An error has occurred. Please report the error code to support."],
133
136
  actionButton: {
134
- text: h["Reload page"],
137
+ text: C["Reload page"],
135
138
  onClick: () => window.location.reload()
136
139
  }
137
140
  });
@@ -139,5 +142,5 @@ const fo = (C, l) => {
139
142
  } };
140
143
  };
141
144
  export {
142
- fo as useStripo
145
+ ye as useStripo
143
146
  };
@@ -227,6 +227,24 @@ export declare const TemplateSchema: v.ObjectSchema<{
227
227
  readonly value: v.StringSchema<undefined>;
228
228
  }, undefined>, undefined>;
229
229
  }, undefined>, undefined>, readonly []>;
230
+ /**
231
+ * Full set of dynamic-content items the account offers (label + token).
232
+ * Used as the label→token map that repairs label-form placeholders
233
+ * (e.g. `{{ Phone Number }}`) in dropped saved modules.
234
+ */
235
+ readonly availableDynamicContent: v.OptionalSchema<v.ArraySchema<v.ObjectSchema<{
236
+ /** Display text for the dynamic content */
237
+ readonly text: v.StringSchema<undefined>;
238
+ /** Template variable value (e.g., {{username}}) */
239
+ readonly value: v.StringSchema<undefined>;
240
+ /** Fallback value if variable is empty */
241
+ readonly fallback: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
242
+ /** Optional formatting options */
243
+ readonly format: v.OptionalSchema<v.ObjectSchema<{
244
+ readonly key: v.StringSchema<undefined>;
245
+ readonly value: v.StringSchema<undefined>;
246
+ }, undefined>, undefined>;
247
+ }, undefined>, undefined>, readonly []>;
230
248
  /** Valid custom field attribute names from the partner's categorized fields */
231
249
  readonly customFieldAttributes: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, readonly []>;
232
250
  /** Selected unsubscribe page IDs */
@@ -622,6 +640,24 @@ export declare const GuidoConfigSchema: v.ObjectSchema<{
622
640
  readonly value: v.StringSchema<undefined>;
623
641
  }, undefined>, undefined>;
624
642
  }, undefined>, undefined>, readonly []>;
643
+ /**
644
+ * Full set of dynamic-content items the account offers (label + token).
645
+ * Used as the label→token map that repairs label-form placeholders
646
+ * (e.g. `{{ Phone Number }}`) in dropped saved modules.
647
+ */
648
+ readonly availableDynamicContent: v.OptionalSchema<v.ArraySchema<v.ObjectSchema<{
649
+ /** Display text for the dynamic content */
650
+ readonly text: v.StringSchema<undefined>;
651
+ /** Template variable value (e.g., {{username}}) */
652
+ readonly value: v.StringSchema<undefined>;
653
+ /** Fallback value if variable is empty */
654
+ readonly fallback: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
655
+ /** Optional formatting options */
656
+ readonly format: v.OptionalSchema<v.ObjectSchema<{
657
+ readonly key: v.StringSchema<undefined>;
658
+ readonly value: v.StringSchema<undefined>;
659
+ }, undefined>, undefined>;
660
+ }, undefined>, undefined>, readonly []>;
625
661
  /** Valid custom field attribute names from the partner's categorized fields */
626
662
  readonly customFieldAttributes: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, readonly []>;
627
663
  /** Selected unsubscribe page IDs */
@@ -34,6 +34,15 @@ export declare const useConfig: () => {
34
34
  value: string;
35
35
  } | undefined;
36
36
  }[];
37
+ availableDynamicContent: {
38
+ text: string;
39
+ value: string;
40
+ fallback?: string | undefined;
41
+ format?: {
42
+ key: string;
43
+ value: string;
44
+ } | undefined;
45
+ }[];
37
46
  customFieldAttributes: string[];
38
47
  selectedUnsubscribePages: number[];
39
48
  forceRecreate: boolean;
@@ -162,6 +171,15 @@ export declare const useConfig: () => {
162
171
  value: string;
163
172
  } | undefined;
164
173
  }[];
174
+ availableDynamicContent: {
175
+ text: string;
176
+ value: string;
177
+ fallback?: string | undefined;
178
+ format?: {
179
+ key: string;
180
+ value: string;
181
+ } | undefined;
182
+ }[];
165
183
  customFieldAttributes: string[];
166
184
  selectedUnsubscribePages: number[];
167
185
  forceRecreate: boolean;
@@ -0,0 +1,18 @@
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
+ export declare const UNRESOLVED_DYNAMIC_CONTENT_ON_DROP_KEY = "dynamic-content.unresolved-attributes-on-drop";
8
+ /**
9
+ * Repairs label-form dynamic-content tokens in saved modules as they are dropped
10
+ * or copied into the editor. Saved modules serialize merge-tag chips by their
11
+ * display label (`{{ Phone Number }}`); this maps them back to their canonical
12
+ * token (`{{phone_number}}`) using the account's dynamic-content list.
13
+ *
14
+ * Wired into Stripo via the `onModuleAdd` init callback (see `useStripo`).
15
+ */
16
+ export declare const useModuleDynamicContentRepair: () => {
17
+ handleModuleAdd: (data: ModuleAddData, modifier: ModuleModifier) => void;
18
+ };
@@ -39,6 +39,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
39
39
  value: string;
40
40
  } | undefined;
41
41
  }[];
42
+ availableDynamicContent: {
43
+ text: string;
44
+ value: string;
45
+ fallback?: string | undefined;
46
+ format?: {
47
+ key: string;
48
+ value: string;
49
+ } | undefined;
50
+ }[];
42
51
  customFieldAttributes: string[];
43
52
  selectedUnsubscribePages: number[];
44
53
  forceRecreate: boolean;
@@ -173,6 +182,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
173
182
  value: string;
174
183
  } | undefined;
175
184
  }[];
185
+ availableDynamicContent: {
186
+ text: string;
187
+ value: string;
188
+ fallback?: string | undefined;
189
+ format?: {
190
+ key: string;
191
+ value: string;
192
+ } | undefined;
193
+ }[];
176
194
  customFieldAttributes: string[];
177
195
  selectedUnsubscribePages: number[];
178
196
  forceRecreate: boolean;
@@ -307,6 +325,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
307
325
  value: string;
308
326
  } | undefined;
309
327
  }[];
328
+ availableDynamicContent: {
329
+ text: string;
330
+ value: string;
331
+ fallback?: string | undefined;
332
+ format?: {
333
+ key: string;
334
+ value: string;
335
+ } | undefined;
336
+ }[];
310
337
  customFieldAttributes: string[];
311
338
  selectedUnsubscribePages: number[];
312
339
  forceRecreate: boolean;
@@ -441,6 +468,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
441
468
  value: string;
442
469
  } | undefined;
443
470
  }[];
471
+ availableDynamicContent: {
472
+ text: string;
473
+ value: string;
474
+ fallback?: string | undefined;
475
+ format?: {
476
+ key: string;
477
+ value: string;
478
+ } | undefined;
479
+ }[];
444
480
  customFieldAttributes: string[];
445
481
  selectedUnsubscribePages: number[];
446
482
  forceRecreate: boolean;
@@ -575,6 +611,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
575
611
  value: string;
576
612
  } | undefined;
577
613
  }[];
614
+ availableDynamicContent: {
615
+ text: string;
616
+ value: string;
617
+ fallback?: string | undefined;
618
+ format?: {
619
+ key: string;
620
+ value: string;
621
+ } | undefined;
622
+ }[];
578
623
  customFieldAttributes: string[];
579
624
  selectedUnsubscribePages: number[];
580
625
  forceRecreate: boolean;
@@ -709,6 +754,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
709
754
  value: string;
710
755
  } | undefined;
711
756
  }[];
757
+ availableDynamicContent: {
758
+ text: string;
759
+ value: string;
760
+ fallback?: string | undefined;
761
+ format?: {
762
+ key: string;
763
+ value: string;
764
+ } | undefined;
765
+ }[];
712
766
  customFieldAttributes: string[];
713
767
  selectedUnsubscribePages: number[];
714
768
  forceRecreate: boolean;
@@ -843,6 +897,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
843
897
  value: string;
844
898
  } | undefined;
845
899
  }[];
900
+ availableDynamicContent: {
901
+ text: string;
902
+ value: string;
903
+ fallback?: string | undefined;
904
+ format?: {
905
+ key: string;
906
+ value: string;
907
+ } | undefined;
908
+ }[];
846
909
  customFieldAttributes: string[];
847
910
  selectedUnsubscribePages: number[];
848
911
  forceRecreate: boolean;
@@ -977,6 +1040,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
977
1040
  value: string;
978
1041
  } | undefined;
979
1042
  }[];
1043
+ availableDynamicContent: {
1044
+ text: string;
1045
+ value: string;
1046
+ fallback?: string | undefined;
1047
+ format?: {
1048
+ key: string;
1049
+ value: string;
1050
+ } | undefined;
1051
+ }[];
980
1052
  customFieldAttributes: string[];
981
1053
  selectedUnsubscribePages: number[];
982
1054
  forceRecreate: boolean;
@@ -1111,6 +1183,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1111
1183
  value: string;
1112
1184
  } | undefined;
1113
1185
  }[];
1186
+ availableDynamicContent: {
1187
+ text: string;
1188
+ value: string;
1189
+ fallback?: string | undefined;
1190
+ format?: {
1191
+ key: string;
1192
+ value: string;
1193
+ } | undefined;
1194
+ }[];
1114
1195
  customFieldAttributes: string[];
1115
1196
  selectedUnsubscribePages: number[];
1116
1197
  forceRecreate: boolean;
@@ -1245,6 +1326,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1245
1326
  value: string;
1246
1327
  } | undefined;
1247
1328
  }[];
1329
+ availableDynamicContent: {
1330
+ text: string;
1331
+ value: string;
1332
+ fallback?: string | undefined;
1333
+ format?: {
1334
+ key: string;
1335
+ value: string;
1336
+ } | undefined;
1337
+ }[];
1248
1338
  customFieldAttributes: string[];
1249
1339
  selectedUnsubscribePages: number[];
1250
1340
  forceRecreate: boolean;
@@ -1379,6 +1469,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1379
1469
  value: string;
1380
1470
  } | undefined;
1381
1471
  }[];
1472
+ availableDynamicContent: {
1473
+ text: string;
1474
+ value: string;
1475
+ fallback?: string | undefined;
1476
+ format?: {
1477
+ key: string;
1478
+ value: string;
1479
+ } | undefined;
1480
+ }[];
1382
1481
  customFieldAttributes: string[];
1383
1482
  selectedUnsubscribePages: number[];
1384
1483
  forceRecreate: boolean;
@@ -1513,6 +1612,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1513
1612
  value: string;
1514
1613
  } | undefined;
1515
1614
  }[];
1615
+ availableDynamicContent: {
1616
+ text: string;
1617
+ value: string;
1618
+ fallback?: string | undefined;
1619
+ format?: {
1620
+ key: string;
1621
+ value: string;
1622
+ } | undefined;
1623
+ }[];
1516
1624
  customFieldAttributes: string[];
1517
1625
  selectedUnsubscribePages: number[];
1518
1626
  forceRecreate: boolean;
@@ -1647,6 +1755,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1647
1755
  value: string;
1648
1756
  } | undefined;
1649
1757
  }[];
1758
+ availableDynamicContent: {
1759
+ text: string;
1760
+ value: string;
1761
+ fallback?: string | undefined;
1762
+ format?: {
1763
+ key: string;
1764
+ value: string;
1765
+ } | undefined;
1766
+ }[];
1650
1767
  customFieldAttributes: string[];
1651
1768
  selectedUnsubscribePages: number[];
1652
1769
  forceRecreate: boolean;
@@ -1781,6 +1898,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1781
1898
  value: string;
1782
1899
  } | undefined;
1783
1900
  }[];
1901
+ availableDynamicContent: {
1902
+ text: string;
1903
+ value: string;
1904
+ fallback?: string | undefined;
1905
+ format?: {
1906
+ key: string;
1907
+ value: string;
1908
+ } | undefined;
1909
+ }[];
1784
1910
  customFieldAttributes: string[];
1785
1911
  selectedUnsubscribePages: number[];
1786
1912
  forceRecreate: boolean;
@@ -1915,6 +2041,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1915
2041
  value: string;
1916
2042
  } | undefined;
1917
2043
  }[];
2044
+ availableDynamicContent: {
2045
+ text: string;
2046
+ value: string;
2047
+ fallback?: string | undefined;
2048
+ format?: {
2049
+ key: string;
2050
+ value: string;
2051
+ } | undefined;
2052
+ }[];
1918
2053
  customFieldAttributes: string[];
1919
2054
  selectedUnsubscribePages: number[];
1920
2055
  forceRecreate: boolean;
@@ -2049,6 +2184,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2049
2184
  value: string;
2050
2185
  } | undefined;
2051
2186
  }[];
2187
+ availableDynamicContent: {
2188
+ text: string;
2189
+ value: string;
2190
+ fallback?: string | undefined;
2191
+ format?: {
2192
+ key: string;
2193
+ value: string;
2194
+ } | undefined;
2195
+ }[];
2052
2196
  customFieldAttributes: string[];
2053
2197
  selectedUnsubscribePages: number[];
2054
2198
  forceRecreate: boolean;
@@ -2183,6 +2327,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2183
2327
  value: string;
2184
2328
  } | undefined;
2185
2329
  }[];
2330
+ availableDynamicContent: {
2331
+ text: string;
2332
+ value: string;
2333
+ fallback?: string | undefined;
2334
+ format?: {
2335
+ key: string;
2336
+ value: string;
2337
+ } | undefined;
2338
+ }[];
2186
2339
  customFieldAttributes: string[];
2187
2340
  selectedUnsubscribePages: number[];
2188
2341
  forceRecreate: boolean;
@@ -2317,6 +2470,15 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2317
2470
  value: string;
2318
2471
  } | undefined;
2319
2472
  }[];
2473
+ availableDynamicContent: {
2474
+ text: string;
2475
+ value: string;
2476
+ fallback?: string | undefined;
2477
+ format?: {
2478
+ key: string;
2479
+ value: string;
2480
+ } | undefined;
2481
+ }[];
2320
2482
  customFieldAttributes: string[];
2321
2483
  selectedUnsubscribePages: number[];
2322
2484
  forceRecreate: boolean;
@@ -0,0 +1,27 @@
1
+ import type { DynamicContent } from '@@/Types/generic';
2
+ export interface DynamicContentConversionResult {
3
+ html: string;
4
+ /** Distinct tokens that matched neither a known value nor a known label (reported to the user). */
5
+ unresolved: string[];
6
+ }
7
+ /**
8
+ * Builds a label→item lookup from one or more dynamic-content lists, keyed by
9
+ * normalized label (trimmed, lowercased). When lists overlap, a later list wins
10
+ * for a shared key.
11
+ */
12
+ export declare const buildDynamicContentLookup: (...lists: DynamicContent[][]) => Map<string, DynamicContent>;
13
+ /**
14
+ * Repairs dynamic-content tokens in a dropped module's HTML, using `lookup`
15
+ * (label→item) built from the account's full dynamic-content list. Returns the
16
+ * rewritten HTML plus the tokens it could not resolve.
17
+ *
18
+ * A token is rewritten to its canonical `{{value}}` form when its name matches a
19
+ * display label (`{{ Phone Number }}` → `{{phone_number}}`). Left untouched: an
20
+ * exact canonical value (already correct — this also stops a valid token being
21
+ * rewritten just because it coincides with an unrelated label), Liquid nested
22
+ * tokens (dotted, e.g. `coupon.code`), recommendation-block variables
23
+ * (`123_0_attr`), allowed system tokens, and `data-*` product tags. The account
24
+ * list is exhaustive, so any remaining token matching neither a value nor a label
25
+ * is reported in `unresolved`; the text itself is always left in place.
26
+ */
27
+ export declare const convertModuleDynamicContent: (html: string, lookup: Map<string, DynamicContent>, liquidSyntax: boolean) => DynamicContentConversionResult;
@@ -1,6 +1,13 @@
1
1
  import type { DynamicContent, MergeTag } from '@@/Types/generic';
2
2
  export declare const mergeTagToDynamicContent: (mergeTag: MergeTag) => DynamicContent;
3
3
  export declare const dynamicContentToMergeTags: (dynamicContentList: DynamicContent[], liquidSyntax?: boolean) => MergeTag[];
4
+ /**
5
+ * Builds the editor merge-tag entries from the template's preselected list plus
6
+ * the account's full available list, deduped by token value (preselected wins so
7
+ * its fallback/format is kept). Registering the full list lets every recognized
8
+ * token — including ones repaired on module drop — render as a chip.
9
+ */
10
+ export declare const buildMergeTagEntries: (preselected: DynamicContent[], available: DynamicContent[], liquidSyntax?: boolean) => MergeTag[];
4
11
  export declare const getCsrfToken: () => string;
5
12
  /**
6
13
  * URL Parameter utilities
@@ -0,0 +1,31 @@
1
+ import { DATA_ATTRIBUTE_REGEX as A, DYNAMIC_CONTENT_TAG_REGEX as m, ALLOWED_DYNAMIC_SYSTEM_TOKENS as D } from "../enums/html-validator.js";
2
+ import { dynamicContentToMergeTags as T } from "./genericUtil.js";
3
+ const p = "guidoDataMask", M = "EndDataMask", f = /guidoDataMask(\d+)EndDataMask/g, h = /^\d+_\d+_.+$/, E = (o) => o.trim().toLowerCase(), I = (...o) => {
4
+ const r = /* @__PURE__ */ new Map();
5
+ return o.forEach((i) => {
6
+ i.forEach((s) => {
7
+ const a = E(s.text);
8
+ a && r.set(a, s);
9
+ });
10
+ }), r;
11
+ }, w = (o, r, i) => {
12
+ const s = /* @__PURE__ */ new Set(), a = new Set([...r.values()].map((e) => e.value)), u = [];
13
+ return { html: o.replace(A, (e) => (u.push(e), `${p}${u.length - 1}${M}`)).replace(m, (e) => {
14
+ const n = e.slice(2, -2), c = n.indexOf("|"), l = c === -1 ? n : n.slice(0, c), t = l.trim();
15
+ if (t === "" || D.includes(t) || t.includes(".") || h.test(t) || a.has(t))
16
+ return e;
17
+ const d = r.get(E(t));
18
+ if (d) {
19
+ if (c !== -1) {
20
+ const _ = n.slice(c);
21
+ return `{{${l.replace(t, () => d.value)}${_}}}`;
22
+ }
23
+ return T([d], i)[0].value;
24
+ }
25
+ return s.add(t), e;
26
+ }).replace(f, (e, n) => u[Number(n)]), unresolved: [...s] };
27
+ };
28
+ export {
29
+ I as buildDynamicContentLookup,
30
+ w as convertModuleDynamicContent
31
+ };
@@ -1,47 +1,54 @@
1
- const s = (e) => {
2
- const a = e.value.match(/\{\{([^}]+)\}\}/)[1].split("|").map((l) => l.trim()), [n] = a, c = {
3
- text: e.label,
1
+ const u = (t) => {
2
+ const a = t.value.match(/\{\{([^}]+)\}\}/)[1].split("|").map((l) => l.trim()), [n] = a, e = {
3
+ text: t.label,
4
4
  value: n || ""
5
5
  };
6
6
  if (a.length >= 2) {
7
- const [, l, r] = a;
7
+ const [, l, f] = a;
8
8
  if (l.startsWith("default:")) {
9
- let t = l.slice(8).trim();
10
- t.startsWith('"') && t.endsWith('"') && (t = t.slice(1, -1)), c.fallback = t;
9
+ let r = l.slice(8).trim();
10
+ r.startsWith('"') && r.endsWith('"') && (r = r.slice(1, -1)), e.fallback = r;
11
11
  } else {
12
- const t = l.includes("=") ? { key: l.split("=")[0].trim(), value: l.split("=")[1].trim() } : null;
13
- t ? c.format = t : r || (c.fallback = l), r && (c.fallback = r);
12
+ const r = l.includes("=") ? { key: l.split("=")[0].trim(), value: l.split("=")[1].trim() } : null;
13
+ r ? e.format = r : f || (e.fallback = l), f && (e.fallback = f);
14
14
  }
15
15
  }
16
- return c;
17
- }, u = (e, f = !1) => e.map((a) => {
16
+ return e;
17
+ }, s = (t, c = !1) => t.map((a) => {
18
18
  if (a.format)
19
19
  return {
20
20
  label: `${a.text} | ${a.format.key}=${a.format.value}`,
21
21
  value: `{{${a.value}|${a.format.key}=${a.format.value}}}`
22
22
  };
23
- if (f) {
23
+ if (c) {
24
24
  if (!a.fallback)
25
25
  return {
26
26
  label: a.text,
27
27
  value: `{{ ${a.value} }}`
28
28
  };
29
- const c = !Number.isNaN(Number(a.fallback)) && a.fallback.trim() !== "" ? a.fallback : `"${a.fallback}"`;
29
+ const e = !Number.isNaN(Number(a.fallback)) && a.fallback.trim() !== "" ? a.fallback : `"${a.fallback}"`;
30
30
  return {
31
31
  label: `${a.text} | ${a.fallback}`,
32
- value: `{{ ${a.value} | default: ${c} }}`
32
+ value: `{{ ${a.value} | default: ${e} }}`
33
33
  };
34
34
  }
35
35
  return {
36
36
  label: a.fallback ? `${a.text} | ${a.fallback}` : a.text,
37
37
  value: a.fallback ? `{{${a.value}|${a.fallback}}}` : `{{${a.value}}}`
38
38
  };
39
- }), o = () => {
40
- var e;
41
- return ((e = document.head.querySelector('meta[name="csrf-token"]')) == null ? void 0 : e.getAttribute("content")) ?? "";
39
+ }), o = (t, c, a = !1) => {
40
+ const n = new Set(t.map((l) => l.value)), e = c.filter((l) => n.has(l.value) ? !1 : (n.add(l.value), !0));
41
+ return [
42
+ ...s(t, a),
43
+ ...s(e, a)
44
+ ];
45
+ }, b = () => {
46
+ var t;
47
+ return ((t = document.head.querySelector('meta[name="csrf-token"]')) == null ? void 0 : t.getAttribute("content")) ?? "";
42
48
  };
43
49
  export {
44
- u as dynamicContentToMergeTags,
45
- o as getCsrfToken,
46
- s as mergeTagToDynamicContent
50
+ o as buildMergeTagEntries,
51
+ s as dynamicContentToMergeTags,
52
+ b as getCsrfToken,
53
+ u as mergeTagToDynamicContent
47
54
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useinsider/guido",
3
- "version": "3.11.0-beta.ad6af17",
3
+ "version": "3.11.0-beta.c237b25",
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",