@useinsider/guido 3.3.0-beta.b7e5793 → 3.3.0-beta.d5c796a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/components/Guido.vue.js +1 -1
  2. package/dist/components/Guido.vue2.js +42 -40
  3. package/dist/components/organisms/header/AiStatusPill.vue.js +18 -0
  4. package/dist/components/organisms/header/AiStatusPill.vue2.js +13 -0
  5. package/dist/components/organisms/header/MiddleSlot.vue.js +7 -7
  6. package/dist/components/organisms/header/MiddleSlot.vue2.js +8 -7
  7. package/dist/composables/useCortexBlueprintBridge.js +91 -0
  8. package/dist/composables/useEmailTemplateApplier.js +41 -0
  9. package/dist/composables/useGuidoStateBridge.js +48 -0
  10. package/dist/composables/useHtmlValidator.js +36 -41
  11. package/dist/composables/useRecommendation.js +2 -2
  12. package/dist/enums/extensions/recommendationBlock.js +41 -95
  13. package/dist/enums/unsubscribe.js +24 -25
  14. package/dist/extensions/Blocks/Checkbox/control.js +23 -23
  15. package/dist/extensions/Blocks/RadioButton/control.js +15 -15
  16. package/dist/extensions/Blocks/Recommendation/block.js +36 -43
  17. package/dist/extensions/Blocks/Recommendation/services/configService.js +26 -33
  18. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +26 -35
  19. package/dist/guido.css +1 -1
  20. package/dist/src/components/organisms/header/AiStatusPill.vue.d.ts +2 -0
  21. package/dist/src/composables/useCortexBlueprintBridge.d.ts +28 -0
  22. package/dist/src/composables/useEmailTemplateApplier.d.ts +21 -0
  23. package/dist/src/composables/useGuidoStateBridge.d.ts +22 -0
  24. package/dist/src/enums/extensions/recommendationBlock.d.ts +1 -5
  25. package/dist/src/enums/unsubscribe.d.ts +3 -8
  26. package/dist/src/extensions/Blocks/Recommendation/services/configService.d.ts +3 -11
  27. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +1 -7
  28. package/dist/src/stores/ai-status.d.ts +41 -0
  29. package/dist/src/stores/guido-email-editor.d.ts +41 -0
  30. package/dist/stores/ai-status.js +25 -0
  31. package/dist/stores/guido-email-editor.js +20 -0
  32. package/package.json +1 -1
  33. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +0 -33
  34. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +0 -21
@@ -1,5 +1,5 @@
1
- import { ModificationDescription as C } from "../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
- import { CURRENT_CONFIG_VERSION as l, DEFAULT_NODE_CONFIG as c } from "../constants/defaultConfig.js";
1
+ import { ModificationDescription as h } from "../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
2
+ import { CURRENT_CONFIG_VERSION as l, DEFAULT_NODE_CONFIG as r } from "../constants/defaultConfig.js";
3
3
  import { setCurrencyAttributes as D } from "../controls/main/utils.js";
4
4
  import { hasMinimalConfig as A } from "../types/nodeConfig.js";
