@useinsider/guido 3.12.0-beta.00cbb4c → 3.12.0-beta.19da4fb

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 (96) hide show
  1. package/dist/@types/config/schemas.js +9 -4
  2. package/dist/components/Guido.vue.js +8 -8
  3. package/dist/components/Guido.vue2.js +58 -56
  4. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue.js +1 -1
  5. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue2.js +8 -8
  6. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue.js +5 -5
  7. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue2.js +10 -10
  8. package/dist/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue.js +38 -0
  9. package/dist/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue2.js +271 -0
  10. package/dist/components/organisms/extensions/recommendation/StrategyInfoBox.vue.js +20 -0
  11. package/dist/components/organisms/extensions/recommendation/StrategyInfoBox.vue2.js +16 -0
  12. package/dist/components/organisms/extensions/recommendation/StrategyPicker.vue.js +20 -0
  13. package/dist/components/organisms/extensions/recommendation/StrategyPicker.vue2.js +80 -0
  14. package/dist/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue.js +18 -0
  15. package/dist/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue2.js +42 -0
  16. package/dist/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue.js +20 -0
  17. package/dist/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue2.js +18 -0
  18. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue.js +20 -0
  19. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue2.js +60 -0
  20. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue.js +19 -0
  21. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue2.js +19 -0
  22. package/dist/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue.js +22 -0
  23. package/dist/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue2.js +29 -0
  24. package/dist/components/organisms/header/ViewOptions.vue.js +3 -3
  25. package/dist/components/organisms/header/version-history/ViewOptions.vue.js +1 -1
  26. package/dist/composables/usePreviewInteractionGuard.js +36 -11
  27. package/dist/composables/useRecommendation.js +82 -51
  28. package/dist/composables/useRecommendationPreview.js +61 -60
  29. package/dist/composables/useStrategyFilters.js +16 -0
  30. package/dist/config/migrator/recommendationMigrator.js +7 -4
  31. package/dist/enums/date.js +4 -3
  32. package/dist/enums/extensions/filteringV2.js +1024 -0
  33. package/dist/enums/extensions/recommendationBlock.js +43 -29
  34. package/dist/enums/extensions/strategyDetail.js +148 -0
  35. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  36. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +12 -11
  37. package/dist/extensions/Blocks/Recommendation/controls/blockBackground/index.js +10 -0
  38. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +137 -119
  39. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +57 -0
  40. package/dist/extensions/Blocks/Recommendation/extension.js +23 -21
  41. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +41 -40
  42. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +392 -112
  43. package/dist/extensions/Blocks/Recommendation/utils/strategyHumanizer.js +97 -0
  44. package/dist/extensions/Blocks/Recommendation/utils/strategyInfoBox.js +24 -0
  45. package/dist/extensions/Blocks/Recommendation/utils/strategyNavigation.js +8 -0
  46. package/dist/extensions/Blocks/Recommendation/utils/strategySummary.js +95 -0
  47. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +24 -10
  48. package/dist/extensions/Blocks/Unsubscribe/utils/constants.js +3 -2
  49. package/dist/extensions/Blocks/controlFactories.js +125 -75
  50. package/dist/guido.css +1 -1
  51. package/dist/node_modules/@vueuse/core/index.js +48 -0
  52. package/dist/node_modules/@vueuse/shared/index.js +57 -27
  53. package/dist/services/recommendationApi.js +86 -29
  54. package/dist/services/stripoApi.js +18 -18
  55. package/dist/src/@types/config/schemas.d.ts +10 -0
  56. package/dist/src/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue.d.ts +2 -0
  57. package/dist/src/components/organisms/extensions/recommendation/StrategyInfoBox.vue.d.ts +26 -0
  58. package/dist/src/components/organisms/extensions/recommendation/StrategyPicker.vue.d.ts +2 -0
  59. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue.d.ts +43 -0
  60. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue.d.ts +29 -0
  61. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue.d.ts +40 -0
  62. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue.d.ts +38 -0
  63. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue.d.ts +51 -0
  64. package/dist/src/composables/useConfig.d.ts +2 -0
  65. package/dist/src/composables/usePreviewInteractionGuard.d.ts +1 -1
  66. package/dist/src/composables/useRecommendation.d.ts +2 -0
  67. package/dist/src/composables/useStrategyFilters.d.ts +24 -0
  68. package/dist/src/enums/extensions/filteringV2.d.ts +72 -0
  69. package/dist/src/enums/extensions/recommendationBlock.d.ts +21 -0
  70. package/dist/src/enums/extensions/strategyDetail.d.ts +90 -0
  71. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +1 -0
  72. package/dist/src/extensions/Blocks/Recommendation/controls/blockBackground/index.d.ts +14 -0
  73. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +1 -0
  74. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +10 -4
  75. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +16 -0
  76. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +894 -1
  77. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +7 -0
  78. package/dist/src/extensions/Blocks/Recommendation/utils/strategyHumanizer.d.ts +50 -0
  79. package/dist/src/extensions/Blocks/Recommendation/utils/strategyInfoBox.d.ts +33 -0
  80. package/dist/src/extensions/Blocks/Recommendation/utils/strategyNavigation.d.ts +10 -0
  81. package/dist/src/extensions/Blocks/Recommendation/utils/strategySummary.d.ts +34 -0
  82. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +8 -1
  83. package/dist/src/extensions/Blocks/Unsubscribe/utils/constants.d.ts +1 -0
  84. package/dist/src/extensions/Blocks/controlFactories.d.ts +36 -0
  85. package/dist/src/mock/api/recommendation-strategies.d.ts +2 -0
  86. package/dist/src/services/recommendationApi.d.ts +8 -1
  87. package/dist/src/stores/config.d.ts +18 -0
  88. package/dist/src/utils/genericUtil.d.ts +9 -0
  89. package/dist/src/utils/urlSchemes.d.ts +6 -0
  90. package/dist/static/styles/base.css.js +25 -0
  91. package/dist/static/styles/components/base-input.css.js +6 -7
  92. package/dist/utils/dateUtil.js +23 -10
  93. package/dist/utils/genericUtil.js +10 -1
  94. package/dist/utils/templatePreparation.js +70 -57
  95. package/dist/utils/urlSchemes.js +4 -0
  96. package/package.json +2 -2
