@designcrowd/library.brand-page 5.18.6 → 5.18.7-ast-padding-1

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 (218) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-DlYq1LAT.js → CreatedOnBrandCrowd-de-de-Cn8zNuGM.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-BssRIQqn.js → CreatedOnBrandCrowd-en-us-CP8xAFc-.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-BGF9pptH.js → CreatedOnBrandCrowd-es-es-Ds8NijwP.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-Caupqu2Z.js → CreatedOnBrandCrowd-fr-ca-BiVV_7lj.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-Caupqu2Z.js → CreatedOnBrandCrowd-fr-fr-BiVV_7lj.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-s9q4uJ4u.js → CreatedOnBrandCrowd-pt-br-CjjrYojo.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-s9q4uJ4u.js → CreatedOnBrandCrowd-pt-pt-CjjrYojo.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-UnYnZbA7.js → CreatedOnDcom-de-de-ChR5jvlP.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-rDv6H12L.js → CreatedOnDcom-en-us-C4rO1BMA.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-DKPLaT6o.js → CreatedOnDcom-es-es-DxGMS6JK.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-DAv34bG7.js → CreatedOnDcom-fr-ca-n31u4t7u.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-DsOHnS8q.js → CreatedOnDcom-fr-fr-gwdsX7YV.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-DpjDBCLj.js → CreatedOnDcom-pt-br-iZjFp--s.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-rFG0unf1.js → CreatedOnDcom-pt-pt-B3Z3-RLC.js} +1 -1
  15. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.d.ts +4 -0
  16. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +8 -4
  17. package/lib/src/brand-page/components/image-gallery/ImageGallery.mixin.d.ts +4 -0
  18. package/lib/src/brand-page/components/image-gallery/ImageGallery.mixin.js +4 -0
  19. package/lib/src/brand-page/components/mailing-list/MailingList.mixin.d.ts +4 -0
  20. package/lib/src/brand-page/components/mailing-list/MailingList.mixin.js +4 -1
  21. package/lib/src/brand-page/components/payment/request-payment/RequestPayment.mixin.d.ts +4 -0
  22. package/lib/src/brand-page/components/payment/request-payment/RequestPayment.mixin.js +4 -0
  23. package/lib/src/brand-page/components/payment/support-me/SupportMe.mixin.d.ts +4 -0
  24. package/lib/src/brand-page/components/payment/support-me/SupportMe.mixin.js +4 -1
  25. package/lib/src/brand-page/components/video/Video.mixin.d.ts +4 -0
  26. package/lib/src/brand-page/components/video/Video.mixin.js +4 -0
  27. package/lib/src/brand-page/constants/misc.constants.d.ts +26 -0
  28. package/lib/src/brand-page/constants/misc.constants.js +42 -0
  29. package/lib/src/brand-page/models/content.model.d.ts +2 -0
  30. package/lib/src/brand-page/utils/content-padding.util.d.ts +13 -0
  31. package/lib/src/brand-page/utils/content-padding.util.js +10 -0
  32. package/lib/src/brand-page/utils/index.d.ts +1 -0
  33. package/lib/src/brand-page/utils/index.js +1 -0
  34. package/lib/src/brand-page/utils/migration/migration-17.d.ts +2 -0
  35. package/lib/src/brand-page/utils/migration/migration-17.js +48 -0
  36. package/lib/src/brand-page/utils/migration.util.js +10 -0
  37. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +7 -2
  38. package/lib/src/brand-page-maker/components/admin-logo-settings/AdminLogoSettings.mixin.d.ts +2 -0
  39. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +19 -2
  40. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +8 -0
  41. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.mixin.d.ts +7 -2
  42. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +1 -0
  43. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +104 -2
  44. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.js +5 -0
  45. package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.d.ts +7 -2
  46. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +3 -0
  47. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.js +11 -0
  48. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +7 -2
  49. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +7 -2
  50. package/lib/src/brand-page-maker/components/social-share/SocialShare.mixin.d.ts +7 -2
  51. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +7 -2
  52. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +9 -0
  53. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +21 -1
  54. package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +20 -0
  55. package/lib/src/brand-page-maker/composables/useEventTracker.js +17 -0
  56. package/lib/src/brand-page-maker/composables/useUndoRedoFlag.d.ts +17 -0
  57. package/lib/src/brand-page-maker/composables/useUndoRedoFlag.js +25 -0
  58. package/lib/src/brand-page-maker/constants/config-settings-constant.js +38 -0
  59. package/lib/src/brand-page-maker/convertors/about-display-style.convertor.js +9 -1
  60. package/lib/src/brand-page-maker/convertors/banner-align-display-style.convertor.js +7 -1
  61. package/lib/src/brand-page-maker/convertors/contact-layout-style.convertor.js +6 -1
  62. package/lib/src/brand-page-maker/models/content-settings.js +36 -2
  63. package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +12 -0
  64. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.d.ts +41 -2
  65. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +307 -5
  66. package/lib/src/brand-page-maker/stores/history/history-store.d.ts +86 -0
  67. package/lib/src/brand-page-maker/stores/history/history-store.js +501 -0
  68. package/lib/src/brand-page-maker/stores/logo-store/logo-store.d.ts +1 -0
  69. package/lib/src/brand-page-maker/stores/user/user-store.js +1 -0
  70. package/lib/src/bundled-translations-BUyt4scr.js +175 -0
  71. package/lib/src/bundled-translations.de-DE-DEw_OVai.js +175 -0
  72. package/lib/src/bundled-translations.es-ES-CFKWmkgY.js +175 -0
  73. package/lib/src/bundled-translations.fr-CA-DCl5LJS4.js +175 -0
  74. package/lib/src/bundled-translations.fr-FR-B5BjgllA.js +175 -0
  75. package/lib/src/bundled-translations.pt-BR-5PWpMPuM.js +175 -0
  76. package/lib/src/bundled-translations.pt-PT-B9_wN0HR.js +175 -0
  77. package/lib/src/bundles/bundled-translations.de-DE.json +5 -1
  78. package/lib/src/bundles/bundled-translations.es-ES.json +5 -1
  79. package/lib/src/bundles/bundled-translations.fr-CA.json +5 -1
  80. package/lib/src/bundles/bundled-translations.fr-FR.json +5 -1
  81. package/lib/src/bundles/bundled-translations.json +5 -1
  82. package/lib/src/bundles/bundled-translations.pt-BR.json +5 -1
  83. package/lib/src/bundles/bundled-translations.pt-PT.json +5 -1
  84. package/lib/src/constants/ui.d.ts +1 -0
  85. package/lib/src/css/library.brand-page-brandCrowd.min.css +128 -131
  86. package/lib/src/css/library.brand-page-brandPage.min.css +128 -132
  87. package/lib/src/css/library.brand-page-designCom.min.css +128 -131
  88. package/lib/src/css/library.brand-page-preview.min.css +128 -131
  89. package/lib/src/index.mjs +1 -1
  90. package/lib/src/{lib-ngdDRkSL.js → lib-CIj_C38s.js} +36378 -35591
  91. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +104 -2
  92. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.js +5 -0
  93. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.stories.js +1 -0
  94. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +37 -2
  95. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +28 -0
  96. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +19 -2
  97. package/lib/src/shared/content-settings/components/draggable-item-list/DraggableItemList.mixin.d.ts +3 -0
  98. package/lib/src/shared/content-settings/components/draggable-item-list/DraggableItemList.mixin.js +19 -2
  99. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.d.ts +12 -0
  100. package/lib/src/shared/content-settings/components/form-fields-field/FormFieldsField.mixin.js +18 -8
  101. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +67 -0
  102. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.js +6 -0
  103. package/lib/src/shared/content-settings/components/slider-field/SliderField.mixin.js +9 -0
  104. package/lib/src/shared/content-settings/store/content-settings-store.d.ts +12 -0
  105. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +19 -0
  106. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.js +11 -0
  107. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +10 -2
  108. package/lib/src/shared/logo-settings/store/editable-logo-store.d.ts +5 -1
  109. package/lib/src/shared/logo-settings/store/editable-logo-store.js +31 -0
  110. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +40 -2
  111. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +24 -2
  112. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +23 -2
  113. package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +16 -0
  114. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +23 -2
  115. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +16 -0
  116. package/lib/src/utils/minify-brand-page-metadata.js +11 -1
  117. package/package.json +1 -1
  118. package/src/brand-page/components/about-section/AboutSection.vue +10 -4
  119. package/src/brand-page/components/banner-section/BannerSection.vue +11 -4
  120. package/src/brand-page/components/booking-section/BookingSection.vue +11 -3
  121. package/src/brand-page/components/contact-form/ContactForm.mixin.ts +12 -6
  122. package/src/brand-page/components/contact-form/ContactForm.vue +105 -104
  123. package/src/brand-page/components/image-gallery/ImageGallery.mixin.ts +5 -0
  124. package/src/brand-page/components/image-gallery/ImageGallery.vue +1 -1
  125. package/src/brand-page/components/mailing-list/MailingList.mixin.ts +8 -1
  126. package/src/brand-page/components/mailing-list/MailingList.vue +1 -1
  127. package/src/brand-page/components/map/Map.vue +5 -1
  128. package/src/brand-page/components/payment/request-payment/RequestPayment.mixin.ts +5 -0
  129. package/src/brand-page/components/payment/request-payment/RequestPayment.vue +1 -0
  130. package/src/brand-page/components/payment/support-me/SupportMe.mixin.ts +8 -1
  131. package/src/brand-page/components/payment/support-me/SupportMe.vue +1 -0
  132. package/src/brand-page/components/shop/Shop.vue +7 -2
  133. package/src/brand-page/components/social-links/SocialLinks.vue +7 -3
  134. package/src/brand-page/components/text/Text.vue +12 -5
  135. package/src/brand-page/components/text-media-section/TextMediaSection.vue +11 -3
  136. package/src/brand-page/components/video/Video.mixin.ts +5 -0
  137. package/src/brand-page/components/video/Video.vue +1 -1
  138. package/src/brand-page/constants/misc.constants.ts +48 -0
  139. package/src/brand-page/models/content.model.ts +2 -0
  140. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +9 -3
  141. package/src/brand-page/utils/content-padding.util.test.ts +29 -0
  142. package/src/brand-page/utils/content-padding.util.ts +13 -0
  143. package/src/brand-page/utils/index.ts +1 -0
  144. package/src/brand-page/utils/migration/migration-17.ts +65 -0
  145. package/src/brand-page/utils/migration.util.test.ts +195 -0
  146. package/src/brand-page/utils/migration.util.ts +11 -0
  147. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +9 -0
  148. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.vue +2 -3
  149. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +1 -0
  150. package/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.ts +5 -0
  151. package/src/brand-page-maker/components/cookie-collection/CookieSettings.vue +18 -17
  152. package/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.ts +13 -0
  153. package/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.vue +26 -3
  154. package/src/brand-page-maker/components/quick-link-bar/RedoButton.vue +66 -0
  155. package/src/brand-page-maker/components/quick-link-bar/UndoButton.vue +66 -0
  156. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.de-DE.json +6 -4
  157. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.es-ES.json +6 -4
  158. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.fr-CA.json +6 -4
  159. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.fr-FR.json +6 -4
  160. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.json +3 -1
  161. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.pt-BR.json +6 -4
  162. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.pt-PT.json +6 -4
  163. package/src/brand-page-maker/components/quick-link-bar/undo-redo-buttons.test.ts +116 -0
  164. package/src/brand-page-maker/components/save-contact/SaveContact.vue +12 -19
  165. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +10 -0
  166. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +28 -1
  167. package/src/brand-page-maker/composables/useEventTracker.ts +29 -0
  168. package/src/brand-page-maker/composables/useUndoRedoFlag.ts +30 -0
  169. package/src/brand-page-maker/constants/config-settings-constant.ts +44 -0
  170. package/src/brand-page-maker/convertors/about-display-style.convertor.ts +10 -0
  171. package/src/brand-page-maker/convertors/banner-align-display-style.convertor.ts +8 -0
  172. package/src/brand-page-maker/convertors/contact-layout-style.convertor.test.ts +50 -0
  173. package/src/brand-page-maker/convertors/contact-layout-style.convertor.ts +8 -0
  174. package/src/brand-page-maker/models/content-settings.ts +38 -2
  175. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.test.ts +334 -0
  176. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +371 -4
  177. package/src/brand-page-maker/stores/history/history-store.test.ts +1120 -0
  178. package/src/brand-page-maker/stores/history/history-store.ts +666 -0
  179. package/src/brand-page-maker/stores/logo-store/logo-store.ts +5 -0
  180. package/src/brand-page-maker/stores/user/user-store.ts +1 -0
  181. package/src/bundles/bundled-translations.de-DE.json +5 -1
  182. package/src/bundles/bundled-translations.es-ES.json +5 -1
  183. package/src/bundles/bundled-translations.fr-CA.json +5 -1
  184. package/src/bundles/bundled-translations.fr-FR.json +5 -1
  185. package/src/bundles/bundled-translations.json +5 -1
  186. package/src/bundles/bundled-translations.pt-BR.json +5 -1
  187. package/src/bundles/bundled-translations.pt-PT.json +5 -1
  188. package/src/constants/ui.ts +1 -0
  189. package/src/shared/content-settings/components/content-settings/ContentSettings.mixin.ts +5 -0
  190. package/src/shared/content-settings/components/content-settings/ContentSettings.stories.ts +1 -0
  191. package/src/shared/content-settings/components/content-settings/i18n/content-settings.de-DE.json +2 -0
  192. package/src/shared/content-settings/components/content-settings/i18n/content-settings.es-ES.json +2 -0
  193. package/src/shared/content-settings/components/content-settings/i18n/content-settings.fr-CA.json +2 -0
  194. package/src/shared/content-settings/components/content-settings/i18n/content-settings.fr-FR.json +2 -0
  195. package/src/shared/content-settings/components/content-settings/i18n/content-settings.json +2 -0
  196. package/src/shared/content-settings/components/content-settings/i18n/content-settings.pt-BR.json +2 -0
  197. package/src/shared/content-settings/components/content-settings/i18n/content-settings.pt-PT.json +2 -0
  198. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.vue +1 -1
  199. package/src/shared/content-settings/components/draggable-item-list/DraggableItemList.mixin.ts +19 -2
  200. package/src/shared/content-settings/components/draggable-item-list/DraggableItemList.vue +1 -1
  201. package/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.vue +2 -2
  202. package/src/shared/content-settings/components/form-fields-field/FormFieldsField.mixin.ts +26 -10
  203. package/src/shared/content-settings/components/logo-selection-field/LogoSelection.vue +63 -48
  204. package/src/shared/content-settings/components/page-dropdown-field/PageDropdownField.vue +4 -0
  205. package/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.ts +7 -0
  206. package/src/shared/content-settings/components/slider-field/SliderField.mixin.ts +9 -0
  207. package/src/shared/content-settings/components/text-field/TextField.vue +1 -0
  208. package/src/shared/content-settings/components/text-image-field/TextImageField.vue +33 -4
  209. package/src/shared/logo-settings/components/EditLogoSettings.mixin.ts +11 -0
  210. package/src/shared/logo-settings/store/editable-logo-store.ts +42 -1
  211. package/src/utils/minify-brand-page-metadata.ts +11 -1
  212. package/lib/src/bundled-translations-B5Fkt3qd.js +0 -175
  213. package/lib/src/bundled-translations.de-DE-DpdVePM9.js +0 -175
  214. package/lib/src/bundled-translations.es-ES-BsIcrzZF.js +0 -175
  215. package/lib/src/bundled-translations.fr-CA-G1XCp2Qh.js +0 -175
  216. package/lib/src/bundled-translations.fr-FR-BdwLXOnB.js +0 -175
  217. package/lib/src/bundled-translations.pt-BR-Bvn5ePe7.js +0 -175
  218. package/lib/src/bundled-translations.pt-PT-CDtkb2k1.js +0 -175
