@useinsider/guido 3.2.1-beta.d58ee72 → 3.3.0-beta.1778fd7

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 (158) hide show
  1. package/README.md +117 -1
  2. package/dist/@types/config/schemas.js +153 -95
  3. package/dist/components/Guido.vue.js +4 -4
  4. package/dist/components/Guido.vue2.js +106 -90
  5. package/dist/components/organisms/AutoSaveController.vue.js +17 -0
  6. package/dist/components/organisms/AutoSaveController.vue2.js +13 -0
  7. package/dist/components/organisms/extensions/recommendation/FilterItem.vue.js +9 -11
  8. package/dist/components/organisms/extensions/recommendation/FilterItem.vue2.js +70 -35
  9. package/dist/components/organisms/header/AutoSaveToggle.vue.js +22 -0
  10. package/dist/components/organisms/header/AutoSaveToggle.vue2.js +19 -0
  11. package/dist/components/organisms/header/MiddleSlot.vue.js +7 -7
  12. package/dist/components/organisms/header/RightSlot.vue.js +8 -8
  13. package/dist/components/organisms/header/RightSlot.vue2.js +9 -8
  14. package/dist/components/organisms/onboarding/AMPOnboarding.vue2.js +51 -31
  15. package/dist/components/organisms/onboarding/GenericOnboarding.vue.js +1 -1
  16. package/dist/components/organisms/onboarding/GenericOnboarding.vue2.js +23 -22
  17. package/dist/components/organisms/onboarding/ItemsOnboarding.vue.js +1 -1
  18. package/dist/components/organisms/onboarding/ItemsOnboarding.vue2.js +37 -39
  19. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue.js +3 -3
  20. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue2.js +30 -41
  21. package/dist/components/organisms/onboarding/VersionHistoryOnboarding.vue2.js +15 -14
  22. package/dist/composables/useAutoSave.js +71 -0
  23. package/dist/composables/useCortexBlueprintBridge.js +66 -0
  24. package/dist/composables/useEmailTemplateApplier.js +41 -0
  25. package/dist/composables/useFullStoryBridge.js +14 -0
  26. package/dist/composables/useGuidoStateBridge.js +48 -0
  27. package/dist/composables/useHtmlValidator.js +41 -36
  28. package/dist/composables/useRecommendation.js +46 -26
  29. package/dist/composables/useRibbonOffset.js +21 -0
  30. package/dist/composables/useSave.js +19 -16
  31. package/dist/composables/useStripo.js +66 -62
  32. package/dist/composables/validators/useCouponBlockValidator.js +24 -0
  33. package/dist/config/compiler/recommendationCompilerRules.js +25 -25
  34. package/dist/config/compiler/unsubscribeCompilerRules.js +40 -37
  35. package/dist/config/compiler/utils/recommendationCompilerUtils.js +107 -71
  36. package/dist/config/migrator/index.js +20 -9
  37. package/dist/config/migrator/radioButtonMigrator.js +92 -47
  38. package/dist/config/migrator/recommendation/compositionMapper.js +98 -0
  39. package/dist/config/migrator/recommendation/extractors.js +27 -0
  40. package/dist/config/migrator/recommendation/htmlBuilder.js +496 -0
  41. package/dist/config/migrator/recommendation/parseLegacyConfig.js +33 -0
  42. package/dist/config/migrator/recommendation/settingsMapper.js +70 -0
  43. package/dist/config/migrator/recommendation/themeMapper.js +93 -0
  44. package/dist/config/migrator/recommendationMigrator.js +74 -290
  45. package/dist/enums/extensions/recommendationBlock.js +103 -47
  46. package/dist/enums/onboarding.js +7 -2
  47. package/dist/enums/unsubscribe.js +39 -31
  48. package/dist/extensions/Blocks/Checkbox/control.js +23 -23
  49. package/dist/extensions/Blocks/RadioButton/control.js +15 -15
  50. package/dist/extensions/Blocks/RadioButton/template.js +7 -7
  51. package/dist/extensions/Blocks/Recommendation/block.js +60 -50
  52. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  53. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +5 -5
  54. package/dist/extensions/Blocks/Recommendation/controls/customAttribute/index.js +21 -18
  55. package/dist/extensions/Blocks/Recommendation/controls/customAttribute/textTrim.js +99 -0
  56. package/dist/extensions/Blocks/Recommendation/controls/main/algorithm.js +27 -26
  57. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +3 -1
  58. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +228 -181
  59. package/dist/extensions/Blocks/Recommendation/controls/name/textTrim.js +27 -57
  60. package/dist/extensions/Blocks/Recommendation/controls/shared/textTrimCssRules.js +14 -0
  61. package/dist/extensions/Blocks/Recommendation/services/configService.js +76 -33
  62. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +18 -17
  63. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +144 -91
  64. package/dist/extensions/Blocks/Recommendation/templates/grid/elementRenderer.js +19 -10
  65. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +8 -8
  66. package/dist/extensions/Blocks/Recommendation/templates/list/elementRenderer.js +25 -15
  67. package/dist/extensions/Blocks/Recommendation/templates/list/template.js +11 -11
  68. package/dist/extensions/Blocks/Recommendation/templates/utils.js +1 -1
  69. package/dist/extensions/Blocks/Recommendation/utils/filterUtil.js +31 -15
  70. package/dist/extensions/Blocks/Recommendation/utils/legacyStrategyMap.js +21 -0
  71. package/dist/extensions/Blocks/Recommendation/utils/preserveTextStyles.js +13 -22
  72. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +33 -0
  73. package/dist/guido.css +1 -1
  74. package/dist/node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js +393 -264
  75. package/dist/node_modules/valibot/dist/index.js +450 -235
  76. package/dist/package.json.js +1 -1
  77. package/dist/services/templateLibraryApi.js +5 -4
  78. package/dist/services/unsubscribeApi.js +6 -6
  79. package/dist/src/@types/config/defaults.d.ts +5 -1
  80. package/dist/src/@types/config/index.d.ts +3 -3
  81. package/dist/src/@types/config/schemas.d.ts +217 -0
  82. package/dist/src/@types/config/types.d.ts +9 -1
  83. package/dist/src/components/Guido.vue.d.ts +1 -1
  84. package/dist/src/components/organisms/AutoSaveController.vue.d.ts +2 -0
  85. package/dist/src/components/organisms/header/AutoSaveToggle.vue.d.ts +2 -0
  86. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +1 -1
  87. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +1 -1
  88. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +1 -1
  89. package/dist/src/components/wrappers/WpModal.vue.d.ts +1 -1
  90. package/dist/src/composables/useAutoSave.d.ts +3 -0
  91. package/dist/src/composables/useConfig.d.ts +58 -0
  92. package/dist/src/composables/useCortexBlueprintBridge.d.ts +25 -0
  93. package/dist/src/composables/useEmailTemplateApplier.d.ts +21 -0
  94. package/dist/src/composables/useFullStoryBridge.d.ts +11 -0
  95. package/dist/src/composables/useGuidoStateBridge.d.ts +22 -0
  96. package/dist/src/composables/useRecommendation.d.ts +10 -1
  97. package/dist/src/composables/useRecommendation.test.d.ts +1 -0
  98. package/dist/src/composables/useRibbonOffset.d.ts +4 -0
  99. package/dist/src/composables/useSave.d.ts +1 -1
  100. package/dist/src/composables/validators/useCouponBlockValidator.d.ts +3 -0
  101. package/dist/src/config/migrator/index.d.ts +2 -1
  102. package/dist/src/config/migrator/recommendation/compositionMapper.d.ts +2 -0
  103. package/dist/src/config/migrator/recommendation/compositionMapper.test.d.ts +1 -0
  104. package/dist/src/config/migrator/recommendation/extractors.d.ts +7 -0
  105. package/dist/src/config/migrator/recommendation/extractors.test.d.ts +1 -0
  106. package/dist/src/config/migrator/recommendation/htmlBuilder.d.ts +11 -0
  107. package/dist/src/config/migrator/recommendation/parseLegacyConfig.d.ts +15 -0
  108. package/dist/src/config/migrator/recommendation/parseLegacyConfig.test.d.ts +1 -0
  109. package/dist/src/config/migrator/recommendation/settingsMapper.d.ts +7 -0
  110. package/dist/src/config/migrator/recommendation/settingsMapper.test.d.ts +1 -0
  111. package/dist/src/config/migrator/recommendation/themeMapper.d.ts +5 -0
  112. package/dist/src/config/migrator/recommendation/themeMapper.test.d.ts +1 -0
  113. package/dist/src/config/migrator/recommendation/types.d.ts +205 -0
  114. package/dist/src/config/migrator/recommendationMigrator.d.ts +13 -1
  115. package/dist/src/config/migrator/recommendationMigrator.test.d.ts +1 -0
  116. package/dist/src/enums/extensions/recommendationBlock.d.ts +6 -1
  117. package/dist/src/enums/onboarding.d.ts +6 -0
  118. package/dist/src/enums/unsubscribe.d.ts +13 -3
  119. package/dist/src/extensions/Blocks/RadioButton/template.d.ts +1 -1
  120. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +1 -0
  121. package/dist/src/extensions/Blocks/Recommendation/controls/customAttribute/index.d.ts +3 -0
  122. package/dist/src/extensions/Blocks/Recommendation/controls/customAttribute/textTrim.d.ts +35 -0
  123. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.test.d.ts +1 -0
  124. package/dist/src/extensions/Blocks/Recommendation/controls/name/textTrim.d.ts +3 -20
  125. package/dist/src/extensions/Blocks/Recommendation/controls/shared/textTrimCssRules.d.ts +29 -0
  126. package/dist/src/extensions/Blocks/Recommendation/services/configService.d.ts +21 -3
  127. package/dist/src/extensions/Blocks/Recommendation/services/configService.test.d.ts +1 -0
  128. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +41 -1
  129. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +1 -1
  130. package/dist/src/extensions/Blocks/Recommendation/utils/filterUtil.d.ts +2 -0
  131. package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMap.d.ts +21 -0
  132. package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMap.test.d.ts +1 -0
  133. package/dist/src/extensions/Blocks/Recommendation/utils/preserveTextStyles.d.ts +0 -3
  134. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +21 -0
  135. package/dist/src/library.d.ts +1 -1
  136. package/dist/src/stores/autosave.d.ts +12 -0
  137. package/dist/src/stores/config.d.ts +522 -0
  138. package/dist/src/stores/editor.d.ts +23 -0
  139. package/dist/src/stores/guido-email-editor.d.ts +41 -0
  140. package/dist/src/stores/onboarding.d.ts +4 -0
  141. package/dist/src/utils/htmlEscape.d.ts +5 -0
  142. package/dist/src/utils/htmlEscape.test.d.ts +1 -0
  143. package/dist/src/utils/timeUtil.d.ts +8 -0
  144. package/dist/static/styles/components/button.css.js +16 -9
  145. package/dist/static/styles/components/loader.css.js +4 -0
  146. package/dist/static/styles/components/narrow-panel.css.js +52 -0
  147. package/dist/stores/autosave.js +17 -0
  148. package/dist/stores/editor.js +3 -1
  149. package/dist/stores/guido-email-editor.js +20 -0
  150. package/dist/stores/onboarding.js +4 -0
  151. package/dist/utils/htmlEscape.js +13 -0
  152. package/dist/utils/pairProductVariables.js +89 -88
  153. package/dist/utils/templatePreparation.js +74 -27
  154. package/dist/utils/timeUtil.js +19 -0
  155. package/package.json +7 -3
  156. package/dist/enums/displayConditions.js +0 -80
  157. package/dist/extensions/Blocks/Recommendation/templates/grid/migration.js +0 -251
  158. package/dist/src/enums/displayConditions.d.ts +0 -2