@@ -54,6 +54,13 @@ export interface RecommendationNodeConfig {
54
54
  * @example 'mostPopular', 'complementaryItems', 'manualMerchandising'
55
55
  */
56
56
  strategy: string;
57
+ /**
58
+ * Reusable Recommendation Strategy (RRS) id from Smart Recommender.
59
+ * Empty string means no strategy is assigned.
60
+ * Only used when the `reusableRecommendationStrategy` feature is enabled;
61
+ * when set, it supersedes `strategy` for building the recommendation URL.
62
+ */
63
+ strategyId: string;
57
64
  /**
58
65
  * Product IDs for manual merchandising strategy
59
66
  * Only used when strategy === 'manualMerchandising'
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Renders one RRS filter condition as readable text for the detail drawer.
3
+ *
4
+ * Ported from design-system-organisms
5
+ * `src/organisms/OrgStrategyDetailModal/helpers/humanizer.helper.ts` (branch `develop`).
6
+ *
7
+ * Two deliberate deviations from the source:
8
+ *
9
+ * 1. `trans` is injected rather than read from a module-level `translations` object,
10
+ * so this stays pure and testable and picks up Guido's editor-config translations.
11
+ * 2. The source wraps every date in `getUnixTimestampFromDate(...)` before formatting.
12
+ * Guido's `formatShortDate` already accepts a raw date string via
13
+ * `convertToDateObject`, so that conversion hop is dropped rather than reimplemented.
14
+ *
15
+ * This humanizes the same JSON as `strategySummary.ts` but for a different surface —
16
+ * see the note in `@@/Enums/extensions/strategyDetail` on why the operator tables differ.
17
+ */
18
+ import type { RrsDetailCondition } from '@@/Types/recommendation';
19
+ /** Translation lookup, matching `useTranslations()`. */
20
+ type TranslateFn = (key: string, params?: Record<string, string | number | boolean>) => string;
21
+ export interface TimeUnitOption {
22
+ text: string;
23
+ value: string;
24
+ }
25
+ export declare const capitalizeFirstLetter: (text: string) => string;
26
+ export declare const getUserEventLabel: (userEvent: string, trans: TranslateFn) => string;
27
+ /** Describes a user-attribute or user-event condition, e.g. "Users' last viewed 3 items". */
28
+ export declare const getUserContextValue: (condition: RrsDetailCondition, trans: TranslateFn) => string;
29
+ /** Renders a `7d`-style duration as " in last 7 days", or '' when absent or malformed. */
30
+ export declare const getDurationSuffix: (duration: string | undefined, timeUnitOptions: TimeUnitOption[], trans: TranslateFn) => string;
31
+ export interface SuitableValueParams {
32
+ condition: RrsDetailCondition;
33
+ selectedOperator: string;
34
+ filterValue: string;
35
+ timeUnit: string;
36
+ inBetweenValues: string[];
37
+ timeUnitOptions: TimeUnitOption[];
38
+ isDateAttribute: () => boolean;
39
+ isMultiSelect: () => boolean;
40
+ isInOrNotInOperator: () => boolean;
41
+ trans: TranslateFn;
42
+ }
43
+ export declare const bracketJoin: (items: string[], and: string) => string;
44
+ /**
45
+ * A multi-select condition stores its values either as a JSON array or, on older
46
+ * strategies, as a bare comma-separated string. Both must humanize the same way.
47
+ */
48
+ export declare const parseMultiSelectValue: (value: string) => string[];
49
+ export declare function getSuitableValue(params: SuitableValueParams): string;
50
+ export {};
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Builds the `infoBoxProps` an `InSelect` option needs to show the strategy hover
3
+ * card — the frame around `StrategyInfoBox.vue`'s four rows.
4
+ *
5
+ * Port of discovery-strategies-fe `src/utils/strategyInfoBox.js`, kept to the same
6
+ * field names so the two stay diff-able. The `view-more` button carries the
7
+ * strategy id as its `value`, which `InSelect` hands back on `infoBoxButtonClick`.
8
+ */
9
+ import type { RrsStrategy } from '@@/Types/recommendation';
10
+ /** Translation lookup, matching `useTranslations().trans`. */
11
+ type TranslateFn = (key: string, params?: Record<string, string | number | boolean>) => string;
12
+ export declare const VIEW_MORE_BUTTON_ID = "view-more";
13
+ export interface StrategyInfoBoxProps {
14
+ id: string;
15
+ theme: string;
16
+ variant: string;
17
+ titleText: string;
18
+ descriptionText: string;
19
+ buttonOptions: {
20
+ id: string;
21
+ type: string;
22
+ text: string;
23
+ value: string;
24
+ status: string;
25
+ icon: string;
26
+ }[];
27
+ slot: unknown;
28
+ slotProps: {
29
+ strategy: RrsStrategy;
30
+ };
31
+ }
32
+ export declare const buildStrategyInfoBoxProps: (strategy: RrsStrategy, trans: TranslateFn, slot: unknown) => StrategyInfoBoxProps;
33
+ export {};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Smart Recommender pages live outside the editor. The task spec is explicit that
3
+ * Create and Edit open in the *same* tab and that the flow finishes over there —
4
+ * there is no return trip, so nothing is stashed before leaving.
5
+ *
6
+ * ponytail: a full page load, not a router push. Guido has no router and is
7
+ * embedded in inone; whatever unload handling the host registers still applies.
8
+ */
9
+ export declare const navigateToStrategyPage: (path: string) => void;
10
+ export declare const buildStrategyEditPath: (id: string | number) => string;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Human-readable summary of a Reusable Recommendation Strategy (RRS).
3
+ *
4
+ * Ported from discovery-strategies-fe
5
+ * `src/components/atoms/GptAppBuilder/StrategyInfoBoxContent.composition.vue`.
6
+ *
7
+ * The source is a Vue SFC whose template is fourteen lines of plain `<div>`s over
8
+ * a `{ title, value }[]` computed. Only the computed carries meaning, so the port
9
+ * is a pure function instead: the panel renders it as an HTML string and the
10
+ * detail drawer renders it as components, both from this one implementation.
11
+ *
12
+ * Produces four rows — Algorithms and Method, Exclusions, Filters, Manual
13
+ * Merchandising — each truncated with "and N more." and falling back to
14
+ * `recommendation-strategy.none` when empty.
15
+ *
16
+ * `trans` is injected rather than pulled from `useTranslations()` so this stays a
17
+ * pure function: no composable lifetime to reason about, and tests need no config.
18
+ */
19
+ import type { RrsStrategy, RrsSummaryRow } from '@@/Types/recommendation';
20
+ /** Translation lookup, matching `useTranslations().trans`. */
21
+ type TranslateFn = (key: string, params?: Record<string, string | number | boolean>) => string;
22
+ /**
23
+ * `TOP_SELLERS` -> `recommendation-algorithm-names.top-sellers`.
24
+ * A pure string transform — Smart Recommender has no algorithm lookup table.
25
+ */
26
+ export declare const getAlgorithmTranslationKey: (algorithm: string) => string;
27
+ export declare const getMethodTranslationKey: (algorithm: string) => string;
28
+ /**
29
+ * Builds the four-row summary shown beside the strategy dropdown.
30
+ * @param strategy - A strategy row as returned by the RRS listing endpoint.
31
+ * @param trans - Translation lookup, e.g. `useTranslations().trans`.
32
+ */
33
+ export declare const buildStrategySummary: (strategy: RrsStrategy, trans: TranslateFn) => RrsSummaryRow[];
34
+ export {};
@@ -7,13 +7,20 @@ import type { Currency, Languages } from '@@/Types/recommendation';
7
7
  export interface ExtensionStoreSlice {
8
8
  languages: Languages;
9
9
  currencyList: Currency[];
10
+ /** Whether the `reusableRecommendationStrategy` feature is enabled. */
11
+ isRrsEnabled: boolean;
12
+ /** Ids of the strategies Smart Recommender currently offers for email. */
13
+ strategyIds: string[];
14
+ /** False while the strategy listing is unavailable, when nothing can be judged. */
15
+ canValidateStrategy: boolean;
10
16
  }
11
17
  export interface RequiredField {
12
18
  key: string;
13
19
  canBeEmpty?: boolean;
20
+ optional?: boolean;
14
21
  getValue: (config: PerBlockConfigs) => string;
15
22
  getAvailableOptions?: (store: ExtensionStoreSlice) => string[];
16
- condition?: (config: PerBlockConfigs) => boolean;
23
+ condition?: (config: PerBlockConfigs, store: ExtensionStoreSlice) => boolean;
17
24
  }
18
25
  export declare const REQUIRED_RECOMMENDATION_FIELDS: RequiredField[];
19
26
  export declare const RecommendationRequiredFieldsKey = "newsletter.recommendation-fill-required-fields";
@@ -9,3 +9,4 @@ export declare const DATA_ATTRIBUTES: {
9
9
  readonly BLOCK_ID: "data-unsubscribe-block-id";
10
10
  };
11
11
  export declare const UNSUBSCRIBE_BLOCK_SELECTOR = ".unsubscribe-block-v2";
12
+ export declare const UNSUBSCRIBE_LINK_SELECTOR = "a[data-unsubscribe-link=\"true\"], a.unsubscribe-link";
@@ -115,6 +115,42 @@ export declare function createTextBackgroundColorControl(controlId: string, targ
115
115
  isVisible(_node: ImmutableHtmlNode): boolean;
116
116
  };
117
117
  };
