@useinsider/guido 3.17.0-beta.e6704da → 3.17.0-beta.ea65303

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 (75) hide show
  1. package/README.md +1 -1
  2. package/dist/@types/config/schemas.js +31 -35
  3. package/dist/components/Guido.vue.js +5 -5
  4. package/dist/components/Guido.vue2.js +77 -77
  5. package/dist/components/organisms/AutoSaveController.vue2.js +9 -8
  6. package/dist/components/organisms/header/EditorActions.vue.js +2 -2
  7. package/dist/components/organisms/header/EditorActions.vue2.js +74 -43
  8. package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
  9. package/dist/components/organisms/header/LeftSlot.vue2.js +16 -12
  10. package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +20 -0
  11. package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +48 -0
  12. package/dist/components/wrappers/WpDrawer.vue.js +9 -7
  13. package/dist/components/wrappers/WpDrawer.vue2.js +1 -1
  14. package/dist/composables/useCustomInterfaceAppearance.js +16 -19
  15. package/dist/composables/useHtmlCompiler.js +37 -26
  16. package/dist/composables/useHtmlValidator.js +2 -0
  17. package/dist/composables/useRecommendation.js +34 -41
  18. package/dist/composables/useSave.js +84 -41
  19. package/dist/composables/useSaveValidation.js +279 -0
  20. package/dist/composables/useSaveValidationRollout.js +9 -0
  21. package/dist/composables/validators/useCouponBlockValidator.js +24 -21
  22. package/dist/composables/validators/useUnsubscribeBlockValidator.js +34 -27
  23. package/dist/config/migrator/recommendationMigrator.js +4 -6
  24. package/dist/enums/academy.js +1 -2
  25. package/dist/enums/date.js +3 -4
  26. package/dist/enums/extensions/recommendationBlock.js +23 -25
  27. package/dist/enums/save-validation.js +4 -0
  28. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +11 -13
  29. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +95 -96
  30. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +154 -156
  31. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +82 -223
  32. package/dist/extensions/Blocks/common-control.js +19 -22
  33. package/dist/guido.css +1 -1
  34. package/dist/services/recommendationApi.js +29 -78
  35. package/dist/src/@types/config/schemas.d.ts +4 -12
  36. package/dist/src/@types/save-validation.d.ts +19 -0
  37. package/dist/src/components/Guido.vue.d.ts +17 -1
  38. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +17 -1
  39. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +17 -1
  40. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +17 -1
  41. package/dist/src/components/organisms/{extensions/recommendation/AssignStrategyDrawer.vue.d.ts → validation/ValidationErrorDrawer.vue.d.ts} +0 -1
  42. package/dist/src/components/wrappers/WpDrawer.vue.d.ts +4 -3
  43. package/dist/src/composables/useConfig.d.ts +2 -2
  44. package/dist/src/composables/useHtmlValidator.d.ts +21 -0
  45. package/dist/src/composables/useRecommendation.d.ts +0 -1
  46. package/dist/src/composables/useSave.d.ts +6 -1
  47. package/dist/src/composables/useSaveValidation.d.ts +6 -0
  48. package/dist/src/composables/useSaveValidationRollout.d.ts +3 -0
  49. package/dist/src/composables/validators/useCouponBlockValidator.d.ts +3 -0
  50. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +10 -0
  51. package/dist/src/enums/academy.d.ts +0 -1
  52. package/dist/src/enums/extensions/recommendationBlock.d.ts +0 -2
  53. package/dist/src/enums/save-validation.d.ts +27 -0
  54. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +3 -14
  55. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +15 -214
  56. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +0 -6
  57. package/dist/src/extensions/Blocks/common-control.d.ts +1 -4
  58. package/dist/src/services/recommendationApi.d.ts +1 -4
  59. package/dist/src/stores/config.d.ts +30 -18
  60. package/dist/src/stores/save-validation.d.ts +22 -0
  61. package/dist/src/utils/templatePreparation.d.ts +30 -0
  62. package/dist/stores/config.js +39 -22
  63. package/dist/stores/save-validation.js +30 -0
  64. package/dist/utils/dateUtil.js +10 -23
  65. package/dist/utils/templatePreparation.js +50 -50
  66. package/package.json +1 -2
  67. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +0 -18
  68. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +0 -70
  69. package/dist/components/organisms/extensions/recommendation/StrategyInfoBox.vue.js +0 -20
  70. package/dist/components/organisms/extensions/recommendation/StrategyInfoBox.vue2.js +0 -55
  71. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +0 -141
  72. package/dist/src/components/organisms/extensions/recommendation/StrategyInfoBox.vue.d.ts +0 -24
  73. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +0 -29
  74. package/dist/src/mock/api/recommendation-strategies.d.ts +0 -43
  75. package/dist/static/styles/components/recommendation-strategy.css.js +0 -171
