@useinsider/guido 3.27.0-beta.cf35c0c → 3.27.0-beta.ee85b86

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
@@ -119,7 +119,8 @@ const config: GuidoConfigInput = {
119
119
  // { [partnerName]: [{ text, value, children }] } (architect is multi-partner).
120
120
  // Guido flattens the active (first) partner's leaf attributes and uses them to
121
121
  // repair label-form tokens (`{{ Phone Number }}` -> `{{phone_number}}`) in
122
- // dropped saved modules; an unresolvable token raises a single warning toaster
122
+ // dropped saved modules; a label shared by several attributes opens a picker
123
+ // modal (see "Saved-module token repair"); an unresolvable token raises a single warning toaster
123
124
  // (translation key `dynamic-content.unresolved-attributes-on-drop`, registered
124
125
  // host-side). Omit it and the repair is a silent no-op. Default: {}.
125
126
  dynamicContentList?: DynamicContentList,
@@ -691,18 +692,22 @@ const closeModal = () => {
691
692
 
692
693
  ### Saved-module token repair
693
694
 
694
- Legacy **saved modules** may serialize their merge-tag chips by display label
695
+ Legacy or hand-written **saved modules** may carry tokens by display label
695
696
  (`{{ Phone Number }}`) instead of the canonical token (`{{phone_number}}`), which the
696
697
  backend can't resolve. When such a module is dropped or copied into the editor, Guido
697
698
  repairs these automatically using the `template.dynamicContentList` catalog (see
698
699
  [Configuration](#configuration)):
699
700
 
700
- - **Label match** → canonical value: `{{ Phone Number }}` → `{{phone_number}}` (any author `| default:` / format tail is preserved).
701
- - Already-canonical values, Liquid nested tokens (`coupon.code`), recommendation-block variables (`123_0_price`), allowed system tokens, and `data-*` product tags are left untouched.
701
+ - **Off under `features.liquidSyntax`**: Liquid templates keep every token exactly as written — no rewrite, no modal, no toaster. The Items currency and legacy-unsubscribe repairs still run.
702
+ - **Label match with one attribute** → canonical value: `{{ Phone Number }}` → `{{phone_number}}` (any author `| default:` / format tail is preserved). A label matches a leaf's `text` or its `tag_text` (e.g. `Wishlist Item (8) Name`), case-insensitively.
703
+ - **Valid tokens are never rewritten**: an exact catalog value, an Items block variable, or an exact entry of `template.customFieldAttributes` (e.g. the host's long-form aliases `name`, `surname`), stays as written — so `{{name | downcase | capitalize}}` survives a drop.
704
+ - **Catalog attributes the module ends up using are registered** for Save's allow-list, the same way the picker registers them, so a repaired or chosen token does not fail Save as an invalid custom field.
705
+ - **Label match with several attributes** (e.g. a bare `Name` shared by a default attribute and a commerce field) is not guessed: the token is left in place and a modal asks the user to pick an attribute per label, showing each option as `category › label`. Applying the choice is one undo step; closing the modal keeps the tokens and shows the warning toaster below. A module whose choice was still open when the design was saved asks again when the design is reopened. Register these keys host-side: `email-editor.ambiguous-dynamic-content-title`, `email-editor.ambiguous-dynamic-content-message` and `email-editor.ambiguous-dynamic-content-apply` (Cancel reuses `action-builder.cancel`).
706
+ - Liquid nested tokens (`coupon.code`), recommendation-block variables (`123_0_price`), allowed system tokens, and `data-*` product tags are left untouched.
702
707
  - An **unresolvable** token is left in place and raises a single **warning toaster** (translation key `dynamic-content.unresolved-attributes-on-drop`; register the copy host-side).
703
708
 
704
709
  Pass `template.dynamicContentList` as a **partner-keyed** tree
705
- (`{ [partnerName]: DynamicContentNode[] }`, where a node is `{ text, value, children? }`).
710
+ (`{ [partnerName]: DynamicContentNode[] }`, where a node is `{ text, tag_text?, value, children? }`).
706
711
  Guido flattens the **active (first) partner's** leaf attributes. Products that manage
707
712
  multiple partners (e.g. architect) should key by partner and place the active partner
708
713
  first. Omit the prop and the repair is a safe no-op — nothing is rewritten and no
@@ -1,20 +1,20 @@
1
1
  import o from "./Guido.vue2.js";
2
2
  /* empty css */
3
3
  import i from "../_virtual/_plugin-vue2_normalizer.js";
4
- var s = function() {
4
+ var n = function() {
5
5
  var a = this, r = a._self._c, e = a._self._setupProxy;
6
- return r("div", { ref: "wrapperRef", staticClass: "guido-editor__wrapper", class: { "guido-editor__no-header": e.noHeader } }, [r(e.HeaderWrapper), r(e.AutoSaveController, { ref: "autoSaveControllerRef" }), e.editorStore.isPreviewModeOpen ? r(e.PreviewContainer) : a._e(), r("div", { directives: [{ name: "show", rawName: "v-show", value: !e.previewStore.isLoaded, expression: "!previewStore.isLoaded" }], staticClass: "guido-editor__container", class: { "guido-editor__no-header": e.noHeader }, attrs: { id: "guido-editor" } }), r(e.Toaster), r(e.AuthErrorModal), r(e.FilterSelectionDrawer), e.isRrsEnabled || e.hasAssignedStrategy ? r(e.AssignStrategyDrawer) : a._e(), e.isRrsEnabled || e.hasAssignedStrategy ? r(e.RemoveStrategyModal) : a._e(), e.isRrsEnabled ? r(e.LegacyStrategyUpdatedModal) : a._e(), e.isLegacyUnsubscribeConversionEnabled ? r(e.LegacyUnsubscribeConvertedModal) : a._e(), r(e.SaveAsTemplateDrawer), e.isSaveValidationEnabled ? r(e.ValidationErrorDrawer) : a._e(), e.isExternalDisplayConditionsEnabled ? r(e.DisplayConditionsHost) : a._e(), e.isTestPartner() ? a._e() : r(e.OnboardingWrapper, { on: { "onboarding-finished": function(l) {
6
+ return r("div", { ref: "wrapperRef", staticClass: "guido-editor__wrapper", class: { "guido-editor__no-header": e.noHeader } }, [r(e.HeaderWrapper), r(e.AutoSaveController, { ref: "autoSaveControllerRef" }), e.editorStore.isPreviewModeOpen ? r(e.PreviewContainer) : a._e(), r("div", { directives: [{ name: "show", rawName: "v-show", value: !e.previewStore.isLoaded, expression: "!previewStore.isLoaded" }], staticClass: "guido-editor__container", class: { "guido-editor__no-header": e.noHeader }, attrs: { id: "guido-editor" } }), r(e.Toaster), r(e.AuthErrorModal), r(e.FilterSelectionDrawer), e.isRrsEnabled || e.hasAssignedStrategy ? r(e.AssignStrategyDrawer) : a._e(), e.isRrsEnabled || e.hasAssignedStrategy ? r(e.RemoveStrategyModal) : a._e(), e.isRrsEnabled ? r(e.LegacyStrategyUpdatedModal) : a._e(), e.isLegacyUnsubscribeConversionEnabled ? r(e.LegacyUnsubscribeConvertedModal) : a._e(), e.moduleDynamicContentStore.queue.length > 0 ? r(e.AmbiguousDynamicContentModal) : a._e(), r(e.SaveAsTemplateDrawer), e.isSaveValidationEnabled ? r(e.ValidationErrorDrawer) : a._e(), e.isExternalDisplayConditionsEnabled ? r(e.DisplayConditionsHost) : a._e(), e.isTestPartner() ? a._e() : r(e.OnboardingWrapper, { on: { "onboarding-finished": function(l) {
7
7
  return e.emit("onboarding:finished");
8
8
  } } }), r(e.UnsubscribeWrapper), r(e.LoadingWrapper)], 1);
9
- }, t = [], n = /* @__PURE__ */ i(
9
+ }, t = [], s = /* @__PURE__ */ i(
10
10
  o,
11
- s,
11
+ n,
12
12
  t,
13
13
  !1,
14
14
  null,
15
- "8fc59697"
15
+ "0c5f11a1"
16
16
  );
17
- const v = n.exports;
17
+ const u = s.exports;
18
18
  export {
19
- v as default
19
+ u as default
20
20
  };
@@ -1,79 +1,82 @@
1
- import { defineComponent as ye, defineAsyncComponent as a, ref as W, computed as l, watch as G, onMounted as be, onUnmounted as ve } from "vue";
2
- import { useCortexBlueprintBridge as Se } from "../composables/useCortexBlueprintBridge.js";
3
- import { useEditorLock as he } from "../composables/useEditorLock.js";
4
- import { startEditorOpenMetrics as Ee } from "../composables/useEditorOpenMetrics.js";
5
- import { provideGuidoActions as Ce } from "../composables/useGuidoActions.js";
6
- import { useGuidoStateBridge as De } from "../composables/useGuidoStateBridge.js";
7
- import { usePartner as we } from "../composables/usePartner.js";
8
- import { useSaveValidationRollout as Te } from "../composables/useSaveValidationRollout.js";
9
- import { useStripo as ke } from "../composables/useStripo.js";
10
- import { useTimerClone as Fe } from "../composables/useTimerClone.js";
11
- import { useToaster as Ie } from "../composables/useToaster.js";
12
- import { useTranslations as Le } from "../composables/useTranslations.js";
13
- import { migrate as Me } from "../config/migrator/index.js";
14
- import { ModuleFolderDefaults as $ } from "../enums/defaults.js";
15
- import { RIBBON_SELECTOR as Re } from "../enums/onboarding.js";
16
- import { ToasterTypeOptions as Oe } from "../enums/toaster.js";
17
- import { useRecommendationExtensionStore as z, disposeStrategyPageReturnListeners as Ue } from "../extensions/Blocks/Recommendation/store/recommendation.js";
18
- import { disposeUnsubscribeListeners as Ne } from "../extensions/Blocks/Unsubscribe/block.js";
19
- import { disposeDisplayConditionRequest as _e } from "../extensions/DisplayConditions/bridge.js";
20
- import Ae from "./organisms/AutoSaveController.vue.js";
21
- import Pe from "./organisms/base/AuthErrorModal.vue.js";
22
- import xe from "./organisms/base/Toaster.vue.js";
23
- import Be from "./organisms/extensions/recommendation/FilterSelectionDrawer.vue.js";
24
- import He from "./organisms/header/HeaderWrapper.vue.js";
25
- import Ve from "./organisms/LoadingWrapper.vue.js";
26
- import We from "./organisms/save-as-template/SaveAsTemplateDrawer.vue.js";
27
- import Ge from "./organisms/unsubscribe/UnsubscribeWrapper.vue.js";
28
- import $e from "./organisms/validation/ValidationErrorDrawer.vue.js";
29
- import { useStripoApi as ze } from "../services/stripoApi.js";
30
- import { useConfigStore as qe } from "../stores/config.js";
31
- import { useDynamicContentStore as Ke } from "../stores/dynamic-content.js";
1
+ import { defineComponent as Se, defineAsyncComponent as a, ref as W, computed as l, watch as $, onMounted as he, onUnmounted as Ee } from "vue";
2
+ import { useCortexBlueprintBridge as Ce } from "../composables/useCortexBlueprintBridge.js";
3
+ import { useEditorLock as De } from "../composables/useEditorLock.js";
4
+ import { startEditorOpenMetrics as we } from "../composables/useEditorOpenMetrics.js";
5
+ import { provideGuidoActions as Te } from "../composables/useGuidoActions.js";
6
+ import { useGuidoStateBridge as ke } from "../composables/useGuidoStateBridge.js";
7
+ import { usePartner as Fe } from "../composables/usePartner.js";
8
+ import { useSaveValidationRollout as Me } from "../composables/useSaveValidationRollout.js";
9
+ import { useStripo as Ie } from "../composables/useStripo.js";
10
+ import { useTimerClone as Le } from "../composables/useTimerClone.js";
11
+ import { useToaster as Re } from "../composables/useToaster.js";
12
+ import { useTranslations as Oe } from "../composables/useTranslations.js";
13
+ import { migrate as Ue } from "../config/migrator/index.js";
14
+ import { ModuleFolderDefaults as G } from "../enums/defaults.js";
15
+ import { RIBBON_SELECTOR as Ae } from "../enums/onboarding.js";
16
+ import { ToasterTypeOptions as Ne } from "../enums/toaster.js";
17
+ import { useRecommendationExtensionStore as z, disposeStrategyPageReturnListeners as _e } from "../extensions/Blocks/Recommendation/store/recommendation.js";
18
+ import { disposeUnsubscribeListeners as Pe } from "../extensions/Blocks/Unsubscribe/block.js";
19
+ import { disposeDisplayConditionRequest as xe } from "../extensions/DisplayConditions/bridge.js";
20
+ import Be from "./organisms/AutoSaveController.vue.js";
21
+ import He from "./organisms/base/AuthErrorModal.vue.js";
22
+ import Ve from "./organisms/base/Toaster.vue.js";
23
+ import We from "./organisms/extensions/recommendation/FilterSelectionDrawer.vue.js";
24
+ import $e from "./organisms/header/HeaderWrapper.vue.js";
25
+ import Ge from "./organisms/LoadingWrapper.vue.js";
26
+ import ze from "./organisms/save-as-template/SaveAsTemplateDrawer.vue.js";
27
+ import qe from "./organisms/unsubscribe/UnsubscribeWrapper.vue.js";
28
+ import Ke from "./organisms/validation/ValidationErrorDrawer.vue.js";
29
+ import { useStripoApi as je } from "../services/stripoApi.js";
30
+ import { useConfigStore as Je } from "../stores/config.js";
31
+ import { useDynamicContentStore as Qe } from "../stores/dynamic-content.js";
32
32
  import { useEditorStore as C } from "../stores/editor.js";
33
- import { useOnboardingStore as je } from "../stores/onboarding.js";
34
- import { usePreviewStore as Je } from "../stores/preview.js";
35
- import { useSaveValidationStore as Qe } from "../stores/save-validation.js";
36
- import { useTemplateStore as Xe } from "../stores/template.js";
37
- import { useUnsubscribeStore as Ye } from "../stores/unsubscribe.js";
38
- import { readTrackFloor as q } from "../utils/linkTrackingIds.js";
39
- const Pt = /* @__PURE__ */ ye({
33
+ import { useModuleDynamicContentStore as q } from "../stores/module-dynamic-content.js";
34
+ import { useOnboardingStore as Xe } from "../stores/onboarding.js";
35
+ import { usePreviewStore as Ye } from "../stores/preview.js";
36
+ import { useSaveValidationStore as Ze } from "../stores/save-validation.js";
37
+ import { useTemplateStore as et } from "../stores/template.js";
38
+ import { useUnsubscribeStore as tt } from "../stores/unsubscribe.js";
39
+ import { readTrackFloor as K } from "../utils/linkTrackingIds.js";
40
+ const Vt = /* @__PURE__ */ Se({
40
41
  __name: "Guido",
41
42
  props: {
42
43
  config: null,
43
44
  ready: { type: Boolean, default: !0 }
44
45
  },
45
46
  emits: ["dynamic-content:open", "back", "save:start", "save:complete", "on-change", "ready", "onboarding:finished", "test-email:click"],
46
- setup(K, { expose: j, emit: i }) {
47
- const c = K, r = Ee(), J = a(
47
+ setup(j, { expose: J, emit: i }) {
48
+ const c = j, r = we(), Q = a(
48
49
  () => import("./organisms/email-preview/PreviewContainer.vue.js")
49
- ), Q = a(
50
- () => import("./organisms/onboarding/OnboardingWrapper.vue.js")
51
50
  ), X = a(
52
- () => import("./organisms/extensions/recommendation/AssignStrategyDrawer.vue.js")
51
+ () => import("./organisms/onboarding/OnboardingWrapper.vue.js")
53
52
  ), Y = a(
54
- () => import("./organisms/extensions/recommendation/RemoveStrategyModal.vue.js")
53
+ () => import("./organisms/extensions/recommendation/AssignStrategyDrawer.vue.js")
55
54
  ), Z = a(
55
+ () => import("./organisms/extensions/recommendation/RemoveStrategyModal.vue.js")
56
+ ), ee = a(
56
57
  () => import("./organisms/display-conditions/DisplayConditionsHost.vue.js")
57
- ), D = W(), ee = a(
58
+ ), D = W(), te = a(
58
59
  () => import("./organisms/extensions/recommendation/LegacyStrategyUpdatedModal.vue.js")
59
- ), te = a(
60
+ ), oe = a(
60
61
  () => import("./organisms/unsubscribe/LegacyUnsubscribeConvertedModal.vue.js")
61
- ), g = W(), y = Ke(), w = Ye(), o = qe();
62
+ ), ne = a(
63
+ () => import("./organisms/dynamic-content/AmbiguousDynamicContentModal.vue.js")
64
+ ), y = W(), g = Qe(), w = tt(), re = q(), o = Je();
62
65
  c.ready && o.init(c.config);
63
- const d = C(), oe = Je(), p = l(() => d.hasChanges), { isTestPartner: ne } = we(), { isEnabled: re, loadRollout: T } = Te(), { showToaster: k } = Ie(), F = Le(), I = () => {
66
+ const d = C(), ie = Ye(), p = l(() => d.hasChanges), { isTestPartner: se } = Fe(), { isEnabled: ae, loadRollout: T } = Me(), { showToaster: k } = Re(), F = Oe(), M = () => {
64
67
  var t;
65
68
  if (d.isVersionHistoryOpen) {
66
69
  console.debug("guido:save:silent:blocked", { isVersionHistoryOpen: !0 }), k({
67
- type: Oe.Warning,
70
+ type: Ne.Warning,
68
71
  message: F("email-editor.save-blocked-outside-editor")
69
72
  });
70
73
  return;
71
74
  }
72
75
  return (t = D.value) == null ? void 0 : t.saveSilent();
73
- }, L = (t) => {
76
+ }, I = (t) => {
74
77
  d.loadingStatus = t;
75
78
  };
76
- Se(), De();
79
+ Ce(), ke();
77
80
  const b = () => {
78
81
  var t, e;
79
82
  return {
@@ -81,20 +84,20 @@ const Pt = /* @__PURE__ */ ye({
81
84
  userId: o.userId,
82
85
  username: o.username,
83
86
  partnerName: o.partnerName,
84
- savedModulesFolderName: ((t = o.editor) == null ? void 0 : t.savedModulesFolderName) || $.SAVED_MODULES,
85
- defaultModulesFolderName: ((e = o.editor) == null ? void 0 : e.defaultModulesFolderName) || $.DEFAULT_MODULES
87
+ savedModulesFolderName: ((t = o.editor) == null ? void 0 : t.savedModulesFolderName) || G.SAVED_MODULES,
88
+ defaultModulesFolderName: ((e = o.editor) == null ? void 0 : e.defaultModulesFolderName) || G.DEFAULT_MODULES
86
89
  };
87
- }, { initPlugin: M, preloadRuntime: R } = ke(r), { runExclusive: O, beginMount: U, isCurrentMount: v } = he();
90
+ }, { initPlugin: L, preloadRuntime: R } = Ie(r), { runExclusive: O, beginMount: U, isCurrentMount: v } = De();
88
91
  let f = 0;
89
- const { getDefaultTemplate: N } = ze(), { cloneTimersOnSave: _, hasTimerBlocks: A } = Fe(), ie = l(() => {
92
+ const { getDefaultTemplate: A } = je(), { cloneTimersOnSave: N, hasTimerBlocks: _ } = Le(), ce = l(() => {
90
93
  var t;
91
94
  return !((t = o.ui) != null && t.showHeader);
92
- }), se = l(() => o.isFeatureEnabled("reusableRecommendationStrategy")), ae = l(() => o.externalDisplayConditions), ce = l(
95
+ }), de = l(() => o.isFeatureEnabled("reusableRecommendationStrategy")), me = l(() => o.externalDisplayConditions), le = l(
93
96
  () => o.isFeatureEnabled("legacyUnsubscribeConversion")
94
- ), de = l(
97
+ ), ue = l(
95
98
  () => !!z().recommendationConfigs.strategyId
96
99
  );
97
- Ce({
100
+ Te({
98
101
  onBack: () => {
99
102
  console.debug("guido:back"), i("back");
100
103
  },
@@ -110,11 +113,11 @@ const Pt = /* @__PURE__ */ ye({
110
113
  }
111
114
  });
112
115
  const P = (t) => {
113
- console.debug("dynamic-content:close", t), y.setSelectedDynamicContent(t), document.dispatchEvent(new CustomEvent("dynamic-content:close", { detail: t }));
116
+ console.debug("dynamic-content:close", t), g.setSelectedDynamicContent(t), document.dispatchEvent(new CustomEvent("dynamic-content:close", { detail: t }));
114
117
  }, x = () => {
115
118
  console.debug("dynamic-content:close", "Without Data"), document.dispatchEvent(new CustomEvent("dynamic-content:close", { detail: { text: "", value: "" } }));
116
119
  };
117
- G(() => p.value, () => {
120
+ $(() => p.value, () => {
118
121
  i("on-change", p.value);
119
122
  });
120
123
  const S = (t) => {
@@ -124,8 +127,8 @@ const Pt = /* @__PURE__ */ ye({
124
127
  let m = null;
125
128
  const h = () => {
126
129
  var e;
127
- const t = document.querySelector(Re);
128
- (e = g.value) == null || e.style.setProperty("--ribbon-offset", `${(t == null ? void 0 : t.offsetHeight) ?? 0}px`);
130
+ const t = document.querySelector(Ae);
131
+ (e = y.value) == null || e.style.setProperty("--ribbon-offset", `${(t == null ? void 0 : t.offsetHeight) ?? 0}px`);
129
132
  }, E = async () => {
130
133
  var t;
131
134
  r.mark("host_ready");
@@ -138,49 +141,49 @@ const Pt = /* @__PURE__ */ ye({
138
141
  });
139
142
  const e = o.template, s = (e == null ? void 0 : e.html) || "", u = (e == null ? void 0 : e.css) || "", B = (e == null ? void 0 : e.preselectedDynamicContent) || [];
140
143
  w.selectedUnsubscribePages = (e == null ? void 0 : e.selectedUnsubscribePages) || [];
141
- const me = ((t = e == null ? void 0 : e.migration) == null ? void 0 : t.recommendationConfigs) ?? {}, H = o.isFeatureEnabled("linkTrackingIds"), le = {
144
+ const pe = ((t = e == null ? void 0 : e.migration) == null ? void 0 : t.recommendationConfigs) ?? {}, H = o.isFeatureEnabled("linkTrackingIds"), fe = {
142
145
  partnerName: o.partnerName,
143
146
  campaignUrls: e == null ? void 0 : e.recommendationCampaignUrls,
144
147
  rrsEnabled: o.isFeatureEnabled("reusableRecommendationStrategy")
145
- }, ue = {
148
+ }, ye = {
146
149
  itemsPriceFormatting: o.isFeatureEnabled("itemsPriceFormatting")
147
- }, V = (ge) => r.timed(
150
+ }, V = (ve) => r.timed(
148
151
  "migration",
149
- () => Me(ge, me, le, ue)
152
+ () => Ue(ve, pe, fe, ye)
150
153
  );
151
154
  let n = {
152
155
  html: s && await V(s),
153
156
  css: u
154
157
  };
155
- n.html || (n = await r.timed("default_template", () => N()), n.html = await V(n.html));
156
- const pe = H ? q(n.html, (e == null ? void 0 : e.trackIdCounter) || 0) : 0;
157
- A(n.html) && (n.html = await r.timed("timer_clone", () => _(n.html))), H && (d.trackIdSessionFloor = Math.max(pe, q(n.html)));
158
- const fe = {
158
+ n.html || (n = await r.timed("default_template", () => A()), n.html = await V(n.html));
159
+ const ge = H ? K(n.html, (e == null ? void 0 : e.trackIdCounter) || 0) : 0;
160
+ _(n.html) && (n.html = await r.timed("timer_clone", () => N(n.html))), H && (d.trackIdSessionFloor = Math.max(ge, K(n.html)));
161
+ const be = {
159
162
  preselectedDynamicContentList: B,
160
163
  onReady: () => {
161
164
  console.debug("guido:ready"), i("ready");
162
165
  }
163
166
  };
164
- f = U(), await M(n, b(), fe, n.html !== s), y.selectedDynamicContentList = B;
167
+ f = U(), await L(n, b(), be, n.html !== s), g.selectedDynamicContentList = B;
165
168
  } catch (e) {
166
169
  console.error("Failed to initialize Stripo editor:", e), r.markFailed();
167
170
  }
168
171
  };
169
- return be(async () => {
172
+ return he(async () => {
170
173
  var e;
171
- console.debug("Guido says happy coding 🎉"), console.debug("🚗 Ka-Chow"), z().$reset(), C().$reset(), Xe().$reset(), Qe().$reset(), je().resetOnboarding("recommendationLegacyStrategyOnboarding"), T(), h();
172
- const t = (e = g.value) == null ? void 0 : e.parentElement;
174
+ console.debug("Guido says happy coding 🎉"), console.debug("🚗 Ka-Chow"), z().$reset(), C().$reset(), et().$reset(), Ze().$reset(), q().$reset(), Xe().resetOnboarding("recommendationLegacyStrategyOnboarding"), T(), h();
175
+ const t = (e = y.value) == null ? void 0 : e.parentElement;
173
176
  if (t && (m = new ResizeObserver(h), m.observe(t)), R(), c.ready)
174
177
  await E();
175
178
  else {
176
- const s = G(() => c.ready, (u) => {
179
+ const s = $(() => c.ready, (u) => {
177
180
  u && (s(), E());
178
181
  });
179
182
  }
180
183
  document.addEventListener("dynamic-content:open", S);
181
- }), ve(() => {
182
- if (Ne(), Ue(), r.dispose(), m == null || m.disconnect(), document.removeEventListener("dynamic-content:open", S), v(f)) {
183
- _e();
184
+ }), Ee(() => {
185
+ if (Pe(), _e(), r.dispose(), m == null || m.disconnect(), document.removeEventListener("dynamic-content:open", S), v(f)) {
186
+ xe();
184
187
  try {
185
188
  const t = C();
186
189
  t.isStripoInitialized = !1, t.isAccessibilityTestingOpen = !1, t.imagesHidden = !1, t.colorVisionDeficiency = "";
@@ -196,17 +199,17 @@ const Pt = /* @__PURE__ */ ye({
196
199
  console.debug("Failed to remove Stripo editor: No editor found");
197
200
  }
198
201
  }), o.reset();
199
- }), j({
202
+ }), J({
200
203
  dynamicContent: {
201
204
  insert: P,
202
205
  close: x
203
206
  },
204
207
  hasChanges: p,
205
- saveSilent: I,
206
- setLoading: L
207
- }), { __sfc: !0, openMetrics: r, PreviewContainer: J, OnboardingWrapper: Q, AssignStrategyDrawer: X, RemoveStrategyModal: Y, DisplayConditionsHost: Z, autoSaveControllerRef: D, LegacyStrategyUpdatedModal: ee, LegacyUnsubscribeConvertedModal: te, wrapperRef: g, dynamicContentStore: y, unsubscribeStore: w, props: c, configStore: o, editorStore: d, previewStore: oe, hasChanges: p, isTestPartner: ne, isSaveValidationEnabled: re, loadRollout: T, showToaster: k, trans: F, saveSilent: I, setLoading: L, emit: i, buildMetadata: b, initPlugin: M, preloadRuntime: R, runExclusive: O, beginMount: U, isCurrentMount: v, mountGeneration: f, getDefaultTemplate: N, cloneTimersOnSave: _, hasTimerBlocks: A, noHeader: ie, isRrsEnabled: se, isExternalDisplayConditionsEnabled: ae, isLegacyUnsubscribeConversionEnabled: ce, hasAssignedStrategy: de, insertDynamicContent: P, closeDynamicContent: x, handleDynamicContentOpen: S, ribbonObserver: m, updateRibbonOffset: h, startEditor: E, AutoSaveController: Ae, AuthErrorModal: Pe, Toaster: xe, FilterSelectionDrawer: Be, HeaderWrapper: He, LoadingWrapper: Ve, SaveAsTemplateDrawer: We, UnsubscribeWrapper: Ge, ValidationErrorDrawer: $e };
208
+ saveSilent: M,
209
+ setLoading: I
210
+ }), { __sfc: !0, openMetrics: r, PreviewContainer: Q, OnboardingWrapper: X, AssignStrategyDrawer: Y, RemoveStrategyModal: Z, DisplayConditionsHost: ee, autoSaveControllerRef: D, LegacyStrategyUpdatedModal: te, LegacyUnsubscribeConvertedModal: oe, AmbiguousDynamicContentModal: ne, wrapperRef: y, dynamicContentStore: g, unsubscribeStore: w, moduleDynamicContentStore: re, props: c, configStore: o, editorStore: d, previewStore: ie, hasChanges: p, isTestPartner: se, isSaveValidationEnabled: ae, loadRollout: T, showToaster: k, trans: F, saveSilent: M, setLoading: I, emit: i, buildMetadata: b, initPlugin: L, preloadRuntime: R, runExclusive: O, beginMount: U, isCurrentMount: v, mountGeneration: f, getDefaultTemplate: A, cloneTimersOnSave: N, hasTimerBlocks: _, noHeader: ce, isRrsEnabled: de, isExternalDisplayConditionsEnabled: me, isLegacyUnsubscribeConversionEnabled: le, hasAssignedStrategy: ue, insertDynamicContent: P, closeDynamicContent: x, handleDynamicContentOpen: S, ribbonObserver: m, updateRibbonOffset: h, startEditor: E, AutoSaveController: Be, AuthErrorModal: He, Toaster: Ve, FilterSelectionDrawer: We, HeaderWrapper: $e, LoadingWrapper: Ge, SaveAsTemplateDrawer: ze, UnsubscribeWrapper: qe, ValidationErrorDrawer: Ke };
208
211
  }
209
212
  });
210
213
  export {
211
- Pt as default
214
+ Vt as default
212
215
  };
@@ -0,0 +1,24 @@
1
+ import i from "./AmbiguousDynamicContentModal.vue2.js";
2
+ /* empty css */
3
+ import l from "../../../_virtual/_plugin-vue2_normalizer.js";
4
+ var c = function() {
5
+ var n = this, a = n._self._c, t = n._self._setupProxy;
6
+ return t.current ? a(t.WpModal, { attrs: { id: "guido__ambiguous-dynamic-content-modal", size: "medium", "close-on-outside-click": !1, "footer-button-options": t.footerButtonOptions, title: t.trans("email-editor.ambiguous-dynamic-content-title") }, on: { close: t.cancel, "primary-action": t.apply } }, [a("div", { staticClass: "ambiguous-dynamic-content-modal__content" }, [a("p", { staticClass: "mb-3" }, [n._v(" " + n._s(t.trans("email-editor.ambiguous-dynamic-content-message")) + " ")]), n._l(t.rows, function(e, s) {
7
+ return a(t.InSelect, { key: e.label, staticClass: "mb-2", attrs: { id: `guido__ambiguous-dynamic-content-select-${s}`, "search-status": "", "button-status": !1, "label-text": `{{ ${e.label} }}`, options: e.options, "placeholder-text": t.trans("email-editor.ambiguous-dynamic-content-placeholder"), state: e.isInvalid ? "error" : "default", "state-message": t.trans("email-editor.ambiguous-dynamic-content-validation"), value: e.selected }, on: { close: function(o) {
8
+ return t.touch(e.label);
9
+ }, select: function(o) {
10
+ return t.select(e.label, o);
11
+ } } });
12
+ })], 2)]) : n._e();
13
+ }, r = [], u = /* @__PURE__ */ l(
14
+ i,
15
+ c,
16
+ r,
17
+ !1,
18
+ null,
19
+ "2643213c"
20
+ );
21
+ const f = u.exports;
22
+ export {
23
+ f as default
24
+ };
@@ -0,0 +1,59 @@
1
+ import { defineComponent as h, ref as y, computed as u, watch as _ } from "vue";
2
+ import x from "../../wrappers/WpModal.vue.js";
3
+ import { useAmbiguousDynamicContent as S } from "../../../composables/useAmbiguousDynamicContent.js";
4
+ import { useTranslations as T } from "../../../composables/useTranslations.js";
5
+ import { useEditorStore as C } from "../../../stores/editor.js";
6
+ import { useModuleDynamicContentStore as B } from "../../../stores/module-dynamic-content.js";
7
+ import { InSelect as D } from "@useinsider/design-system-vue";
8
+ const G = /* @__PURE__ */ h({
9
+ __name: "AmbiguousDynamicContentModal",
10
+ setup(I) {
11
+ const s = T(), i = C(), c = B(), { resolve: r, dismiss: p } = S(), o = y({}), l = y([]), t = u(() => i.isStripoInitialized ? c.current : null);
12
+ _(() => {
13
+ var e;
14
+ return (e = t.value) == null ? void 0 : e.id;
15
+ }, () => {
16
+ o.value = {}, l.value = [];
17
+ });
18
+ const m = (e) => ({
19
+ text: [...e.path, e.tagText].join(" › "),
20
+ value: e.value
21
+ }), v = u(() => {
22
+ var e;
23
+ return (((e = t.value) == null ? void 0 : e.items) ?? []).map((a) => {
24
+ const d = a.candidates.map(m), f = d.filter((g) => g.value === o.value[a.label]);
25
+ return {
26
+ label: a.label,
27
+ options: d,
28
+ selected: f,
29
+ isInvalid: f.length === 0 && l.value.includes(a.label)
30
+ };
31
+ });
32
+ }), n = u(() => v.value.every((e) => e.selected.length > 0)), b = u(() => ({
33
+ cancelOrBackButton: {
34
+ type: "secondary",
35
+ labelText: s("action-builder.cancel"),
36
+ styling: "ghost"
37
+ },
38
+ primaryButton: {
39
+ type: "primary",
40
+ labelText: s("email-editor.ambiguous-dynamic-content-apply"),
41
+ disabledStatus: !n.value,
42
+ isTooltip: !n.value,
43
+ tooltipText: n.value ? "" : s("email-editor.ambiguous-dynamic-content-apply-tooltip")
44
+ }
45
+ }));
46
+ return { __sfc: !0, trans: s, editorStore: i, store: c, resolve: r, dismiss: p, selections: o, touched: l, current: t, toOption: m, rows: v, isComplete: n, footerButtonOptions: b, select: (e, { value: a }) => {
47
+ o.value = { ...o.value, [e]: a };
48
+ }, touch: (e) => {
49
+ l.value.includes(e) || (l.value = [...l.value, e]);
50
+ }, apply: () => {
51
+ t.value && n.value && r(t.value, o.value);
52
+ }, cancel: () => {
53
+ t.value && p(t.value.id);
54
+ }, WpModal: x, InSelect: D };
55
+ }
56
+ });
57
+ export {
58
+ G as default
59
+ };
@@ -0,0 +1,72 @@
1
+ import { useModuleRepair as A, UNRESOLVED_DYNAMIC_CONTENT_ON_DROP_KEY as D, registerUsedAttributes as C } from "./useModuleRepair.js";
2
+ import { useToaster as v } from "./useToaster.js";
3
+ import { useTranslations as R } from "./useTranslations.js";
4
+ import { ModuleDynamicContentAttribute as b } from "../enums/dynamic-content.js";
5
+ import { ToasterTypeOptions as h } from "../enums/toaster.js";
6
+ import { useModuleDynamicContentStore as O } from "../stores/module-dynamic-content.js";
7
+ import { applyDynamicContentChoices as H } from "../utils/dynamicContentConverter.js";
8
+ import { stripPendingMarker as _ } from "../utils/dynamicContentPendingMarker.js";
9
+ import { getEditorApi as w, EDITOR_NOT_READY_MESSAGE as N } from "../utils/stripoEditorApi.js";
10
+ import { ModificationDescription as u } from "../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
11
+ const c = b.Pending, M = () => {
12
+ const { editorCopilotApi: t } = w();
13
+ if (!t)
14
+ throw new Error(N);
15
+ return { api: t, root: t.getDocumentRootHtmlNode() };
16
+ }, E = (t) => {
17
+ const { api: p, root: a } = M(), m = a.querySelector(`[${c}="${t}"]`);
18
+ return { api: p, node: m };
19
+ }, G = () => {
20
+ const t = O(), { convertDynamicContent: p } = A(), { showToaster: a, handleError: m } = v(), f = R();
21
+ return { resolve: ({ id: o, items: r }, i) => {
22
+ try {
23
+ const { api: e, node: n } = E(o);
24
+ if (n) {
25
+ const s = /* @__PURE__ */ new Map();
26
+ r.forEach((l) => {
27
+ const g = i[l.label];
28
+ g && l.spellings.forEach((T) => s.set(T, g));
29
+ });
30
+ const d = _(H(n.getOuterHTML(), s)), y = e.getTemplateModifier();
31
+ y.modifyHtml(n).replaceWith(d), y.apply(new u("Resolved dynamic content")), C(d, r.flatMap((l) => l.candidates)), a({
32
+ type: h.Success,
33
+ message: f("email-editor.ambiguous-dynamic-content-success")
34
+ });
35
+ }
36
+ t.remove(o);
37
+ } catch (e) {
38
+ m(e, "Failed to apply the dynamic content choice");
39
+ }
40
+ }, dismiss: (o) => {
41
+ try {
42
+ const { api: r, node: i } = E(o);
43
+ if (i) {
44
+ const e = r.getTemplateModifier();
45
+ e.modifyHtml(i).removeAttribute(c), e.apply(new u("Cleared pending dynamic content").asHidden()), a({
46
+ type: h.Warning,
47
+ message: f(D)
48
+ });
49
+ }
50
+ } catch (r) {
51
+ m(r, "Failed to dismiss the dynamic content choice");
52
+ } finally {
53
+ t.remove(o);
54
+ }
55
+ }, restorePending: () => {
56
+ try {
57
+ const { api: o, root: r } = M(), i = [];
58
+ if (r.querySelectorAll(`[${c}]`).forEach((e) => {
59
+ const n = e.getAttribute(c), { ambiguous: s } = p(e.getOuterHTML());
60
+ !n || s.length === 0 ? i.push(e) : t.queue.some((d) => d.id === n) || t.enqueue({ id: n, items: s });
61
+ }), i.length > 0) {
62
+ const e = o.getTemplateModifier();
63
+ i.forEach((n) => e.modifyHtml(n).removeAttribute(c)), e.apply(new u("Cleared pending dynamic content").asHidden());
64
+ }
65
+ } catch (o) {
66
+ console.error("Failed to restore pending dynamic content:", o);
67
+ }
68
+ } };
69
+ };
70
+ export {
71
+ G as useAmbiguousDynamicContent
72
+ };
@@ -1,8 +1,8 @@
1
1
  import { defaultHtmlCompilerRules as d } from "../config/compiler/htmlCompilerRules.js";
2
2
  import { itemsCompilerRules as f } from "../config/compiler/itemsCompilerRules.js";
3
3
  import { liquidCompilerRules as g } from "../config/compiler/liquidCompilerRules.js";
4
- import { outlookCompilerRules as R } from "../config/compiler/outlookCompilerRules.js";
5
- import { recommendationCompilerRules as v } from "../config/compiler/recommendationCompilerRules.js";
4
+ import { outlookCompilerRules as v } from "../config/compiler/outlookCompilerRules.js";
5
+ import { recommendationCompilerRules as R } from "../config/compiler/recommendationCompilerRules.js";
6
6
  import { socialCompilerRules as H } from "../config/compiler/socialCompilerRules.js";
7
7
  import { unsubscribeCompilerRules as b } from "../config/compiler/unsubscribeCompilerRules.js";
8
8
  import { createHtmlCompiler as n } from "../utils/htmlCompiler.js";
@@ -32,19 +32,20 @@ const x = /* @__PURE__ */ new Set([
32
32
  "liquid-coupon-code",
33
33
  // Editor-only attribute strip — AMP4EMAIL has no `contenteditable`, so leaving
34
34
  // it in narrows/breaks rendering. Pure attribute removal, emits nothing.
35
- "remove-contenteditable"
35
+ "remove-contenteditable",
36
+ "remove-dc-pending-marker"
36
37
  ]), I = () => {
37
- const { compiler: i, isFeatureEnabled: u, partner: a } = C(), c = p(() => {
38
+ const { compiler: i, isFeatureEnabled: a, partner: u } = C(), c = p(() => {
38
39
  var m, l;
39
40
  const e = ((m = i.value) == null ? void 0 : m.customRules) || [];
40
41
  return [
41
42
  ...!!((l = i.value) != null && l.ignoreDefaultRules) ? [] : d,
42
- ...v,
43
+ ...R,
43
44
  ...b,
44
45
  ...f,
45
- ...R,
46
+ ...v,
46
47
  ...H,
47
- ...u("liquidSyntax") ? g : [],
48
+ ...a("liquidSyntax") ? g : [],
48
49
  ...e.map((t, o) => ({
49
50
  ...t,
50
51
  priority: t.priority + 1e3 + o
@@ -53,7 +54,7 @@ const x = /* @__PURE__ */ new Set([
53
54
  ];
54
55
  }), r = p(() => {
55
56
  var t;
56
- const e = c.value, s = n(e), m = n(e.filter((o) => x.has(o.id))), l = (t = a.value) == null ? void 0 : t.fallbackFont;
57
+ const e = c.value, s = n(e), m = n(e.filter((o) => x.has(o.id))), l = (t = u.value) == null ? void 0 : t.fallbackFont;
57
58
  return {
58
59
  compileHtml: (o) => s.compile(o, void 0, l),
59
60
  compileAmpHtml: (o) => m.compile(o, void 0, l)