118
+ /**
119
+ * Factory for a background-color control that paints a whole extension block (SD-145082).
120
+ *
121
+ * Base is `ButtonBackgroundColorBuiltInControl` because its `GENERAL.BACKGROUND_COLOR` form key
122
+ * round-trips through `getValues()`; the block-text base (`TextBlockBackgroundBuiltInControl`, the
123
+ * built-in this replaces) reports `textBlockBackgroundColor`, which our read path never sees. That
124
+ * borrowed base targets the block's Buy buttons, so `getTargetNodes` here is a GUARD that keeps it
125
+ * off the buttons — not the write path.
126
+ *
127
+ * The write is entirely ours (`getAdditionalModifications`): Stripo's built-in emits a stylesheet
128
+ * rule that inline styles from regeneration outrank (SD-143023), so it would silently no-op on the
129
+ * next edit. We write inline `background-color` + `bgcolor` on the block `<td>` instead — surviving
130
+ * regeneration and reaching Outlook, matching the migrator's convention.
131
+ */
132
+ export declare function createBlockBackgroundColorControl(controlId: string, blockRootSelector?: string): {
133
+ new (): {
134
+ getId(): string;
135
+ /** The parent control renders its own generic title; restore the block-level wording. */
136
+ getLabels(): {
137
+ title: string;
138
+ };
139
+ /** Guard: redirect the borrowed button parent onto the block `<td>` so it never paints the Buy buttons. */
140
+ getTargetNodes(root: ImmutableHtmlNode): ImmutableHtmlNode[];
141
+ /**
142
+ * Writes the picked background onto the block as an inline style plus a `bgcolor`
143
+ * attribute. This is the whole mechanism, not a mirror: the parent control's own
144
+ * write never reaches the block element (verified at runtime). `bgcolor` is the
145
+ * channel Outlook honours on a `<td>`, and matches what the migrator emits.
146
+ */
147
+ getAdditionalModifications(root: ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
148
+ getParentControlId(): string;
149
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
150
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
151
+ isVisible(_node: ImmutableHtmlNode): boolean;
152
+ };
153
+ };
118
154
  /**
119
155
  * Factory function to create paddings controls for different block elements
120
156
  * When targetBlockId and containerSelector are not provided, applies directly to root
@@ -0,0 +1,2 @@
1
+ declare const _default: import("msw").HttpHandler[];
2
+ export default _default;
@@ -1,6 +1,13 @@
1
- import type { InitialResponse, FiltersResponse, RecommendationProduct, RecommendationProductsParams } from '@@/Types/recommendation';
1
+ import type { InitialResponse, FiltersResponse, RecommendationProduct, RecommendationProductsParams, RrsCampaign, RrsStrategy, RrsStrategyUrl } from '@@/Types/recommendation';
2
+ /** Translation lookup, matching `useTranslations()`. */
3
+ type TranslateFn = (key: string, params?: Record<string, string | number | boolean>) => string;
2
4
  export declare const useRecommendationApi: () => {
5
+ duplicateStrategy: (strategyId: string) => Promise<string | null>;
3
6
  fetchRecommendationCreateData: () => Promise<InitialResponse>;
4
7
  fetchRecommendationFilters: () => Promise<FiltersResponse>;
5
8
  fetchRecommendationProducts: (strategy: string, params: RecommendationProductsParams) => Promise<RecommendationProduct[]>;
9
+ fetchRecommendationStrategies: () => Promise<RrsStrategy[]>;
10
+ fetchStrategyCampaigns: (strategyId: string, trans: TranslateFn) => Promise<RrsCampaign[]>;
11
+ fetchStrategyRecommendationUrl: (strategyId: string) => Promise<RrsStrategyUrl | null>;
6
12
  };
13
+ export {};
@@ -101,6 +101,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
101
101
  modulesDisabled: boolean;
102
102
  liquidSyntax: boolean;
103
103
  autosave: boolean;
104
+ reusableRecommendationStrategy: boolean;
104
105
  };