@@ -1,21 +1,21 @@
1
- import { getRecommendationFeedSourceMaps as p, getOperatorOptions as R, PriceAttributes as C } from "../../../../enums/extensions/recommendationBlock.js";
2
- import { useRecommendationApi as w } from "../../../../services/recommendationApi.js";
3
- import { useConfigStore as G } from "../../../../stores/config.js";
4
- import { defineStore as P } from "pinia";
1
+ import { getRecommendationFeedSourceMaps as g, getOperatorOptions as C, PriceAttributes as y } from "../../../../enums/extensions/recommendationBlock.js";
2
+ import { useRecommendationApi as G } from "../../../../services/recommendationApi.js";
3
+ import { useConfigStore as P } from "../../../../stores/config.js";
4
+ import { defineStore as F } from "pinia";
5
5
  import "../constants/selectors.js";
6
- import { DEFAULT_MOBILE_CARDS_IN_ROW as D, DEFAULT_CARDS_IN_ROW as F } from "../constants/layout.js";
7
- import { DEFAULT_NODE_CONFIG as v, EXCLUDED_ALGORITHM_IDS as _ } from "../constants/defaultConfig.js";
8
- import { getDefaultProducts as y } from "../templates/utils.js";
6
+ import { DEFAULT_MOBILE_CARDS_IN_ROW as D, DEFAULT_CARDS_IN_ROW as v } from "../constants/layout.js";
7
+ import { EXCLUDED_ALGORITHM_IDS as w } from "../constants/defaultConfig.js";
8
+ import { getDefaultProducts as S } from "../templates/utils.js";
9
9
  import { generateCompleteFilterQuery as b } from "../utils/filterUtil.js";
10
- import { getPartnerRecommendationParams as U } from "../utils/partnerCustomizations.js";
11
- import { sanitizeProductIds as N } from "../utils/productIds.js";
10
+ import { getPartnerRecommendationParams as N } from "../utils/partnerCustomizations.js";
11
+ import { sanitizeProductIds as x } from "../utils/productIds.js";
12
12
  import { isFilterValid as L } from "../validation/filterSchema.js";