5
5
  function S(e) {
@@ -8,7 +8,7 @@ function S(e) {
8
8
  function N(e) {
9
9
  return e === "." || e === "," || e === " " || e === "";
10
10
  }
11
- class M {
11
+ class V {
12
12
  // ========================================================================
13
13
  // Read Operations
14
14
  // ========================================================================
@@ -83,7 +83,7 @@ class M {
83
83
  * @returns The new complete configuration
84
84
  */
85
85
  static updateConfig(i, t, o, n) {
86
- const r = this.getConfig(t), u = this.deepMerge(r, o);
86
+ const c = this.getConfig(t), u = this.deepMerge(c, o);
87
87
  return this.saveConfig(i, t, u, n), u;
88
88
  }
89
89
  /**
@@ -91,28 +91,21 @@ class M {
91
91
  *
92
92
  * Called when a block is first created (dropped into template).
93
93
  * Can optionally merge in partial config from migration.
94
- *
95
- * The `wasFreshDrop` flag distinguishes a brand-new drop (no prior config)
96
- * from a clone (Stripo replays the source's setNodeConfig payload before
97
- * onCreated fires). Callers use this to skip side-effects already inherited
98
- * from the source.
99
94
  * @example
100
95
  * // In Block.onCreated lifecycle
101
- * const { config, wasFreshDrop } = RecommendationConfigService.initializeConfig(this.api, node);
96
+ * RecommendationConfigService.initializeConfig(this.api, node);
102
97
  * @param api - Stripo extension API with document modifier
103
98
  * @param node - The immutable HTML node to initialize
104
99
  * @param partialConfig - Optional partial config to merge with defaults
105
- * @returns The initialized configuration and whether the node was a fresh drop
100
+ * @returns The initialized configuration
106
101
  */
107
102
  static initializeConfig(i, t, o) {
108
- if (this.hasConfig(t))
109
- return { config: o ? this.updateConfig(i, t, o, "Initialize recommendation block") : this.getConfig(t), wasFreshDrop: !1 };
110
103
  const n = o ? this.mergeWithDefaults(o) : this.cloneDefaults();
111
104
  return this.saveConfig(i, t, n, "Initialize recommendation block"), D({
112
105
  currentNode: t,
113
106
  documentModifier: i.getDocumentModifier(),
114
107
  currency: n.currency
115
- }), { config: n, wasFreshDrop: !0 };
108
+ }), n;
116
109
  }
117
110
  /**
118
111
  * Save complete configuration to a node
@@ -125,9 +118,9 @@ class M {
125
118
  */
126
119
  static saveConfig(i, t, o, n) {
127
120
  try {
128
- i.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new C(n));
129
- } catch (r) {
130
- console.warn("[RecommendationConfigService] Failed to save config:", r);
121
+ i.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new h(n));
122
+ } catch (c) {
123
+ console.warn("[RecommendationConfigService] Failed to save config:", c);
131
124
  }
132
125
  }
133
126
  // ========================================================================
@@ -164,29 +157,29 @@ class M {
164
157
  s && typeof s == "object" && (Object.assign(o, s), o.configVersion = l);
165
158
  } catch {
166
159
  }
167
- const r = t.getAttribute("data-layout");
168
- r === "list" || r === "horizontal" ? o.layout = "list" : (r === "grid" || r === "vertical") && (o.layout = "grid");
160
+ const c = t.getAttribute("data-layout");
161
+ c === "list" || c === "horizontal" ? o.layout = "list" : (c === "grid" || c === "vertical") && (o.layout = "grid");
169
162
  const u = t.getAttribute("data-card-composition");
170
163
  u && (o.composition = u.split(",").filter(Boolean));
171
- const b = t.getAttribute("data-column-spacing");
172
- b && (o.columnSpacing = parseInt(b) || c.columnSpacing);
173
- const p = t.getAttribute("data-row-spacing");
174
- if (p && (o.rowSpacing = parseInt(p) || c.rowSpacing), !o.currency) {
164
+ const p = t.getAttribute("data-column-spacing");
165
+ p && (o.columnSpacing = parseInt(p) || r.columnSpacing);
166
+ const b = t.getAttribute("data-row-spacing");
167
+ if (b && (o.rowSpacing = parseInt(b) || r.rowSpacing), !o.currency) {
175
168
  const s = t.getAttribute("currency"), y = t.getAttribute("currency-symbol"), d = t.getAttribute("currency-alignment"), f = t.getAttribute("currency-thousand-separator"), g = t.getAttribute("currency-decimal-separator"), m = t.getAttribute("currency-decimal-count");
176
169
  if (s || y || d || f || g || m) {
177
- const a = c.currency, h = m ? parseInt(m) : NaN;
170
+ const a = r.currency, C = m ? parseInt(m) : NaN;
178
171
  o.currency = {
179
172
  code: s ?? a.code,
180
173
  symbol: y ?? a.symbol,
181
174
  alignment: d === "0" ? "before" : "after",
182
- decimalCount: Number.isFinite(h) ? h : a.decimalCount,
175
+ decimalCount: Number.isFinite(C) ? C : a.decimalCount,
183
176
  decimalSeparator: S(g) ? g : a.decimalSeparator,
184
177
  thousandSeparator: N(f) ? f : a.thousandSeparator
185
178
  };
186
179
  }
187
180
  }
188
181
  }
189
- return this.initializeConfig(i, t, o).config;
182
+ return this.initializeConfig(i, t, o);
190
183
  }
191
184
  /**
192
185
  * Check if configuration needs migration
@@ -204,12 +197,12 @@ class M {
204
197
  */
