@useinsider/guido 3.17.0 → 3.18.0

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 (110) hide show
  1. package/README.md +6 -5
  2. package/dist/@types/config/schemas.js +7 -6
  3. package/dist/components/Guido.vue.js +10 -10
  4. package/dist/components/Guido.vue2.js +82 -76
  5. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +21 -0
  6. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +106 -0
  7. package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue.js +17 -0
  8. package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue2.js +57 -0
  9. package/dist/components/organisms/extensions/recommendation/StrategyInfoBoxContent.vue.js +19 -0
  10. package/dist/components/organisms/extensions/recommendation/StrategyInfoBoxContent.vue2.js +63 -0
  11. package/dist/composables/useCustomInterfaceAppearance.js +52 -38
  12. package/dist/composables/useModuleRepair.js +33 -0
  13. package/dist/composables/useRecommendation.js +41 -34
  14. package/dist/composables/useSave.js +30 -29
  15. package/dist/composables/useStripo.js +15 -15
  16. package/dist/composables/useSyncModuleExtractor.js +15 -13
  17. package/dist/composables/validators/useSyncModuleBlockValidator.js +24 -0
  18. package/dist/config/i18n/en/labels.json.js +1 -1
  19. package/dist/config/migrator/index.js +9 -8
  20. package/dist/config/migrator/recommendation/compositionMapper.js +20 -20
  21. package/dist/config/migrator/recommendation/extractors.js +29 -24
  22. package/dist/config/migrator/recommendation/htmlBuilder.js +116 -130
  23. package/dist/config/migrator/recommendation/settingsMapper.js +17 -17
  24. package/dist/config/migrator/recommendationMigrator.js +31 -25
  25. package/dist/config/migrator/recommendationTitleMigrator.js +119 -0
  26. package/dist/enums/academy.js +2 -1
  27. package/dist/enums/date.js +5 -3
  28. package/dist/enums/extensions/recommendationBlock.js +3 -1
  29. package/dist/extensions/Blocks/Items/block.js +118 -49
  30. package/dist/extensions/Blocks/Items/constants/currency.js +25 -0
  31. package/dist/extensions/Blocks/Items/controls/price/currencyLocation.js +25 -24
  32. package/dist/extensions/Blocks/Items/controls/price/currencySymbol.js +12 -11
  33. package/dist/extensions/Blocks/Items/template.js +153 -152
  34. package/dist/extensions/Blocks/Items/utils/constants.js +4 -0
  35. package/dist/extensions/Blocks/Recommendation/block.js +35 -29
  36. package/dist/extensions/Blocks/Recommendation/constants/blockIds.js +1 -1
  37. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  38. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +29 -27
  39. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +35 -28
  40. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +309 -236
  41. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +241 -180
  42. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +124 -0
  43. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +1 -0
  44. package/dist/extensions/Blocks/Recommendation/controls/title/index.js +44 -0
  45. package/dist/extensions/Blocks/Recommendation/extension.js +31 -29
  46. package/dist/extensions/Blocks/Recommendation/services/configService.js +54 -36
  47. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +23 -4
  48. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +175 -79
  49. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +3 -3
  50. package/dist/extensions/Blocks/Recommendation/templates/titleRows.js +33 -0
  51. package/dist/extensions/Blocks/Recommendation/templates/utils.js +85 -107
  52. package/dist/extensions/Blocks/Recommendation/utils/nodeConfigWriter.js +22 -0
  53. package/dist/extensions/Blocks/Recommendation/utils/recommendationBlockCount.js +10 -6
  54. package/dist/extensions/Blocks/common-control.js +22 -19
  55. package/dist/guido.css +1 -1
  56. package/dist/services/recommendationApi.js +69 -30
  57. package/dist/src/@types/config/schemas.d.ts +2 -0
  58. package/dist/src/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.d.ts +3 -0
  59. package/dist/src/components/organisms/extensions/recommendation/RemoveStrategyModal.vue.d.ts +2 -0
  60. package/dist/src/components/organisms/extensions/recommendation/StrategyInfoBoxContent.vue.d.ts +15 -0
  61. package/dist/src/composables/useConfig.d.ts +2 -0
  62. package/dist/src/composables/useModuleRepair.d.ts +18 -0
  63. package/dist/src/composables/useRecommendation.d.ts +1 -0
  64. package/dist/src/composables/useSyncModuleExtractor.d.ts +2 -0
  65. package/dist/src/composables/validators/useSyncModuleBlockValidator.d.ts +3 -0
  66. package/dist/src/config/migrator/recommendation/extractors.d.ts +8 -0
  67. package/dist/src/config/migrator/recommendationTitleMigrator.d.ts +5 -0
  68. package/dist/src/enums/academy.d.ts +1 -0
  69. package/dist/src/enums/date.d.ts +1 -0
  70. package/dist/src/enums/extensions/recommendationBlock.d.ts +2 -0
  71. package/dist/src/extensions/Blocks/Items/block.d.ts +35 -0
  72. package/dist/src/extensions/Blocks/Items/constants/currency.d.ts +43 -0
  73. package/dist/src/extensions/Blocks/Items/utils/constants.d.ts +6 -0
  74. package/dist/src/extensions/Blocks/Recommendation/constants/blockIds.d.ts +2 -1
  75. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +7 -0
  76. package/dist/src/extensions/Blocks/Recommendation/constants/defaultConfig.d.ts +3 -6
  77. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +2 -2
  78. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +12 -0
  79. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +11 -0
  80. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +1 -0
  81. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +44 -8
  82. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +16 -0
  83. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +1 -0
  84. package/dist/src/extensions/Blocks/Recommendation/controls/title/index.d.ts +86 -0
  85. package/dist/src/extensions/Blocks/Recommendation/services/configService.d.ts +12 -1
  86. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +176 -10
  87. package/dist/src/extensions/Blocks/Recommendation/templates/titleRows.d.ts +13 -0
  88. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +7 -0
  89. package/dist/src/extensions/Blocks/Recommendation/utils/nodeConfigWriter.d.ts +31 -0
  90. package/dist/src/extensions/Blocks/Recommendation/utils/recommendationBlockCount.d.ts +6 -2
  91. package/dist/src/extensions/Blocks/common-control.d.ts +4 -1
  92. package/dist/src/mock/api/recommendation-strategies.d.ts +2 -0
  93. package/dist/src/services/recommendationApi.d.ts +4 -1
  94. package/dist/src/stores/config.d.ts +18 -0
  95. package/dist/src/stores/save-as-template.d.ts +4 -3
  96. package/dist/src/utils/genericUtil.d.ts +5 -0
  97. package/dist/src/utils/itemsCurrencyRepair.d.ts +17 -0
  98. package/dist/static/assets/icons/line-change.svg.js +5 -0
  99. package/dist/static/assets/icons/line-delete.svg.js +5 -0
  100. package/dist/static/assets/icons/line-plus.svg.js +5 -0
  101. package/dist/static/styles/components/recommendation-strategy.css.js +155 -0
  102. package/dist/static/styles/customEditorStyle.css.js +3 -1
  103. package/dist/utils/dateUtil.js +23 -10
  104. package/dist/utils/genericUtil.js +28 -27
  105. package/dist/utils/itemsCurrencyRepair.js +39 -0
  106. package/dist/utils/pairProductVariables.js +58 -58
  107. package/dist/utils/templatePreparation.js +35 -34
  108. package/package.json +3 -2
  109. package/dist/composables/useModuleDynamicContentRepair.js +0 -37
  110. package/dist/src/composables/useModuleDynamicContentRepair.d.ts +0 -13
