@useinsider/guido 3.17.0-beta.668ca65 → 3.17.0-beta.6b602e7

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 (73) hide show
  1. package/README.md +5 -5
  2. package/dist/components/Guido.vue.js +7 -7
  3. package/dist/components/Guido.vue2.js +65 -61
  4. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue.js +3 -3
  5. package/dist/components/organisms/extensions/recommendation/AssignStrategyDrawer.vue2.js +78 -52
  6. package/dist/components/organisms/extensions/recommendation/LegacyStrategyUpdatedModal.vue.js +18 -0
  7. package/dist/components/organisms/extensions/recommendation/LegacyStrategyUpdatedModal.vue2.js +25 -0
  8. package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue.js +17 -0
  9. package/dist/components/organisms/extensions/recommendation/RemoveStrategyModal.vue2.js +57 -0
  10. package/dist/components/organisms/extensions/recommendation/StrategyInfoBoxContent.vue2.js +36 -35
  11. package/dist/composables/useCustomInterfaceAppearance.js +46 -42
  12. package/dist/composables/useLegacyStrategyConversion.js +52 -0
  13. package/dist/composables/useStripo.js +14 -14
  14. package/dist/config/i18n/en/labels.json.js +1 -1
  15. package/dist/config/migrator/index.js +9 -8
  16. package/dist/config/migrator/recommendation/compositionMapper.js +20 -20
  17. package/dist/config/migrator/recommendation/extractors.js +29 -24
  18. package/dist/config/migrator/recommendation/htmlBuilder.js +116 -130
  19. package/dist/config/migrator/recommendationMigrator.js +30 -25
  20. package/dist/config/migrator/recommendationTitleMigrator.js +119 -0
  21. package/dist/enums/extensions/recommendationBlock.js +2 -1
  22. package/dist/extensions/Blocks/Recommendation/block.js +12 -11
  23. package/dist/extensions/Blocks/Recommendation/constants/blockIds.js +1 -1
  24. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  25. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +24 -22
  26. package/dist/extensions/Blocks/Recommendation/constants/selectors.js +35 -28
  27. package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +329 -257
  28. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +211 -145
  29. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +66 -31
  30. package/dist/extensions/Blocks/Recommendation/controls/main/utils.js +1 -0
  31. package/dist/extensions/Blocks/Recommendation/controls/title/index.js +44 -0
  32. package/dist/extensions/Blocks/Recommendation/extension.js +31 -29
  33. package/dist/extensions/Blocks/Recommendation/services/configService.js +34 -16
  34. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +23 -4
  35. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +93 -42
  36. package/dist/extensions/Blocks/Recommendation/templates/grid/template.js +3 -3
  37. package/dist/extensions/Blocks/Recommendation/templates/titleRows.js +33 -0
  38. package/dist/extensions/Blocks/Recommendation/templates/utils.js +85 -107
  39. package/dist/extensions/Blocks/Recommendation/utils/legacyStrategyMigration.js +58 -0
  40. package/dist/extensions/Blocks/Recommendation/utils/nodeConfigWriter.js +22 -0
  41. package/dist/guido.css +1 -1
  42. package/dist/services/recommendationApi.js +61 -52
  43. package/dist/src/components/organisms/extensions/recommendation/LegacyStrategyUpdatedModal.vue.d.ts +2 -0
  44. package/dist/src/components/organisms/extensions/recommendation/RemoveStrategyModal.vue.d.ts +2 -0
  45. package/dist/src/composables/useLegacyStrategyConversion.d.ts +3 -0
  46. package/dist/src/config/migrator/recommendation/extractors.d.ts +8 -0
  47. package/dist/src/config/migrator/recommendationTitleMigrator.d.ts +5 -0
  48. package/dist/src/enums/extensions/recommendationBlock.d.ts +1 -0
  49. package/dist/src/extensions/Blocks/Recommendation/constants/blockIds.d.ts +2 -1
  50. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +7 -0
  51. package/dist/src/extensions/Blocks/Recommendation/constants/defaultConfig.d.ts +1 -0
  52. package/dist/src/extensions/Blocks/Recommendation/constants/index.d.ts +1 -1
  53. package/dist/src/extensions/Blocks/Recommendation/constants/selectors.d.ts +12 -0
  54. package/dist/src/extensions/Blocks/Recommendation/controls/cardComposition/index.d.ts +11 -0
  55. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +1 -0
  56. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +38 -8
  57. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +1 -0
  58. package/dist/src/extensions/Blocks/Recommendation/controls/main/utils.d.ts +1 -0
  59. package/dist/src/extensions/Blocks/Recommendation/controls/title/index.d.ts +86 -0
  60. package/dist/src/extensions/Blocks/Recommendation/services/configService.d.ts +12 -1
  61. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +80 -1
  62. package/dist/src/extensions/Blocks/Recommendation/templates/titleRows.d.ts +13 -0
  63. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +6 -0
  64. package/dist/src/extensions/Blocks/Recommendation/utils/legacyStrategyMigration.d.ts +39 -0
  65. package/dist/src/extensions/Blocks/Recommendation/utils/nodeConfigWriter.d.ts +31 -0
  66. package/dist/src/services/recommendationApi.d.ts +2 -1
  67. package/dist/src/utils/genericUtil.d.ts +5 -0
  68. package/dist/static/assets/icons/filled-arrow-rising.svg.js +5 -0
  69. package/dist/static/assets/icons/line-info.svg.js +5 -0
  70. package/dist/static/styles/components/recommendation-strategy.css.js +70 -5
  71. package/dist/static/styles/customEditorStyle.css.js +3 -1
  72. package/dist/utils/genericUtil.js +28 -27
  73. package/package.json +2 -2