@@ -0,0 +1,4 @@
1
+ export declare const useRibbonOffset: () => {
2
+ ribbonOffset: import("vue").Ref<number>;
3
+ getTopPosition: (baseTop: number) => string;
4
+ };
@@ -1,4 +1,4 @@
1
1
  import type { SavedTemplateDetails } from '@@/Types/stripo';
2
2
  export declare const useSave: () => {
3
- save: (isSilent?: boolean) => Promise<Omit<SavedTemplateDetails, "metadata"> | undefined>;
3
+ save: (suppressEmit?: boolean, silent?: boolean) => Promise<Omit<SavedTemplateDetails, "metadata" | "silent"> | undefined>;
4
4
  };
@@ -0,0 +1,3 @@
1
+ export declare const useCouponBlockValidator: () => {
2
+ validateCouponBlockTags: (html: string) => boolean;
3
+ };
@@ -1 +1,2 @@
1
- export declare const migrate: (html: string) => Promise<string>;
1
+ import type { LegacyRecommendationConfig } from '@@/Types/config';
2
+ export declare const migrate: (html: string, recommendationConfigs?: Record<string, LegacyRecommendationConfig>) => Promise<string>;
@@ -0,0 +1,2 @@
1
+ import { type LegacyVariable, type MappedComposition } from './types';
2
+ export declare function mapComposition(variables: LegacyVariable[] | undefined): MappedComposition;
@@ -0,0 +1,7 @@
1
+ export declare function extractTitleText(block: Element): string;
2
+ /**
3
+ * Card background lives on either the block (`bgcolor`/`style`) or per-card
4
+ * cells (`.product-card`). Returns empty string when no background is set —
5
+ * the caller treats that as "no segment background".
6
+ */
7
+ export declare function extractCardBgColor(block: Element): string;
@@ -0,0 +1,11 @@
1
+ import type { MappedComposition } from './types';
2
+ import type { RecommendationNodeConfig } from '@@/Extensions/Blocks/Recommendation/types/nodeConfig';
3
+ export declare function buildBlockHtml(params: {
4
+ nodeConfig: RecommendationNodeConfig;
5
+ composition: MappedComposition;
6
+ cardBg: string;
7
+ titleText: string;
8
+ extraClasses?: string;
9
+ legacyId?: string;
10
+ legacyBgColor?: string;
11
+ }): string;
@@ -0,0 +1,15 @@
1
+ import type { LegacyProductConfig } from './types';
2
+ /**
3
+ * `esd-dev-product-config` is double-HTML-encoded JSON: serialization
4
+ * escapes once to embed inside the attribute, and Stripo's templating
5
+ * layer escapes a second time when round-tripping. Returns `null` when
6
+ * the attribute is missing or unable to be parsed — the caller treats
7
+ * either case as an abort.
8
+ */
9
+ export declare function parseLegacyConfig(block: Element): LegacyProductConfig | null;
10
+ /**
11
+ * Older legacy templates omitted the canonical
12
+ * `ins-recommendation-v3-campaign-id` attribute but always included the
13
+ * `ins-recommendation-v3-block-<id>` class — fall back to scraping it.
14
+ */
15
+ export declare function parseRecommendationId(block: Element): number | null;
@@ -0,0 +1,7 @@
1
+ import type { LegacyProductConfig } from './types';
2
+ import type { CurrencyConfig, RecommendationNodeConfig } from '@@/Extensions/Blocks/Recommendation/types/nodeConfig';
3
+ import type { LegacyRecommendationConfig } from '@@/Types/config';
4
+ /** Defensive against missing/malformed legacy `currencySettings` — defaults are per-field, not whole-object. */
5
+ export declare function mapCurrency(legacy: unknown): CurrencyConfig;
6
+ export type SettingsPartial = Pick<RecommendationNodeConfig, 'recommendationId' | 'strategy' | 'productIds' | 'size' | 'shuffleProducts' | 'language' | 'currency' | 'filters' | 'layout' | 'cardsInRow' | 'mobileCardsInRow' | 'mobileLayoutEnabled' | 'previousMobileCardsInRow' | 'columnSpacing' | 'rowSpacing' | 'mobileColumnSpacing' | 'mobileRowSpacing' | 'omnibusPrice' | 'omnibusDiscount' | 'configVersion'>;
7
+ export declare function mapSettings(recCfg: LegacyRecommendationConfig, parsed: LegacyProductConfig, id: number): SettingsPartial;
@@ -0,0 +1,5 @@
1
+ import type { LegacyButtonTheme, LegacyImageTheme, LegacyTextTheme, MappedButtonStyle, MappedImageStyle, MappedTextStyle } from './types';
2
+ export declare function mapTextTheme(theme: LegacyTextTheme | undefined): MappedTextStyle;
3
+ export declare function mapButtonTheme(theme: LegacyButtonTheme | undefined): MappedButtonStyle;
4
+ export declare function mapImageTheme(theme: LegacyImageTheme | undefined): MappedImageStyle;
5
+ export declare function appendStyle(existing: string | undefined, addition: string | undefined): string;
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Internal types for the legacy Recommendation block migrator.
3
+ *
4
+ * The legacy block stores its full state as a JSON blob inside the
5
+ * `esd-dev-product-config` HTML attribute. These interfaces describe
6
+ * the subset of that schema we depend on. All fields are optional —
7
+ * legacy data is heterogeneous and we use defensive optional chaining
8
+ * everywhere downstream.
9
+ */
10
+ /** Per-side numeric values used by border / padding theme fields. */
11
+ export interface LegacySides<T = number> {
12
+ left?: T;
13
+ right?: T;
14
+ top?: T;
15
+ bottom?: T;
16
+ }
17
+ /** Padding shape used for both desktop and mobile in legacy theme. */
18
+ export interface LegacyPaddingViewport {
19
+ all?: number;
20
+ top?: number;
21
+ right?: number;
22
+ bottom?: number;
23
+ left?: number;
24
+ }
25
+ /** Padding object covering both desktop and mobile breakpoints. */
26
+ export interface LegacyPadding {
27
+ desktop?: LegacyPaddingViewport;
28
+ mobile?: LegacyPaddingViewport;
29
+ }
30
+ /** Border-radius shape — observed in two flavours: shorthand or per-corner. */
31
+ export interface LegacyBorderRadius {
32
+ /** Single shorthand value (e.g. `radius: 4`). */
33
+ radius?: number;
34
+ /** Per-corner: top-left / top-right / bottom-right / bottom-left (in px). */
35
+ lt?: number;
36
+ rt?: number;
37
+ rb?: number;
38
+ lb?: number;
39
+ }
40
+ /** Theme attached to text-like variables (name, prices, omnibus, custom-attr). */
41
+ export interface LegacyTextTheme {
42
+ textStyle?: {
43
+ 'font-family'?: string;
44
+ 'font-size'?: string;
45
+ 'font-weight'?: string;
46
+ 'font-style'?: string;
47
+ };
48
+ textColor?: {
49
+ color?: string;
50
+ };
51
+ textBackgroundColor?: {
52
+ color?: string;
53
+ };
54
+ textPadding?: LegacyPadding;
55
+ textLineSpacing?: {
56
+ lineSpacing?: string;
57
+ 'font-lineSpacing'?: string;
58
+ };
59
+ textAlign?: {
60
+ align?: string;
61
+ };
62
+ textTrimming?: boolean;
63
+ }
64
+ /** Theme attached to image variables. */
65
+ export interface LegacyImageTheme {
66
+ imageSize?: {
67
+ width?: number;
68
+ height?: number;
69
+ };
70
+ }
71
+ /** Theme attached to the button variable. */
72
+ export interface LegacyButtonTheme {
73
+ buttonStyle?: {
74
+ 'font-family'?: string;
75
+ 'font-size'?: string;
76
+ 'font-weight'?: string;
77
+ 'font-style'?: string;
78
+ };
79
+ buttonStretch?: {
80
+ stretched?: boolean;
81
+ };
82
+ buttonInternalPadding?: LegacyPadding;
83
+ buttonExternalPadding?: LegacyPadding;
84
+ buttonText?: {
85
+ text?: string;
86
+ };
87
+ buttonColor?: {
88
+ color?: string;
89
+ };
90
+ textButtonColor?: {
91
+ color?: string;
92
+ };
93
+ buttonBorder?: {
94
+ width?: LegacySides<number>;
95
+ style?: LegacySides<string>;
96
+ color?: LegacySides<string>;
97
+ };
98
+ buttonBorderRadius?: LegacyBorderRadius;
99
+ buttonAlign?: {
100
+ align?: string;
101
+ };
102
+ }
103
+ /** Block-level theme. */
104
+ export interface LegacyBlockTheme {
105
+ blockAdapt?: {
106
+ adapt?: boolean;
107
+ };
108
+ blockPadding?: LegacyPadding;
109
+ }
110
+ /** Single composition.variable entry from `esd-dev-product-config`. */
111
+ export interface LegacyVariable {
112
+ /** e.g. `p_name`, `p_button`, `p_attribute_<ts>`. */
113
+ variable?: string;
114
+ /** Marker that distinguishes custom product attributes (`'p_attribute'`). */
115
+ variableType?: string;
116
+ /** Selector legacy used to address this element in the HTML. */
117
+ visibilityAreaSelector?: string;
118
+ /** Custom-attribute value (e.g. `product_attribute.brand`). */
119
+ value?: string;
120
+ /** Whether the element is hidden in the legacy block. */
121
+ hidden?: boolean;
122
+ /** Sort order across composition entries (lower = earlier). */
123
+ positionIdx?: number;
124
+ /** Element-kind-specific theme bag. */
125
+ theme?: LegacyTextTheme & LegacyImageTheme & LegacyButtonTheme;
126
+ /** Custom attribute marker. */
127
+ custom?: boolean;
128
+ }
129
+ /** Full decoded `esd-dev-product-config` shape. */
130
+ export interface LegacyProductConfig {
131
+ rowCount?: number;
132
+ totalCount?: number;
133
+ orientation?: string;
134
+ composition?: {
135
+ variables?: LegacyVariable[];
136
+ };
137
+ theme?: LegacyBlockTheme;
138
+ }
139
+ /** Element kind dispatched from a legacy `variable` value. */
140
+ export type ElementKind = 'image' | 'text' | 'button' | 'custom' | 'title';
141
+ /** Mapping output from a single legacy variable. */
142
+ export interface MappedVariable {
143
+ /** Composition key (`productName`, `customAttr:brand`, …) or `__title__` for titles. */
144
+ key: string;
145
+ kind: ElementKind;
146
+ hidden: boolean;
147
+ positionIdx: number;
148
+ /** Original legacy variable (kept for downstream theme mapping). */
149
+ legacy: LegacyVariable;
150
+ /** For custom attrs: bare attribute name (e.g. `brand`). */
151
+ customAttrName?: string;
152
+ /** For custom attrs: full `product-attr` value (e.g. `product_attribute.brand`). */
153
+ customAttrValue?: string;
154
+ }
155
+ /** CSS style strings + attributes resolved from a single text-like theme. */
156
+ export interface MappedTextStyle {
157
+ /** Inline CSS for outer `<td>` (background, padding). */
158
+ tdStyle: string;
159
+ /** Inline CSS for inner `<p>` (typography, color, line-height, text-align). */
160
+ pStyle: string;
161
+ /** Value for `<td align>` attribute, if any. */
162
+ align?: string;
163
+ /** Whether to wrap content in `<strong>`. */
164
+ wrapBold: boolean;
165
+ /** Whether to wrap content in `<em>`. */
166
+ wrapItalic: boolean;
167
+ /** True if `textTrimming: true` in theme. */
168
+ textTrimming: boolean;
169
+ }
170
+ /** Resolved button styles. */
171
+ export interface MappedButtonStyle {
172
+ /** Inline CSS for outer `<td>` (external padding). */
173
+ tdStyle: string;
174
+ /** Value for `<td align>`, if any. */
175
+ align?: string;
176
+ /** Inline CSS for `<span class="es-button-border">`. */
177
+ spanStyle: string;
178
+ /** Inline CSS for `<a class="es-button">`. */
179
+ aStyle: string;
180
+ /** Button label text. */
181
+ text?: string;
182
+ }
183
+ /** Resolved image styles. */
184
+ export interface MappedImageStyle {
185
+ /** Width in pixels (number form) — applied as both `width=` attr and CSS. */
186
+ width?: number;
187
+ /** Height in pixels. */
188
+ height?: number;
189
+ }
190
+ /** Per-variable mapped style bag, keyed by composition key. */
191
+ export type PerElementStyles = Record<string, MappedTextStyle | MappedButtonStyle | MappedImageStyle | undefined>;
192
+ /** Composition mapper output. */
193
+ export interface MappedComposition {
194
+ composition: string[];
195
+ visibility: Record<string, boolean>;
196
+ customAttributes: string[];
197
+ /** Mapping of customAttr key → full `product-attr` value. */
198
+ customAttrValues: Record<string, string>;
199
+ /** Title variable, if present (for separate title rendering). */
200
+ titleVariable?: LegacyVariable;
201
+ /** Resolved styles per composition key. */
202
+ perElementStyles: PerElementStyles;
203
+ /** Whether *any* element had `textTrimming: true`. */
204
+ anyTextTrimming: boolean;
205
+ }
@@ -1 +1,13 @@
1
- export declare function migrateRecommendation(html: string): string;
1
+ /**
2
+ * Idempotent: the selector matches only blocks that have
3
+ * `esd-dev-product-config` AND lack `recommendation-block-v2`, so
4
+ * re-running on a migrated template is a no-op.
5
+ */
6
+ import type { LegacyRecommendationConfig } from '@@/Types/config';
7
+ /**
8
+ * Migrate all legacy Recommendation Blocks in the given HTML string.
9
+ * @param html - Full template HTML.
10
+ * @param recommendationConfigs - Map of campaign-id → legacy config (`Record<string, LegacyRecommendationConfig>`).
11
+ * @returns Migrated HTML, or the original HTML if no legacy blocks exist.
12
+ */
13
+ export declare function migrateRecommendation(html: string, recommendationConfigs?: Record<string, LegacyRecommendationConfig>): string;
@@ -6,11 +6,16 @@ export declare const URLS: {
6
6
  export declare const QUERY_PARAMS: {
7
7
  CLIENT_ID: string;
8
8
  };
9
- export declare const RecommendationFeedSourceMaps: RecommendationFeedItem[];
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[];
10
14
  export declare const PriceAttributes: string[];
11
15
  export declare const currencyLocationMaps: TextValueObject[];
12
16
  export declare const currencyOperators: TextValueObject[];
13
17
  export declare const currencyDecimalCounts: TextValueObject[];
18
+ export declare const OP_ANY_OF = "||";
14
19
  export declare const operatorOptionsForStrings: TextValueObject[];
15
20
  export declare const operatorOptionsForArrayOfStrings: TextValueObject[];
16
21
  export declare const operatorOptionsForNumbers: TextValueObject[];
@@ -3,3 +3,9 @@ export declare const UI_EDITOR_SELECTOR = "ui-editor";
3
3
  export declare const CARD_COMPOSITION_TAB_SELECTOR = "button[role=\"tab\"][aria-label=\"Card Composition\"]";
4
4
  export declare const SETTINGS_TAB_SELECTOR = "button[role=\"tab\"][aria-label=\"Settings\"]";
5
5
  export declare const RIBBON_SELECTOR = ".in-ribbons-wrapper";
6
+ export declare const DYNAMIC_CONTENT_BUTTON_SELECTOR = "#guido__btn-add-dynamic-content";
7
+ export declare const AMP_TOGGLE_BUTTON_SELECTOR = ".guido__amp-toggle-html";
8
+ export declare const AMP_TOGGLE_WRAPPER_SELECTOR = ".in-segments-wrapper";
9
+ export declare const HEADER_SELECTOR = "[data-testid=\"guido-header\"]";
10
+ export declare const POPOVER_LEFT_OFFSET = 158;
11
+ export declare const POPOVER_TOP_GAP = 10;
@@ -12,10 +12,20 @@ export declare const URLS: {
12
12
  UNSUBSCRIBE_URL: string;
13
13
  PREFERENCES_URL: string;
14
14
  };
15
+ export declare const PRODUCT_TYPE_URL_SEGMENTS: {
16
+ readonly 60: "email";
17
+ readonly 49: "journey";
18
+ readonly 97: "email";
19
+ };
15
20
  export declare const INSIDER_ID = "iid";
16
- export declare const DEFAULT_UNSUBSCRIBE_GROUP: {
17
- readonly name: "Global Unsubscribe";
18
- readonly sendGridId: "G";
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;
19
29
  };
20
30
  export declare const UNSUBSCRIBE_PAGES_LINK = "/email/unsubscribe-pages";
21
31
  export declare const PAGE_TYPES: {
@@ -1,4 +1,4 @@
1
- declare const migrationTemplate = "\n <td\n align=\"left\"\n esd-extension-block-id=\"radio-button-block\"\n esd-handler-name=\"esd-extension-RadioButtonBlock\"\n class=\"\n radio-button\n radio-button-v2\n esd-block-ra\n esd-radio-button-block\n esd-extension-block\n es-p10t\n es-p10b\n es-p30r\n es-p30l\"\n >\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n <td align=\"left\" width=\"70%\" style=\"vertical-align: top;\">\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n {-{-TITLE-}-}\n </tr>\n <tr>\n {-{-DESCRIPTION-}-}\n </tr>\n </tbody>\n </table>\n </td>\n <td align=\"right\" width=\"30%\" style=\"vertical-align: middle;\">\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n <td width=\"50%\">\n <table width=\"100%\">\n <tr>\n <td width=\"24\">\n <input\n type=\"radio\"\n id=\"radioYes\"\n name=\"unsubscribe\"\n data-cke-editable=\"1\"\n style=\"margin: 0px; vertical-align: middle;>\n </td>\n {-{-YES-}-}\n </tr>\n </table>\n </td>\n <td width=\"50%\">\n <table width=\"100%\">\n <tr>\n <td width=\"24\">\n <input\n type=\"radio\"\n id=\"radioNo\"\n name=\"unsubscribe\"\n data-cke-editable=\"1\"\n style=\"margin: 0px; vertical-align: middle;\">\n </td>\n {-{-NO-}-}\n </tr>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n";
1
+ declare const migrationTemplate = "\n <td\n align=\"left\"\n esd-extension-block-id=\"radio-button-block\"\n esd-handler-name=\"esd-extension-RadioButtonBlock\"\n class=\"\n radio-button-block\n radio-button-v2\n esd-block-ra\n esd-radio-button-block\n esd-extension-block\n es-p10t\n es-p10b\n es-p30r\n es-p30l\"\n >\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n <td align=\"left\" width=\"70%\" style=\"vertical-align: top;\">\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n {-{-TITLE-}-}\n </tr>\n <tr>\n {-{-DESCRIPTION-}-}\n </tr>\n </tbody>\n </table>\n </td>\n <td align=\"right\" width=\"30%\" style=\"vertical-align: middle;\">\n <table cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">\n <tbody>\n <tr>\n <td width=\"50%\">\n <table width=\"100%\">\n <tr>\n <td width=\"24\">\n <input\n type=\"radio\"\n id=\"radioYes\"\n name=\"unsubscribe\"\n data-cke-editable=\"1\"\n style=\"margin: 0px; vertical-align: middle;\">\n </td>\n {-{-YES-}-}\n </tr>\n </table>\n </td>\n <td width=\"50%\">\n <table width=\"100%\">\n <tr>\n <td width=\"24\">\n <input\n type=\"radio\"\n id=\"radioNo\"\n name=\"unsubscribe\"\n data-cke-editable=\"1\"\n style=\"margin: 0px; vertical-align: middle;\">\n </td>\n {-{-NO-}-}\n </tr>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n";
2
2
  /**
3
3
  * @returns The template for the default checkbox block
4
4
  */
@@ -63,5 +63,6 @@ export declare enum RecommendationControlId {
63
63
  CUSTOM_ATTR_PADDINGS = "recommendation-block-custom-attr-paddings-control",
64
64
  CUSTOM_ATTR_SIZE = "recommendation-block-custom-attr-size-control",
65
65
  CUSTOM_ATTR_STYLE = "recommendation-block-custom-attr-style-control",
66
+ CUSTOM_ATTR_TEXT_TRIM = "recommendation-block-custom-attr-text-trim-control",
66
67
  SYNC_INFO_MESSAGE = "recommendation-block-sync-info-message"
67
68
  }
@@ -13,10 +13,12 @@
13
13
  * product cards — changing "brand" color won't affect "rating_star" color.
14
14
  */
15
15
  import { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
16
+ import { CustomAttributeTextTrimControl } from './textTrim';
16
17
  /**
17
18
  * Grouped Custom Attribute Controls
18
19
  * Use this object for cleaner imports in extension.ts
19
20
  */
21
+ export { CustomAttributeTextTrimControl } from './textTrim';
20
22
  export declare const CustomAttributeControls: {
21
23
  readonly align: {
22
24
  new (): {
@@ -102,4 +104,5 @@ export declare const CustomAttributeControls: {
102
104
  isVisible(_node: ImmutableHtmlNode): boolean;
103
105
  };
104
106
  };
107
+ readonly textTrim: typeof CustomAttributeTextTrimControl;
105
108
  };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Custom Attribute Text Trim Control
3
+ *
4
+ * Per-attribute text trimming for custom product attributes.
5
+ * Unlike NameTextTrimControl which applies to ALL product names globally,
6
+ * this control is scoped to a specific custom attribute type via the
7
+ * `product-attr` HTML attribute — changing text-trim on "brand" won't
8
+ * affect "rating_star".
9
+ *
10
+ * CSS rules are shared with NameTextTrimControl via shared/textTrimCssRules.ts.
11
+ */
12
+ import { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
13
+ import { CommonControl } from '../../../common-control';
14
+ /**
15
+ * Control for enabling/disabling text trimming on individual custom attributes.
16
+ * Scoped per attribute type using the `product-attr` HTML attribute.
17
+ */
18
+ export declare class CustomAttributeTextTrimControl extends CommonControl {
19
+ getId(): string;
20
+ getTemplate(): string;
21
+ onRender(): void;
22
+ onTemplateNodeUpdated(node: ImmutableHtmlNode): void;
23
+ _setFormValues(): void;
24
+ _getCurrentTrimState(): boolean;
25
+ /**
26
+ * Finds all custom attribute elements of the same type as the current node.
27
+ *
28
+ * Similar to getCustomAttrTargetNodes in customAttribute/index.ts, but adapted
29
+ * for CommonControl lifecycle where currentNode is a class property (not a
30
+ * parameter from Stripo's getTargetNodes override).
31
+ */
32
+ _getTargetElements(): ImmutableHtmlNode[];
33
+ _onTextTrimChange(enabled: boolean): void;
34
+ _listenToFormUpdates(): void;
35
+ }
@@ -1,9 +1,9 @@
1
- import { ImmutableCssNode, ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
1
+ import { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
2
2
  import { CommonControl } from '../../../common-control';
3
3
  /**
4
- * Control for enabling/disabling text trimming on product names
4
+ * Control for enabling/disabling text trimming on product names.
5
5
  * When enabled, adds a CSS class that applies text-overflow: ellipsis
6
- * and injects the required CSS rules into the document stylesheet
6
+ * and injects the required CSS rules into the document stylesheet.
7
7
  */
8
8
  export declare class NameTextTrimControl extends CommonControl {
9
9
  getId(): string;
@@ -12,23 +12,6 @@ export declare class NameTextTrimControl extends CommonControl {
12
12
  onTemplateNodeUpdated(node: ImmutableHtmlNode): void;
13
13
  _setFormValues(): void;
14
14
  _getCurrentTrimState(): boolean;
15
- /**
16
- * Finds an existing CSS rule in the document stylesheet by exact query
17
- * @param query - The CSS query to search for (uses Stripo's CSS query syntax)
18
- * @returns The CSS rule node if found, undefined otherwise
19
- */
20
- _findCssRule(query: string): ImmutableCssNode | undefined;
21
- /**
22
- * Finds the .text-trim-enabled p rule by searching all text-trim rules and comparing selectors
23
- * This is needed because Stripo's CSS query syntax interprets spaces as path separators
24
- * @returns true if the rule exists
25
- */
26
- _hasParagraphRule(): boolean;
27
- /**
28
- * Ensures the text-trim CSS rules exist in the document stylesheet
29
- * Only adds rules if they don't already exist (prevents duplicates across multiple blocks)
30
- */
31
- _ensureCssRulesExist(): void;
32
15
  _onTextTrimChange(enabled: boolean): void;
33
16
  _listenToFormUpdates(): void;
34
17
  }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Shared Text Trim CSS Rule Management
3
+ *
4
+ * Extracted from NameTextTrimControl to be reused by CustomAttributeTextTrimControl.
5
+ * Follows the same pattern as controls/mobileLayout/cssRules.ts — standalone
6
+ * functions that accept an API parameter, importable by any control class.
7
+ *
8
+ * The CSS rules are document-scoped (shared across all recommendation blocks).
9
+ * Once injected, they persist until the template is closed.
10
+ */
11
+ import type { ImmutableCssNode } from '@stripoinc/ui-editor-extensions';
12
+ import { ModificationDescription } from '@stripoinc/ui-editor-extensions';
13
+ export declare const CSS_CLASS_TEXT_TRIM = "text-trim-enabled";
14
+ interface CssCapableApi {
15
+ getDocumentRootCssNode: () => ImmutableCssNode | null;
16
+ getDocumentModifier: () => {
17
+ modifyCss: (node: ImmutableCssNode) => {
18
+ appendRule: (rule: string) => void;
19
+ };
20
+ apply: (desc: ModificationDescription) => void;
21
+ };
22
+ }
23
+ /**
24
+ * Ensures the text-trim CSS rules exist in the document stylesheet.
25
+ * Only adds rules if they don't already exist (prevents duplicates across blocks).
26
+ * Calls getDocumentRootCssNode once and passes it to both checks.
27
+ */
28
+ export declare function ensureTextTrimCssRulesExist(api: CssCapableApi): void;
29
+ export {};
@@ -95,15 +95,23 @@ 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.
98
103
  * @example
99
104
  * // In Block.onCreated lifecycle
100
- * RecommendationConfigService.initializeConfig(this.api, node);
105
+ * const { config, wasFreshDrop } = RecommendationConfigService.initializeConfig(this.api, node);
101
106
  * @param api - Stripo extension API with document modifier
102
107
  * @param node - The immutable HTML node to initialize
103
108
  * @param partialConfig - Optional partial config to merge with defaults
104
- * @returns The initialized configuration
109
+ * @returns The initialized configuration and whether the node was a fresh drop
105
110
  */
106
- static initializeConfig(api: DocumentModifierApi, node: ImmutableHtmlNode, partialConfig?: PartialNodeConfig): RecommendationNodeConfig;
111
+ static initializeConfig(api: DocumentModifierApi, node: ImmutableHtmlNode, partialConfig?: PartialNodeConfig): {
112
+ config: RecommendationNodeConfig;
113
+ wasFreshDrop: boolean;
114
+ };
107
115
  /**
108
116
  * Save complete configuration to a node
109
117
  *
@@ -119,6 +127,16 @@ export declare class RecommendationConfigService {
119
127
  *
120
128
  * Reads existing data-attributes and creates a proper node config.
121
129
  * Used when loading templates created before node config was implemented.
130
+ *
131
+ * Sources, in priority order (later overrides earlier):
132
+ * 1. `esd-ext-config` JSON blob — emitted by the recommendation migrator and
133
+ * by hand-authored new templates. Carries the full RecommendationNodeConfig
134
+ * (strategy, language, currency, filters, productIds, etc.) which the
135
+ * discrete data-* attrs below cannot capture.
136
+ * 2. Discrete `data-*` attributes — runtime source of truth for
137
+ * layout/composition/spacing; controls keep these in sync as the user
138
+ * edits, so we let them override the JSON blob if they disagree.
139
+ * 3. `currency-*` attributes — durable currency source on the block element.
122
140
  * @param api - Stripo extension API with document modifier
123
141
  * @param node - The block node to migrate
124
142
  * @returns The migrated configuration
@@ -1,5 +1,5 @@
1
1
  import type { Orientation, Languages, Currency, NumericSeparator, FiltersResponse, Filter, RecommendationProduct } from '@@/Types/recommendation';
2
- interface PerBlockConfigs {
2
+ export interface PerBlockConfigs {
3
3
  cardsInRow: number;
4
4
  currencySettings: {
5
5
  name: string;
@@ -36,6 +36,25 @@ interface PerBlockState {
36
36
  /** Snapshot of filters taken when the drawer opens, used to revert on cancel */
37
37
  filterSnapshot: Filter[] | null;
38
38
  }
39
+ /**
40
+ * Persisted-config subset used by `seedBlockUrlConfig` to reconstruct
41
+ * `recommendationConfigs` from a node-config JSON blob without coupling the
42
+ * caller to the full `RecommendationNodeConfig` shape. Separator strings are
43
+ * narrowed at the seed boundary.
44
+ */
45
+ export interface PerBlockUrlConfig {
46
+ strategy: string;
47
+ language: string;
48
+ size: string;
49
+ productIds: string[];
50
+ filters: Filter[];
51
+ shuffleProducts: boolean;
52
+ currencyCode: string;
53
+ currencyAlignment: 'before' | 'after';
54
+ currencyDecimalCount: number;
55
+ currencyDecimalSeparator: string;
56
+ currencyThousandSeparator: string;
57
+ }
39
58
  interface StoreState {
40
59
  recommendationCampaignUrls: Record<string, string>;
41
60
  activePredictiveAlgorithms: number[];
@@ -202,6 +221,21 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
202
221
  * Automatically cleaned up when removeBlockState deletes the block entry.
203
222
  */
204
223
  markBlockInitialized(id: number): void;
224
+ /**
225
+ * Seeds the URL-relevant fields of a block from a persisted node config.
226
+ *
227
+ * Used at save-time to ensure the campaign URL is built from the
228
+ * persisted truth (the `esd-ext-config` blob in the raw HTML) even when
229
+ * the user never selected the block in this editor session — without
230
+ * this seed, `_syncNodeConfigToStore` would never have run for that
231
+ * block and the store would hold default values (USD/en_US/mostPopular)
232
+ * instead of the real config.
233
+ *
234
+ * Creates the block entry if missing; otherwise patches only the URL-
235
+ * relevant subset and leaves runtime fields (e.g., `recommendedProducts`,
236
+ * `isInitialized`) alone.
237
+ */
238
+ seedBlockUrlConfig(id: number, config: PerBlockUrlConfig): void;
205
239
  /**
206
240
  * Patches the current block's recommendationConfigs.
207
241
  * Replaces `store.$patch({ recommendationConfigs: { ... } })` pattern.
@@ -238,6 +272,12 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
238
272
  deleteFilter(filter: Filter): void;
239
273
  addFilter(filter: Filter): void;
240
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;
241
281
  fetchRecommendationProducts(): Promise<void>;
242
282
  _doFetchProducts(): Promise<void>;
243
283
  }>;