105
106
  blocks: {
106
107
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -238,6 +239,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
238
239
  modulesDisabled: boolean;
239
240
  liquidSyntax: boolean;
240
241
  autosave: boolean;
242
+ reusableRecommendationStrategy: boolean;
241
243
  };
242
244
  blocks: {
243
245
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -375,6 +377,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
375
377
  modulesDisabled: boolean;
376
378
  liquidSyntax: boolean;
377
379
  autosave: boolean;
380
+ reusableRecommendationStrategy: boolean;
378
381
  };
379
382
  blocks: {
380
383
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -512,6 +515,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
512
515
  modulesDisabled: boolean;
513
516
  liquidSyntax: boolean;
514
517
  autosave: boolean;
518
+ reusableRecommendationStrategy: boolean;
515
519
  };
516
520
  blocks: {
517
521
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -649,6 +653,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
649
653
  modulesDisabled: boolean;
650
654
  liquidSyntax: boolean;
651
655
  autosave: boolean;
656
+ reusableRecommendationStrategy: boolean;
652
657
  };
653
658
  blocks: {
654
659
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -786,6 +791,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
786
791
  modulesDisabled: boolean;
787
792
  liquidSyntax: boolean;
788
793
  autosave: boolean;
794
+ reusableRecommendationStrategy: boolean;
789
795
  };