@@ -65,5 +65,12 @@ export declare enum RecommendationControlId {
65
65
  CUSTOM_ATTR_SIZE = "recommendation-block-custom-attr-size-control",
66
66
  CUSTOM_ATTR_STYLE = "recommendation-block-custom-attr-style-control",
67
67
  CUSTOM_ATTR_TEXT_TRIM = "recommendation-block-custom-attr-text-trim-control",
68
+ TITLE_ALIGN = "recommendation-block-title-align-control",
69
+ TITLE_BACKGROUND = "recommendation-block-title-background-control",
70
+ TITLE_COLOR = "recommendation-block-title-color-control",
71
+ TITLE_FONT_FAMILY = "recommendation-block-title-font-family-control",
72
+ TITLE_PADDINGS = "recommendation-block-title-paddings-control",
73
+ TITLE_SIZE = "recommendation-block-title-size-control",
74
+ TITLE_STYLE = "recommendation-block-title-style-control",
68
75
  SYNC_INFO_MESSAGE = "recommendation-block-sync-info-message"
69
76
  }
@@ -34,6 +34,7 @@ export declare const DEFAULT_COMPOSITION: string[];
34
34
  /**
35
35
  * Default visibility settings for card elements
36
36
  * Omnibus elements are hidden by default (not all regions require them)
37
+ * The block-level title is visible on a fresh drop.
37
38
  */
38
39
  export declare const DEFAULT_VISIBILITY: Record<string, boolean>;
39
40
  /**
@@ -8,6 +8,6 @@
8
8
  */
9
9
  export { RecommendationBlockId } from './blockIds';
10
10
  export { RecommendationControlId } from './controlIds';
11
- export { BLOCK_ROOT_SELECTOR, CONTAINER_SELECTOR, DESKTOP_CONTAINER_SELECTOR, MOBILE_CONTAINER_SELECTOR, MOBILE_ROW_SELECTOR, CSS_CLASS_RECO_BUTTON, CSS_CLASS_SKIP_COMPILE, RECOMMENDATION_VARIABLE_BUILTIN_ATTRIBUTES, CURRENCY_ATTR, ATTR_PRODUCT_IMAGE, ATTR_PRODUCT_NAME, ATTR_PRODUCT_PRICE, ATTR_PRODUCT_OLD_PRICE, ATTR_PRODUCT_OMNIBUS_PRICE, ATTR_PRODUCT_OMNIBUS_DISCOUNT, ATTR_PRODUCT_BUTTON, ATTR_CUSTOM_PREFIX, ATTR_DATA_CARD_COMPOSITION, ATTR_DATA_CUSTOM_ATTRIBUTES, ATTR_PRODUCT_ATTR, SELECTOR_ATTRIBUTE_ROW, ATTR_DATA_ATTRIBUTE_TYPE, PRODUCT_ATTRIBUTE_PREFIX, BUILT_IN_DEFAULT_ATTRIBUTES, } from './selectors';
11
+ export { BLOCK_ROOT_SELECTOR, CONTAINER_SELECTOR, DESKTOP_CONTAINER_SELECTOR, MOBILE_CONTAINER_SELECTOR, MOBILE_ROW_SELECTOR, CSS_CLASS_RECO_BUTTON, CSS_CLASS_SKIP_COMPILE, RECOMMENDATION_VARIABLE_BUILTIN_ATTRIBUTES, CURRENCY_ATTR, ATTR_PRODUCT_IMAGE, ATTR_PRODUCT_NAME, ATTR_PRODUCT_PRICE, ATTR_PRODUCT_OLD_PRICE, ATTR_PRODUCT_OMNIBUS_PRICE, ATTR_PRODUCT_OMNIBUS_DISCOUNT, ATTR_PRODUCT_BUTTON, ATTR_RECOMMENDATION_TITLE, SELECTOR_TITLE_CELL, ATTR_CUSTOM_PREFIX, ATTR_DATA_CARD_COMPOSITION, ATTR_DATA_CUSTOM_ATTRIBUTES, ATTR_PRODUCT_ATTR, CSS_CLASS_ATTRIBUTE_ROW, CSS_CLASS_SPACER, SELECTOR_ATTRIBUTE_ROW, ATTR_DATA_ATTRIBUTE_TYPE, ATTR_DATA_VISIBILITY, PRODUCT_ATTRIBUTE_PREFIX, BUILT_IN_DEFAULT_ATTRIBUTES, } from './selectors';
12
12
  export { LAYOUT_VALUES, LAYOUT_OPTIONS, DEFAULT_PRODUCTS_PER_ROW, DEFAULT_CARDS_IN_ROW, DEFAULT_MOBILE_CARDS_IN_ROW, MAX_PRODUCT_COUNT, MIN_PRODUCT_COUNT, MAX_PRODUCTS_PER_ROW, MIN_PRODUCTS_PER_ROW, MAX_MOBILE_PRODUCTS_PER_ROW, MIN_MOBILE_PRODUCTS_PER_ROW, DEFAULT_COLUMN_SPACING, DEFAULT_ROW_SPACING, DEFAULT_MOBILE_COLUMN_SPACING, DEFAULT_MOBILE_ROW_SPACING, MIN_SPACING, MAX_SPACING, SPACING_STEP, } from './layout';