@@ -0,0 +1,66 @@
1
+ <template>
2
+ <Tooltip
3
+ direction="top"
4
+ alignment="center"
5
+ :should-show-icon="false"
6
+ class="c-redo-button-tooltip"
7
+ >
8
+ <template #open>
9
+ <Icon
10
+ data-component="redo-button"
11
+ data-test-redo-button
12
+ name="redo"
13
+ size="md"
14
+ :aria-label="quickLinkBarTr('redoLabel')"
15
+ role="button"
16
+ tabindex="0"
17
+ :class="[
18
+ 'tw-p-2 tw-rounded tw-transition-colors',
19
+ isDisabled
20
+ ? 'tw-text-grayscale-500 tw-cursor-not-allowed tw-opacity-50'
21
+ : 'tw-text-grayscale-800 tw-cursor-pointer hover:tw-bg-grayscale-300',
22
+ ]"
23
+ @click.stop="onClick"
24
+ @keydown.enter.stop="onClick"
25
+ @keydown.space.stop.prevent="onClick"
26
+ />
27
+ </template>
28
+ <template #content>
29
+ <span class="tw-whitespace-nowrap tw-text-sm">
30
+ {{ quickLinkBarTr('redoLabel') }}
31
+ </span>
32
+ </template>
33
+ </Tooltip>
34
+ </template>
35
+
36
+ <script setup lang="ts">
37
+ import { computed } from 'vue';
38
+ import { Icon, Tooltip } from '@designcrowd/fe-shared-lib';
39
+ import { useHistoryStore } from '@brand-page-maker/stores/history/history-store';
40
+ import { useAiWebsiteBuilderThreadStore } from '@brand-page-maker/stores/ai-thread/ai-website-builder-thread-store';
41
+ import { useBrandPageContainerStore } from '@brand-page-maker/stores/brandpage-container';
42
+ import { quickLinkBarTr } from '@src/composables/useTranslate';
43
+
44
+ const historyStore = useHistoryStore();
45
+ const aiThreadStore = useAiWebsiteBuilderThreadStore();
46
+ const containerStore = useBrandPageContainerStore();
47
+
48
+ const isDisabled = computed(
49
+ () =>
50
+ !historyStore.canRedo ||
51
+ aiThreadStore.isProcessing ||
52
+ containerStore.isSaving,
53
+ );
54
+
55
+ const onClick = () => {
56
+ if (isDisabled.value) return;
57
+ historyStore.redo();
58
+ };
59
+ </script>
60
+
61
+ <style scoped>
62
+ .c-redo-button-tooltip :deep(.shared-tooltip__body) {
63
+ width: auto;
64
+ padding: 4px 8px;
65
+ }
66
+ </style>
@@ -0,0 +1,66 @@
1
+ <template>
2
+ <Tooltip
3
+ direction="top"
4
+ alignment="center"
5
+ :should-show-icon="false"
6
+ class="c-undo-button-tooltip"
7
+ >
8
+ <template #open>
9
+ <Icon
10
+ data-component="undo-button"
11
+ data-test-undo-button
12
+ name="undo"
13
+ size="md"
14
+ :aria-label="quickLinkBarTr('undoLabel')"
15
+ role="button"
16
+ tabindex="0"
17
+ :class="[
18
+ 'tw-p-2 tw-rounded tw-transition-colors',
19
+ isDisabled
20
+ ? 'tw-text-grayscale-500 tw-cursor-not-allowed tw-opacity-50'
21
+ : 'tw-text-grayscale-800 tw-cursor-pointer hover:tw-bg-grayscale-300',
22
+ ]"
23
+ @click.stop="onClick"
24
+ @keydown.enter.stop="onClick"
25
+ @keydown.space.stop.prevent="onClick"
26
+ />
27
+ </template>
28
+ <template #content>
29
+ <span class="tw-whitespace-nowrap tw-text-sm">
30
+ {{ quickLinkBarTr('undoLabel') }}
31
+ </span>
32
+ </template>
33
+ </Tooltip>
34
+ </template>
35
+
36
+ <script setup lang="ts">
37
+ import { computed } from 'vue';
38
+ import { Icon, Tooltip } from '@designcrowd/fe-shared-lib';
39
+ import { useHistoryStore } from '@brand-page-maker/stores/history/history-store';
40
+ import { useAiWebsiteBuilderThreadStore } from '@brand-page-maker/stores/ai-thread/ai-website-builder-thread-store';
41
+ import { useBrandPageContainerStore } from '@brand-page-maker/stores/brandpage-container';
42
+ import { quickLinkBarTr } from '@src/composables/useTranslate';
43
+
44
+ const historyStore = useHistoryStore();
45
+ const aiThreadStore = useAiWebsiteBuilderThreadStore();
46
+ const containerStore = useBrandPageContainerStore();
47
+
48
+ const isDisabled = computed(
49
+ () =>
50
+ !historyStore.canUndo ||
51
+ aiThreadStore.isProcessing ||
52
+ containerStore.isSaving,
53
+ );
54
+
55
+ const onClick = () => {
56
+ if (isDisabled.value) return;
57
+ historyStore.undo();
58
+ };
59
+ </script>
60
+
61
+ <style scoped>
62
+ .c-undo-button-tooltip :deep(.shared-tooltip__body) {
63
+ width: auto;
64
+ padding: 4px 8px;
65
+ }
66
+ </style>
@@ -1,7 +1,9 @@
1
1
  {
2
- "quickLinkBar" : {
3
- "myWebPageLabel" : "Meine Webseite:",
4
- "copyButton" : "Kopieren",
5
- "copiedButton" : "Kopiert!"
2
+ "quickLinkBar": {
3
+ "myWebPageLabel": "Meine Webseite:",
4
+ "copyButton": "Kopieren",
5
+ "copiedButton": "Kopiert!",
6
+ "undoLabel": "Rückgängig machen",
7
+ "redoLabel": "Wiederholen"
6
8
  }
7
9
  }
@@ -1,7 +1,9 @@
1
1
  {
2
- "quickLinkBar" : {
3
- "myWebPageLabel" : "Mi página web:",
4
- "copyButton" : "Copiar",
5
- "copiedButton" : "¡Copiado!"
2
+ "quickLinkBar": {
3
+ "myWebPageLabel": "Mi página web:",
4
+ "copyButton": "Copiar",
5
+ "copiedButton": "¡Copiado!",
6
+ "undoLabel": "Deshacer",
7
+ "redoLabel": "Rehacer"
6
8
  }
7
9
  }
@@ -1,7 +1,9 @@
1
1
  {
2
- "quickLinkBar" : {
3
- "myWebPageLabel" : "Ma page Web :",
4
- "copyButton" : "Copier",
5
- "copiedButton" : "Copié !"
2
+ "quickLinkBar": {
3
+ "myWebPageLabel": "Ma page Web :",
4
+ "copyButton": "Copier",
5
+ "copiedButton": "Copié !",
6
+ "undoLabel": "Annuler",
7
+ "redoLabel": "Rétablir"
6
8
  }
7
9
  }
@@ -1,7 +1,9 @@
1
1
  {
2
- "quickLinkBar" : {
3
- "myWebPageLabel" : "Ma page Web :",
4
- "copyButton" : "Copier",
5
- "copiedButton" : "Copié !"
2
+ "quickLinkBar": {
3
+ "myWebPageLabel": "Ma page Web :",
4
+ "copyButton": "Copier",
5
+ "copiedButton": "Copié !",
6
+ "undoLabel": "Annuler",
7
+ "redoLabel": "Rétablir"
6
8
  }
7
9
  }
@@ -2,6 +2,8 @@
2
2
  "quickLinkBar": {
3
3
  "myWebPageLabel": "My web page:",
4
4
  "copyButton": "Copy",
5
- "copiedButton": "Copied!"
5
+ "copiedButton": "Copied!",
6
+ "undoLabel": "Undo",
7
+ "redoLabel": "Redo"
6
8
  }
7
9
  }
@@ -1,7 +1,9 @@
1
1
  {
2
- "quickLinkBar" : {
3
- "myWebPageLabel" : "Minha página web:",
4
- "copyButton" : "Copiar",
5
- "copiedButton" : "Copiado!"
2
+ "quickLinkBar": {
3
+ "myWebPageLabel": "Minha página web:",
4
+ "copyButton": "Copiar",
5
+ "copiedButton": "Copiado!",
6
+ "undoLabel": "Desfazer",
7
+ "redoLabel": "Refazer"
6
8
  }
7
9
  }
@@ -1,7 +1,9 @@
1
1
  {
2
- "quickLinkBar" : {
3
- "myWebPageLabel" : "A minha página Web:",
4
- "copyButton" : "Copiar",
5
- "copiedButton" : "Copiado!"
2
+ "quickLinkBar": {
3
+ "myWebPageLabel": "A minha página Web:",
4
+ "copyButton": "Copiar",
5
+ "copiedButton": "Copiado!",
6
+ "undoLabel": "Desfazer",
7
+ "redoLabel": "Refazer"
6
8
  }
7
9
  }
@@ -0,0 +1,116 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { createPinia, setActivePinia } from 'pinia';
4
+ import UndoButton from './UndoButton.vue';
5
+ import RedoButton from './RedoButton.vue';
6
+
7
+ // These tests cover the click-dispatch contract for the undo/redo buttons:
8
+ // - Click → historyStore.undo()/redo() ONLY when enabled.
9
+ // - Disabled when any of three guards is true: canUndo/canRedo,
10
+ // aiThread.isProcessing, container.isSaving.
11
+ //
12
+ // Markup, class names, and i18n strings are intentionally not asserted —
13
+ // they're covered by e2e tests and the data-component / data-test-*
14
+ // conventions.
15
+
16
+ vi.mock('@src/composables/useTranslate', () => ({
17
+ quickLinkBarTr: (key: string) => key,
18
+ }));
19
+
20
+ // Minimal stubs for fe-shared-lib so wrapper.find('[data-test-…]') can
21
+ // match the icon element regardless of how the real components render.
22
+ vi.mock('@designcrowd/fe-shared-lib', () => ({
23
+ Icon: {
24
+ name: 'Icon',
25
+ inheritAttrs: true,
26
+ template: '<span v-bind="$attrs" @click="$emit(\'click\', $event)" />',
27
+ },
28
+ Tooltip: {
29
+ name: 'Tooltip',
30
+ template: '<div><slot name="open" /><slot name="content" /></div>',
31
+ },
32
+ }));
33
+
34
+ const historyStoreMock = {
35
+ canUndo: true,
36
+ canRedo: true,
37
+ undo: vi.fn(),
38
+ redo: vi.fn(),
39
+ };
40
+
41
+ vi.mock('@brand-page-maker/stores/history/history-store', () => ({
42
+ useHistoryStore: vi.fn(() => historyStoreMock),
43
+ }));
44
+
45
+ const aiStoreMock = { isProcessing: false };
46
+
47
+ vi.mock(
48
+ '@brand-page-maker/stores/ai-thread/ai-website-builder-thread-store',
49
+ () => ({
50
+ useAiWebsiteBuilderThreadStore: vi.fn(() => aiStoreMock),
51
+ }),
52
+ );
53
+
54
+ const containerStoreMock = { isSaving: false };
55
+
56
+ vi.mock('@brand-page-maker/stores/brandpage-container', () => ({
57
+ useBrandPageContainerStore: vi.fn(() => containerStoreMock),
58
+ }));
59
+
60
+ beforeEach(() => {
61
+ setActivePinia(createPinia());
62
+ historyStoreMock.canUndo = true;
63
+ historyStoreMock.canRedo = true;
64
+ historyStoreMock.undo = vi.fn();
65
+ historyStoreMock.redo = vi.fn();
66
+ aiStoreMock.isProcessing = false;
67
+ containerStoreMock.isSaving = false;
68
+ });
69
+
70
+ describe('UndoButton', () => {
71
+ it('dispatches historyStore.undo() when enabled', async () => {
72
+ const wrapper = mount(UndoButton);
73
+
74
+ await wrapper.find('[data-test-undo-button]').trigger('click');
75
+
76
+ expect(historyStoreMock.undo).toHaveBeenCalled();
77
+ });
78
+
79
+ it.each([
80
+ ['canUndo is false', () => (historyStoreMock.canUndo = false)],
81
+ ['AI is processing', () => (aiStoreMock.isProcessing = true)],
82
+ ['save is in flight', () => (containerStoreMock.isSaving = true)],
83
+ ])('does not dispatch when %s', async (_label, applyGuard) => {
84
+ applyGuard();
85
+
86
+ const wrapper = mount(UndoButton);
87
+
88
+ await wrapper.find('[data-test-undo-button]').trigger('click');
89
+
90
+ expect(historyStoreMock.undo).not.toHaveBeenCalled();
91
+ });
92
+ });
93
+
94
+ describe('RedoButton', () => {
95
+ it('dispatches historyStore.redo() when enabled', async () => {
96
+ const wrapper = mount(RedoButton);
97
+
98
+ await wrapper.find('[data-test-redo-button]').trigger('click');
99
+
100
+ expect(historyStoreMock.redo).toHaveBeenCalled();
101
+ });
102
+
103
+ it.each([
104
+ ['canRedo is false', () => (historyStoreMock.canRedo = false)],
105
+ ['AI is processing', () => (aiStoreMock.isProcessing = true)],
106
+ ['save is in flight', () => (containerStoreMock.isSaving = true)],
107
+ ])('does not dispatch when %s', async (_label, applyGuard) => {
108
+ applyGuard();
109
+
110
+ const wrapper = mount(RedoButton);
111
+
112
+ await wrapper.find('[data-test-redo-button]').trigger('click');
113
+
114
+ expect(historyStoreMock.redo).not.toHaveBeenCalled();
115
+ });
116
+ });
@@ -19,7 +19,7 @@
19
19
  </div>
20
20
  <Toggle
21
21
  id="save-contact-enabled"
22
- :model-value="data.isToggled"
22
+ :model-value="isSavingEnabled"
23
23
  color="primary"
24
24
  @update:model-value="onSavingEnabled()"
25
25
  />
@@ -27,7 +27,7 @@
27
27
  </div>
28
28
  <div class="tw-flex-none tw-mt-4">
29
29
  <div
30
- v-if="!isAdmin && !isPrePurchase && data.isToggled"
30
+ v-if="!isAdmin && !isPrePurchase && isSavingEnabled"
31
31
  class="tw-flex tw-items-center tw-justify-between tw-cursor-pointer tw-gap-1"
32
32
  @click="onHideBranding"
33
33
  >
@@ -62,7 +62,7 @@
62
62
  </div>
63
63
  <Toggle
64
64
  id="show-preview-cookie-banner"
65
- :model-value="data.hideBranding"
65
+ :model-value="hideBranding"
66
66
  color="primary"
67
67
  @update:model-value="onHideBranding()"
68
68
  />
@@ -78,7 +78,7 @@ import {
78
78
  useBrandPageContainerStore,
79
79
  } from '@brand-page-maker/stores';
80
80
  import { saveContactSettingsTr } from '@src/composables/useTranslate';
81
- import { computed, reactive } from 'vue';
81
+ import { computed } from 'vue';
82
82
  import {
83
83
  EVENTS,
84
84
  useEventTracker,
@@ -107,11 +107,6 @@ const showBrandCrowdLogo = computed(
107
107
  () => brandPageMakerStore.showBrandCrowdLogo,
108
108
  );
109
109
 
110
- const data = reactive({
111
- isToggled: isSavingEnabled.value,
112
- hideBranding: hideBranding.value,
113
- });
114
-
115
110
  const siteName = computed((): string => {
116
111
  return props.isDesignCom ? 'Design.com' : 'BrandCrowd';
117
112
  });
@@ -127,30 +122,28 @@ const hideBrandingTooltip = computed((): string => {
127
122
  });
128
123
 
129
124
  const onSavingEnabled = () => {
130
- data.isToggled = !data.isToggled;
131
- brandPageMakerStore.setSavingEnabled({
132
- isSavingEnabled: data.isToggled,
133
- });
125
+ const next = !isSavingEnabled.value;
126
+
127
+ brandPageMakerStore.setSavingEnabled({ isSavingEnabled: next });
134
128
  eventTracker.sendWebsiteEvent({
135
129
  eventName: EVENTS.WEBSITE_MAKER_DIGITAL_BUSINESS_CARDS_SAVE_CONTACT_TOGGLE,
136
130
  eventProps: {
137
131
  cta_location: UPGRADE_CTA_LOCATIONS.SAVE_CONTACT_SETTINGS,
138
- toggle_value: data.isToggled ? 'enabled' : 'disabled',
132
+ toggle_value: next ? 'enabled' : 'disabled',
139
133
  },
140
134
  });
141
135
  };
142
136
 
143
137
  const onHideBranding = async () => {
144
138
  if (!showBrandCrowdLogo.value) {
145
- data.hideBranding = !data.hideBranding;
146
- brandPageMakerStore.setHideBranding({
147
- hideBranding: data.hideBranding,
148
- });
139
+ const next = !hideBranding.value;
140
+
141
+ brandPageMakerStore.setHideBranding({ hideBranding: next });
149
142
  eventTracker.sendWebsiteEvent({
150
143
  eventName: EVENTS.WEBSITE_MAKER_DIGITAL_BUSINESS_CARDS_HIDE_POPUP,
151
144
  eventProps: {
152
145
  cta_location: UPGRADE_CTA_LOCATIONS.SAVE_CONTACT_SETTINGS,
153
- toggle_value: data.hideBranding ? 'enabled' : 'disabled',
146
+ toggle_value: next ? 'enabled' : 'disabled',
154
147
  },
155
148
  });
156
149
  } else {
@@ -29,6 +29,8 @@ import {
29
29
  useUiStore,
30
30
  } from '../../stores';
31
31
  import { useBrandPageMakerStore } from '../../stores';
32
+ import { useHistoryStore } from '../../stores/history/history-store';
33
+ import { useUndoRedoFlag } from '../useUndoRedoFlag';
32
34
  import { useBrandPageMakerUserStore } from '../../stores/user/user-store';
33
35
  import { useAiMetadataBuilder } from './useAiMetadataBuilder';
34
36
  import {
@@ -55,6 +57,8 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
55
57
  const aiThreadStore = useAiWebsiteBuilderThreadStore();
56
58
  const snapshotStore = useSnapshotStore();
57
59
  const brandPageMakerStore = useBrandPageMakerStore();
60
+ const historyStore = useHistoryStore();
61
+ const undoRedoEnabled = useUndoRedoFlag();
58
62
  const containerStore = useBrandPageContainerStore();
59
63
  const userStore = useBrandPageMakerUserStore();
60
64
  const uiStore = useUiStore();
@@ -441,6 +445,12 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
441
445
 
442
446
  await nextTick();
443
447
 
448
+ // One history entry per AI prompt (US-E1). `captureAiChangeset` cancels
449
+ // the auto-watcher's pending flush so the apply doesn't double-capture.
450
+ if (undoRedoEnabled.value) {
451
+ historyStore.captureAiChangeset();
452
+ }
453
+
444
454
  // Re-capture snapshot so subsequent messages reflect the updated page
445
455
  await snapshotStore.refreshSnapshotAsync();
446
456
 
@@ -47,6 +47,7 @@ import {
47
47
  backgroundColorConvertor,
48
48
  } from '@brand-page-maker/convertors';
49
49
  import { useUiContentSettingsStore } from '@shared/content-settings/store';
50
+ import { useHistoryStore } from '@brand-page-maker/stores/history/history-store';
50
51
  import { AllSelection } from 'prosemirror-state';
51
52
  import { MaxLength } from './extensions/MaxLength';
52
53
  import { AutoLink } from './extensions/AutoLink';
@@ -208,6 +209,12 @@ export const useRichTextEditor = (
208
209
  isHyperlinkPopupOpen: Ref<boolean>,
209
210
  selectedLinkTypeValue: Ref<HyperlinkType>,
210
211
  ): UseRichTextEditorReturn => {
212
+ const historyStore = useHistoryStore();
213
+
214
+ // TipTap rehydrates and writes back canonical HTML on mount — fold
215
+ // that into the current head instead of pushing a phantom entry.
216
+ historyStore.mergeNextMutation();
217
+
211
218
  const editor = new Editor({
212
219
  content: model.value ?? '',
213
220
  editable: !props.readOnly,
@@ -453,10 +460,23 @@ export const useRichTextEditor = (
453
460
  },
454
461
  );
455
462
 
463
+ let lastAppliedToken = historyStore.snapshotAppliedToken;
464
+
456
465
  watch(
457
466
  () => model.value,
458
467
  (newValue) => {
459
- if (updateRichTextEditor.value && editor.getHTML() !== newValue) {
468
+ // Re-seed only on external mutations: an undo/redo apply (detected via
469
+ // the history token) or an explicit override (reorder/template). Never
470
+ // on the editor's own edits — re-running setContent would reset the
471
+ // styles the user just applied (e.g. font size).
472
+ const appliedSinceLast =
473
+ historyStore.snapshotAppliedToken !== lastAppliedToken;
474
+
475
+ lastAppliedToken = historyStore.snapshotAppliedToken;
476
+
477
+ const isExternalMutation = updateRichTextEditor.value || appliedSinceLast;
478
+
479
+ if (isExternalMutation && editor.getHTML() !== newValue) {
460
480
  editor.commands.setContent(newValue, false);
461
481
  updateRichTextEditor.value = false;
462
482
  //text style type is not switching when reordering
@@ -487,6 +507,13 @@ export const useRichTextEditor = (
487
507
  });
488
508
 
489
509
  editor.on('selectionUpdate', () => {
510
+ // Skip while a history snapshot is being applied — `setContent` fires
511
+ // selectionUpdate before the editor has fully rehydrated, which causes
512
+ // toolbar buttons (alignment, letter-spacing) to flash through bogus
513
+ // active states before settling. The editor emits a final selectionUpdate
514
+ // after apply completes, which lands the correct values (BP-5506).
515
+ if (historyStore.isApplyingSnapshot) return;
516
+
490
517
  const textStyleAttributes = editor.getAttributes('textStyle');
491
518
  const highlightAttributes = editor.getAttributes('highlight');
492
519
  const focusedAlignment = editor.getAttributes(
@@ -70,6 +70,10 @@ export const EVENTS = {
70
70
  'website_maker_digital_business_cards_hide_popup',
71
71
  WEBSITE_MAKER_GETTY_VIDEO: 'website_maker_getty_video',
72
72
  WEBSITE_MAKER_SHIPPING_TOGGLE: 'website_maker_shipping_toggle',
73
+ WEBSITE_MAKER_UNDO: 'website_maker_undo',
74
+ WEBSITE_MAKER_REDO: 'website_maker_redo',
75
+ WEBSITE_MAKER_HISTORY_CAPPED: 'website_maker_history_capped',
76
+ WEBSITE_MAKER_HISTORY_OVERSIZED: 'website_maker_history_oversized',
73
77
  };
74
78
 
75
79
  export const SELECT_LOCATIONS = {
@@ -420,6 +424,31 @@ const useEventTracker = () => {
420
424
  is_enabled: isEnabled,
421
425
  });
422
426
  },
427
+ // Undo / Redo (BP-5506)
428
+ sendUndoEvent: (props: {
429
+ entries_count: number;
430
+ actor: string | undefined;
431
+ }) => {
432
+ _publishEvent(EVENTS.WEBSITE_MAKER_UNDO, props);
433
+ },
434
+ sendRedoEvent: (props: {
435
+ entries_count: number;
436
+ actor: string | undefined;
437
+ }) => {
438
+ _publishEvent(EVENTS.WEBSITE_MAKER_REDO, props);
439
+ },
440
+ sendHistoryCappedEvent: (props: {
441
+ entries_count: number;
442
+ size_bytes: number;
443
+ }) => {
444
+ _publishEvent(EVENTS.WEBSITE_MAKER_HISTORY_CAPPED, props);
445
+ },
446
+ sendHistoryOversizedEvent: (props: {
447
+ attempted_bytes: number;
448
+ source: string;
449
+ }) => {
450
+ _publishEvent(EVENTS.WEBSITE_MAKER_HISTORY_OVERSIZED, props);
451
+ },
423
452
  };
424
453
  };
425
454
 
@@ -0,0 +1,30 @@
1
+ import { computed, type ComputedRef } from 'vue';
2
+ import { useBrandPageMakerUserStore } from '@brand-page-maker/stores/user';
3
+ import { useBrandPageMakerStore } from '@brand-page-maker/stores/brandpage-maker/brandpage-maker-store';
4
+
5
+ /**
6
+ * Returns whether undo/redo is active for the current maker session. All
7
+ * three conditions must hold:
8
+ *
9
+ * 1. `userStore.features.canUseUndoRedo` is true. The flag is populated by
10
+ * the consumer (BrandCrowd.Net / web-renderer) via the
11
+ * `WebsiteDesignUserFeatures.CanUseUndoRedo` boolean on the ViewModel —
12
+ * same precedent as RichTextEditor / GettyVideos / AITextGenerator.
13
+ * 2. The maker is a brand-site (Website Maker). V1 scope is brand-site
14
+ * only; brand-page (single-page profile) and brand-contact (vCard) are
15
+ * out of scope.
16
+ * 3. The session is NOT in admin-maker mode (`userStore.isAdmin`). The
17
+ * admin maker uses the same components but a different editing flow
18
+ * (template authoring) where undo/redo is out of scope.
19
+ */
20
+ export function useUndoRedoFlag(): ComputedRef<boolean> {
21
+ const userStore = useBrandPageMakerUserStore();
22
+ const brandPageMakerStore = useBrandPageMakerStore();
23
+
24
+ return computed(
25
+ () =>
26
+ userStore.features.canUseUndoRedo &&
27
+ brandPageMakerStore.isBrandSite &&
28
+ !userStore.isAdmin,
29
+ );
30
+ }