790
796
  blocks: {
791
797
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -923,6 +929,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
923
929
  modulesDisabled: boolean;
924
930
  liquidSyntax: boolean;
925
931
  autosave: boolean;
932
+ reusableRecommendationStrategy: boolean;
926
933
  };
927
934
  blocks: {
928
935
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1060,6 +1067,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1060
1067
  modulesDisabled: boolean;
1061
1068
  liquidSyntax: boolean;
1062
1069
  autosave: boolean;
1070
+ reusableRecommendationStrategy: boolean;
1063
1071
  };
1064
1072
  blocks: {
1065
1073
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1197,6 +1205,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1197
1205
  modulesDisabled: boolean;
1198
1206
  liquidSyntax: boolean;
1199
1207
  autosave: boolean;
1208
+ reusableRecommendationStrategy: boolean;
1200
1209
  };
1201
1210
  blocks: {
1202
1211
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1334,6 +1343,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1334
1343
  modulesDisabled: boolean;
1335
1344
  liquidSyntax: boolean;
1336
1345
  autosave: boolean;
1346
+ reusableRecommendationStrategy: boolean;
1337
1347
  };
1338
1348
  blocks: {
1339
1349
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1471,6 +1481,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1471
1481
  modulesDisabled: boolean;
1472
1482
  liquidSyntax: boolean;
1473
1483
  autosave: boolean;
1484
+ reusableRecommendationStrategy: boolean;
1474
1485
  };