13
13
  export { DEFAULT_NODE_CONFIG, DEFAULT_CURRENCY, DEFAULT_COMPOSITION, DEFAULT_VISIBILITY, CURRENT_CONFIG_VERSION, PRE_RRS_CONFIG_VERSION, EXCLUDED_ALGORITHM_IDS, } from './defaultConfig';
@@ -58,6 +58,10 @@ export declare const ATTR_PRODUCT_OLD_PRICE = "productOldPrice";
58
58
  export declare const ATTR_PRODUCT_OMNIBUS_PRICE = "productOmnibusPrice";
59
59
  export declare const ATTR_PRODUCT_OMNIBUS_DISCOUNT = "productOmnibusDiscount";
60
60
  export declare const ATTR_PRODUCT_BUTTON = "productButton";
61
+ export declare const ATTR_RECOMMENDATION_TITLE = "recommendationTitle";
62
+ export declare const CSS_CLASS_TITLE_CELL = "ins-recommendation-title";
63
+ /** Title cell — the only `esd-block-text` living outside the product containers. */
64
+ export declare const SELECTOR_TITLE_CELL = ".ins-recommendation-title";
61
65
  /**
62
66
  * Custom attribute constants
63
67
  * Used for dynamically added product attributes in Card Composition
@@ -68,12 +72,20 @@ export declare const ATTR_CUSTOM_PREFIX = "customAttr:";
68
72
  export declare const ATTR_DATA_CUSTOM_ATTRIBUTES = "data-custom-attributes";
69
73
  /** HTML data attribute on the block root holding the comma-joined composition order */
70
74
  export declare const ATTR_DATA_CARD_COMPOSITION = "data-card-composition";
75
+ /** HTML attribute on the block root carrying its per-document unique id */
76
+ export declare const RECOMMENDATION_ID_ATTR = "recommendation-id";
71
77
  /** HTML attribute on <td> elements identifying the product attribute for compiler template variable generation */
72
78
  export declare const ATTR_PRODUCT_ATTR = "product-attr";
79
+ /** Class every composition attribute row carries — the visibility toggle and the compiler both key on it */
80
+ export declare const CSS_CLASS_ATTRIBUTE_ROW = "recommendation-attribute-row";
73
81
  /** Row wrapper of one composition entry inside a product card */
74
82
  export declare const SELECTOR_ATTRIBUTE_ROW = ".recommendation-attribute-row";
75
83
  /** HTML attribute on an attribute row holding its composition key */
76
84
  export declare const ATTR_DATA_ATTRIBUTE_TYPE = "data-attribute-type";
85
+ /** HTML attribute on an attribute row holding its visibility (`1` / `0`) */
86
+ export declare const ATTR_DATA_VISIBILITY = "data-visibility";
87
+ /** Class on the spacer cell of the row that trails a title */
88
+ export declare const CSS_CLASS_SPACER = "spacer";
77
89
  /** Prefix `resolveProductAttrValue` puts on custom (non-default) product attributes in `product-attr` values */
78
90
  export declare const PRODUCT_ATTRIBUTE_PREFIX = "product_attribute.";
79
91
  /**
@@ -33,6 +33,8 @@ export declare class RecommendationCardCompositionControl extends CommonControl
33
33
  private _normalizeComposition;
34
34
  private _readCustomAttributesFromNode;
35
35
  private _readVisibilityFromRows;
36
+ private _withTitlePresence;
37
+ private _hasTitle;
36
38
  private _getDefaultVisibilities;
37
39
  private _extractVisibilityFromRows;
38
40
  private _parseVisibilityValue;
@@ -187,6 +189,15 @@ export declare class RecommendationCardCompositionControl extends CommonControl
187
189
  */
188
190
  private _buildCompositionHtml;
189
191
  private _applyVisibilityToBlock;
192
+ private _applyTitleVisibilityToBlock;
193
+ private _resolveTitleInjection;
194
+ private _readTitleHtml;
195
+ private _findTitleRows;
196
+ private _findTitleRowsByClass;
197
+ private _isSpacerRow;
198
+ private _findTitleContainerBody;
199
+ private _readInnerHtml;
200
+ private _defaultTitleParagraph;
190
201
  /**
191
202
  * Finds the composition array index for the Nth custom attribute (0-based).
192
203
  * Scans left-to-right, counting only entries with the custom prefix.
@@ -11,6 +11,7 @@
11
11
  */