@@ -0,0 +1,124 @@
1
+ var d = Object.defineProperty;
2
+ var y = (r, s, t) => s in r ? d(r, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[s] = t;
3
+ var o = (r, s, t) => y(r, typeof s != "symbol" ? s + "" : s, t);
4
+ import { ACADEMY_LINKS as m } from "../../../../../enums/academy.js";
5
+ import { CommonControl as h } from "../../../common-control.js";
6
+ import { useRecommendationExtensionStore as u } from "../../store/recommendation.js";
7
+ const p = "recommendation-strategy-control";
8
+ class f extends h {
9
+ constructor() {
10
+ super(...arguments);
11
+ o(this, "store", u());
12
+ o(this, "storeUnsubscription", () => {
13
+ });
14
+ o(this, "listeners", []);
15
+ o(this, "lastPaintedState", null);
16
+ }
17
+ getId() {
18
+ return p;
19
+ }
20
+ getTemplate() {
21
+ const t = this.api.translate(
22
+ "You can assign a recommendation strategy by selecting an existing one or creating a new one to show specific content."
23
+ ), e = this.api.translate("For more information, you can"), a = this.api.translate("visit Academy");
24
+ return `
25
+ <div class="strategy-control-container">
26
+ <div class="strategy-control-header">
27
+ <h3 class="strategy-control-title">${this.api.translate("Recommendation Strategy")}</h3>
28
+ </div>
29
+ <div class="strategy-control-description-row">
30
+ <p class="strategy-control-description">
31
+ ${t}
32
+ ${e}
33
+ <a
34
+ href="${m.EMAIL_RECOMMENDATION_BLOCK}"
35
+ target="_blank"
36
+ rel="noopener noreferrer">${a}</a>.
37
+ </p>
38
+ </div>
39
+ <div data-strategy-empty-state>
40
+ ${this._GuTwoColumns([
41
+ this._GuButton({
42
+ name: "assignStrategy",
43
+ label: this.api.translate("Assign Strategy"),
44
+ id: "guido__btn-assign-strategy",
45
+ className: "strategy-control-cta on"
46
+ }),
47
+ this._GuButton({
48
+ name: "createStrategy",
49
+ label: this.api.translate("Create New Strategy"),
50
+ id: "guido__btn-create-strategy",
51
+ className: "strategy-control-cta primary",
52
+ disabled: !0
53
+ })
54
+ ], "strategy-control-cta-row")}
55
+ </div>
56
+ <div data-strategy-assigned-state style="display: none;">
57
+ <div class="strategy-control-name-band">
58
+ <span class="strategy-control-name" data-strategy-name></span>
59
+ </div>
60
+ <div class="strategy-control-actions">
61
+ ${this._strategyAction({
62
+ id: "guido__btn-change-strategy",
63
+ label: this.api.translate("Change"),
64
+ modifier: "change"
65
+ })}
66
+ ${this._strategyAction({
67
+ id: "guido__btn-remove-strategy",
68
+ label: this.api.translate("Remove"),
69
+ modifier: "remove"
70
+ })}
71
+ </div>
72
+ </div>
73
+ </div>
74
+ `;
75
+ }
76
+ _strategyAction({ id: t, label: e, modifier: a }) {
77
+ return `
78
+ <button
79
+ id="${t}"
80
+ type="button"
81
+ class="strategy-control-action strategy-control-action--${a}">
82
+ <span>${e}</span>
83
+ </button>
84
+ `;
85
+ }
86
+ onRender() {
87
+ this._teardown(), this._setupButtonListeners(), this._renderState(), this.storeUnsubscription = this.store.$subscribe(() => {
88
+ this._renderState();
89
+ }), this.store.recommendationConfigs.strategyId && !this.store.strategies.length && this.store.fetchStrategies().catch(() => {
90
+ });
91
+ }
92
+ onDestroy() {
93
+ this._teardown();
94
+ }
95
+ _teardown() {
96
+ this.storeUnsubscription(), this.storeUnsubscription = () => {
97
+ }, this.lastPaintedState = null, this.listeners.forEach(({ element: t, handler: e }) => t.removeEventListener("click", e)), this.listeners = [];
98
+ }
99
+ _setupButtonListeners() {
100
+ const t = this.getContainer();
101
+ if (!t)
102
+ return;
103
+ const e = (a, n) => {
104
+ const i = t.querySelector(a);
105
+ i && (i.addEventListener("click", n), this.listeners.push({ element: i, handler: n }));
106
+ };
107
+ e("#guido__btn-assign-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-change-strategy", () => this.store.openAssignStrategyDrawer()), e("#guido__btn-remove-strategy", () => this.store.openRemoveStrategyModal());
108
+ }
109
+ _renderState() {
110
+ var l;
111
+ const { strategyId: t } = this.store.recommendationConfigs, e = t ? ((l = this.store.assignedStrategy) == null ? void 0 : l.name) ?? "" : "", a = `${t}\0${e}`;
112
+ if (a === this.lastPaintedState)
113
+ return;
114
+ const n = this.getContainer();
115
+ if (!n)
116
+ return;
117
+ const i = n.querySelector("[data-strategy-empty-state]"), c = n.querySelector("[data-strategy-assigned-state]"), g = n.querySelector("[data-strategy-name]");
118
+ !i || !c || (i.style.display = t ? "none" : "", c.style.display = t ? "" : "none", g && t && (g.textContent = e || `#${t}`), this.lastPaintedState = a);
119
+ }
120
+ }
121
+ export {
122
+ p as STRATEGY_CONTROL_ID,
123
+ f as StrategyControl
124
+ };
@@ -438,6 +438,7 @@ export {
438
438
  C as getBlockElement,
439
439
  U as getCardComposition,
440
440
  P as getCurrentLayout,
441
+ oe as getMainContainerTbody,
441
442
  ee as isPartnerManagedBlock,
442
443
  Pe as regenerateMobileProductRows,
443
444
  Te as regenerateProductRowsWithStyles,
@@ -0,0 +1,44 @@
1
+ import { createPaddingsControl as r, createTextBackgroundColorControl as T, createTextFontFamilyControl as e, createTextStyleControl as l, createTextSizeControl as C, createTextColorControl as c, createTextAlignControl as I } from "../../../controlFactories.js";
2
+ import { RecommendationBlockId as o } from "../../constants/blockIds.js";
3
+ import { RecommendationControlId as t } from "../../constants/controlIds.js";
4
+ import { BLOCK_ROOT_SELECTOR as n } from "../../constants/selectors.js";
5
+ const L = I(
6
+ t.TITLE_ALIGN,
7
+ o.TITLE,
8
+ n
9
+ ), a = c(
10
+ t.TITLE_COLOR,
11
+ o.TITLE,
12
+ n
13
+ ), i = C(
14
+ t.TITLE_SIZE,
15
+ o.TITLE,
16
+ n
17
+ ), E = l(
18
+ t.TITLE_STYLE,
19
+ o.TITLE,
20
+ n
21
+ ), m = e(
22
+ t.TITLE_FONT_FAMILY,
23
+ o.TITLE,
24
+ n
25
+ ), s = T(
26
+ t.TITLE_BACKGROUND,
27
+ o.TITLE,
28
+ n
29
+ ), d = r(
30
+ t.TITLE_PADDINGS,
31
+ o.TITLE,
32
+ n
33
+ ), x = {
34
+ align: L,
35
+ color: a,
36
+ size: i,
37
+ style: E,
38
+ fontFamily: m,
39
+ background: s,
40
+ paddings: d
41
+ };
42
+ export {
43
+ x as TitleControls
44
+ };
@@ -5,23 +5,24 @@ import { RecommendationBlockControl as i } from "./controls/main/index.js";
5
5
  import "./constants/selectors.js";
6
6
  import "./store/recommendation.js";
7
7
  import "./utils/captureStyleTemplates.js";
8
- import { NameControls as e } from "./controls/name/index.js";
9
- import { PriceControls as l } from "./controls/price/index.js";
10
- import { OldPriceControls as s } from "./controls/oldPrice/index.js";
11
- import { OmnibusPriceControls as p } from "./controls/omnibusPrice/index.js";
12
- import { OmnibusDiscountControls as a } from "./controls/omnibusDiscount/index.js";
13
- import { ButtonControls as c } from "./controls/button/index.js";
14
- import { ImageControls as C } from "./controls/image/index.js";
15
- import { CustomAttributeControls as f } from "./controls/customAttribute/index.js";
16
- import { SpacingControl as d } from "./controls/spacing/index.js";
17
- import { BlockBackgroundColorControl as u } from "./controls/blockBackground/index.js";
18
- import { CardBackgroundColorControl as g } from "./controls/cardBackground/index.js";
19
- import { RecommendationCardCompositionControl as y } from "./controls/cardComposition/index.js";
20
- import { SyncInfoMessageControl as B } from "./controls/syncInfoMessage.js";
21
- import { RecommendationIconsRegistry as P } from "./iconsRegistry.js";
22
- import R from "./recommendation.css.js";
23
- import { SettingsPanel as S } from "./settingsPanel.js";
24
- const k = [
8
+ import { TitleControls as e } from "./controls/title/index.js";
9
+ import { NameControls as l } from "./controls/name/index.js";
10
+ import { PriceControls as s } from "./controls/price/index.js";
11
+ import { OldPriceControls as p } from "./controls/oldPrice/index.js";
12
+ import { OmnibusPriceControls as a } from "./controls/omnibusPrice/index.js";
13
+ import { OmnibusDiscountControls as c } from "./controls/omnibusDiscount/index.js";
14
+ import { ButtonControls as C } from "./controls/button/index.js";
15
+ import { ImageControls as f } from "./controls/image/index.js";
16
+ import { CustomAttributeControls as d } from "./controls/customAttribute/index.js";
17
+ import { SpacingControl as u } from "./controls/spacing/index.js";
18
+ import { BlockBackgroundColorControl as g } from "./controls/blockBackground/index.js";
19
+ import { CardBackgroundColorControl as y } from "./controls/cardBackground/index.js";
20
+ import { RecommendationCardCompositionControl as B } from "./controls/cardComposition/index.js";
21
+ import { SyncInfoMessageControl as P } from "./controls/syncInfoMessage.js";
22
+ import { RecommendationIconsRegistry as R } from "./iconsRegistry.js";
23
+ import S from "./recommendation.css.js";
24
+ import { SettingsPanel as k } from "./settingsPanel.js";
25
+ const w = [
25
26
  e,
26
27
  l,
27
28
  s,
@@ -29,21 +30,22 @@ const k = [
29
30
  a,
30
31
  c,
31
32
  C,
32
- f
33
- ], w = [
33
+ f,
34
+ d
35
+ ], b = [
34
36
  i,
35
- u,
36
37
  g,
37
- d,
38
38
  y,
39
- B
40
- ], b = [
41
- ...w,
42
- ...k.flatMap((o) => Object.values(o))
43
- ], W = b.reduce(
39
+ u,
40
+ B,
41
+ P
42
+ ], v = [
43
+ ...b,
44
+ ...w.flatMap((o) => Object.values(o))
45
+ ], Y = v.reduce(
44
46
  (o, r) => o.addControl(r),
45
- new t().addBlock(m).withSettingsPanelRegistry(S)
46
- ).addStyles(R).withPreviewStyles(n).withIconsRegistry(P).build();
47
+ new t().addBlock(m).withSettingsPanelRegistry(k)
48
+ ).addStyles(S).withPreviewStyles(n).withIconsRegistry(R).build();
47
49
  export {
48
- W as default
50
+ Y as default
49
51
  };
@@ -1,12 +1,12 @@
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 { PRE_RRS_CONFIG_VERSION as C, DEFAULT_NODE_CONFIG as c } from "../constants/defaultConfig.js";
3
3
  import { setCurrencyAttributes as D } from "../controls/main/utils.js";
4
- import { hasMinimalConfig as A } from "../types/nodeConfig.js";
5
- function S(e) {
6
- return e === "." || e === "," || e === " ";
4
+ import { hasMinimalConfig as S } from "../types/nodeConfig.js";
5
+ function A(r) {
6
+ return r === "." || r === "," || r === " ";
7
7
  }
8
- function N(e) {
9
- return e === "." || e === "," || e === " " || e === "";
8
+ function N(r) {
9
+ return r === "." || r === "," || r === " " || r === "";
10
10
  }
11
11
  class M {
12
12
  // ========================================================================
@@ -48,7 +48,7 @@ class M {
48
48
  return !1;
49
49
  try {
50
50
  const t = i.getNodeConfig();
51
- return A(t);
51
+ return S(t);
52
52
  } catch {
53
53
  return !1;
54
54
  }
@@ -82,9 +82,27 @@ class M {
82
82
  * @param description - Human-readable description for undo/redo
83
83
  * @returns The new complete configuration
84
84
  */
85
- static updateConfig(i, t, o, n) {
86
- const r = this.getConfig(t), u = this.deepMerge(r, o);
87
- return this.saveConfig(i, t, u, n), u;
85
+ static updateConfig(i, t, o, e) {
86
+ const n = this.getConfig(t), u = this.deepMerge(n, o);
87
+ return this.saveConfig(i, t, u, e), u;
88
+ }
89
+ /**
90
+ * Accumulate a config update into an existing modifier batch, WITHOUT applying.
91
+ *
92
+ * The caller owns the apply(), so the write shares one undo entry with the rest
93
+ * of that batch — a change whose visible result only lands after an async product
94
+ * fetch would otherwise cost the user two undo presses (SD-149105).
95
+ * @param documentModifier - The batch to accumulate into (caller applies)
96
+ * @param node - The immutable HTML node to write to
97
+ * @param updates - Partial config with values to update
98
+ */
99
+ static stageConfig(i, t, o) {
100
+ const e = this.deepMerge(this.getConfig(t), o);
101
+ try {
102
+ i.modifyHtml(t).setNodeConfig(e);
103
+ } catch (n) {
104
+ console.warn("[RecommendationConfigService] Failed to stage config:", n);
105
+ }
88
106
  }
89
107
  /**
90
108
  * Initialize configuration for a new block
@@ -107,12 +125,12 @@ class M {
107
125
  static initializeConfig(i, t, o) {
108
126
  if (this.hasConfig(t))
109
127
  return { config: o ? this.updateConfig(i, t, o, "Initialize recommendation block") : this.getConfig(t), wasFreshDrop: !1 };
110
- const n = o ? this.mergeWithDefaults(o) : this.cloneDefaults();
111
- return this.saveConfig(i, t, n, "Initialize recommendation block"), D({
128
+ const e = o ? this.mergeWithDefaults(o) : this.cloneDefaults();
129
+ return this.saveConfig(i, t, e, "Initialize recommendation block"), D({
112
130
  currentNode: t,
113
131
  documentModifier: i.getDocumentModifier(),
114
- currency: n.currency
115
- }), { config: n, wasFreshDrop: !0 };
132
+ currency: e.currency
133
+ }), { config: e, wasFreshDrop: !0 };
116
134
  }
117
135
  /**
118
136
  * Save complete configuration to a node
@@ -123,11 +141,11 @@ class M {
123
141
  * @param config - Complete configuration to save
124
142
  * @param description - Human-readable description for undo/redo
125
143
  */
126
- static saveConfig(i, t, o, n) {
144
+ static saveConfig(i, t, o, e) {
127
145
  try {
128
- i.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new C(n));
129
- } catch (r) {
130
- console.warn("[RecommendationConfigService] Failed to save config:", r);
146
+ i.getDocumentModifier().modifyHtml(t).setNodeConfig(o).apply(new h(e));
147
+ } catch (n) {
148
+ console.warn("[RecommendationConfigService] Failed to save config:", n);
131
149
  }
132
150
  }
133
151
  // ========================================================================
@@ -154,33 +172,33 @@ class M {
154
172
  */
155
173
  static migrateFromDataAttributes(i, t) {
156
174
  const o = {
157
- configVersion: l
175
+ configVersion: C
158
176
  };
159
177
  if ("getAttribute" in t && typeof t.getAttribute == "function") {
160
- const n = t.getAttribute("esd-ext-config");
161
- if (n)
178
+ const e = t.getAttribute("esd-ext-config");
179
+ if (e)
162
180
  try {
163
- const s = JSON.parse(n);
164
- s && typeof s == "object" && (Object.assign(o, s), o.configVersion = l);
181
+ const s = JSON.parse(e);
182
+ s && typeof s == "object" && Object.assign(o, s);
165
183
  } catch {
166
184
  }
167
- const r = t.getAttribute("data-layout");
168
- r === "list" || r === "horizontal" ? o.layout = "list" : (r === "grid" || r === "vertical") && (o.layout = "grid");
185
+ const n = t.getAttribute("data-layout");
186
+ n === "list" || n === "horizontal" ? o.layout = "list" : (n === "grid" || n === "vertical") && (o.layout = "grid");
169
187
  const u = t.getAttribute("data-card-composition");
170
188
  u && (o.composition = u.split(",").filter(Boolean));
171
- const b = t.getAttribute("data-column-spacing");
172
- b && (o.columnSpacing = parseInt(b) || c.columnSpacing);
189
+ const l = t.getAttribute("data-column-spacing");
190
+ l && (o.columnSpacing = parseInt(l) || c.columnSpacing);
173
191
  const p = t.getAttribute("data-row-spacing");
174
192
  if (p && (o.rowSpacing = parseInt(p) || c.rowSpacing), !o.currency) {
175
- 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
- if (s || y || d || f || g || m) {
177
- const a = c.currency, h = m ? parseInt(m) : NaN;
193
+ const s = t.getAttribute("currency"), b = t.getAttribute("currency-symbol"), y = t.getAttribute("currency-alignment"), f = t.getAttribute("currency-thousand-separator"), g = t.getAttribute("currency-decimal-separator"), m = t.getAttribute("currency-decimal-count");
194
+ if (s || b || y || f || g || m) {
195
+ const a = c.currency, d = m ? parseInt(m) : NaN;
178
196
  o.currency = {
179
197
  code: s ?? a.code,
180
- symbol: y ?? a.symbol,
181
- alignment: d === "0" ? "before" : "after",
182
- decimalCount: Number.isFinite(h) ? h : a.decimalCount,
183
- decimalSeparator: S(g) ? g : a.decimalSeparator,
198
+ symbol: b ?? a.symbol,
199
+ alignment: y === "0" ? "before" : "after",
200
+ decimalCount: Number.isFinite(d) ? d : a.decimalCount,
201
+ decimalSeparator: A(g) ? g : a.decimalSeparator,
184
202
  thousandSeparator: N(f) ? f : a.thousandSeparator
185
203
  };
186
204
  }
@@ -194,7 +212,7 @@ class M {
194
212
  * @returns True if migration is needed
195
213
  */
196
214
  static needsMigration(i) {
197
- return i ? this.hasConfig(i) ? this.getConfigVersion(i) < l : !0 : !1;
215
+ return i ? !this.hasConfig(i) : !1;
198
216
  }
199
217
  // ========================================================================
200
218
  // Internal Helpers
@@ -3,9 +3,10 @@ import { BLOCK_ID as R } from "./block.js";
3
3
  import { RecommendationBlockId as S } from "./constants/blockIds.js";
4
4
  import { RecommendationControlId as T } from "./constants/controlIds.js";
5
5
  import "./constants/selectors.js";
6
- import { CONTROL_BLOCK_ID as U } from "./controls/main/index.js";
6
+ import { CONTROL_BLOCK_ID as L } from "./controls/main/index.js";
7
7
  import "./store/recommendation.js";
8
8
  import "./utils/captureStyleTemplates.js";
9
+ import "./controls/title/index.js";
9
10
  import "./controls/name/index.js";
10
11
  import "./controls/price/index.js";
11
12
  import "./controls/oldPrice/index.js";
@@ -15,7 +16,7 @@ import "./controls/button/index.js";
15
16
  import "./controls/image/index.js";
16
17
  import { SPACING_CONTROL_ID as A } from "./controls/spacing/index.js";
17
18
  import "./controls/blockBackground/index.js";
18
- import { CARD_BACKGROUND_COLOR_CONTROL_ID as L } from "./controls/cardBackground/index.js";
19
+ import { CARD_BACKGROUND_COLOR_CONTROL_ID as U } from "./controls/cardBackground/index.js";
19
20
  import { COMPOSITION_CONTROL_BLOCK_ID as D } from "./controls/cardComposition/index.js";
20
21
  import { SYNC_INFO_MESSAGE_CONTROL_ID as N } from "./controls/syncInfoMessage.js";
21
22
  class s extends E {
@@ -24,7 +25,7 @@ class s extends E {
24
25
  new _(
25
26
  I.SETTINGS,
26
27
  [
27
- U,
28
+ L,
28
29
  C.EXTERNAL_INDENTS
29
30
  ]
30
31
  ),
@@ -32,7 +33,7 @@ class s extends E {
32
33
  I.STYLES,
33
34
  [
34
35
  T.BLOCK_BACKGROUND,
35
- L,
36
+ U,
36
37
  A
37
38
  ]
38
39
  ),
@@ -42,6 +43,24 @@ class s extends E {
42
43
  D
43
44
  ]
44
45
  ).withLabel(this.api.translate("Card Composition"))
46
+ ], O[S.TITLE] = [
47
+ new _(
48
+ I.SETTINGS,
49
+ [
50
+ T.TITLE_STYLE,
51
+ T.TITLE_ALIGN,
52
+ T.TITLE_PADDINGS
53
+ ]
54
+ ),
55
+ new _(
56
+ I.STYLES,
57
+ [
58
+ T.TITLE_BACKGROUND,
59
+ T.TITLE_FONT_FAMILY,
60
+ T.TITLE_SIZE,
61
+ T.TITLE_COLOR
62
+ ]
63
+ )
45
64
  ], O[S.NAME] = [
46
65
  new _(
47
66
  I.SETTINGS,