1475
1486
  blocks: {
1476
1487
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1608,6 +1619,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1608
1619
  modulesDisabled: boolean;
1609
1620
  liquidSyntax: boolean;
1610
1621
  autosave: boolean;
1622
+ reusableRecommendationStrategy: boolean;
1611
1623
  };
1612
1624
  blocks: {
1613
1625
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1745,6 +1757,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1745
1757
  modulesDisabled: boolean;
1746
1758
  liquidSyntax: boolean;
1747
1759
  autosave: boolean;
1760
+ reusableRecommendationStrategy: boolean;
1748
1761
  };
1749
1762
  blocks: {
1750
1763
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1882,6 +1895,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1882
1895
  modulesDisabled: boolean;
1883
1896
  liquidSyntax: boolean;
1884
1897
  autosave: boolean;
1898
+ reusableRecommendationStrategy: boolean;
1885
1899
  };
1886
1900
  blocks: {
1887
1901
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2019,6 +2033,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2019
2033
  modulesDisabled: boolean;
2020
2034
  liquidSyntax: boolean;
2021
2035
  autosave: boolean;
2036
+ reusableRecommendationStrategy: boolean;
2022
2037
  };
2023
2038
  blocks: {
2024
2039
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2156,6 +2171,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2156
2171
  modulesDisabled: boolean;
2157
2172
  liquidSyntax: boolean;
2158
2173
  autosave: boolean;
2174
+ reusableRecommendationStrategy: boolean;
2159
2175
  };