13
- import { isConfigValid as x } from "../validation/requiredFields.js";
14
- const u = w();
15
- let d = null, f = null, h = null, g = null;
16
- function k() {
13
+ import { isConfigValid as I } from "../validation/requiredFields.js";
14
+ const h = G();
15
+ let m = null, u = null, d = null;
16
+ function R() {
17
17
  return {
18
- cardsInRow: F,
18
+ cardsInRow: v,
19
19
  currencySettings: {
20
20
  name: "USD",
21
21
  value: "USD",
@@ -36,7 +36,6 @@ function k() {
36
36
  sendProductRequestFlag: !1,
37
37
  shuffleProducts: !1,
38
38
  strategy: "mostPopular",
39
- strategyId: "",
40
39
  textTrimming: !0,
41
40
  unresponsive: !1,
42
41
  size: "6",
@@ -45,32 +44,28 @@ function k() {
45
44
  priceHideIfSameAsDiscounted: !1
46
45
  };
47
46
  }
48
- function I() {
47
+ function k() {
49
48
  return {
50
- recommendationConfigs: k(),
49
+ recommendationConfigs: R(),
51
50
  recommendationProducts: [],
52
51
  filterStatus: !1,
53
52
  filterSelectionDrawerStatus: !1,
54
- assignStrategyDrawerStatus: !1,
55
53
  filterGroup: 1,
56
54
  isInitialized: !1,
57
55
  filterSnapshot: null
58
56
  };
59
57
  }
60
- const A = () => ({
58
+ const U = () => ({
61
59
  recommendationCampaignUrls: {},
62
60
  activePredictiveAlgorithms: [],
63
61
  languages: {},
64
62
  currencyList: [],
65
63
  filterList: {},
66
- strategies: [],
67
- strategiesLoading: !1,
68
- strategyUrlTemplates: {},
69
64
  blockStates: {},
70
65
  currentRecommendationId: null,
71
66
  configVersion: 0
72
- }), J = P("guidoRecommendationExtension", {
73
- state: () => A(),
67
+ }), H = F("guidoRecommendationExtension", {
68
+ state: () => U(),
74
69
  getters: {
75
70
  // ====================================================================
76
71
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -80,7 +75,7 @@ const A = () => ({
80
75
  * This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
81
76
  */
82
77
  recommendationConfigs(t) {
83
- return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : k();
78
+ return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : R();
84
79
  },
85
80
  /**
86
81
  * Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
@@ -94,20 +89,6 @@ const A = () => ({
94
89
  filterSelectionDrawerStatus(t) {
95
90
  return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].filterSelectionDrawerStatus : !1;
96
91
  },
97
- /**
98
- * Proxy getter: delegates to blockStates[currentRecommendationId].assignStrategyDrawerStatus
99
- */
100
- assignStrategyDrawerStatus(t) {
101
- return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].assignStrategyDrawerStatus : !1;
102
- },
103
- /**
104
- * The current block's assigned strategy record from the listing,
105
- * or null when nothing is assigned / the strategy is no longer listed.
106
- */
107
- assignedStrategy(t) {
108
- const { strategyId: e } = this.recommendationConfigs;
109
- return e ? t.strategies.find((r) => String(r.strategy_id) === e) ?? null : null;
110
- },
111
92
  /**
112
93
  * Proxy getter: delegates to blockStates[currentRecommendationId].filterStatus
113
94
  */
@@ -139,9 +120,9 @@ const A = () => ({
139
120
  return [...new Set(t.map((e) => e.filterGroup))].sort((e, r) => e - r);
140
121
  },
141
122
  getActivePredictiveAlgorithms: (t) => {
142
- const e = p(), r = [];
143
- return t.activePredictiveAlgorithms.filter((n) => !_.includes(n)).forEach((n) => {
144
- r.push(...e.filter((s) => s.id === n));
123
+ const e = g(), r = [];
124
+ return t.activePredictiveAlgorithms.filter((n) => !w.includes(n)).forEach((n) => {
125
+ r.push(...e.filter((c) => c.id === n));
145
126
  }), r.map((n) => ({
146
127
  text: n.name,
147
128
  value: n.key
@@ -158,7 +139,7 @@ const A = () => ({
158
139
  getFilterList() {
159
140
  return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
160
141
  let e;
161
- return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : C.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
142
+ return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : y.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
162
143
  text: t.displayName,
163
144
  value: e,
164
145
  type: t.attributeType
@@ -180,7 +161,7 @@ const A = () => ({
180
161
  setCurrentBlock(t) {
181
162
  this.blockStates[t] || (this.blockStates = {
182
163
  ...this.blockStates,
183
- [t]: I()
164
+ [t]: k()
184
165
  }), this.currentRecommendationId = t;
185
166
  },
186
167
  /**
@@ -212,10 +193,10 @@ const A = () => ({
212
193
  [e]: o
213
194
  };
214
195
  }
215
- const n = t.toString(), s = e.toString(), i = this.recommendationCampaignUrls[n];
196
+ const n = t.toString(), c = e.toString(), i = this.recommendationCampaignUrls[n];
216
197
  i && (this.recommendationCampaignUrls = {
217
198
  ...this.recommendationCampaignUrls,
218
- [s]: i
199
+ [c]: i
219
200
  });
220
201
  },
221
202
  /**
@@ -240,7 +221,7 @@ const A = () => ({
240
221
  * `isInitialized`) alone.
241
222
  */
242
223
  seedBlockUrlConfig(t, e) {
243
- const r = (o, a) => o === "." || o === "," || o === " " ? o : a, n = {
224
+ const r = (o, s) => o === "." || o === "," || o === " " ? o : s, n = {
244
225
  name: e.currencyCode,
245
226
  value: e.currencyCode,
246
227
  symbol: e.currencyCode,
@@ -248,18 +229,17 @@ const A = () => ({
248
229
  decimalCount: String(e.currencyDecimalCount),
249
230
  decimalSeparator: r(e.currencyDecimalSeparator, ","),
250
231
  thousandSeparator: r(e.currencyThousandSeparator, ".")
251
- }, s = !this.blockStates[t], i = s ? I() : this.blockStates[t];
232
+ }, c = !this.blockStates[t], i = c ? k() : this.blockStates[t];
252
233
  i.recommendationConfigs = {
253
234
  ...i.recommendationConfigs,
254
235
  strategy: e.strategy,
255
- strategyId: e.strategyId,
256
236
  language: e.language,
257
237
  size: e.size,
258
238
  productIds: e.productIds,
259
239
  filters: e.filters,
260
240
  shuffleProducts: e.shuffleProducts,
261
241
  currencySettings: n
262
- }, s && (this.blockStates = {
242
+ }, c && (this.blockStates = {
263
243
  ...this.blockStates,
264
244
  [t]: i
265
245
  });
@@ -287,7 +267,7 @@ const A = () => ({
287
267
  * Creates a filter with the first available attribute and operator pre-selected.
288
268
  */
289
269
  createDefaultFilter(t, e) {
290
- const [r] = this.getFilterList, [n] = R(r == null ? void 0 : r.type);
270
+ const [r] = this.getFilterList, [n] = C(r == null ? void 0 : r.type);
291
271
  return {
292
272
  type: "standardFilter",
293
273
  attribute: (r == null ? void 0 : r.value) ?? "",
@@ -335,107 +315,20 @@ const A = () => ({
335
315
  t.filterSnapshot !== null && (t.recommendationConfigs.filters = t.filterSnapshot, t.filterSnapshot = null), t.filterSelectionDrawerStatus = !1;
336
316
  },
337
317
  // ====================================================================
338
- // Reusable Recommendation Strategy (SD-149105)
339
- // ====================================================================
340
- /**
341
- * Opens the Assign Strategy drawer for the current block.
342
- * Selection state lives in the drawer component until Assign is
343
- * pressed, so closing without assigning needs no snapshot/revert.
344
- */
345
- openAssignStrategyDrawer() {
346
- this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].assignStrategyDrawerStatus = !0);
347
- },
348
- closeAssignStrategyDrawer() {
349
- this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId] || (this.blockStates[this.currentRecommendationId].assignStrategyDrawerStatus = !1);
350
- },
351
- /**
352
- * Fetches the active + EMAIL strategy listing. Always fetches fresh
353
- * (dead-strategy detection and post-create refresh need freshness);
354
- * only concurrent in-flight calls share one request.
355
- */
356
- async fetchStrategies() {
357
- if (g) {
358
- await g;
359
- return;
360
- }
361
- g = (async () => {
362
- this.strategiesLoading = !0;
363
- try {
364
- this.strategies = await u.fetchRecommendationStrategies();
365
- } finally {
366
- this.strategiesLoading = !1;
367
- }
368
- })();
369
- try {
370
- await g;
371
- } finally {
372
- g = null;
373
- }
374
- },
375
- /**
376
- * Resolves a strategy's recommendation endpoint template via
377
- * endpoint-generate, cached per strategy id + revision. Failures throw
378
- * and are NOT cached, so the next attempt retries.
379
- *
380
- * The revision is part of the key because `full_path` bakes in the
381
- * algorithm path and `size=` as they were when it was generated, while
382
- * the listing is deliberately never cached — editing a strategy in the
383
- * panel and re-assigning it here would otherwise call a stale endpoint.
384
- *
385
- * A record with no revision is not cached at all: a `<id>:` key can
386
- * never change, so it would serve the stale template forever — exactly
387
- * what the revision is here to prevent.
388
- */
389
- async resolveStrategyUrlTemplate(t) {
390
- const e = typeof t.updated_at == "string" ? t.updated_at : "", r = e ? `${t.strategy_id}:${e}` : "", n = r ? this.strategyUrlTemplates[r] : void 0;
391
- if (n)
392
- return n;
393
- const { full_path: s } = await u.generateStrategyEndpoint(t);
394
- return r && (this.strategyUrlTemplates = {
395
- ...this.strategyUrlTemplates,
396
- [r]: s
397
- }), s;
398
- },
399
- /**
400
- * Assigns a strategy to the current block. The config write is
401
- * synchronous (before any await) so a block switch cannot misdirect
402
- * it; the configVersion bump persists it to node config and triggers
403
- * the product refetch via the coordinator control's subscription.
404
- */
405
- assignStrategy(t) {
406
- this.patchCurrentBlockConfig({
407
- strategyId: String(t.strategy_id),
408
- ...t.size !== void 0 ? { size: String(t.size) } : {}
409
- });
410
- },
411
- /**
412
- * Clears the current block's strategy; it falls back to the per-block
413
- * algorithm settings.
414
- *
415
- * `size` goes back to the default rather than staying on the removed
416
- * strategy's count: the Number of Products control is hidden in RRS
417
- * mode (D2), so a block left on the strategy's count would have no UI
418
- * to correct it. The pre-assign count is not restored — that would
419
- * need a persisted snapshot field; tracked for a later phase.
420
- */
421
- removeStrategy() {
422
- this.patchCurrentBlockConfig({ strategyId: "", size: v.size });
423
- },
424
- // ====================================================================
425
318
  // Shared Data Fetching (fetched once, used by all blocks)
426
319
  // ====================================================================
427
320
  async fetchRecommendationCreateData() {
428
321
  if (!this.activePredictiveAlgorithms.length) {
429
- if (d) {
430
- await d;
322
+ if (m) {
323
+ await m;
431
324
  return;
432
325
  }
433
- d = (async () => {
326
+ m = (async () => {
434
327
  const {
435
328
  activePredictiveAlgorithms: t,
436
329
  languages: e,
437
330
  currencies: r
438
- } = await u.fetchRecommendationCreateData();
331
+ } = await h.fetchRecommendationCreateData();
439
332
  if (this.activePredictiveAlgorithms = t, this.languages = e, this.currentRecommendationId !== null && this.blockStates[this.currentRecommendationId]) {
440
333
  const n = this.blockStates[this.currentRecommendationId];
441
334
  n.filterStatus = !!n.recommendationConfigs.filters.length;
@@ -443,26 +336,26 @@ const A = () => ({
443
336
  this.currencyList = r;
444
337
  })();
445
338
  try {
446
- await d;
339
+ await m;
447
340
  } finally {
448
- d = null;
341
+ m = null;
449
342
  }
450
343
  }
451
344
  },
452
345
  async fetchRecommendationFilters() {
453
346
  if (!Object.keys(this.filterList).length) {
454
- if (f) {
455
- await f;
347
+ if (u) {
348
+ await u;
456
349
  return;
457
350
  }
458
- f = (async () => {
459
- const t = await u.fetchRecommendationFilters();
351
+ u = (async () => {
352
+ const t = await h.fetchRecommendationFilters();
460
353
  this.filterList = t;
461
354
  })();
462
355
  try {
463
- await f;
356
+ await u;
464
357
  } finally {
465
- f = null;
358
+ u = null;
466
359
  }
467
360
  }
468
361
  },
@@ -477,10 +370,10 @@ const A = () => ({
477
370
  deleteFilterGroup(t) {
478
371
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
479
372
  return;
480
- const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.filter((i) => i.filterGroup !== t), n = [...new Set(r.map((i) => i.filterGroup))].sort((i, o) => i - o), s = new Map(n.map((i, o) => [i, o + 1]));
373
+ const e = this.blockStates[this.currentRecommendationId], r = e.recommendationConfigs.filters.filter((i) => i.filterGroup !== t), n = [...new Set(r.map((i) => i.filterGroup))].sort((i, o) => i - o), c = new Map(n.map((i, o) => [i, o + 1]));
481
374
  e.recommendationConfigs.filters = r.map((i) => ({
482
375
  ...i,
483
- filterGroup: s.get(i.filterGroup) ?? i.filterGroup
376
+ filterGroup: c.get(i.filterGroup) ?? i.filterGroup
484
377
  }));
485
378
  },
486
379
  updateFilter(t) {
@@ -505,10 +398,10 @@ const A = () => ({
505
398
  let i = 1;
506
399
  n = n.map((o) => o.filterGroup === t.filterGroup ? { ...o, filterNumber: i++ } : o);
507
400
  } else {
508
- const i = [...new Set(n.map((a) => a.filterGroup))].sort((a, c) => a - c), o = new Map(i.map((a, c) => [a, c + 1]));
509
- n = n.map((a) => ({
510
- ...a,
511
- filterGroup: o.get(a.filterGroup) ?? a.filterGroup
401
+ const i = [...new Set(n.map((s) => s.filterGroup))].sort((s, a) => s - a), o = new Map(i.map((s, a) => [s, a + 1]));
402
+ n = n.map((s) => ({
403
+ ...s,
404
+ filterGroup: o.get(s.filterGroup) ?? s.filterGroup
512
405
  }));
513
406
  }
514
407
  e.recommendationConfigs.filters = n;
@@ -517,15 +410,15 @@ const A = () => ({
517
410
  addFilter(t) {
518
411
  if (this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])
519
412
  return;
520
- const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters], s = r.filter(
413
+ const e = this.blockStates[this.currentRecommendationId], r = [...e.recommendationConfigs.filters], c = r.filter(
521
414
  (o) => o.filterGroup === t.filterGroup
522
415
  ).length + 1, i = r.findLastIndex((o) => o.filterGroup === t.filterGroup);
523
416
  i !== -1 ? r.splice(i + 1, 0, {
524
417
  ...t,
525
- filterNumber: s
418
+ filterNumber: c
526
419
  }) : r.push({
527
420
  ...t,
528
- filterNumber: s
421
+ filterNumber: c
529
422
  }), e.recommendationConfigs.filters = r;
530
423
  },
531
424
  generateFilterQuery() {
@@ -537,96 +430,62 @@ const A = () => ({
537
430
  * every block's recommendationConfigs across user edits.
538
431
  */
539
432
  hasInvalidBlock() {
540
- return Object.values(this.blockStates).some((t) => !x(t.recommendationConfigs, this));
433
+ return Object.values(this.blockStates).some((t) => !I(t.recommendationConfigs, this));
434
+ },
435
+ /**
436
+ * Per-instance counterpart to `hasInvalidBlock`: the ids of every block
437
+ * that fails required-field validation, one drawer row each (SD-147430).
438
+ */
439
+ invalidBlockIds() {
440
+ return Object.entries(this.blockStates).filter(([, t]) => !I(t.recommendationConfigs, this)).map(([t]) => t);
541
441
  },
542
442
  // ====================================================================
543
443
  // Per-Block Product Fetching
544
444
  // ====================================================================
545
445
  async fetchRecommendationProducts() {
546
446
  if (!(this.currentRecommendationId === null || !this.blockStates[this.currentRecommendationId])) {
547
- if (h) {
548
- await h;
447
+ if (d) {
448
+ await d;
549
449
  return;
550
450
  }
551
- h = this._doFetchProducts();
451
+ d = this._doFetchProducts();
552
452
  try {
553
- await h;
453
+ await d;
554
454
  } finally {
555
- h = null;
455
+ d = null;
556
456
  }
557
457
  }
558
458
  },
559
459
  async _doFetchProducts() {
560
- var S;
561
- const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = G();
562
- if (n.isFeatureEnabled("reusableRecommendationStrategy") && r.strategyId) {
563
- await this._doFetchStrategyProducts(t);
564
- return;
565
- }
566
- const s = r.filters.filter((m) => m.isValid), i = b(s), o = ((S = p().find((m) => m.key === r.strategy)) == null ? void 0 : S.path) || "", a = parseInt(r.size) || 6, c = {
460
+ var p;
461
+ 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 = P(), s = parseInt(r.size) || 6, a = {
567
462
  locale: r.language,
568
463
  currency: r.currencySettings.value,
569
- partnerName: n.partnerName,
464
+ partnerName: o.partnerName,
570
465
  size: r.size,
571
466
  details: !0,
572
- campaignId: n.variationId
467
+ campaignId: o.variationId
573
468
  };
574
- r.strategy === "manualMerchandising" ? c.productId = N(r.productIds) : r.strategy === "similarViewed" && (c.productId = "{itemId}"), r.strategy === "userBased" && (c.userId = "{user_id}"), i && (c.filter = i), r.shuffleProducts && (c.shuffle = !0), Object.assign(
575
- c,
576
- U(n.partnerName, r.strategy)
469
+ r.strategy === "manualMerchandising" ? a.productId = x(r.productIds) : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"), c && (a.filter = c), r.shuffleProducts && (a.shuffle = !0), Object.assign(
470
+ a,
471
+ N(o.partnerName, r.strategy)
577
472
  );
578
- let l;
473
+ let f;
579
474
  try {
580
- l = await u.fetchRecommendationProducts(o, c);
475
+ f = await h.fetchRecommendationProducts(i, a);
581
476
  } catch {
582
- l = [];
477
+ f = [];
583
478
  }
584
- this._setBlockProducts(t, l, a);
585
- },
586
- /**
587
- * Fetches products for a strategy-backed block: listing lookup →
588
- * endpoint-generate template → fill {locale}/{currency}, drop
589
- * {user_id} (resolved at send time) → fetch. Any failure (dead
590
- * strategy, resolution error) falls back to placeholder products.
591
- */
592
- async _doFetchStrategyProducts(t) {
593
- const { recommendationConfigs: e } = this.blockStates[t];
594
- let r = [], n = parseInt(e.size) || 6;
595
- try {
596
- this.strategies.length || await this.fetchStrategies();
597
- const s = this.strategies.find(
598
- (i) => String(i.strategy_id) === e.strategyId
599
- );
600
- if (s) {
601
- n = parseInt(String(s.size ?? "")) || n;
602
- const o = (await this.resolveStrategyUrlTemplate(s)).replaceAll("{locale}", e.language).replaceAll("{currency}", e.currencySettings.value), a = o.indexOf("?"), c = a === -1 ? o : o.slice(0, a), l = (a === -1 ? "" : o.slice(a + 1)).split("&").filter((m) => m && !m.startsWith("user_id=")), S = l.length ? `${c}?${l.join("&")}` : c;
603
- r = await u.fetchRecommendationProductsByUrl(S);
604
- }
605
- } catch {
606
- r = [];
479
+ if (this.blockStates[t]) {
480
+ const l = f.length > 0 ? f : S(s);
481
+ l.length < s ? this.blockStates[t].recommendationProducts = [
482
+ ...l,
483
+ ...S(s - l.length)
484
+ ] : l.length > s ? this.blockStates[t].recommendationProducts = l.slice(0, s) : this.blockStates[t].recommendationProducts = l;
607
485
  }
608
- this._setBlockProducts(t, r, n);
609
- },
610
- /**
611
- * Writes products to a block's state, padded/clipped to `size` so
612
- * in-place content updates (which compare DOM element count vs
613
- * products.length) succeed:
614
- * - API returned nothing → fill entirely with defaults
615
- * - fewer than requested → pad with dummies
616
- * - more than requested → clip, preventing the renderer from extending
617
- * its slot grid (which would reset migrator-applied styles).
618
- */
619
- _setBlockProducts(t, e, r) {
620
- if (!this.blockStates[t])
621
- return;
622
- const n = e.length > 0 ? e : y(r);
623
- n.length < r ? this.blockStates[t].recommendationProducts = [
624
- ...n,
625
- ...y(r - n.length)
626
- ] : n.length > r ? this.blockStates[t].recommendationProducts = n.slice(0, r) : this.blockStates[t].recommendationProducts = n;
627
486
  }
628
487
  }
629
488
  });
630
489
  export {
631
- J as useRecommendationExtensionStore
490
+ H as useRecommendationExtensionStore
632
491
  };
@@ -1,12 +1,12 @@
1
1
  var c = Object.defineProperty;
2
- var i = (l, a, e) => a in l ? c(l, a, { enumerable: !0, configurable: !0, writable: !0, value: e }) : l[a] = e;
3
- var T = (l, a, e) => i(l, typeof a != "symbol" ? a + "" : a, e);
2
+ var i = (s, r, e) => r in s ? c(s, r, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[r] = e;
3
+ var u = (s, r, e) => i(s, typeof r != "symbol" ? r + "" : r, e);
4
4
  import { Control as I, UIElementType as n, UEAttr as $ } from "../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- class _ extends I {
5
+ class d extends I {
6
6
  constructor() {
7
7
  super(...arguments);
8
- T(this, "currentNode");
9
- T(this, "lastBlockInstanceId", null);
8
+ u(this, "currentNode");
9
+ u(this, "lastBlockInstanceId", null);
10
10
  }
11
11
  getContainer() {
12
12
  var e;
@@ -91,21 +91,21 @@ class _ extends I {
91
91
  * and combines it with the text in Stripo's alert-message layout.
92
92
  */
93
93
  _setInfoMessageValue(e, t) {
94
- const E = this.getContainer(), o = E.querySelector(`[data-icon-key="${e}"]`), r = o == null ? void 0 : o.querySelector("ue-icon-component"), s = (r == null ? void 0 : r.outerHTML) ?? "";
94
+ const E = this.getContainer(), o = E.querySelector(`[data-icon-key="${e}"]`), a = o == null ? void 0 : o.querySelector("ue-icon-component"), l = (a == null ? void 0 : a.outerHTML) ?? "";
95
95
  this.api.setUIEAttribute(
96
96
  e,
97
97
  "value",
98
98
  `
99
99
  <div class="service-element alert-message-main" style="align-items: start;">
100
- ${s}
100
+ ${l}
101
101
  <div class="service-element alert-message-text">
102
102
  ${t}
103
103
  </div>
104
104
  </div>
105
105
  `
106
106
  );
107
- const u = E.querySelector(`[data-info-message="${e}"]`);
108
- u && (u.style.display = "");
107
+ const T = E.querySelector(`[data-info-message="${e}"]`);
108
+ T && (T.style.display = "");
109
109
  }
110
110
  _GuToggle(e) {
111
111
  return `
@@ -126,15 +126,15 @@ class _ extends I {
126
126
  placeholder: t,
127
127
  options: E,
128
128
  className: o = "es-180w",
129
- searchable: r = !1
129
+ searchable: a = !1
130
130
  }) {
131
131
  return `
132
132
  <${n.SELECTPICKER}
133
133
  class="${o}"
134
134
  ${$.SELECTPICKER.name}="${e}"
135
- ${r ? `${$.SELECTPICKER.searchable}="true"` : ""}
135
+ ${a ? `${$.SELECTPICKER.searchable}="true"` : ""}
136
136
  ${$.SELECTPICKER.placeholder}="${t}">
137
- ${E.map((s) => this._GuSelectItem(s)).join("")}
137
+ ${E.map((l) => this._GuSelectItem(l)).join("")}
138
138
  </${n.SELECTPICKER}>
139
139
  `;
140
140
  }
@@ -176,15 +176,12 @@ class _ extends I {
176
176
  </${n.RADIO_BUTTONS}>
177
177
  `;
178
178
  }
179
- _GuButton({ name: e, label: t, id: E = "", className: o = "", icon: r = "", disabled: s = !1 }) {
179
+ _GuButton({ name: e, label: t, id: E = "" }) {
180
180
  return `
181
181
  <${n.BUTTON}
182
182
  ${$.BUTTON.name}="${e}"
183
183
  ${E ? `id="${E}"` : ""}
184
- ${o ? `class="${o}"` : ""}
185
- ${r ? `${$.BUTTON.icon}="${r}"` : ""}
186
- ${s ? `${$.BUTTON.disabled}="true"` : ""}
187
- ${$.BUTTON.caption}="${t}">
184
+ ${$.BUTTON.caption}="${t}"}>
188
185
  </${n.BUTTON}>
189
186
  `;
190
187
  }
@@ -233,11 +230,11 @@ class _ extends I {
233
230
  */
234
231
  _GuOrderable(e, t) {
235
232
  let E = "";
236
- t.forEach((r) => {
237
- const s = $.ORDERABLE_ITEM && "name" in $.ORDERABLE_ITEM ? $.ORDERABLE_ITEM.name : "name";
233
+ t.forEach((a) => {
234
+ const l = $.ORDERABLE_ITEM && "name" in $.ORDERABLE_ITEM ? $.ORDERABLE_ITEM.name : "name";
238
235
  E += `
239
- <${n.ORDERABLE_ITEM} ${s}="${r.key}">
240
- ${r.content}
236
+ <${n.ORDERABLE_ITEM} ${l}="${a.key}">
237
+ ${a.content}
241
238
  </${n.ORDERABLE_ITEM}>
242
239
  `;
243
240
  });
@@ -308,5 +305,5 @@ class _ extends I {
308
305
  }
309
306
  }
310
307
  export {
311
- _ as CommonControl
308
+ d as CommonControl
312
309
  };
package/dist/guido.css CHANGED
@@ -1 +1 @@
1
- @charset "UTF-8";.gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-87033101]{position:relative;display:inline-block}.new-tag[data-v-87033101]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-87033101] .guido__view-option-selection-desktop svg,[data-v-87033101] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-87033101] .in-segments-wrapper__button_selected,[data-v-87033101] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-87033101] .in-tooltip-wrapper-v2__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{gap:4px}.view-options-wrapper[data-v-6ebabfe4]{position:relative;display:inline-block}.new-tag[data-v-6ebabfe4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-6ebabfe4] .guido__verion-history-view-option-selection-desktop svg,[data-v-6ebabfe4] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-6ebabfe4] .in-segments-wrapper__button_selected,[data-v-6ebabfe4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-6ebabfe4] .in-tooltip-wrapper-v2__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-6e711fd7]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-f258bc9e]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-f258bc9e]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-f258bc9e]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom:110px!important}.strategy-info-box__name[data-v-fe4491e6]{color:var(--guido-color-gray-800, #2c3546);font-size:13px;font-weight:600;line-height:20px}.strategy-info-box__updated[data-v-fe4491e6]{margin-top:2px;color:var(--guido-color-gray-600, #6d7686);font-size:12px;line-height:18px}.strategy-info-box__rows[data-v-fe4491e6]{margin-top:12px}.strategy-info-box__row+.strategy-info-box__row[data-v-fe4491e6]{margin-top:10px}.strategy-info-box__label[data-v-fe4491e6]{color:var(--guido-color-gray-600, #6d7686);font-size:12px;line-height:18px}.strategy-info-box__value[data-v-fe4491e6]{color:var(--guido-color-gray-800, #2c3546);font-size:12px;line-height:18px;word-break:break-word}.assign-strategy-drawer-content[data-v-710137fe]{display:flex;flex-direction:column;gap:16px}.assign-strategy-select[data-v-710137fe]{width:100%;max-width:none}.assign-strategy-select[data-v-710137fe] .in-dropdown-box__container{width:58%}.assign-strategy-detail[data-v-710137fe] .in-drawer__container,.assign-strategy-detail[data-v-710137fe] .in-drawer__wrapper{position:static!important;inset:auto!important;width:100%!important;max-width:none!important;height:auto!important;border:none!important;box-shadow:none!important;transform:none!important}.assign-strategy-detail[data-v-710137fe] .in-drawer__content{height:auto!important;max-height:none!important;overflow:visible!important}.assign-strategy-detail[data-v-710137fe] .in-drawer__content .px-5{padding-left:0!important;padding-right:0!important}.assign-strategy-detail[data-v-710137fe] .filter-humanizer{margin-right:0!important;margin-left:0!important}.assign-strategy-detail[data-v-710137fe] .in-drawer__header-main{padding:0!important}.assign-strategy-detail[data-v-710137fe] .in-drawer__title,.assign-strategy-detail[data-v-710137fe] [class*=overlay],.assign-strategy-detail[data-v-710137fe] #close-button-strategy-details-drawer{display:none!important}
1
+ .gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-b8f24f34] .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-87033101]{position:relative;display:inline-block}.new-tag[data-v-87033101]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-87033101] .guido__view-option-selection-desktop svg,[data-v-87033101] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-87033101] .in-segments-wrapper__button_selected,[data-v-87033101] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-87033101] .in-tooltip-wrapper-v2__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{gap:4px}.view-options-wrapper[data-v-6ebabfe4]{position:relative;display:inline-block}.new-tag[data-v-6ebabfe4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-6ebabfe4] .guido__verion-history-view-option-selection-desktop svg,[data-v-6ebabfe4] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-6ebabfe4] .in-segments-wrapper__button_selected,[data-v-6ebabfe4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-6ebabfe4] .in-tooltip-wrapper-v2__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-eedf7416]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.validation-error-list[data-v-30d2e1f9]{gap:16px}.validation-error-row[data-v-30d2e1f9]{gap:8px;padding:24px;border:1px solid #E1E1E1;border-radius:8px}.validation-error-row__icon[data-v-30d2e1f9]{flex-shrink:0;font-size:24px;line-height:0}.validation-error-row__text[data-v-30d2e1f9]{font-size:13px;line-height:16px;color:#292929;word-break:break-word}.guido-editor__wrapper[data-v-f02fdde0]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-f02fdde0]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-f02fdde0]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom:110px!important}