@useinsider/guido 3.14.1-beta.9ad6513 → 3.15.0-beta.166619d

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,43 +1,57 @@
1
- import { useActionsApi as T } from "./useActionsApi.js";
2
- import { useToaster as f } from "./useToaster.js";
3
- import { useTranslations as y } from "./useTranslations.js";
4
- import { ToasterTypeOptions as n } from "../enums/toaster.js";
5
- import { ref as v } from "vue";
6
- const E = 250, s = v({}), o = {}, j = () => {
7
- const { updateHtmlAndCss: l } = T(), { showToaster: p } = f(), i = y(), u = (e) => {
8
- s.value[e] === void 0 && (s.value = { ...s.value, [e]: "pending" });
9
- }, c = (e, t, r) => {
10
- s.value = { ...s.value, [e]: "applying" };
1
+ import { useActionsApi as d } from "./useActionsApi.js";
2
+ import { useConfig as D } from "./useConfig.js";
3
+ import { useToaster as h } from "./useToaster.js";
4
+ import { useTranslations as E } from "./useTranslations.js";
5
+ import { ToasterTypeOptions as f } from "../enums/toaster.js";
6
+ import { useDynamicContentStore as A } from "../stores/dynamic-content.js";
7
+ import { flattenDynamicContentList as S } from "../utils/genericUtil.js";
8
+ import { extractUsedDynamicContent as O } from "../utils/registerUsedDynamicContent.js";
9
+ import { ref as U } from "vue";
10
+ const w = 250, o = U({}), s = {}, u = /* @__PURE__ */ new Map(), z = () => {
11
+ const { updateHtmlAndCss: c } = d(), { showToaster: m } = h(), y = E(), { template: T } = D(), v = A(), g = (e, t) => {
12
+ const a = y(e);
13
+ return a === e ? t : a;
14
+ }, C = (e) => {
15
+ o.value[e] === void 0 && (o.value = { ...o.value, [e]: "pending" });
16
+ }, l = (e, t, a) => {
17
+ var n;
18
+ o.value = { ...o.value, [e]: "applying" };
11
19
  try {
12
- l(t, r), s.value = { ...s.value, [e]: "applied" }, p({
13
- type: n.Success,
14
- message: i("newsletter.ai-template-applied")
15
- });
16
- } catch (a) {
17
- s.value = { ...s.value, [e]: "failed" }, p({
18
- type: n.Error,
19
- message: a instanceof Error ? a.message : "Failed to apply template"
20
+ c(t, a), o.value = { ...o.value, [e]: "applied" };
21
+ try {
22
+ const r = S((n = T.value) == null ? void 0 : n.dynamicContentList), p = O(t, r);
23
+ p.length > 0 && v.registerDynamicContent(p);
24
+ } catch {
25
+ }
26
+ u.get(e) !== t && (u.set(e, t), m({
27
+ type: f.Success,
28
+ message: g("newsletter.ai-template-applied", "Template applied to the editor")
29
+ }));
30
+ } catch (r) {
31
+ o.value = { ...o.value, [e]: "failed" }, m({
32
+ type: f.Error,
33
+ message: r instanceof Error ? r.message : "Failed to apply template"
20
34
  });
21
35
  }
22
- }, m = (e) => {
23
- const t = o[e];
24
- t && (clearTimeout(t), delete o[e]);
36
+ }, i = (e) => {
37
+ const t = s[e];
38
+ t && (clearTimeout(t), delete s[e]);
25
39
  };
26
- return { applyStatus: s, markPending: u, applyTemplate: (e, t) => {
27
- t.html && (m(e), c(e, t.html, t.css ?? ""));
40
+ return { applyStatus: o, markPending: C, applyTemplate: (e, t) => {
41
+ t.html && (i(e), l(e, t.html, t.css ?? ""));
28
42
  }, applyTemplateDebounced: (e, t) => {
29
43
  if (!t.html)
30
44
  return;
31
- m(e);
32
- const { html: r } = t, a = t.css ?? "";
33
- o[e] = setTimeout(() => {
34
- delete o[e], c(e, r, a);
35
- }, E);
45
+ i(e);
46
+ const { html: a } = t, n = t.css ?? "";
47
+ s[e] = setTimeout(() => {
48
+ delete s[e], l(e, a, n);
49
+ }, w);
36
50
  } };
37
- }, k = () => {
38
- Object.values(o).forEach(clearTimeout), Object.keys(o).forEach((l) => delete o[l]), s.value = {};
51
+ }, G = () => {
52
+ Object.values(s).forEach(clearTimeout), Object.keys(s).forEach((c) => delete s[c]), o.value = {};
39
53
  };
40
54
  export {
41
- k as resetEmailTemplateApplier,
42
- j as useEmailTemplateApplier
55
+ G as resetEmailTemplateApplier,
56
+ z as useEmailTemplateApplier
43
57
  };
@@ -1,64 +1,69 @@
1
- import { useConfig as W } from "./useConfig.js";
2
- import { getIgnoredRecommendationBlockIds as G } from "../config/compiler/utils/recommendationIgnoreUtils.js";
3
- import { TemplateTypes as H } from "../enums/defaults.js";
4
- import { DISPLAY_CONDITIONS_REGEX as P, DISPLAY_CONDITIONS_EXCEPTIONS_REGEX as X, CampaignCouldNotBeSavedKey as $, CanNotMakeAnyChangesForRunningKey as Y, DATA_ATTRIBUTE_REGEX as U, DYNAMIC_CONTENT_TAG_REGEX as j, ALLOWED_DYNAMIC_SYSTEM_TOKENS as q, VALID_DYNAMIC_VARIABLE_REGEX as K } from "../enums/html-validator.js";
1
+ import { useConfig as V } from "./useConfig.js";
2
+ import { getIgnoredRecommendationBlockIds as M } from "../config/compiler/utils/recommendationIgnoreUtils.js";
3
+ import { TemplateTypes as W } from "../enums/defaults.js";
4
+ import { DISPLAY_CONDITIONS_REGEX as G, DISPLAY_CONDITIONS_EXCEPTIONS_REGEX as H, CampaignCouldNotBeSavedKey as P, CanNotMakeAnyChangesForRunningKey as X, DATA_ATTRIBUTE_REGEX as $, DYNAMIC_CONTENT_TAG_REGEX as Y, ALLOWED_DYNAMIC_SYSTEM_TOKENS as U, VALID_DYNAMIC_VARIABLE_REGEX as j } from "../enums/html-validator.js";
5
5
  import { ToasterTypeOptions as c } from "../enums/toaster.js";
6
- import { itemsBlockDynamicVariables as z } from "../extensions/Blocks/Items/enums/productEnums.js";
7
- import { RECOMMENDATION_VARIABLE_BUILTIN_ATTRIBUTES as Z } from "../extensions/Blocks/Recommendation/constants/selectors.js";
8
- import { useRecommendationExtensionStore as J } from "../extensions/Blocks/Recommendation/store/recommendation.js";
9
- import { resolveProductAttrValue as Q } from "../extensions/Blocks/Recommendation/templates/utils.js";
10
- import { RecommendationRequiredFieldsKey as ee } from "../extensions/Blocks/Recommendation/validation/requiredFields.js";
11
- import { useRecommendationStore as te } from "../stores/recommendation.js";
12
- import { base64EncodeWithSpecialChars as ne } from "../utils/base64.js";
13
- import { useHttp as se } from "./useHttp.js";
14
- import { useToaster as re } from "./useToaster.js";
15
- import { useTranslations as oe } from "./useTranslations.js";
16
- const ie = /recommendation-id="(\d+)"/g;
17
- function ae(t) {
18
- return [...t.matchAll(ie)].map((m) => m[1]);
6
+ import { itemsBlockDynamicVariables as q } from "../extensions/Blocks/Items/enums/productEnums.js";
7
+ import { RECOMMENDATION_VARIABLE_BUILTIN_ATTRIBUTES as K } from "../extensions/Blocks/Recommendation/constants/selectors.js";
8
+ import { useRecommendationExtensionStore as z } from "../extensions/Blocks/Recommendation/store/recommendation.js";
9
+ import { resolveProductAttrValue as Z } from "../extensions/Blocks/Recommendation/templates/utils.js";
10
+ import { RecommendationRequiredFieldsKey as J } from "../extensions/Blocks/Recommendation/validation/requiredFields.js";
11
+ import { useRecommendationStore as Q } from "../stores/recommendation.js";
12
+ import { base64EncodeWithSpecialChars as ee } from "../utils/base64.js";
13
+ import { computed as te } from "vue";
14
+ import { useHttp as ne } from "./useHttp.js";
15
+ import { useToaster as se } from "./useToaster.js";
16
+ import { useTranslations as re } from "./useTranslations.js";
17
+ const oe = /recommendation-id="(\d+)"/g;
18
+ function ie(n) {
19
+ return [...n.matchAll(oe)].map((m) => m[1]);
19
20
  }
20
- function ce(t, m) {
21
- return m.some((f) => t.startsWith(`${f}_`));
21
+ function ae(n, m) {
22
+ return m.some((f) => n.startsWith(`${f}_`));
22
23
  }
23
- const le = /^(\d+)_\d+_(.+)$/;
24
- function me(t) {
25
- const m = t.match(le);
24
+ const ce = /^(\d+)_\d+_(.+)$/;
25
+ function le(n) {
26
+ const m = n.match(ce);
26
27
  return m ? { recoId: m[1], attribute: m[2] } : null;
27
28
  }
28
- function ue(t) {
29
- const m = new Set(Z);
30
- return Object.values(t).forEach((f) => {
31
- m.add(Q(f.attributeName, t).toLowerCase());
29
+ function me(n) {
30
+ const m = new Set(K);
31
+ return Object.values(n).forEach((f) => {
32
+ m.add(Z(f.attributeName, n).toLowerCase());
32
33
  }), m;
33
34
  }
34
- function de(t, m, f, o, d) {
35
- const u = t.match(/{{([a-zA-Z0-9_.\s]*)}}/gm);
35
+ function ue(n, m, f, o, d) {
36
+ const u = n.match(/{{([a-zA-Z0-9_.\s]*)}}/gm);
36
37
  if (!u)
37
38
  return [];
38
- const p = new Set(m.map((h) => h.toLowerCase())), E = [];
39
+ const g = new Set(m.map((h) => h.toLowerCase())), E = [];
39
40
  return u.forEach((h) => {
40
- const g = h.slice(2, -2).trim().toLowerCase();
41
- if (g !== "" && p.has(g))
41
+ const p = h.slice(2, -2).trim().toLowerCase();
42
+ if (p !== "" && g.has(p))
42
43
  return;
43
- const y = me(g);
44
+ const y = le(p);
44
45
  if (y && o.has(y.recoId)) {
45
46
  if (!d || d.has(y.attribute))
46
47
  return;
47
- E.push(g);
48
+ E.push(p);
48
49
  return;
49
50
  }
50
- ce(g, f) || g.includes(".") || E.push(g);
51
+ ae(p, f) || p.includes(".") || E.push(p);
51
52
  }), E;
52
53
  }
53
- function fe(t) {
54
- return (t.replace(U, "$1").match(j) ?? []).filter((o) => {
55
- const d = o.slice(2, -2), u = d.indexOf("|"), p = (u === -1 ? d : d.slice(0, u)).trim();
56
- return q.includes(p) ? !1 : K.test(p) ? u !== -1 && d.slice(u + 1).trim() === "" : !0;
54
+ function de(n) {
55
+ return (n.replace($, "$1").match(Y) ?? []).filter((o) => {
56
+ const d = o.slice(2, -2), u = d.indexOf("|"), g = (u === -1 ? d : d.slice(0, u)).trim();
57
+ return U.includes(g) ? !1 : j.test(g) ? u !== -1 && d.slice(u + 1).trim() === "" : !0;
57
58
  });
58
59
  }
59
60
  const xe = () => {
60
- var b, S;
61
- const { showToaster: t } = re(), { post: m } = se(), { config: f } = W(), o = oe(), d = te(), u = J(), p = ((S = (b = f.value) == null ? void 0 : b.partner) == null ? void 0 : S.messageType) === H.transactional, E = async (e) => {
61
+ const { showToaster: n } = se(), { post: m } = ne(), { config: f } = V(), o = re(), d = Q(), u = z(), g = te(
62
+ () => {
63
+ var e, t;
64
+ return ((t = (e = f.value) == null ? void 0 : e.partner) == null ? void 0 : t.messageType) === W.transactional;
65
+ }
66
+ ), E = async (e) => {
62
67
  if (e.size === 0)
63
68
  return /* @__PURE__ */ new Set();
64
69
  try {
@@ -66,61 +71,61 @@ const xe = () => {
66
71
  } catch {
67
72
  return null;
68
73
  }
69
- return ue(u.filterList);
74
+ return me(u.filterList);
70
75
  }, h = async (e) => {
71
- const n = await m(
76
+ const s = await m(
72
77
  "/newsletter/template-library/check-template-html-body",
73
- { html: ne(e) }
74
- ), { status: r, message: a } = n.data;
75
- return r || t({
78
+ { html: ee(e) }
79
+ ), { status: r, message: a } = s.data;
80
+ return r || n({
76
81
  type: c.Error,
77
82
  message: r === void 0 ? a : o("newsletter.invalid-url-link-for-toaster")
78
- }), o($), a === o(Y) && t({
83
+ }), o(P), a === o(X) && n({
79
84
  type: c.Error,
80
85
  message: o("newsletter.already-in-progress")
81
86
  }), r;
82
- }, g = (e) => !["if", "endif", "else", "elif", "now"].includes(e.toLowerCase()), y = (e) => ["if", "endif"].includes(e.toLowerCase()), T = (e, s) => {
83
- const n = e.match(/({%(.*?)%})/g);
87
+ }, p = (e) => !["if", "endif", "else", "elif", "now"].includes(e.toLowerCase()), y = (e) => ["if", "endif"].includes(e.toLowerCase()), S = (e, t) => {
88
+ const s = e.match(/({%(.*?)%})/g);
84
89
  let r = !0;
85
- return n !== null && !p && n.forEach((a) => {
90
+ return s !== null && !g.value && s.forEach((a) => {
86
91
  const i = a.slice(2, -2).trim().match(/(".*?"|[^"\s]+)(?=\s*|\s*$)/g);
87
92
  if (i && i.length > 0) {
88
93
  const [l] = i;
89
- g(l) && !s.includes(l) && (t({
94
+ p(l) && !t.includes(l) && (n({
90
95
  type: c.Warning,
91
96
  message: o("custom-fields.invalid-custom-fields")
92
97
  }), r = !1);
93
98
  }
94
99
  }), r;
95
- }, w = async (e, s, n) => {
96
- const r = n ? await h(e) : !0;
97
- return T(e, s) && r;
98
- }, R = (e) => e.length > 0 ? !0 : (t({
100
+ }, A = async (e, t, s) => {
101
+ const r = s ? await h(e) : !0;
102
+ return S(e, t) && r;
103
+ }, T = (e) => e.length > 0 ? !0 : (n({
99
104
  type: c.Warning,
100
105
  message: o("newsletter.html-content-is-empty")
101
- }), !1), _ = (e) => {
102
- const s = (e.match(/{/gm) || []).length, n = (e.match(/}/gm) || []).length;
103
- return s > n && t({
106
+ }), !1), w = (e) => {
107
+ const t = (e.match(/{/gm) || []).length, s = (e.match(/}/gm) || []).length;
108
+ return t > s && n({
104
109
  type: c.Warning,
105
110
  message: o("custom-fields.missing-closing-braces")
106
- }), s < n && t({
111
+ }), t < s && n({
107
112
  type: c.Warning,
108
113
  message: o("custom-fields.missing-opening-braces")
109
- }), s === n;
110
- }, N = (e) => {
111
- const s = fe(e).length === 0;
112
- return s || t({
114
+ }), t === s;
115
+ }, R = (e) => {
116
+ const t = de(e).length === 0;
117
+ return t || n({
113
118
  type: c.Warning,
114
119
  message: o("custom-fields.invalid-custom-fields")
115
- }), s;
116
- }, x = (e, s, n, r) => {
117
- if (p)
120
+ }), t;
121
+ }, _ = (e, t, s, r) => {
122
+ if (g.value)
118
123
  return !0;
119
- const a = de(
124
+ const a = ue(
120
125
  e,
126
+ t,
127
+ ie(e),
121
128
  s,
122
- ae(e),
123
- n,
124
129
  r
125
130
  );
126
131
  if (a.length > 0) {
@@ -129,91 +134,91 @@ const xe = () => {
129
134
  ${a.map((l) => `<li>${l}</li>`).join("")}
130
135
  </ul>
131
136
  `;
132
- return t({
137
+ return n({
133
138
  type: c.Error,
134
139
  message: o("custom-fields.invalid-custom-fields") + i
135
140
  }), !1;
136
141
  }
137
142
  return !0;
138
- }, O = (e) => {
139
- const s = e.match(/{%(.*?)%}/g), n = [];
143
+ }, N = (e) => {
144
+ const t = e.match(/{%(.*?)%}/g), s = [];
140
145
  let r = !0;
141
- if (s && s.forEach((a) => {
142
- const i = a.match(P), l = a.match(X), v = (i == null ? void 0 : i.join("")) || "";
143
- (!i || a !== v) && !l && (t({
146
+ if (t && t.forEach((a) => {
147
+ const i = a.match(G), l = a.match(H), C = (i == null ? void 0 : i.join("")) || "";
148
+ (!i || a !== C) && !l && (n({
144
149
  type: c.Error,
145
150
  message: o("newsletter.display-conditions-invalid-syntax")
146
- }), r = !1), i && i.forEach((C) => {
147
- C.trim() === "=" && (t({
151
+ }), r = !1), i && i.forEach((v) => {
152
+ v.trim() === "=" && (n({
148
153
  type: c.Error,
149
154
  message: o("custom-conditions.wrong-equality-operators")
150
155
  }), r = !1);
151
- const I = C.match(/^[a-zA-Z]*$/g);
152
- I && I.forEach((A) => {
153
- y(A) && n.push(A);
156
+ const I = v.match(/^[a-zA-Z]*$/g);
157
+ I && I.forEach((b) => {
158
+ y(b) && s.push(b);
154
159
  });
155
160
  });
156
- }), n.length) {
157
- const a = n.filter((l) => l === "if"), i = n.filter((l) => l === "endif");
158
- a.length !== i.length && (t({
161
+ }), s.length) {
162
+ const a = s.filter((l) => l === "if"), i = s.filter((l) => l === "endif");
163
+ a.length !== i.length && (n({
159
164
  type: c.Error,
160
165
  message: o("custom-conditions.missing-if-endif-tag")
161
166
  }), r = !1);
162
167
  }
163
168
  return r;
164
- }, k = (e) => {
165
- const s = (e.match(/{% /gm) || []).length, n = (e.match(/ %}/gm) || []).length, r = s === n;
166
- return r || t({
169
+ }, x = (e) => {
170
+ const t = (e.match(/{% /gm) || []).length, s = (e.match(/ %}/gm) || []).length, r = t === s;
171
+ return r || n({
167
172
  type: c.Warning,
168
173
  message: o("custom-conditions.no-space-after-braces")
169
174
  }), r;
170
- }, B = (e) => (e.match(/({%(.*?)%})/g) || []).filter((n) => n.includes("if")).map((n) => (n.match(/{{.*}}/gm) || []).length).reduce((n, r) => n + r, 0) > 0 ? (t({
175
+ }, O = (e) => (e.match(/({%(.*?)%})/g) || []).filter((s) => s.includes("if")).map((s) => (s.match(/{{.*}}/gm) || []).length).reduce((s, r) => s + r, 0) > 0 ? (n({
171
176
  type: c.Warning,
172
177
  message: o("custom-conditions.no-braces-inside-if-tag")
173
- }), !1) : !0, D = () => u.hasInvalidBlock() ? (t({
178
+ }), !1) : !0, k = () => u.hasInvalidBlock() ? (n({
174
179
  type: c.Error,
175
- message: o(ee)
176
- }), !1) : !0, F = () => d.recommendationConfigs && Object.values(d.recommendationConfigs).find((s) => s.filters.find((n) => n.value === "")) !== void 0 ? (t({
180
+ message: o(J)
181
+ }), !1) : !0, B = () => d.recommendationConfigs && Object.values(d.recommendationConfigs).find((t) => t.filters.find((s) => s.value === "")) !== void 0 ? (n({
177
182
  type: c.Error,
178
183
  message: o("newsletter.fill-all-necessary-fields")
179
- }), !1) : !0, L = (e) => {
180
- const s = /src="[^"]*\.(svg|pst)"/gm;
181
- return e.match(s) === null ? !0 : (t({
184
+ }), !1) : !0, D = (e) => {
185
+ const t = /src="[^"]*\.(svg|pst)"/gm;
186
+ return e.match(t) === null ? !0 : (n({
182
187
  type: c.Error,
183
188
  message: o("newsletter.invalid-image-type")
184
189
  }), !1);
185
- }, V = (e) => {
190
+ }, F = (e) => {
186
191
  const r = new DOMParser().parseFromString(e, "text/html").querySelectorAll(".checkbox-block-v2");
187
192
  return Array.from(r).find((i) => {
188
193
  var l;
189
194
  return !((l = i.id) != null && l.trim());
190
- }) ? (t({
195
+ }) ? (n({
191
196
  type: c.Error,
192
197
  message: o("unsubscribe-templates.select-checkbox-groups")
193
198
  }), !1) : !0;
194
- }, M = (e) => {
199
+ }, L = (e) => {
195
200
  const r = new DOMParser().parseFromString(e, "text/html").querySelectorAll(".radio-button-v2");
196
201
  return Array.from(r).find((i) => {
197
202
  var l;
198
203
  return !((l = i.id) != null && l.trim());
199
- }) ? (t({
204
+ }) ? (n({
200
205
  type: c.Error,
201
206
  message: o("unsubscribe-templates.select-radio-button-groups")
202
207
  }), !1) : !0;
203
208
  };
204
- return { validateHtml: async (e, s, n = !1) => {
205
- var v, C;
209
+ return { validateHtml: async (e, t, s = !1) => {
210
+ var C, v;
206
211
  const r = [
207
- ...s.map((I) => I.value),
208
- ...z,
209
- ...((C = (v = f.value) == null ? void 0 : v.template) == null ? void 0 : C.customFieldAttributes) ?? []
210
- ], a = G(e), i = await E(a);
211
- return await w(e, r, n) && R(e) && _(e) && N(e) && x(e, r, a, i) && O(e) && k(e) && B(e) && D() && F() && L(e) && V(e) && M(e);
212
+ ...t.map((I) => I.value),
213
+ ...q,
214
+ ...((v = (C = f.value) == null ? void 0 : C.template) == null ? void 0 : v.customFieldAttributes) ?? []
215
+ ], a = M(e), i = await E(a);
216
+ return await A(e, r, s) && T(e) && w(e) && R(e) && _(e, r, a, i) && N(e) && x(e) && O(e) && k() && B() && D(e) && F(e) && L(e);
212
217
  } };
213
218
  };
214
219
  export {
215
- ue as buildPartnerAttributeNameSet,
216
- de as findInvalidCustomFields,
217
- me as parseRecommendationVariable,
220
+ me as buildPartnerAttributeNameSet,
221
+ ue as findInvalidCustomFields,
222
+ le as parseRecommendationVariable,
218
223
  xe as useHtmlValidator
219
224
  };
@@ -1,7 +1,7 @@
1
1
  import { ModificationDescription as d } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
2
  import { CommonControl as s } from "../../../common-control.js";
3
3
  import { getCurrentLayout as l } from "../main/utils.js";
4
- const i = "recommendation-card-background-color-control", e = {
4
+ const u = "recommendation-card-background-color-control", e = {
5
5
  CARD_BACKGROUND_COLOR: "cardBackgroundColor"
6
6
  }, a = {
7
7
  GRID: ".product-card-segment",
@@ -9,17 +9,13 @@ const i = "recommendation-card-background-color-control", e = {
9
9
  };
10
10
  class _ extends s {
11
11
  getId() {
12
- return i;
12
+ return u;
13
13
  }
14
14
  getTemplate() {
15
- return `
16
- <div class="card-bg-control-container">
17
- ${this._GuTwoColumns([
15
+ return this._GuTwoColumns([
18
16
  this._GuLabel({ text: this.api.translate("Card Background Color") }),
19
17
  this._GuColorPicker(e.CARD_BACKGROUND_COLOR)
20
- ])}
21
- </div>
22
- `;
18
+ ]);
23
19
  }
24
20
  onRender() {
25
21
  this._setFormValues(), this._listenToFormUpdates();
@@ -75,6 +71,6 @@ class _ extends s {
75
71
  }
76
72
  }
77
73
  export {
78
- i as CARD_BACKGROUND_COLOR_CONTROL_ID,
74
+ u as CARD_BACKGROUND_COLOR_CONTROL_ID,
79
75
  _ as CardBackgroundColorControl
80
76
  };
@@ -1,16 +1,16 @@
1
- var G = Object.defineProperty;
2
- var P = (u, s, t) => s in u ? G(u, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : u[s] = t;
3
- var C = (u, s, t) => P(u, typeof s != "symbol" ? s + "" : s, t);
4
- import { ModificationDescription as m } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- import { CommonControl as R } from "../../../common-control.js";
6
- import { DESKTOP_CONTAINER_SELECTOR as S, MOBILE_CONTAINER_SELECTOR as M } from "../../constants/selectors.js";
7
- import { SPACING_STEP as _, MAX_SPACING as N, MIN_SPACING as b, DEFAULT_COLUMN_SPACING as g, DEFAULT_ROW_SPACING as E } from "../../constants/layout.js";
1
+ var y = Object.defineProperty;
2
+ var M = (u, s, t) => s in u ? y(u, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : u[s] = t;
3
+ var g = (u, s, t) => M(u, typeof s != "symbol" ? s + "" : s, t);
4
+ import { ModificationDescription as S } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
+ import { CommonControl as E } from "../../../common-control.js";
6
+ import { DESKTOP_CONTAINER_SELECTOR as m, MOBILE_CONTAINER_SELECTOR as L } from "../../constants/selectors.js";
7
+ import { SPACING_STEP as P, MAX_SPACING as G, MIN_SPACING as R, DEFAULT_COLUMN_SPACING as C, DEFAULT_ROW_SPACING as I } from "../../constants/layout.js";
8
8
  import { RecommendationConfigService as p } from "../../services/configService.js";
9
9
  import { useRecommendationExtensionStore as w } from "../../store/recommendation.js";
10
- import { safeGetStyle as L, safeGetParent as V } from "../../utils/tagName.js";
11
- import { getCurrentLayout as f, getBlockElement as B } from "../main/utils.js";
12
- import { useDebounceFn as O } from "../../../../../node_modules/@vueuse/shared/index.js";
13
- const U = "recommendation-spacing-control", n = {
10
+ import { safeGetStyle as f, safeGetParent as B } from "../../utils/tagName.js";
11
+ import { getCurrentLayout as _, getBlockElement as U } from "../main/utils.js";
12
+ import { useDebounceFn as N } from "../../../../../node_modules/@vueuse/shared/index.js";
13
+ const V = "recommendation-spacing-control", o = {
14
14
  COLUMN_SPACING: "columnSpacing",
15
15
  COLUMN_SPACING_LABEL: "columnSpacingLabel",
16
16
  ROW_SPACING: "rowSpacing",
@@ -19,97 +19,67 @@ const U = "recommendation-spacing-control", n = {
19
19
  MOBILE_COLUMN_SPACING_LABEL: "mobileColumnSpacingLabel",
20
20
  MOBILE_ROW_SPACING: "mobileRowSpacing",
21
21
  MOBILE_ROW_SPACING_LABEL: "mobileRowSpacingLabel"
22
- }, A = {
22
+ }, b = {
23
23
  COLUMN_SPACING: "data-column-spacing",
24
24
  ROW_SPACING: "data-row-spacing",
25
25
  MOBILE_COLUMN_SPACING: "data-mobile-column-spacing",
26
26
  MOBILE_ROW_SPACING: "data-mobile-row-spacing"
27
27
  };
28
- function I(u, s) {
28
+ function O(u, s) {
29
29
  if (!u)
30
30
  return s;
31
31
  const t = parseFloat(u);
32
32
  return Number.isNaN(t) ? s : t;
33
33
  }
34
- class j extends R {
34
+ class j extends E {
35
35
  constructor() {
36
36
  super(...arguments);
37
- C(this, "store", w());
38
- C(this, "unsubscribeOrientation", null);
37
+ g(this, "store", w());
38
+ g(this, "unsubscribeOrientation", null);
39
39
  /**
40
40
  * Debounced version of _onColumnSpacingChange
41
41
  * Prevents excessive DOM updates when user rapidly adjusts the counter
42
42
  */
43
- C(this, "_debouncedOnColumnSpacingChange", O((t) => {
43
+ g(this, "_debouncedOnColumnSpacingChange", N((t) => {
44
44
  this._onColumnSpacingChange(t);
45
45
  }, 300));
46
46
  /**
47
47
  * Debounced version of _onRowSpacingChange
48
48
  */
49
- C(this, "_debouncedOnRowSpacingChange", O((t) => {
49
+ g(this, "_debouncedOnRowSpacingChange", N((t) => {
50
50
  this._onRowSpacingChange(t);
51
51
  }, 300));
52
52
  /**
53
53
  * Debounced version of _onMobileColumnSpacingChange
54
54
  */
55
- C(this, "_debouncedOnMobileColumnSpacingChange", O((t) => {
55
+ g(this, "_debouncedOnMobileColumnSpacingChange", N((t) => {
56
56
  this._onMobileColumnSpacingChange(t);
57
57
  }, 300));
58
58
  /**
59
59
  * Debounced version of _onMobileRowSpacingChange
60
60
  */
61
- C(this, "_debouncedOnMobileRowSpacingChange", O((t) => {
61
+ g(this, "_debouncedOnMobileRowSpacingChange", N((t) => {
62
62
  this._onMobileRowSpacingChange(t);
63
63
  }, 300));
64
64
  }
65
65
  getId() {
66
- return U;
66
+ return V;
67
67
  }
68
68
  getTemplate() {
69
69
  return `
70
70
  <div class="spacing-control-container">
71
- ${this._GuTwoColumns([
72
- this._GuLabel({
73
- text: this.api.translate("Column Spacing (px)"),
74
- name: n.COLUMN_SPACING_LABEL
75
- }),
76
- this._GuCounter({
77
- name: n.COLUMN_SPACING,
78
- minValue: b,
79
- maxValue: N,
80
- step: _
81
- }),
82
- this._GuLabel({
83
- text: this.api.translate("Row Spacing (px)"),
84
- name: n.ROW_SPACING_LABEL
85
- }),
86
- this._GuCounter({
87
- name: n.ROW_SPACING,
88
- minValue: b,
89
- maxValue: N,
90
- step: _
91
- }),
92
- this._GuLabel({
93
- text: this.api.translate("Column Spacing on Mobile (px)"),
94
- name: n.MOBILE_COLUMN_SPACING_LABEL
95
- }),
96
- this._GuCounter({
97
- name: n.MOBILE_COLUMN_SPACING,
98
- minValue: b,
99
- maxValue: N,
100
- step: _
101
- }),
102
- this._GuLabel({
103
- text: this.api.translate("Row Spacing on Mobile (px)"),
104
- name: n.MOBILE_ROW_SPACING_LABEL
105
- }),
106
- this._GuCounter({
107
- name: n.MOBILE_ROW_SPACING,
108
- minValue: b,
109
- maxValue: N,
110
- step: _
111
- })
112
- ])}
71
+ ${this._spacingRow("Column Spacing (px)", o.COLUMN_SPACING_LABEL, o.COLUMN_SPACING)}
72
+ ${this._spacingRow("Row Spacing (px)", o.ROW_SPACING_LABEL, o.ROW_SPACING)}
73
+ ${this._spacingRow(
74
+ "Column Spacing on Mobile (px)",
75
+ o.MOBILE_COLUMN_SPACING_LABEL,
76
+ o.MOBILE_COLUMN_SPACING
77
+ )}
78
+ ${this._spacingRow(
79
+ "Row Spacing on Mobile (px)",
80
+ o.MOBILE_ROW_SPACING_LABEL,
81
+ o.MOBILE_ROW_SPACING
82
+ )}
113
83
  </div>
114
84
  `;
115
85
  }
@@ -122,6 +92,21 @@ class j extends R {
122
92
  onDestroy() {
123
93
  this.unsubscribeOrientation && (this.unsubscribeOrientation(), this.unsubscribeOrientation = null);
124
94
  }
95
+ /**
96
+ * One label + counter per two-column row, so every spacing field gets the same
97
+ * 16px-padded row the color controls already render instead of sharing one grid.
98
+ */
99
+ _spacingRow(t, e, n) {
100
+ return this._GuTwoColumns([
101
+ this._GuLabel({ text: this.api.translate(t), name: e }),
102
+ this._GuCounter({
103
+ name: n,
104
+ minValue: R,
105
+ maxValue: G,
106
+ step: P
107
+ })
108
+ ]);
109
+ }
125
110
  /**
126
111
  * Updates spacing control visibility based on layout orientation and mobileLayoutEnabled.
127
112
  * - List layout: hide column spacing (products are full-width), hide ALL mobile
@@ -132,14 +117,14 @@ class j extends R {
132
117
  _updateSpacingVisibility() {
133
118
  if (!this.api)
134
119
  return;
135
- const t = p.getConfig(this.currentNode), e = this.store.recommendationConfigs.orientation, i = (t.layout || e || f(this.currentNode)) === "grid", { mobileLayoutEnabled: a } = t, r = t.cardsInRow > 1, c = t.mobileCardsInRow > 1;
136
- this.api.setVisibility(n.COLUMN_SPACING, i && r), this.api.setVisibility(n.COLUMN_SPACING_LABEL, i && r), this.api.setVisibility(n.ROW_SPACING, !0), this.api.setVisibility(n.ROW_SPACING_LABEL, !0), this.api.setVisibility(
137
- n.MOBILE_COLUMN_SPACING,
120
+ const t = p.getConfig(this.currentNode), e = this.store.recommendationConfigs.orientation, i = (t.layout || e || _(this.currentNode)) === "grid", { mobileLayoutEnabled: a } = t, r = t.cardsInRow > 1, c = t.mobileCardsInRow > 1;
121
+ this.api.setVisibility(o.COLUMN_SPACING, i && r), this.api.setVisibility(o.COLUMN_SPACING_LABEL, i && r), this.api.setVisibility(o.ROW_SPACING, !0), this.api.setVisibility(o.ROW_SPACING_LABEL, !0), this.api.setVisibility(
122
+ o.MOBILE_COLUMN_SPACING,
138
123
  i && a && c
139
124
  ), this.api.setVisibility(
140
- n.MOBILE_COLUMN_SPACING_LABEL,
125
+ o.MOBILE_COLUMN_SPACING_LABEL,
141
126
  i && a && c
142
- ), this.api.setVisibility(n.MOBILE_ROW_SPACING, i && a), this.api.setVisibility(n.MOBILE_ROW_SPACING_LABEL, i && a);
127
+ ), this.api.setVisibility(o.MOBILE_ROW_SPACING, i && a), this.api.setVisibility(o.MOBILE_ROW_SPACING_LABEL, i && a);
143
128
  }
144
129
  /**
145
130
  * Reads spacing values from node config first, falls back to DOM styles.
@@ -148,12 +133,12 @@ class j extends R {
148
133
  _setFormValues() {
149
134
  if (!this.api)
150
135
  return;
151
- const t = p.hasConfig(this.currentNode), e = p.getConfig(this.currentNode), o = t ? e.columnSpacing : this._getStoredColumnSpacing(), i = t ? e.rowSpacing : this._getStoredRowSpacing();
136
+ const t = p.hasConfig(this.currentNode), e = p.getConfig(this.currentNode), n = t ? e.columnSpacing : this._getStoredColumnSpacing(), i = t ? e.rowSpacing : this._getStoredRowSpacing();
152
137
  this.api.updateValues({
153
- [n.COLUMN_SPACING]: o,
154
- [n.ROW_SPACING]: i,
155
- [n.MOBILE_COLUMN_SPACING]: e.mobileColumnSpacing,
156
- [n.MOBILE_ROW_SPACING]: e.mobileRowSpacing
138
+ [o.COLUMN_SPACING]: n,
139
+ [o.ROW_SPACING]: i,
140
+ [o.MOBILE_COLUMN_SPACING]: e.mobileColumnSpacing,
141
+ [o.MOBILE_ROW_SPACING]: e.mobileRowSpacing
157
142
  });
158
143
  }
159
144
  /**
@@ -165,20 +150,20 @@ class j extends R {
165
150
  */
166
151
  _getStoredColumnSpacing() {
167
152
  if (!this.currentNode)
168
- return g;
169
- const t = this.currentNode.querySelector(S) ?? this.currentNode;
170
- if (f(this.currentNode) === "grid") {
171
- const h = t.querySelector(".recommendation-attribute-row"), l = (h == null ? void 0 : h.querySelector("td")) ?? null, d = L(l, "padding");
153
+ return C;
154
+ const t = this.currentNode.querySelector(m) ?? this.currentNode;
155
+ if (_(this.currentNode) === "grid") {
156
+ const h = t.querySelector(".recommendation-attribute-row"), l = (h == null ? void 0 : h.querySelector("td")) ?? null, d = f(l, "padding");
172
157
  if (!d)
173
- return g;
174
- const y = d.trim().split(/\s+/);
175
- return y.length < 2 ? g : I(y[1], g / 2) * 2;
158
+ return C;
159
+ const A = d.trim().split(/\s+/);
160
+ return A.length < 2 ? C : O(A[1], C / 2) * 2;
176
161
  }
177
- const o = t.querySelector(".product-card-wrapper") ?? null, i = V(o), a = L(i, "padding");
162
+ const n = t.querySelector(".product-card-wrapper") ?? null, i = B(n), a = f(i, "padding");
178
163
  if (!a)
179
- return g;
164
+ return C;
180
165
  const r = a.trim().split(/\s+/);
181
- return r.length < 2 ? g : I(r[1], g / 2) * 2;
166
+ return r.length < 2 ? C : O(r[1], C / 2) * 2;
182
167
  }
183
168
  /**
184
169
  * Gets stored row spacing from the first spacer element's height style
@@ -186,9 +171,9 @@ class j extends R {
186
171
  */
187
172
  _getStoredRowSpacing() {
188
173
  if (!this.currentNode)
189
- return E;
190
- const e = (this.currentNode.querySelector(S) ?? this.currentNode).querySelector(".spacer") ?? null, o = L(e, "height");
191
- return I(o, E);
174
+ return I;
175
+ const e = (this.currentNode.querySelector(m) ?? this.currentNode).querySelector(".spacer") ?? null, n = f(e, "height");
176
+ return O(n, I);
192
177
  }
193
178
  // ========================================================================
194
179
  // Desktop Spacing Handlers
@@ -205,9 +190,9 @@ class j extends R {
205
190
  this.currentNode,
206
191
  { columnSpacing: t },
207
192
  `Changed column spacing to ${t}px`
208
- ), this._storeDataAttribute(A.COLUMN_SPACING, t);
209
- const o = p.getConfig(this.currentNode).layout || f(this.currentNode), i = this.api.getDocumentModifier(), r = `0 ${t / 2}px`, c = this.currentNode.querySelector(S);
210
- c && (o === "grid" ? Array.from(
193
+ ), this._storeDataAttribute(b.COLUMN_SPACING, t);
194
+ const n = p.getConfig(this.currentNode).layout || _(this.currentNode), i = this.api.getDocumentModifier(), r = `0 ${t / 2}px`, c = this.currentNode.querySelector(m);
195
+ c && (n === "grid" ? Array.from(
211
196
  c.querySelectorAll(".attribute-cell")
212
197
  ).forEach((l) => {
213
198
  i.modifyHtml(l).setStyle("padding", r);
@@ -216,7 +201,7 @@ class j extends R {
216
201
  ).forEach((l) => {
217
202
  const d = "parent" in l ? l.parent() : void 0;
218
203
  d && i.modifyHtml(d).setStyle("padding", r);
219
- }), i.apply(new m(`Update column spacing to ${t}px`)));
204
+ }), i.apply(new S(`Update column spacing to ${t}px`)));
220
205
  }
221
206
  /**
222
207
  * Handles row spacing changes for desktop.
@@ -230,19 +215,19 @@ class j extends R {
230
215
  this.currentNode,
231
216
  { rowSpacing: t },
232
217
  `Changed row spacing to ${t}px`
233
- ), this._storeDataAttribute(A.ROW_SPACING, t);
234
- const e = this.currentNode.querySelector(S);
218
+ ), this._storeDataAttribute(b.ROW_SPACING, t);
219
+ const e = this.currentNode.querySelector(m);
235
220
  if (!e)
236
221
  return;
237
- const o = Array.from(
222
+ const n = Array.from(
238
223
  e.querySelectorAll(".spacer")
239
224
  );
240
- if (!o.length)
225
+ if (!n.length)
241
226
  return;
242
227
  const i = this.api.getDocumentModifier(), a = `${t}px`;
243
- o.forEach((r) => {
228
+ n.forEach((r) => {
244
229
  i.modifyHtml(r).setStyle("height", a);
245
- }), i.apply(new m(`Update row spacing to ${t}px`));
230
+ }), i.apply(new S(`Update row spacing to ${t}px`));
246
231
  }
247
232
  // ========================================================================
248
233
  // Mobile Spacing Handlers
@@ -259,9 +244,9 @@ class j extends R {
259
244
  this.currentNode,
260
245
  { mobileColumnSpacing: t },
261
246
  `Changed mobile column spacing to ${t}px`
262
- ), this._storeDataAttribute(A.MOBILE_COLUMN_SPACING, t);
263
- const o = p.getConfig(this.currentNode).layout || f(this.currentNode), i = this.api.getDocumentModifier(), r = `0 ${t / 2}px`, c = this.currentNode.querySelector(M);
264
- c && (o === "grid" ? Array.from(
247
+ ), this._storeDataAttribute(b.MOBILE_COLUMN_SPACING, t);
248
+ const n = p.getConfig(this.currentNode).layout || _(this.currentNode), i = this.api.getDocumentModifier(), r = `0 ${t / 2}px`, c = this.currentNode.querySelector(L);
249
+ c && (n === "grid" ? Array.from(
265
250
  c.querySelectorAll(".attribute-cell")
266
251
  ).forEach((l) => {
267
252
  i.modifyHtml(l).setStyle("padding", r);
@@ -270,7 +255,7 @@ class j extends R {
270
255
  ).forEach((l) => {
271
256
  const d = "parent" in l ? l.parent() : void 0;
272
257
  d && i.modifyHtml(d).setStyle("padding", r);
273
- }), i.apply(new m(`Update mobile column spacing to ${t}px`)));
258
+ }), i.apply(new S(`Update mobile column spacing to ${t}px`)));
274
259
  }
275
260
  /**
276
261
  * Handles row spacing changes for mobile.
@@ -284,19 +269,19 @@ class j extends R {
284
269
  this.currentNode,
285
270
  { mobileRowSpacing: t },
286
271
  `Changed mobile row spacing to ${t}px`
287
- ), this._storeDataAttribute(A.MOBILE_ROW_SPACING, t);
288
- const e = this.currentNode.querySelector(M);
272
+ ), this._storeDataAttribute(b.MOBILE_ROW_SPACING, t);
273
+ const e = this.currentNode.querySelector(L);
289
274
  if (!e)
290
275
  return;
291
- const o = Array.from(
276
+ const n = Array.from(
292
277
  e.querySelectorAll(".spacer")
293
278
  );
294
- if (!o.length)
279
+ if (!n.length)
295
280
  return;
296
281
  const i = this.api.getDocumentModifier(), a = `${t}px`;
297
- o.forEach((r) => {
282
+ n.forEach((r) => {
298
283
  i.modifyHtml(r).setStyle("height", a);
299
- }), i.apply(new m(`Update mobile row spacing to ${t}px`));
284
+ }), i.apply(new S(`Update mobile row spacing to ${t}px`));
300
285
  }
301
286
  // ========================================================================
302
287
  // Data Attribute Storage
@@ -305,23 +290,23 @@ class j extends R {
305
290
  * Stores a spacing value as a data attribute on the block root element
306
291
  */
307
292
  _storeDataAttribute(t, e) {
308
- const o = B(this.currentNode);
309
- o && this.api.getDocumentModifier().modifyHtml(o).setAttribute(t, e.toString()).apply(new m(`Store ${t}`));
293
+ const n = U(this.currentNode);
294
+ n && this.api.getDocumentModifier().modifyHtml(n).setAttribute(t, e.toString()).apply(new S(`Store ${t}`));
310
295
  }
311
296
  // ========================================================================
312
297
  // Event Listeners
313
298
  // ========================================================================
314
299
  _listenToFormUpdates() {
315
- this.api.onValueChanged(n.COLUMN_SPACING, (t) => {
300
+ this.api.onValueChanged(o.COLUMN_SPACING, (t) => {
316
301
  const e = parseInt(t);
317
302
  Number.isNaN(e) || this._debouncedOnColumnSpacingChange(e);
318
- }), this.api.onValueChanged(n.ROW_SPACING, (t) => {
303
+ }), this.api.onValueChanged(o.ROW_SPACING, (t) => {
319
304
  const e = parseInt(t);
320
305
  Number.isNaN(e) || this._debouncedOnRowSpacingChange(e);
321
- }), this.api.onValueChanged(n.MOBILE_COLUMN_SPACING, (t) => {
306
+ }), this.api.onValueChanged(o.MOBILE_COLUMN_SPACING, (t) => {
322
307
  const e = parseInt(t);
323
308
  Number.isNaN(e) || this._debouncedOnMobileColumnSpacingChange(e);
324
- }), this.api.onValueChanged(n.MOBILE_ROW_SPACING, (t) => {
309
+ }), this.api.onValueChanged(o.MOBILE_ROW_SPACING, (t) => {
325
310
  const e = parseInt(t);
326
311
  Number.isNaN(e) || this._debouncedOnMobileRowSpacingChange(e);
327
312
  });
@@ -337,6 +322,6 @@ class j extends R {
337
322
  }
338
323
  }
339
324
  export {
340
- U as SPACING_CONTROL_ID,
325
+ V as SPACING_CONTROL_ID,
341
326
  j as SpacingControl
342
327
  };
@@ -25,6 +25,27 @@ const n = `/* Utils */
25
25
  border-top: none;
26
26
  }
27
27
 
28
+ /* The editor's row-separator rule only reaches \`.container\`s that are direct
29
+ children of \`.container-wrapper\`; the spacing rows sit one level deeper, so the
30
+ first one loses its line. Restore it with the editor's own border token. */
31
+ .spacing-control-container > .container {
32
+ border-top: var(--ue-border-width-1, 1px) solid var(--ue-panels-border-color, rgba(0, 0, 0, 0.07));
33
+ }
34
+
35
+ /* A spacing field that doesn't apply (mobile rows, column spacing in list layout) is
36
+ removed from its row and leaves the wrapper behind — collapse it so it can't
37
+ render as an empty separator-topped strip. */
38
+ .spacing-control-container > .container:not(:has(ue-counter)) {
39
+ display: none;
40
+ }
41
+
42
+ /* The block background row stacks the editor's "inherits background color" banner
43
+ above its field, separated by the editor's default 10px gap — match the tab's 16px.
44
+ Scoped via the spacing control so other blocks keep the default. */
45
+ .ue-scrollable-content:has(.spacing-control-container) .e2e-block-bg-color {
46
+ row-gap: 16px;
47
+ }
48
+
28
49
  /* Right-align the mobile layout toggle within the two-column grid */
29
50
  .product-layout-control-container ue-switcher {
30
51
  justify-self: end;
@@ -30,6 +30,11 @@ export declare class SpacingControl extends CommonControl {
30
30
  onRender(): void;
31
31
  onTemplateNodeUpdated(node: ImmutableHtmlNode): void;
32
32
  onDestroy(): void;
33
+ /**
34
+ * One label + counter per two-column row, so every spacing field gets the same
35
+ * 16px-padded row the color controls already render instead of sharing one grid.
36
+ */
37
+ _spacingRow(label: string, labelName: string, counterName: string): string;
33
38
  /**
34
39
  * Updates spacing control visibility based on layout orientation and mobileLayoutEnabled.
35
40
  * - List layout: hide column spacing (products are full-width), hide ALL mobile
@@ -36,4 +36,12 @@ export declare const useDynamicContentStore: import("pinia").StoreDefinition<"gu
36
36
  }[];
37
37
  }, {
38
38
  setSelectedDynamicContent(dynamicContent: DynamicContent): void;
39
+ /**
40
+ * Append-only bulk registration for dynamic content used by an
41
+ * AI-applied template. Unlike `setSelectedDynamicContent` it doesn't
42
+ * touch `selectedDynamicContent`; the getter removes duplicates, so
43
+ * re-registering the same entries is harmless. Items are shallow-copied so Pinia
44
+ * reactivity can't write a later mutation back into the host's catalog.
45
+ */
46
+ registerDynamicContent(items: DynamicContent[]): void;
39
47
  }>;
@@ -0,0 +1,8 @@
1
+ import type { DynamicContent } from '@@/Types/generic';
2
+ export declare const extractUsedDynamicContent: (html: string, flattenedCatalog: DynamicContent[] | undefined | null) => DynamicContent[];
3
+ /**
4
+ * Dev-diagnostic sibling of `extractUsedDynamicContent`: the used token names
5
+ * with NO catalog match (so intentionally not registered). Surfaced via a
6
+ * dev-gated `console.debug` to spot a neo↔guido vocabulary mismatch. Never throws.
7
+ */
8
+ export declare const getUnmatchedDynamicContentTokens: (html: string, flattenedCatalog: DynamicContent[] | undefined | null) => string[];
@@ -1,6 +1,6 @@
1
1
  import { removeDuplicatesFromArray as n } from "../utils/arrayUtil.js";
2
2
  import { defineStore as a } from "pinia";
3
- const s = a("guidoDynamicContent", {
3
+ const c = a("guidoDynamicContent", {
4
4
  state: () => ({
5
5
  selectedDynamicContent: { text: "", value: "" },
6
6
  selectedDynamicContentList: []
@@ -8,6 +8,16 @@ const s = a("guidoDynamicContent", {
8
8
  actions: {
9
9
  setSelectedDynamicContent(t) {
10
10
  this.selectedDynamicContent = t, this.selectedDynamicContentList.push(t);
11
+ },
12
+ /**
13
+ * Append-only bulk registration for dynamic content used by an
14
+ * AI-applied template. Unlike `setSelectedDynamicContent` it doesn't
15
+ * touch `selectedDynamicContent`; the getter removes duplicates, so
16
+ * re-registering the same entries is harmless. Items are shallow-copied so Pinia
17
+ * reactivity can't write a later mutation back into the host's catalog.
18
+ */
19
+ registerDynamicContent(t) {
20
+ this.selectedDynamicContentList.push(...t.map((e) => ({ ...e })));
11
21
  }
12
22
  },
13
23
  getters: {
@@ -20,5 +30,5 @@ const s = a("guidoDynamicContent", {
20
30
  }
21
31
  });
22
32
  export {
23
- s as useDynamicContentStore
33
+ c as useDynamicContentStore
24
34
  };
@@ -0,0 +1,23 @@
1
+ import { DATA_ATTRIBUTE_REGEX as T } from "../enums/html-validator.js";
2
+ const d = /{{([a-zA-Z0-9_.\s]*)}}/gm, h = (n, e) => {
3
+ if (!e || e.length === 0)
4
+ return { matched: [], unmatchedTokens: [] };
5
+ const s = n.replace(T, "$1").match(d);
6
+ if (!s)
7
+ return { matched: [], unmatchedTokens: [] };
8
+ const u = new Map(
9
+ e.map((c) => [c.value.trim().toLowerCase(), c])
10
+ ), a = /* @__PURE__ */ new Set(), o = [], m = [];
11
+ return s.forEach((c) => {
12
+ const t = c.slice(2, -2).trim().toLowerCase();
13
+ if (t === "" || a.has(t))
14
+ return;
15
+ a.add(t);
16
+ const r = u.get(t);
17
+ r ? o.push(r) : m.push(t);
18
+ }), { matched: o, unmatchedTokens: m };
19
+ }, E = (n, e) => h(n, e).matched, k = (n, e) => h(n, e).unmatchedTokens;
20
+ export {
21
+ E as extractUsedDynamicContent,
22
+ k as getUnmatchedDynamicContentTokens
23
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useinsider/guido",
3
- "version": "3.14.1-beta.9ad6513",
3
+ "version": "3.15.0-beta.166619d",
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",