2160
2176
  blocks: {
2161
2177
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2293,6 +2309,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2293
2309
  modulesDisabled: boolean;
2294
2310
  liquidSyntax: boolean;
2295
2311
  autosave: boolean;
2312
+ reusableRecommendationStrategy: boolean;
2296
2313
  };
2297
2314
  blocks: {
2298
2315
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2430,6 +2447,7 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2430
2447
  modulesDisabled: boolean;
2431
2448
  liquidSyntax: boolean;
2432
2449
  autosave: boolean;
2450
+ reusableRecommendationStrategy: boolean;
2433
2451
  };
2434
2452
  blocks: {
2435
2453
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -25,3 +25,12 @@ 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
+ * Parses a JSON-string field, yielding `fallback` on absent or malformed input.
30
+ *
31
+ * Smart Recommender returns several strategy fields (`filters`, `base_product`, ...)
32
+ * as embedded JSON strings, and every surface that reads them must degrade to a
33
+ * sensible empty value rather than break. Shared so a hardening change (rejecting
34
+ * non-object payloads, logging a malformed blob) lands on all of them at once.
35
+ */
36
+ export declare const parseJsonField: <T>(raw: string | undefined, fallback: T) => T;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * URL schemes that are safe to follow from partner-authored email HTML. Shared so a
3
+ * future bypass fix lands in one place — the preview guard and the recommendation
4
+ * sanitizer both gate on it. Stored with the trailing colon to match `anchor.protocol`.
5
+ */
6
+ export declare const BASE_SAFE_URL_SCHEMES: readonly string[];
@@ -10,6 +10,31 @@ ue-stripe-thumb,
10
10
  height: 100% !important;
11
11
  min-height: 0 !important;
12
12
  }