205
198
  static cloneDefaults() {
206
199
  return {
207
- ...c,
208
- currency: { ...c.currency },
209
- omnibusPrice: { ...c.omnibusPrice },
210
- omnibusDiscount: { ...c.omnibusDiscount },
211
- composition: [...c.composition],
212
- visibility: { ...c.visibility },
200
+ ...r,
201
+ currency: { ...r.currency },
202
+ omnibusPrice: { ...r.omnibusPrice },
203
+ omnibusDiscount: { ...r.omnibusDiscount },
204
+ composition: [...r.composition],
205
+ visibility: { ...r.visibility },
213
206
  filters: [],
214
207
  productIds: [],
215
208
  recommendationId: 0
@@ -279,5 +272,5 @@ class M {
279
272
  }
280
273
  }
281
274
  export {
282
- M as RecommendationConfigService
275
+ V as RecommendationConfigService
283
276
  };
@@ -1,15 +1,14 @@
1
- import { getRecommendationFeedSourceMaps as g, getOperatorOptions as R, PriceAttributes as y } from "../../../../enums/extensions/recommendationBlock.js";
1
+ import { RecommendationFeedSourceMaps as S, getOperatorOptions as R, PriceAttributes as y } from "../../../../enums/extensions/recommendationBlock.js";
2
2
  import { useRecommendationApi as C } from "../../../../services/recommendationApi.js";
3
3
  import { useConfigStore as G } from "../../../../stores/config.js";
4
4
  import { defineStore as P } from "pinia";
5
5
  import { DEFAULT_CARDS_IN_ROW as F } from "../constants/layout.js";
6
6
  import { EXCLUDED_ALGORITHM_IDS as D } from "../constants/defaultConfig.js";
7
- import { getDefaultProducts as S } from "../templates/utils.js";
7
+ import { getDefaultProducts as g } from "../templates/utils.js";
8
8
  import { generateCompleteFilterQuery as b } from "../utils/filterUtil.js";
9
9
  import { isFilterValid as w } from "../validation/filterSchema.js";
10
- import { isConfigValid as v } from "../validation/requiredFields.js";
11
10
  const h = C();
12
- let m = null, u = null, d = null;
11
+ let u = null, m = null, d = null;
13
12
  function k() {
14
13
  return {
15
14
  cardsInRow: F,
@@ -49,7 +48,7 @@ function I() {
49
48
  filterSnapshot: null
50
49
  };
51
50
  }
52
- const N = () => ({
51
+ const v = () => ({
53
52
  recommendationCampaignUrls: {},
54
53
  activePredictiveAlgorithms: [],
55
54
  languages: {},
@@ -58,8 +57,8 @@ const N = () => ({
58
57
  blockStates: {},
59
58
  currentRecommendationId: null,
60
59
  configVersion: 0
61
- }), M = P("guidoRecommendationExtension", {
62
- state: () => N(),
60
+ }), _ = P("guidoRecommendationExtension", {
61
+ state: () => v(),
63
62
  getters: {
64
63
  // ====================================================================
65
64
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -114,12 +113,12 @@ const N = () => ({
114
113
  return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
115
114
  },
116
115
  getActivePredictiveAlgorithms: (t) => {
117
- const e = g(), r = [];
118
- return t.activePredictiveAlgorithms.filter((n) => !D.includes(n)).forEach((n) => {
119
- r.push(...e.filter((c) => c.id === n));
120
- }), r.map((n) => ({
121
- text: n.name,
122
- value: n.key
116
+ const e = [];
117
+ return t.activePredictiveAlgorithms.filter((r) => !D.includes(r)).forEach((r) => {
118
+ e.push(...S.filter((n) => n.id === r));
119
+ }), e.map((r) => ({
120
+ text: r.name,
121
+ value: r.key
123
122
  }));
124
123
  },
125
124
  getLanguages: (t) => Object.entries(t.languages).map(([e, r]) => ({
@@ -294,11 +293,11 @@ const N = () => ({
294
293
  // ====================================================================
295
294
  async fetchRecommendationCreateData() {
296
295
  if (!this.activePredictiveAlgorithms.length) {
297
- if (m) {
298
- await m;
296
+ if (u) {
297
+ await u;
299
298
  return;
300
299
  }
301
- m = (async () => {
300
+ u = (async () => {
302
301
  const {
303
302
  activePredictiveAlgorithms: t,
304
303
  languages: e,
@@ -311,26 +310,26 @@ const N = () => ({
311
310
  this.currencyList = r;
312
311
  })();
313
312
  try {
314
- await m;
313
+ await u;
315
314
  } finally {
316
- m = null;
315
+ u = null;
317
316
  }
318
317
  }
319
318
  },
320
319
  async fetchRecommendationFilters() {
321
320
  if (!Object.keys(this.filterList).length) {
322
- if (u) {
323
- await u;
321
+ if (m) {
322
+ await m;
324
323
  return;
325
324
  }
326
- u = (async () => {
325
+ m = (async () => {
327
326
  const t = await h.fetchRecommendationFilters();
328
327
  this.filterList = t;
329
328
  })();
330
329
  try {
331
- await u;
330
+ await m;
332
331
  } finally {
333
- u = null;
332
+ m = null;
334
333
  }
335
334
  }
336
335
  },
@@ -399,14 +398,6 @@ const N = () => ({
399
398
  generateFilterQuery() {
400
399
  return b(this.recommendationConfigs.filters);
401
400
  },
402
- /**
403
- * Validation-only check invoked at save-CTA time. Defined as an action
404
- * (not a getter) so reading it does not register reactive tracking on
405
- * every block's recommendationConfigs across user edits.
406
- */
407
- hasInvalidBlock() {
408
- return Object.values(this.blockStates).some((t) => !v(t.recommendationConfigs, this));
409
- },
410
401
  // ====================================================================
411
402
  // Per-Block Product Fetching
412
403
  // ====================================================================
@@ -426,7 +417,7 @@ const N = () => ({
426
417
  },
427
418
  async _doFetchProducts() {
428
419
  var p;
429
- const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = r.filters.filter((l) => l.isValid), c = b(n), i = ((p = g().find((l) => l.key === r.strategy)) == null ? void 0 : p.path) || "", o = G(), s = parseInt(r.size) || 6, a = {
420
+ const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = r.filters.filter((l) => l.isValid), c = b(n), i = ((p = S.find((l) => l.key === r.strategy)) == null ? void 0 : p.path) || "", o = G(), s = parseInt(r.size) || 6, a = {
430
421
  locale: r.language,
431
422
  currency: r.currencySettings.value,
432
423
  partnerName: o.partnerName,
@@ -442,15 +433,15 @@ const N = () => ({
442
433
  f = [];
443
434
  }
444
435
  if (this.blockStates[t]) {
445
- const l = f.length > 0 ? f : S(s);
436
+ const l = f.length > 0 ? f : g(s);
446
437
  l.length < s ? this.blockStates[t].recommendationProducts = [
447
438
  ...l,
448
- ...S(s - l.length)
439
+ ...g(s - l.length)
449
440
  ] : l.length > s ? this.blockStates[t].recommendationProducts = l.slice(0, s) : this.blockStates[t].recommendationProducts = l;
450
441
  }
451
442
  }
452
443
  }
453
444
  });
454
445
  export {
455
- M as useRecommendationExtensionStore
446
+ _ as useRecommendationExtensionStore
456
447
  };
package/dist/guido.css CHANGED
@@ -1 +1 @@
1
- .gap-16[data-v-3b53a736],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-cd76c125] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-195ab6d4]{position:relative;display:inline-block}.new-tag[data-v-195ab6d4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-195ab6d4] .guido__view-option-selection-desktop svg,[data-v-195ab6d4] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-195ab6d4] .in-segments-wrapper__button_selected,[data-v-195ab6d4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-195ab6d4] .in-tooltip-wrapper__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-64c52560]{gap:8px}.version-history__toolbar[data-v-64c52560]{gap:4px}.view-options-wrapper[data-v-d405ca59]{position:relative;display:inline-block}.new-tag[data-v-d405ca59]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-d405ca59] .guido__verion-history-view-option-selection-desktop svg,[data-v-d405ca59] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-d405ca59] .in-segments-wrapper__button_selected,[data-v-d405ca59] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-d405ca59] .in-tooltip-wrapper__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-4e2a4adb]{gap:4px}.header-wrapper[data-v-5c02dcc7]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-1a4e7084]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-1a4e7084]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-1a4e7084]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-df672485]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-df672485]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-df672485]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-df672485]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-df672485]{object-fit:cover;transform:scale(1)}[data-v-43c617a7] .guido__verion-history-view-option-selection-desktop svg,[data-v-43c617a7] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-43c617a7] .in-segments-wrapper__button_selected,[data-v-43c617a7] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-988f8da6]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-988f8da6]{min-height:504px}.iframe-wrapper[data-v-e0424e99]{width:258px}.iframe-scaled[data-v-e0424e99]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-7419ae06] .vueperslides__bullets,[data-v-796d193b] .vueperslides__bullets{pointer-events:none!important}[data-v-796d193b] .vueperslides__parallax-wrapper{height:110px!important}[data-v-cadfc82d] .vueperslides__bullets{pointer-events:none!important}[data-v-cadfc82d] .vueperslides__parallax-wrapper{height:110px!important}
1
+ .gap-16[data-v-3b53a736],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-cd76c125] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.ai-status-pill[data-v-dea97ab5]{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;margin:0 8px;border-radius:12px;background:#3c5af014;color:#2c5282;font-size:12px;font-weight:500;line-height:1.2;-webkit-user-select:none;user-select:none}.ai-status-pill__dot[data-v-dea97ab5]{width:6px;height:6px;border-radius:50%;background:#3c5af0;animation:ai-pulse-dea97ab5 1.4s ease-in-out infinite}.ai-status-pill__label[data-v-dea97ab5]{white-space:nowrap}@keyframes ai-pulse-dea97ab5{0%,to{opacity:.4;transform:scale(1)}50%{opacity:1;transform:scale(1.15)}}.view-options-wrapper[data-v-195ab6d4]{position:relative;display:inline-block}.new-tag[data-v-195ab6d4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-195ab6d4] .guido__view-option-selection-desktop svg,[data-v-195ab6d4] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-195ab6d4] .in-segments-wrapper__button_selected,[data-v-195ab6d4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-195ab6d4] .in-tooltip-wrapper__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-64c52560]{gap:8px}.version-history__toolbar[data-v-64c52560]{gap:4px}.view-options-wrapper[data-v-d405ca59]{position:relative;display:inline-block}.new-tag[data-v-d405ca59]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-d405ca59] .guido__verion-history-view-option-selection-desktop svg,[data-v-d405ca59] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-d405ca59] .in-segments-wrapper__button_selected,[data-v-d405ca59] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-d405ca59] .in-tooltip-wrapper__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-4e2a4adb]{gap:4px}.header-wrapper[data-v-5c02dcc7]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-cdee3452]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-cdee3452]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-cdee3452]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-df672485]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-df672485]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-df672485]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-df672485]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-df672485]{object-fit:cover;transform:scale(1)}[data-v-43c617a7] .guido__verion-history-view-option-selection-desktop svg,[data-v-43c617a7] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-43c617a7] .in-segments-wrapper__button_selected,[data-v-43c617a7] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-988f8da6]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-988f8da6]{min-height:504px}.iframe-wrapper[data-v-e0424e99]{width:258px}.iframe-scaled[data-v-e0424e99]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-7419ae06] .vueperslides__bullets,[data-v-796d193b] .vueperslides__bullets{pointer-events:none!important}[data-v-796d193b] .vueperslides__parallax-wrapper{height:110px!important}[data-v-cadfc82d] .vueperslides__bullets{pointer-events:none!important}[data-v-cadfc82d] .vueperslides__parallax-wrapper{height:110px!important}
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
2
+ export default _default;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Bridges cortex-fe's chat store to Guido's Stripo editor.
3
+ *
4
+ * cortex-fe is a Module Federation MFE that registers `defineStore('chat', …)`
5
+ * on the host's shared (singleton) Pinia. Guido — bundled inside MFE consumers
6
+ * that also share that pinia — reads the same store at runtime via
7
+ * `getActivePinia()._s.get('chat')`. No build-time dep on cortex-fe.
8
+ *
9
+ * The bridge handles three segment shapes:
10
+ *
11
+ * - `blueprint` segments with `blueprintType === 'email_template'` are
12
+ * applied to the Stripo editor (immediate on first sighting, debounced on
13
+ * update, identical-html no-ops).
14
+ * - `tool` segments toggle the AI status pill via `useAiStatusStore`. The
15
+ * pill goes up when `isRunning: true` and clears on `isRunning: false`,
16
+ * `error: true`, or any `error` segment / message-level `isError` flag.
17
+ * - `error` segments (and message-level `isError` / `isCancelled`) trigger
18
+ * a localized toaster and clear the status pill.
19
+ *
20
+ * In dev (`import.meta.env.DEV`) every observed segment + message-level flag
21
+ * change is also `console.debug`-logged with the prefix `[guido:cortex]` so a
22
+ * developer can watch the agent stream in real time. In production this is
23
+ * gated behind `localStorage.guido:debug:ai === '1'`.
24
+ *
25
+ * If cortex-fe isn't loaded the chat store is undefined and the bridge is a
26
+ * no-op (intentional — Guido must still work standalone).
27
+ */
28
+ export declare const useCortexBlueprintBridge: () => void;
@@ -0,0 +1,21 @@
1
+ type ApplyStatus = 'pending' | 'applying' | 'applied' | 'failed';
2
+ /**
3
+ * Applies email_template blueprints from the chat agent to the Stripo editor.
4
+ *
5
+ * - blueprint_create + manual Apply click: immediate.
6
+ * - blueprint_update: debounced 250 ms trailing so rapid mid-stream updates
7
+ * don't thrash updateHtmlAndCss.
8
+ */
9
+ export declare const useEmailTemplateApplier: () => {
10
+ applyStatus: import("vue").Ref<Record<string, ApplyStatus>>;
11
+ applyTemplate: (blueprintId: string, data: {
12
+ html?: string;
13
+ css?: string;
14
+ }) => void;
15
+ applyTemplateDebounced: (blueprintId: string, data: {
16
+ html?: string;
17
+ css?: string;
18
+ }) => void;
19
+ };
20
+ export declare const resetEmailTemplateApplier: () => void;
21
+ export {};
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Outbound bridge — publishes the current editor's html/css to the shared
3
+ * Pinia store `guidoEmailEditor` so cortex-fe can read it at chat-stream
4
+ * submit time and forward it as `clientState.editor`. This is what enables
5
+ * sub-case 1.3 (refine an already-loaded template via the email-agent).
6
+ *
7
+ * The bridge writes on three triggers:
8
+ *
9
+ * 1. Stripo finishes initializing — first snapshot.
10
+ * 2. Editor `hasChanges` flips to true — debounced 500 ms so a streaming
11
+ * Stripo edit doesn't thrash the store.
12
+ * 3. Editor `hasChanges` flips back to false (post-save / autosave) —
13
+ * immediate, since this is the canonical "saved" snapshot.
14
+ *
15
+ * `getTemplateData()` is a Stripo iframe call; it must not run before
16
+ * `isStripoInitialized` is true. The bridge guards against that.
17
+ *
18
+ * If cortex-fe isn't loaded the store still gets written — that's fine;
19
+ * the store has zero overhead and any future reader will find consistent
20
+ * state. The bridge is a one-way pipe with no consumer dependency.
21
+ */
22
+ export declare const useGuidoStateBridge: () => void;
@@ -6,11 +6,7 @@ export declare const URLS: {
6
6
  export declare const QUERY_PARAMS: {
7
7
  CLIENT_ID: string;
8
8
  };
9
- /**
10
- * Get recommendation feed source maps lazily so translated names resolve at access time.
11
- * Must be called within a Vue component context or after Pinia is initialized.
12
- */
13
- export declare const getRecommendationFeedSourceMaps: () => RecommendationFeedItem[];
9
+ export declare const RecommendationFeedSourceMaps: RecommendationFeedItem[];
14
10
  export declare const PriceAttributes: string[];
15
11
  export declare const currencyLocationMaps: TextValueObject[];
16
12
  export declare const currencyOperators: TextValueObject[];
@@ -18,14 +18,9 @@ export declare const PRODUCT_TYPE_URL_SEGMENTS: {
18
18
  readonly 97: "email";
19
19
  };
20
20
  export declare const INSIDER_ID = "iid";
21
- export declare const DEFAULT_UNSUBSCRIBE_GROUP_SEND_GRID_ID = "G";
22
- /**
23
- * Get the default unsubscribe group lazily so the translated name resolves at access time.
24
- * Must be called within a Vue component context or after Pinia is initialized.
25
- */
26
- export declare const getDefaultUnsubscribeGroup: () => {
27
- name: string;
28
- sendGridId: string;
21
+ export declare const DEFAULT_UNSUBSCRIBE_GROUP: {
22
+ readonly name: "Global Unsubscribe";
23
+ readonly sendGridId: "G";
29
24
  };
30
25
  export declare const UNSUBSCRIBE_PAGES_LINK = "/email/unsubscribe-pages";
31
26
  export declare const PAGE_TYPES: {
@@ -95,23 +95,15 @@ export declare class RecommendationConfigService {
95
95
  *
96
96
  * Called when a block is first created (dropped into template).
97
97
  * Can optionally merge in partial config from migration.
98
- *
99
- * The `wasFreshDrop` flag distinguishes a brand-new drop (no prior config)
100
- * from a clone (Stripo replays the source's setNodeConfig payload before
101
- * onCreated fires). Callers use this to skip side-effects already inherited
102
- * from the source.
103
98
  * @example
104
99
  * // In Block.onCreated lifecycle
105
- * const { config, wasFreshDrop } = RecommendationConfigService.initializeConfig(this.api, node);
100
+ * RecommendationConfigService.initializeConfig(this.api, node);
106
101
  * @param api - Stripo extension API with document modifier
107
102
  * @param node - The immutable HTML node to initialize
108
103
  * @param partialConfig - Optional partial config to merge with defaults
109
- * @returns The initialized configuration and whether the node was a fresh drop
104
+ * @returns The initialized configuration
110
105
  */
111
- static initializeConfig(api: DocumentModifierApi, node: ImmutableHtmlNode, partialConfig?: PartialNodeConfig): {
112
- config: RecommendationNodeConfig;
113
- wasFreshDrop: boolean;
114
- };
106
+ static initializeConfig(api: DocumentModifierApi, node: ImmutableHtmlNode, partialConfig?: PartialNodeConfig): RecommendationNodeConfig;
115
107
  /**
116
108
  * Save complete configuration to a node
117
109
  *
@@ -1,5 +1,5 @@
1
1
  import type { Orientation, Languages, Currency, NumericSeparator, FiltersResponse, Filter, RecommendationProduct } from '@@/Types/recommendation';
2
- export interface PerBlockConfigs {
2
+ interface PerBlockConfigs {
3
3
  cardsInRow: number;
4
4
  currencySettings: {
5
5
  name: string;
@@ -272,12 +272,6 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
272
272
  deleteFilter(filter: Filter): void;
273
273
  addFilter(filter: Filter): void;
274
274
  generateFilterQuery(): string;
275
- /**
276
- * Validation-only check invoked at save-CTA time. Defined as an action
277
- * (not a getter) so reading it does not register reactive tracking on
278
- * every block's recommendationConfigs across user edits.
279
- */
280
- hasInvalidBlock(): boolean;
281
275
  fetchRecommendationProducts(): Promise<void>;
282
276
  _doFetchProducts(): Promise<void>;
283
277
  }>;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Lightweight read-only store the cortex-fe blueprint bridge writes into and
3
+ * the editor toolbar reads from to show the "AI is generating…" pill while a
4
+ * tool call is in flight. State + getters only — per architecture invariant
5
+ * the bridge mutates state directly via `$patch`, no actions live here.
6
+ */
7
+ export declare const useAiStatusStore: import("pinia").StoreDefinition<"guidoAiStatus", {
8
+ /** A tool is currently running on the agent side. */
9
+ isWorking: boolean;
10
+ /** Tool name the agent is currently invoking, e.g. `tool__qmechanics__compile_template`. */
11
+ currentTool: string;
12
+ /** Last user-facing error message from the stream, cleared on the next tool_call. */
13
+ lastError: string;
14
+ }, {
15
+ /** Show the toolbar pill iff a tool is running AND no error overrides it. */
16
+ showStatusPill: (state: {
17
+ isWorking: boolean;
18
+ currentTool: string;
19
+ lastError: string;
20
+ } & import("pinia").PiniaCustomStateProperties<{
21
+ /** A tool is currently running on the agent side. */
22
+ isWorking: boolean;
23
+ /** Tool name the agent is currently invoking, e.g. `tool__qmechanics__compile_template`. */
24
+ currentTool: string;
25
+ /** Last user-facing error message from the stream, cleared on the next tool_call. */
26
+ lastError: string;
27
+ }>) => boolean;
28
+ /** Display label for the pill — short, suitable for inline UI. */
29
+ statusLabel: (state: {
30
+ isWorking: boolean;
31
+ currentTool: string;
32
+ lastError: string;
33
+ } & import("pinia").PiniaCustomStateProperties<{
34
+ /** A tool is currently running on the agent side. */
35
+ isWorking: boolean;
36
+ /** Tool name the agent is currently invoking, e.g. `tool__qmechanics__compile_template`. */
37
+ currentTool: string;
38
+ /** Last user-facing error message from the stream, cleared on the next tool_call. */
39
+ lastError: string;
40
+ }>) => string;
41
+ }, {}>;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Outbound contract Guido publishes to the host's shared Pinia singleton so
3
+ * cortex-fe can pick up the current editor state and forward it as
4
+ * `clientState.editor` on the next chat-stream POST.
5
+ *
6
+ * State + getters only — `useGuidoStateBridge` writes via `$patch`, no actions
7
+ * live here (architecture invariant).
8
+ *
9
+ * Store id `guidoEmailEditor` is cross-MFE: cortex-fe reads it via
10
+ * `pinia._s.get('guidoEmailEditor')` at runtime. The internal Guido editor
11
+ * store keeps id `guidoEditor` — these are intentionally separate. The
12
+ * internal store carries UI state that cortex-fe should not see; this public
13
+ * store carries the contract Guido is willing to share.
14
+ */
15
+ export declare const useGuidoEmailEditorStore: import("pinia").StoreDefinition<"guidoEmailEditor", {
16
+ /** Current Stripo HTML — stripe tables only, no DOCTYPE/wrapper. */
17
+ html: string;
18
+ /** Current Stripo CSS — es-p* utility classes used by the html. */
19
+ css: string;
20
+ /** Wall-clock ms when html/css were last written by the bridge. */
21
+ lastUpdatedAt: number;
22
+ /** Template id from the editor config, for cortex-fe to scope per-template. */
23
+ templateId: string;
24
+ }, {
25
+ /** True once the bridge has published at least one snapshot. */
26
+ hasSnapshot: (state: {
27
+ html: string;
28
+ css: string;
29
+ lastUpdatedAt: number;
30
+ templateId: string;
31
+ } & import("pinia").PiniaCustomStateProperties<{
32
+ /** Current Stripo HTML — stripe tables only, no DOCTYPE/wrapper. */
33
+ html: string;
34
+ /** Current Stripo CSS — es-p* utility classes used by the html. */
35
+ css: string;
36
+ /** Wall-clock ms when html/css were last written by the bridge. */
37
+ lastUpdatedAt: number;
38
+ /** Template id from the editor config, for cortex-fe to scope per-template. */
39
+ templateId: string;
40
+ }>) => boolean;
41
+ }, {}>;
@@ -0,0 +1,25 @@
1
+ import { defineStore as o } from "pinia";
2
+ const i = o("guidoAiStatus", {
3
+ state: () => ({
4
+ /** A tool is currently running on the agent side. */
5
+ isWorking: !1,
6
+ /** Tool name the agent is currently invoking, e.g. `tool__qmechanics__compile_template`. */
7
+ currentTool: "",
8
+ /** Last user-facing error message from the stream, cleared on the next tool_call. */
9
+ lastError: ""
10
+ }),
11
+ getters: {
12
+ /** Show the toolbar pill iff a tool is running AND no error overrides it. */
13
+ showStatusPill: (r) => r.isWorking && r.lastError === "",
14
+ /** Display label for the pill — short, suitable for inline UI. */
15
+ statusLabel: (r) => {
16
+ if (!r.isWorking)
17
+ return "";
18
+ const t = r.currentTool.replace(/^tool__[^_]+__/, "");
19
+ return t ? `AI: ${t}…` : "AI is generating…";
20
+ }
21
+ }
22
+ });
23
+ export {
24
+ i as useAiStatusStore
25
+ };
@@ -0,0 +1,20 @@
1
+ import { defineStore as e } from "pinia";
2
+ const d = e("guidoEmailEditor", {
3
+ state: () => ({
4
+ /** Current Stripo HTML — stripe tables only, no DOCTYPE/wrapper. */
5
+ html: "",
6
+ /** Current Stripo CSS — es-p* utility classes used by the html. */
7
+ css: "",
8
+ /** Wall-clock ms when html/css were last written by the bridge. */
9
+ lastUpdatedAt: 0,
10
+ /** Template id from the editor config, for cortex-fe to scope per-template. */
11
+ templateId: ""
12
+ }),
13
+ getters: {
14
+ /** True once the bridge has published at least one snapshot. */
15
+ hasSnapshot: (t) => t.lastUpdatedAt > 0 && t.html !== ""
16
+ }
17
+ });
18
+ export {
19
+ d as useGuidoEmailEditorStore
20
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useinsider/guido",
3
- "version": "3.3.0-beta.b7e5793",
3
+ "version": "3.3.0-beta.d5c796a",
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",
@@ -1,33 +0,0 @@
1
- const o = [
2
- {
3
- key: "locale",
4
- getValue: (e) => e.language,
5
- getAvailableOptions: (e) => Object.keys(e.languages)
6
- },
7
- {
8
- key: "currency",
9
- getValue: (e) => e.currencySettings.value,
10
- getAvailableOptions: (e) => e.currencyList.map((n) => n.text)
11
- }
12
- ], l = "newsletter.recommendation-fill-required-fields";
13
- function u(e, n) {
14
- return o.filter((t) => {
15
- var a;
16
- if (t.condition && !t.condition(e))
17
- return !1;
18
- const i = t.getValue(e);
19
- if (!i)
20
- return !0;
21
- const r = (a = t.getAvailableOptions) == null ? void 0 : a.call(t, n);
22
- return r !== void 0 && !r.includes(i);
23
- }).map((t) => t.key);
24
- }
25
- function s(e, n) {
26
- return u(e, n).length === 0;
27
- }
28
- export {
29
- o as REQUIRED_RECOMMENDATION_FIELDS,
30
- l as RecommendationRequiredFieldsKey,
31
- u as getInvalidFields,
32
- s as isConfigValid
33
- };