12
12
  export { RecommendationBlockControl, CONTROL_BLOCK_ID, AlgorithmControl, ALGORITHM_CONTROL_ID, LocaleControl, LOCALE_CONTROL_ID, CurrencyControl, CURRENCY_CONTROL_ID, ProductCountControl, PRODUCT_COUNT_CONTROL_ID, ProductLayoutControl, PRODUCT_LAYOUT_CONTROL_ID, FiltersControl, FILTERS_CONTROL_ID, ShuffleControl, SHUFFLE_CONTROL_ID, LayoutOrientationControl, LAYOUT_ORIENTATION_CONTROL_ID, PricePlacementControl, PRICE_PLACEMENT_CONTROL_ID, } from './main';
13
13
  export * from './main/utils';
14
+ export { TitleControls } from './title';
14
15
  export { NameControls } from './name';
15
16
  export { PriceControls } from './price';
16
17
  export { OldPriceControls } from './oldPrice';
@@ -20,7 +20,9 @@ export * from './utils';
20
20
  export declare class RecommendationBlockControl extends CommonControl {
21
21
  private store;
22
22
  private storeUnsubscription;
23
- private syncedBlockIds;
23
+ private syncedConfigs;
24
+ private pendingNodeConfigWrite;
25
+ private pendingConfigFlushTimer;
24
26
  private algorithmControl;
25
27
  private localeControl;
26
28
  private currencyControl;
@@ -54,6 +56,41 @@ export declare class RecommendationBlockControl extends CommonControl {
54
56
  * Uses triggerRefetch: false because the fetch hasn't happened yet —
55
57
  * values are being prepared for the upcoming initial fetch.
56
58
  */
59
+ /**
60
+ * Re-syncs the store when the node config moved since we last read it.
61
+ *
62
+ * Undo/redo, the code editor and version-history restore rewrite the node
63
+ * config behind the store's back — Stripo exposes no undo signal, so a
64
+ * fingerprint diff on this hook is the only way to notice (SD-149105).
65
+ * An unchanged fingerprint means the move came from us, so runtime store
66
+ * edits are never clobbered (SD-141049).
67
+ */
68
+ private _syncNodeConfigIfChanged;
69
+ private _capturePendingNodeConfigWrite;
70
+ /**
71
+ * Accumulates the pending write into `documentModifier` without applying, so the
72
+ * caller's apply() commits config and re-render as ONE undo entry.
73
+ *
74
+ * Deliberately does not consume the pending record: the product helpers call
75
+ * apply() only when they actually changed something, so this batch may never be
76
+ * committed and the flush has to stay able to write it standalone.
77
+ * @param documentModifier - The batch to accumulate into
78
+ * @returns Whether anything was staged
79
+ */
80
+ private _stagePendingNodeConfig;
81
+ /**
82
+ * Arms the guaranteed fallback write, once the product fetch has settled. The
83
+ * delay only has to outlast the re-render debounce that fetch triggers — firing
84
+ * earlier writes the config on its own and the undo entry splits in two again.
85
+ */
86
+ private _scheduleConfigFlush;
87
+ private _clearConfigFlush;
88
+ /**
89
+ * Writes the deferred change standalone when the batch it was staged into never
90
+ * committed. Always targets the LIVE node — the captured one is an immutable
91
+ * snapshot the intervening re-render staled, and saveConfig swallows that failure.
92
+ */
93
+ private _flushPendingNodeConfig;
57
94
  private _syncNodeConfigToStore;
58
95
  /**
59
96
  * Fetches initial data for a block in three phases:
@@ -110,11 +147,4 @@ export declare class RecommendationBlockControl extends CommonControl {
110
147
  * We skip that tick and update tracking references instead.
111
148
  */
112
149
  private _listenStateUpdates;
113
- /**
114
- * Persists filter + strategy state to the Stripo node config so it survives
115
- * save/reload (strategyId must reach esd-ext-config before any save).
116
- * `size` rides along: assigning a strategy writes it to the store only, and a
117
- * stale node config would rebuild the block against the old slot count.
118
- */
119
- private _persistFiltersToNodeConfig;
120
150
  }
@@ -2,6 +2,7 @@ import { CommonControl } from '../../../common-control';
2
2
  export declare const STRATEGY_CONTROL_ID = "recommendation-strategy-control";
3
3
  export declare class StrategyControl extends CommonControl {
4
4
  private store;
5
+ private legacyConversion;
5
6
  private storeUnsubscription;
6
7
  private listeners;
7
8
  private lastPaintedState;
@@ -70,6 +70,7 @@ export interface RegenerateProductRowsOptions {
70
70
  /** Optional: pass composition directly to avoid stale DOM reads after setAttribute */
71
71
  composition?: string[];
72
72
  }
73
+ export declare function getMainContainerTbody(currentNode: ImmutableHtmlNode): ImmutableHtmlNode | null;
73
74
  /**
74
75
  * Regenerates only the mobile product container rows and commits in a single apply().
75
76
  * Standalone entry point for callers that change mobile-only settings
@@ -0,0 +1,86 @@
1
+ export declare const TitleControls: {
2
+ readonly align: {
3
+ new (): {
4
+ getId(): string;
5
+ getTargetNodes(root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode[];
6
+ getParentControlId(): string;
7
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
8
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
9
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
10
+ getAdditionalModifications(_root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
11
+ isVisible(_node: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): boolean;
12
+ };
13
+ };
14
+ readonly color: {
15
+ new (): {
16
+ getId(): string;
17
+ getTargetNodes(root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode[];
18
+ getParentControlId(): string;
19
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
20
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
21
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
22
+ getAdditionalModifications(_root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
23
+ isVisible(_node: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): boolean;
24
+ };
25
+ };
26
+ readonly size: {
27
+ new (): {
28
+ getId(): string;
29
+ getTargetNodes(root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode[];
30
+ getParentControlId(): string;
31
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
32
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
33
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
34
+ getAdditionalModifications(_root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
35
+ isVisible(_node: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): boolean;
36
+ };
37
+ };
38
+ readonly style: {
39
+ new (): {
40
+ getId(): string;
41
+ getTargetNodes(root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode[];
42
+ getParentControlId(): string;
43
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
44
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
45
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
46
+ getAdditionalModifications(_root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
47
+ isVisible(_node: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): boolean;
48
+ };
49
+ };
50
+ readonly fontFamily: {
51
+ new (): {
52
+ getId(): string;
53
+ getTargetNodes(root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode[];
54
+ getParentControlId(): string;
55
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
56
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
57
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
58
+ getAdditionalModifications(_root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
59
+ isVisible(_node: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): boolean;
60
+ };
61
+ };
62
+ readonly background: {
63
+ new (): {
64
+ getId(): string;
65
+ getTargetNodes(root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode[];
66
+ getAdditionalModifications(root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
67
+ getParentControlId(): string;
68
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
69
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
70
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
71
+ isVisible(_node: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): boolean;
72
+ };
73
+ };
74
+ readonly paddings: {
75
+ new (): {
76
+ getId(): string;
77
+ getTargetNodes(root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode[];
78
+ getParentControlId(): string;
79
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
80
+ getLabels(): import("@stripoinc/ui-editor-extensions").ControlLabels | undefined;
81
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
82
+ getAdditionalModifications(_root: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
83
+ isVisible(_node: import("@stripoinc/ui-editor-extensions").ImmutableHtmlNode): boolean;
84
+ };
85
+ };
86
+ };
@@ -25,7 +25,7 @@ import { ModificationDescription } from '@stripoinc/ui-editor-extensions';
25
25
  * Stripo Extension API interface for document modifications
26
26
  * Extracted to allow type-safe usage without importing full Stripo types
27
27
  */
28
- interface DocumentModifier {
28
+ export interface DocumentModifier {
29
29
  modifyHtml: (node: ImmutableHtmlNode) => {
30
30
  setNodeConfig: (config: Record<string, unknown>) => DocumentModifier;
31
31
  setAttribute: (name: string, value: string) => DocumentModifier;
@@ -90,6 +90,17 @@ export declare class RecommendationConfigService {
90
90
  * @returns The new complete configuration
91
91
  */
92
92
  static updateConfig(api: DocumentModifierApi, node: ImmutableHtmlNode, updates: PartialNodeConfig, description: string): RecommendationNodeConfig;
93
+ /**
94
+ * Accumulate a config update into an existing modifier batch, WITHOUT applying.
95
+ *
96
+ * The caller owns the apply(), so the write shares one undo entry with the rest
97
+ * of that batch — a change whose visible result only lands after an async product
98
+ * fetch would otherwise cost the user two undo presses (SD-149105).
99
+ * @param documentModifier - The batch to accumulate into (caller applies)
100
+ * @param node - The immutable HTML node to write to
101
+ * @param updates - Partial config with values to update
102
+ */
103
+ static stageConfig(documentModifier: DocumentModifier, node: ImmutableHtmlNode, updates: PartialNodeConfig): void;
93
104
  /**
94
105
  * Initialize configuration for a new block
95
106
  *
@@ -1,4 +1,4 @@
1
- import type { Orientation, Languages, Currency, NumericSeparator, FiltersResponse, Filter, RecommendationCampaignUrlValue, RecommendationProduct, RrsStrategy } from '@@/Types/recommendation';
1
+ import type { Orientation, Languages, Currency, NumericSeparator, FiltersResponse, Filter, RecommendationCampaignUrlValue, RecommendationProduct, RrsMigrationPayload, RrsStrategy } from '@@/Types/recommendation';
2
2
  export interface PerBlockConfigs {
3
3
  cardsInRow: number;
4
4
  currencySettings: {
@@ -22,6 +22,7 @@ export interface PerBlockConfigs {
22
22
  shuffleProducts: boolean;
23
23
  strategy: string;
24
24
  strategyId: string;
25
+ nodeConfigVersion: number;
25
26
  textTrimming: boolean;
26
27
  unresponsive: boolean;
27
28
  size: string;
@@ -35,11 +36,14 @@ interface PerBlockState {
35
36
  filterStatus: boolean;
36
37
  filterSelectionDrawerStatus: boolean;
37
38
  assignStrategyDrawerStatus: boolean;
39
+ removeStrategyModalStatus: boolean;
40
+ legacyStrategyUpdatedModalStatus: boolean;
38
41
  filterGroup: number;
39
42
  /** Whether initial API data (filters, algorithms, products) has been fetched for this block */
40
43
  isInitialized: boolean;
41
44
  /** Snapshot of filters taken when the drawer opens, used to revert on cancel */
42
45
  filterSnapshot: Filter[] | null;
46
+ legacyConversionLoading: boolean;
43
47
  }
44
48
  /**
45
49
  * Persisted-config subset used by `seedBlockUrlConfig` to reconstruct
@@ -177,6 +181,76 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
177
181
  currentRecommendationId: number | null;
178
182
  configVersion: number;
179
183
  } & import("pinia").PiniaCustomStateProperties<StoreState>): boolean;
184
+ removeStrategyModalStatus(state: {
185
+ recommendationCampaignUrls: Record<string, RecommendationCampaignUrlValue>;
186
+ activePredictiveAlgorithms: number[];
187
+ languages: Languages;
188
+ currencyList: {
189
+ text: string;
190
+ value: string;
191
+ }[];
192
+ filterList: FiltersResponse;
193
+ strategies: {
194
+ [x: string]: unknown;
195
+ strategy_id: number;
196
+ name: string;
197
+ status: string;
198
+ suitable_channels: string;
199
+ size?: (number | string) | undefined;
200
+ }[];
201
+ strategiesLoading: boolean;
202
+ strategyUrlTemplates: Record<string, string>;
203
+ blockStates: Record<number, PerBlockState>;
204
+ currentRecommendationId: number | null;
205
+ configVersion: number;
206
+ } & import("pinia").PiniaCustomStateProperties<StoreState>): boolean;
207
+ isLegacyStrategyBlock(): boolean;
208
+ legacyConversionLoading(state: {
209
+ recommendationCampaignUrls: Record<string, RecommendationCampaignUrlValue>;
210
+ activePredictiveAlgorithms: number[];
211
+ languages: Languages;
212
+ currencyList: {
213
+ text: string;
214
+ value: string;
215
+ }[];
216
+ filterList: FiltersResponse;
217
+ strategies: {
218
+ [x: string]: unknown;
219
+ strategy_id: number;
220
+ name: string;
221
+ status: string;
222
+ suitable_channels: string;
223
+ size?: (number | string) | undefined;
224
+ }[];
225
+ strategiesLoading: boolean;
226
+ strategyUrlTemplates: Record<string, string>;
227
+ blockStates: Record<number, PerBlockState>;
228
+ currentRecommendationId: number | null;
229
+ configVersion: number;
230
+ } & import("pinia").PiniaCustomStateProperties<StoreState>): boolean;
231
+ legacyStrategyUpdatedModalStatus(state: {
232
+ recommendationCampaignUrls: Record<string, RecommendationCampaignUrlValue>;
233
+ activePredictiveAlgorithms: number[];
234
+ languages: Languages;
235
+ currencyList: {
236
+ text: string;
237
+ value: string;
238
+ }[];
239
+ filterList: FiltersResponse;
240
+ strategies: {
241
+ [x: string]: unknown;
242
+ strategy_id: number;
243
+ name: string;
244
+ status: string;
245
+ suitable_channels: string;
246
+ size?: (number | string) | undefined;
247
+ }[];
248
+ strategiesLoading: boolean;
249
+ strategyUrlTemplates: Record<string, string>;
250
+ blockStates: Record<number, PerBlockState>;
251
+ currentRecommendationId: number | null;
252
+ configVersion: number;
253
+ } & import("pinia").PiniaCustomStateProperties<StoreState>): boolean;
180
254
  assignedStrategy(state: {
181
255
  recommendationCampaignUrls: Record<string, RecommendationCampaignUrlValue>;
182
256
  activePredictiveAlgorithms: number[];
@@ -406,9 +480,14 @@ export declare const useRecommendationExtensionStore: import("pinia").StoreDefin
406
480
  cancelFilterDrawer(): void;
407
481
  openAssignStrategyDrawer(): void;
408
482
  closeAssignStrategyDrawer(): void;
483
+ openLegacyStrategyUpdatedModal(): void;
484
+ closeLegacyStrategyUpdatedModal(): void;
485
+ openRemoveStrategyModal(): void;
486
+ closeRemoveStrategyModal(): void;
409
487
  fetchStrategies(): Promise<void>;
410
488
  resolveStrategyUrlTemplate(strategy: RrsStrategy, currency: string): Promise<string>;
411
489
  assignStrategy(strategy: RrsStrategy): void;
490
+ convertLegacyStrategy(payload: RrsMigrationPayload): Promise<string>;
412
491
  removeStrategy(): void;
413
492
  fetchRecommendationCreateData(): Promise<void>;
414
493
  fetchRecommendationFilters(): Promise<void>;
@@ -0,0 +1,13 @@
1
+ export declare const DEFAULT_TITLE_TEXT = "You May Also Like!";
2
+ export declare const DEFAULT_TITLE_PARAGRAPH: string;
3
+ /**
4
+ * Builds the two block-level title rows: the title itself and the spacer below it.
5
+ * Both carry the same `recommendation-attribute-row` tagging a card attribute row has,
6
+ * so the Card Composition toggle hides the pair together and the compiler's
7
+ * `[data-visibility="0"]` sweep drops both — no leading gap when the title is off.
8
+ * @param paragraphHtml - Full `<p>` markup rendered inside the title cell
9
+ * @param visible - Whether the title starts visible
10
+ * @param spacerHeightPx - Height of the spacer row below the title
11
+ * @returns HTML string with the title row followed by its spacer row
12
+ */
13
+ export declare function buildTitleRows(paragraphHtml?: string, visible?: boolean, spacerHeightPx?: number): string;
@@ -139,6 +139,12 @@ export interface RecommendationNodeConfig {
139
139
  * Keys are ATTR_PRODUCT_* constants
140
140
  */
141
141
  visibility: Record<string, boolean>;
142
+ /**
143
+ * Snapshot of the block title's row markup before it's removed.
144
+ * Restored verbatim when the title is re-enabled.
145
+ * @default ''
146
+ */
147
+ previousTitleHtml: string;
142
148
  /**
143
149
  * Omnibus price text configuration
144
150
  * For EU Omnibus Directive compliance
@@ -0,0 +1,39 @@
1
+ import type { Filter, RrsMigrationFilterGroup, RrsMigrationPayload } from '@@/Types/recommendation';
2
+ type FilterAttribute = {
3
+ value: string;
4
+ type: string;
5
+ };
6
+ /**
7
+ * A block authored before reusable strategies: it still carries a plain feed
8
+ * configuration (compiled into a feed URL string) instead of a strategy id.
9
+ * Blocks created under RRS stamp `CURRENT_CONFIG_VERSION`, so the version is
10
+ * what separates "never converted" from "not assigned yet".
11
+ */
12
+ export declare function isLegacyStrategyConfig({ strategyId, nodeConfigVersion }: {
13
+ strategyId: string;
14
+ nodeConfigVersion: number;
15
+ }): boolean;
16
+ /**
17
+ * Groups the block's valid filters the way the feed URL groups them, in the
18
+ * shape the migration endpoint validates. The URL's filter DSL states no
19
+ * attribute type and cannot express a mixed-operator group, which is exactly
20
+ * what this override exists to supply.
21
+ */
22
+ export declare function toMigrationFilterGroups(filters: Filter[], attributes: FilterAttribute[]): RrsMigrationFilterGroup[];
23
+ interface MigrationInputs {
24
+ blockId: number;
25
+ url: string;
26
+ templateId: string;
27
+ variationId: string;
28
+ strategy: string;
29
+ productIds: string[];
30
+ filters: Filter[];
31
+ attributes: FilterAttribute[];
32
+ }
33
+ /**
34
+ * The server names and de-duplicates the strategy, so nothing here derives a
35
+ * name; `campaignId`/`variationId` only feed that name and fall back to 0 for
36
+ * templates and modules, which carry no numeric campaign.
37
+ */
38
+ export declare function buildLegacyMigrationPayload({ blockId, url, templateId, variationId, strategy, productIds, filters, attributes, }: MigrationInputs): RrsMigrationPayload;
39
+ export {};
@@ -0,0 +1,31 @@
1
+ import type { DocumentModifier } from '../services/configService';
2
+ import type { PartialNodeConfig } from '../types/nodeConfig';
3
+ import type { RecommendationProduct } from '@@/Types/recommendation';
4
+ import { ImmutableHtmlNode } from '@stripoinc/ui-editor-extensions';
5
+ interface ConfigWriterApi {
6
+ getDocumentModifier: () => DocumentModifier;
7
+ getDocumentRootHtmlNode: () => ImmutableHtmlNode;
8
+ }
9
+ /**
10
+ * Keeps a document handle alive for writes the settings panel can no longer make.
11
+ * Deliberately not cleared on deselect — that is the exact moment the handle is
12
+ * needed. It is replaced on the next render, and goes stale only if the editor
13
+ * itself is torn down.
14
+ */
15
+ export declare function registerNodeConfigWriter(api: ConfigWriterApi): void;
16
+ /**
17
+ * Commits a config change and its product re-render straight to the document,
18
+ * bypassing the settings panel.
19
+ *
20
+ * Host-rendered UI (the remove-strategy modal) lives outside `ui-editor`, and
21
+ * Stripo deselects the block on the first click that lands there — destroying the
22
+ * control that normally does both, before the store change even happens.
23
+ *
24
+ * Mirrors the control's regenerate routine so config and cards land in ONE undo
25
+ * entry, and so partner-managed blocks keep their hand-authored cells.
26
+ * @param description - labels the undo entry only when there are no cards to
27
+ * re-render; the product helpers label their own batch.
28
+ * @returns whether the write reached a node
29
+ */
30
+ export declare function commitBlockChangeOutsidePanel(blockId: number, updates: PartialNodeConfig, products: RecommendationProduct[], description: string): boolean;
31
+ export {};
@@ -1,4 +1,4 @@
1
- import type { InitialResponse, FiltersResponse, RecommendationProduct, RecommendationProductsParams, RrsEndpointGenerateResponse, RrsStrategy } from '@@/Types/recommendation';
1
+ import type { InitialResponse, FiltersResponse, RecommendationProduct, RecommendationProductsParams, RrsEndpointGenerateResponse, RrsMigrationPayload, RrsStrategy } from '@@/Types/recommendation';
2
2
  export declare const useRecommendationApi: () => {
3
3
  fetchRecommendationCreateData: () => Promise<InitialResponse>;
4
4
  fetchRecommendationFilters: () => Promise<FiltersResponse>;
@@ -6,4 +6,5 @@ export declare const useRecommendationApi: () => {
6
6
  fetchRecommendationProductsByUrl: (url: string) => Promise<RecommendationProduct[]>;
7
7
  fetchRecommendationStrategies: () => Promise<RrsStrategy[]>;
8
8
  generateStrategyEndpoint: (strategy: RrsStrategy, currency: string) => Promise<RrsEndpointGenerateResponse | null>;
9
+ migrateLegacyStrategy: (payload: RrsMigrationPayload) => Promise<string>;
9
10
  };
@@ -25,3 +25,8 @@ export declare const getCsrfToken: () => string;
25
25
  export declare const getUrlParams: () => URLSearchParams;
26
26
  export declare const getUrlParam: (paramName: string, defaultValue?: string) => string;
27
27
  export declare const replaceString: (string?: string, values?: Record<string, string | number>, isRemoveEmptyValues?: boolean) => string;
28
+ /**
29
+ * Narrows an unknown API value to a printable string; anything that is neither
30
+ * a string nor a number becomes ''.
31
+ */
32
+ export declare const asText: (value: unknown) => string;
@@ -0,0 +1,5 @@
1
+ const C = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12.9403 5.51003L17.6328 11.0273C17.8845 11.3769 18.0068 11.8382 17.9997 12.2963L18 12.3333C18 13.2538 17.4627 14 16.8 14H15V15C15 15.5523 14.5523 16 14 16H10C9.44772 16 9 15.5523 9 15V14H7.2C6.53726 14 6 13.2538 6 12.3333L6.00029 12.2963C5.9932 11.8382 6.11551 11.3769 6.36722 11.0273L11.0597 5.51003C11.3182 5.15101 11.6615 4.98153 12 5.00159C12.3386 4.98153 12.6818 5.15101 12.9403 5.51003Z"/><path d="M10 18C9.44772 18 9 18.4477 9 19C9 19.5523 9.44772 20 10 20H14C14.5523 20 15 19.5523 15 19C15 18.4477 14.5523 18 14 18H10Z"/></svg>
2
+ `;
3
+ export {
4
+ C as default
5
+ };
@@ -0,0 +1,5 @@
1
+ const C = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 4C14.1217 4 16.1566 4.84285 17.6569 6.34315C19.1571 7.84344 20 9.87827 20 12C20 14.1217 19.1571 16.1566 17.6569 17.6569C16.1566 19.1571 14.1217 20 12 20C9.87827 20 7.84344 19.1571 6.34315 17.6569C4.84285 16.1566 4 14.1217 4 12C4 9.87827 4.84285 7.84344 6.34315 6.34315C7.84344 4.84285 9.87827 4 12 4ZM12 5.6C10.3026 5.6 8.67475 6.27428 7.47452 7.47452C6.27428 8.67475 5.6 10.3026 5.6 12C5.6 13.6974 6.27428 15.3253 7.47452 16.5255C8.67475 17.7257 10.3026 18.4 12 18.4C13.6974 18.4 15.3253 17.7257 16.5255 16.5255C17.7257 15.3253 18.4 13.6974 18.4 12C18.4 10.3026 17.7257 8.67475 16.5255 7.47452C15.3253 6.27428 13.6974 5.6 12 5.6ZM11.496 10.448C11.928 10.448 12.264 10.552 12.496 10.768C12.736 10.976 12.848 11.248 12.848 11.584V11.688L12.824 11.944C12.816 12.064 12.8 12.176 12.776 12.28L12.736 12.432L12.288 14.024L12.248 14.152C12.1976 14.376 12.1629 14.6032 12.144 14.832C12.144 15.04 12.192 15.184 12.288 15.264C12.384 15.344 12.544 15.376 12.776 15.376C12.888 15.376 13.016 15.36 13.152 15.32L13.392 15.24L13.448 15.216L13.328 15.712L12.848 15.896L12.584 15.992L12.464 16.032C12.248 16.112 12 16.144 11.72 16.144C11.28 16.144 10.944 16.04 10.696 15.824C10.5803 15.7241 10.488 15.5998 10.4258 15.4602C10.3636 15.3205 10.3329 15.1689 10.336 15.016C10.3355 14.787 10.3624 14.5587 10.416 14.336L10.448 14.176L10.896 12.576L10.936 12.424L11 12.144C11.024 12.008 11.04 11.88 11.04 11.768C11.04 11.56 11 11.424 10.912 11.344C10.832 11.264 10.672 11.224 10.432 11.224C10.312 11.224 10.192 11.24 10.072 11.272L9.856 11.352L9.76 11.376L9.88 10.888L10.168 10.776C10.36 10.696 10.544 10.632 10.728 10.576C11 10.496 11.256 10.448 11.496 10.448ZM12.496 7.536C12.792 7.536 13.04 7.632 13.248 7.832C13.464 8.024 13.568 8.264 13.568 8.544C13.568 8.816 13.464 9.056 13.248 9.248C13.04 9.44 12.792 9.544 12.496 9.544C12.2 9.544 11.944 9.44 11.736 9.248C11.52 9.056 11.416 8.816 11.416 8.544C11.416 8.264 11.52 8.024 11.736 7.824C11.944 7.632 12.2 7.536 12.496 7.536Z"/></svg>
2
+ `;
3
+ export {
4
+ C as default
5
+ };