13
+
14
+ /*
15
+ * Recommendation Strategy control. The select itself is a design-system InSelect
16
+ * rendered at app level and positioned over \`.strategy-control__anchor\`; only the
17
+ * anchor's box matters here, since it is what the overlay measures.
18
+ */
19
+ .strategy-control__select-row {
20
+ display: flex;
21
+ align-items: center;
22
+ gap: 8px;
23
+ }
24
+
25
+ .strategy-control__anchor {
26
+ flex: 1;
27
+ min-width: 0;
28
+ height: 32px;
29
+ }
30
+
31
+ .strategy-control__create-row {
32
+ margin-top: 8px;
33
+ }
34
+
35
+ .strategy-control__create-row .button {
36
+ width: 100%;
37
+ }
13
38
  `;
14
39
  export {
15
40
  n as default
@@ -3,8 +3,12 @@ const n = `.base-input {
3
3
  border-radius: 4px;
4
4
  background-color: var(--guido-color-gray-0);
5
5
  align-items: center;
6
- height: 40px;
7
- max-height: 40px;
6
+ /* 40px is a floor, never a cap: \`.base-input\` is shared by every settings field,
7
+ including Stripo's own auto-height inputs (e.g. the link field) that expand on
8
+ focus. A fixed max-height clamped their inner box; keep 40px only as the floor. */
9
+ min-height: 40px;
10
+ height: auto;
11
+ max-height: none;
8
12
  box-sizing: content-box;
9
13
  outline: none;
10
14
  resize: vertical;
@@ -36,11 +40,6 @@ ue-contenteditable .base-input {
36
40
  line-height: 24px;
37
41
  }
38
42
 
39
- /* Image link input is auto-height; lift the fixed 40px cap so it can grow. */
40
- ue-contenteditable.link-autoheight-contenteditable .base-input {
41
- max-height: none;
42
- }
43
-
44
43
  ue-icon-component.icon-button.icon-clear-input {
45
44
  right: 2px;
46
45
  top: 2px;
@@ -1,10 +1,19 @@
1
- import { DEFAULT_LOCALE as r, DEFAULT as o } from "../enums/date.js";
2
- const c = () => {
1
+ import { DEFAULT_LOCALE as r, DEFAULT as a, DEFAULT_TZ as c } from "../enums/date.js";
2
+ const m = (t) => t ? t instanceof Date ? t : typeof t == "string" ? t.length === 10 && /^\d{10}$/.test(t) ? new Date(Number(t) * 1e3) : new Date(t) : typeof t == "number" ? t.toString().length === 10 ? new Date(t * 1e3) : new Date(t) : /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date(), s = () => {
3
3
  const t = window.l10n;
4
- return (t == null ? void 0 : t.locale) === o ? r : (t == null ? void 0 : t.locale) || r;
5
- };
6
- new Intl.DateTimeFormat().resolvedOptions().timeZone;
7
- const i = (t) => {
4
+ return (t == null ? void 0 : t.locale) === a ? r : (t == null ? void 0 : t.locale) || r;
5
+ }, u = new Intl.DateTimeFormat().resolvedOptions().timeZone, f = () => {
6
+ const t = window.l10n;
7
+ return (t == null ? void 0 : t.timezone) || u || c;
8
+ }, D = (t, e = s(), n = f()) => {
9
+ const o = {
10
+ year: "numeric",
11
+ month: "2-digit",
12
+ day: "2-digit",
13
+ timeZone: n
14
+ }, i = m(t);
15
+ return new Intl.DateTimeFormat(e, o).format(i);
16
+ }, T = (t) => {
8
17
  if (!t)
9
18
  return "";
10
19
  const e = new Date(t);
@@ -16,9 +25,13 @@ const i = (t) => {
16
25
  second: "2-digit",
17
26
  hour12: !1
18
27
  }).format(e);
19
- }, m = (t) => t ? Math.floor(Date.now() / 1e3) >= t : !1;
28
+ }, h = (t) => t ? Math.floor(Date.now() / 1e3) >= t : !1;
20
29
  export {
21
- i as formatPatchDate,
22
- m as isAfterDate,
23
- c as locale
30
+ m as convertToDateObject,
31
+ T as formatPatchDate,
32
+ D as formatShortDate,
33
+ h as isAfterDate,
34
+ u as localTz,
35
+ s as locale,
36
+ f as timezone
24
37
  };
@@ -58,11 +58,20 @@ const o = (l) => {
58
58
  }, i = () => {
59
59
  var l;
60
60
  return ((l = document.head.querySelector('meta[name="csrf-token"]')) == null ? void 0 : l.getAttribute("content")) ?? "";
61
+ }, k = (l, s) => {
62
+ if (!l)
63
+ return s;
64
+ try {
65
+ return JSON.parse(l);
66
+ } catch {
67
+ return s;
68
+ }
61
69
  };
62
70
  export {
63
71
  v as buildMergeTagEntries,
64
72
  f as dynamicContentToMergeTags,
65
73
  b as flattenDynamicContentList,
66
74
  i as getCsrfToken,
67
- o as mergeTagToDynamicContent
75
+ o as mergeTagToDynamicContent,
76
+ k as parseJsonField
68
77
  };