@designcrowd/library.brand-page 6.1.1-json-prereq-4 → 6.1.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 (216) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-Dk-G3RbI.js → CreatedOnBrandCrowd-de-de-B5B5ThJ6.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-AVAWRjbj.js → CreatedOnBrandCrowd-en-us-a48CaORy.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-C5Xq4Jln.js → CreatedOnBrandCrowd-es-es-xpuZ317-.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-BQXjmcrr.js → CreatedOnBrandCrowd-fr-ca-V-iUMArW.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-BQXjmcrr.js → CreatedOnBrandCrowd-fr-fr-V-iUMArW.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-DRMRc1Zk.js → CreatedOnBrandCrowd-pt-br-DZ0NKSAy.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-DRMRc1Zk.js → CreatedOnBrandCrowd-pt-pt-DZ0NKSAy.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-Dd9-oQ34.js → CreatedOnDcom-de-de-_950cgGp.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-CnDfLzF4.js → CreatedOnDcom-en-us-DogKzH-g.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-bvqvI9gT.js → CreatedOnDcom-es-es-CmGM3IPX.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-BG7UGFe8.js → CreatedOnDcom-fr-ca-BonEDkWZ.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-xdz35O_B.js → CreatedOnDcom-fr-fr-DQHtKrd_.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-CLfHnCkk.js → CreatedOnDcom-pt-br-CtYJSBjs.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-Z7FY0BXt.js → CreatedOnDcom-pt-pt-DoO9r4lA.js} +1 -1
  15. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +7 -8
  16. package/lib/src/brand-page/components/mailing-list/MailingList.mixin.js +6 -4
  17. package/lib/src/brand-page/components/page-footer/PageFooter.mixin.d.ts +1 -0
  18. package/lib/src/brand-page/components/page-footer/PageFooter.mixin.js +4 -1
  19. package/lib/src/brand-page/components/payment/Payment.mixin.js +4 -3
  20. package/lib/src/brand-page/components/payment/__fixtures__/request-payment.d.ts +1 -0
  21. package/lib/src/brand-page/components/payment/__fixtures__/support-me.d.ts +1 -0
  22. package/lib/src/brand-page/components/payment/request-payment/RequestPayment.mixin.d.ts +1 -0
  23. package/lib/src/brand-page/components/payment/request-payment/RequestPayment.mixin.js +4 -1
  24. package/lib/src/brand-page/composables/consent.composable.d.ts +2 -0
  25. package/lib/src/brand-page/composables/consent.composable.js +9 -3
  26. package/lib/src/brand-page/models/brand-page-context.model.d.ts +7 -0
  27. package/lib/src/brand-page/models/index.d.ts +26 -34
  28. package/lib/src/brand-page/utils/context.util.d.ts +8 -0
  29. package/lib/src/brand-page/utils/context.util.js +8 -0
  30. package/lib/src/brand-page/utils/migration.util.js +0 -11
  31. package/lib/src/brand-page/utils/text.util.d.ts +1 -13
  32. package/lib/src/brand-page/utils/text.util.js +0 -64
  33. package/lib/src/brand-page/utils/vcard.util.js +4 -4
  34. package/lib/src/brand-page/utils/view-model.util.d.ts +0 -1
  35. package/lib/src/brand-page/utils/view-model.util.js +20 -68
  36. package/lib/src/brand-page/view-models.d.ts +3 -3
  37. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +1 -1
  38. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.js +51 -18
  39. package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.d.ts +1 -0
  40. package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.d.ts +9 -2
  41. package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.js +10 -6
  42. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.d.ts +20 -0
  43. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.js +44 -0
  44. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.d.ts +27 -0
  45. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.js +44 -0
  46. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +1 -1
  47. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +103 -24
  48. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.js +8 -0
  49. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +9 -3
  50. package/lib/src/brand-page-maker/stores/ui/ui-store.d.ts +1 -1
  51. package/lib/src/brand-page-maker/utils/index.d.ts +0 -2
  52. package/lib/src/brand-page-maker/utils/index.js +0 -2
  53. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +5 -36
  54. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +5 -65
  55. package/lib/src/brand-page-maker/validators/field.validator.js +2 -10
  56. package/lib/src/clients/models.d.ts +2 -0
  57. package/lib/src/css/library.brand-page-brandCrowd.min.css +99 -102
  58. package/lib/src/css/library.brand-page-brandPage.min.css +99 -102
  59. package/lib/src/css/library.brand-page-designCom.min.css +99 -102
  60. package/lib/src/css/library.brand-page-preview.min.css +99 -102
  61. package/lib/src/index.d.ts +1 -1
  62. package/lib/src/index.mjs +219 -221
  63. package/lib/src/{lib-uYwxz8qR.js → lib-DtalJKI3.js} +71861 -75344
  64. package/lib/src/lib.js +1 -2
  65. package/lib/src/metadata-builder/ai-metadata-executor.d.ts +1 -0
  66. package/lib/src/metadata-builder/ai-metadata-executor.js +19 -0
  67. package/lib/src/metadata-builder/builders/section-builders/about-section-builder.js +3 -2
  68. package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.js +3 -2
  69. package/lib/src/metadata-builder/builders/section-builders/booking-section-builder.js +3 -2
  70. package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.js +4 -3
  71. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +3 -2
  72. package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.js +2 -1
  73. package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.js +2 -1
  74. package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.js +4 -3
  75. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +3 -2
  76. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +2 -1
  77. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +11 -15
  78. package/lib/src/metadata-builder/builders/update-content-text-builder.d.ts +17 -0
  79. package/lib/src/metadata-builder/builders/update-content-text-builder.js +103 -0
  80. package/lib/src/metadata-builder/builders/update-content-text-style-builder.d.ts +16 -0
  81. package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +216 -0
  82. package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +13 -11
  83. package/lib/src/metadata-builder/helpers/rte.helpers.js +73 -14
  84. package/lib/src/metadata-builder/metadata-executor.js +4 -0
  85. package/lib/src/metadata-builder/models.d.ts +2 -0
  86. package/lib/src/metadata-builder/models.js +2 -0
  87. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +19 -0
  88. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +4 -0
  89. package/lib/src/metadata-builder/sanitizers/rte-schema-check.d.ts +8 -7
  90. package/lib/src/metadata-builder/sanitizers/rte-schema-check.js +52 -21
  91. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.d.ts +1 -1
  92. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.js +10 -35
  93. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +1 -1
  94. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +3 -12
  95. package/lib/src/shared/content-settings/components/email-field/EmailField.mixin.d.ts +1 -1
  96. package/lib/src/shared/content-settings/components/tel-field/TelField.mixin.d.ts +1 -1
  97. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.d.ts +2 -3
  98. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.js +3 -10
  99. package/lib/src/shared/content-settings/components/text-field/TextField.mixin.d.ts +1 -1
  100. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.d.ts +1 -2
  101. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.js +1 -4
  102. package/lib/src/shared/content-settings/components/textarea-field/TextareaField.mixin.d.ts +1 -1
  103. package/lib/src/shared/content-settings/components/url-field/UrlField.mixin.d.ts +1 -1
  104. package/lib/src/shared/content-settings/components/youtube-video-field/YoutubeVideoField.mixin.d.ts +1 -1
  105. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +1 -1
  106. package/lib/src/shared/logo-versions/LogoVersions.mixin.d.ts +1 -1
  107. package/lib/src/shared/my-designs/MyDesigns.mixin.d.ts +1 -1
  108. package/lib/src/shared/my-uploads/MyMedia.mixin.d.ts +1 -1
  109. package/lib/src/shared/my-uploads/MyUploads.mixin.d.ts +1 -1
  110. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +1 -1
  111. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +1 -1
  112. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +1 -1
  113. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +1 -1
  114. package/lib/src/shared/template-settings/store/template-settings-store.d.ts +2 -0
  115. package/lib/src/shared/template-settings/store/template-settings-store.js +72 -24
  116. package/lib/src/shared/unsplash-library/UnsplashLibrary.mixin.d.ts +1 -1
  117. package/lib/src/utils/minify-brand-page-metadata.js +1 -4
  118. package/package.json +15 -17
  119. package/src/brand-page/components/contact-form/ContactForm.mixin.ts +9 -7
  120. package/src/brand-page/components/cookie-consent/CookieConsent.vue +1 -0
  121. package/src/brand-page/components/mailing-list/MailingList.mixin.ts +8 -3
  122. package/src/brand-page/components/navigation/navigation-carousel/NavigationCarousel.vue +5 -1
  123. package/src/brand-page/components/page-footer/PageFooter.mixin.ts +4 -1
  124. package/src/brand-page/components/page-footer/PageFooter.vue +1 -1
  125. package/src/brand-page/components/payment/Payment.mixin.ts +4 -3
  126. package/src/brand-page/components/payment/request-payment/RequestPayment.mixin.ts +8 -1
  127. package/src/brand-page/components/payment/request-payment/RequestPayment.vue +1 -1
  128. package/src/brand-page/components/save-contact/SaveContactWidget.vue +2 -1
  129. package/src/brand-page/components/shop/Shop.vue +6 -3
  130. package/src/brand-page/components/social-links/SocialLinks.vue +5 -2
  131. package/src/brand-page/components/text-wrapper/TextWrapper.vue +6 -17
  132. package/src/brand-page/composables/consent.composable.ts +18 -4
  133. package/src/brand-page/models/brand-page-context.model.ts +7 -0
  134. package/src/brand-page/models/index.ts +26 -35
  135. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -3
  136. package/src/brand-page/utils/context.util.test.ts +43 -0
  137. package/src/brand-page/utils/context.util.ts +10 -0
  138. package/src/brand-page/utils/migration.util.ts +0 -12
  139. package/src/brand-page/utils/style.util.test.ts +0 -51
  140. package/src/brand-page/utils/text.util.ts +4 -83
  141. package/src/brand-page/utils/vcard.util.ts +4 -6
  142. package/src/brand-page/utils/view-model.util.ts +26 -94
  143. package/src/brand-page/view-models.ts +2 -3
  144. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +4 -5
  145. package/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.test.ts +135 -0
  146. package/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.ts +62 -21
  147. package/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.ts +1 -0
  148. package/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.ts +10 -6
  149. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.ts +73 -0
  150. package/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.ts +85 -0
  151. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +111 -27
  152. package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.ts +9 -0
  153. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +11 -3
  154. package/src/brand-page-maker/stores/ui/ui-store.ts +1 -1
  155. package/src/brand-page-maker/utils/index.ts +0 -2
  156. package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +3 -62
  157. package/src/brand-page-maker/utils/rich-text-editor.util.ts +11 -86
  158. package/src/brand-page-maker/validators/field.validator.ts +2 -10
  159. package/src/clients/models.ts +3 -0
  160. package/src/lib.ts +1 -9
  161. package/src/metadata-builder/ai-metadata-executor.ts +26 -0
  162. package/src/metadata-builder/builders/section-builders/about-section-builder.ts +3 -2
  163. package/src/metadata-builder/builders/section-builders/banner-section-builder.ts +3 -2
  164. package/src/metadata-builder/builders/section-builders/booking-section-builder.ts +5 -2
  165. package/src/metadata-builder/builders/section-builders/contact-form-builder.ts +4 -3
  166. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +3 -2
  167. package/src/metadata-builder/builders/section-builders/mailing-list-builder.ts +2 -1
  168. package/src/metadata-builder/builders/section-builders/mailing-list-section-builder.ts +2 -1
  169. package/src/metadata-builder/builders/section-builders/profile-section-builder.ts +6 -3
  170. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +3 -2
  171. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +2 -1
  172. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +13 -19
  173. package/src/metadata-builder/builders/update-content-text-builder.ts +148 -0
  174. package/src/metadata-builder/builders/update-content-text-style-builder.ts +352 -0
  175. package/src/metadata-builder/helpers/rte.helpers.ts +101 -15
  176. package/src/metadata-builder/metadata-executor.ts +5 -0
  177. package/src/metadata-builder/models.ts +3 -0
  178. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +27 -0
  179. package/src/metadata-builder/sanitizers/rte-schema-check.ts +53 -24
  180. package/src/shared/content-settings/components/GenericTextInput.mixin.ts +11 -42
  181. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +3 -16
  182. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.ts +4 -13
  183. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.vue +4 -6
  184. package/src/shared/content-settings/components/text-image-field/TextImageField.vue +3 -6
  185. package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.ts +1 -6
  186. package/src/shared/content-settings/components/text-items-field/TextItemsField.vue +1 -1
  187. package/src/shared/template-settings/store/template-settings-store.ts +93 -27
  188. package/src/utils/minify-brand-page-metadata.ts +1 -4
  189. package/lib/src/brand-page/utils/migration/migration-20.d.ts +0 -2
  190. package/lib/src/brand-page/utils/migration/migration-20.js +0 -66
  191. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.d.ts +0 -22
  192. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.js +0 -22
  193. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.d.ts +0 -14
  194. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.js +0 -39
  195. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.d.ts +0 -28
  196. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.js +0 -117
  197. package/lib/src/brand-page-maker/utils/rte-render.util.d.ts +0 -63
  198. package/lib/src/brand-page-maker/utils/rte-render.util.js +0 -95
  199. package/lib/src/brand-page-maker/utils/rte-sanitize.util.d.ts +0 -6
  200. package/lib/src/brand-page-maker/utils/rte-sanitize.util.js +0 -156
  201. package/src/brand-page/utils/migration/migration-20.server.test.ts +0 -125
  202. package/src/brand-page/utils/migration/migration-20.test.ts +0 -72
  203. package/src/brand-page/utils/migration/migration-20.ts +0 -87
  204. package/src/brand-page/utils/text.util.test.ts +0 -106
  205. package/src/brand-page/utils/view-model.pageid-rte.test.ts +0 -104
  206. package/src/brand-page-maker/__fixtures__/complex-rte.fixture.ts +0 -23
  207. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +0 -52
  208. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.ts +0 -42
  209. package/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.ts +0 -162
  210. package/src/brand-page-maker/utils/__snapshots__/rte-render-parity.test.ts.snap +0 -202
  211. package/src/brand-page-maker/utils/rte-features.test.ts +0 -378
  212. package/src/brand-page-maker/utils/rte-render-parity.test.ts +0 -164
  213. package/src/brand-page-maker/utils/rte-render.util.ts +0 -165
  214. package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +0 -172
  215. package/src/brand-page-maker/utils/rte-sanitize.util.ts +0 -192
  216. package/src/brand-page-maker/validators/field.validator.test.ts +0 -55
@@ -23,16 +23,20 @@ export const useBubbleMenu = (
23
23
  return false;
24
24
  };
25
25
 
26
- // v3 BubbleMenu uses Floating UI (was tippy.js in v2). Only placement + offset
27
- // carry over; the rest of the old tippy config (trigger/interactive/hideOnClick/
28
- // zIndex) is either the v3 default or handled in CSS.
29
- const bubbleMenuOptions = {
26
+ const tippyOptions = {
27
+ duration: 100,
30
28
  placement: 'bottom' as const,
31
- offset: 10,
29
+ interactive: true,
30
+ appendTo: 'parent' as const,
31
+ hideOnClick: false,
32
+ arrow: false,
33
+ offset: [0, 10] as [number, number],
34
+ trigger: 'manual' as const,
35
+ zIndex: 10,
32
36
  };
33
37
 
34
38
  return {
35
39
  shouldShow,
36
- bubbleMenuOptions,
40
+ tippyOptions,
37
41
  };
38
42
  };
@@ -0,0 +1,73 @@
1
+ import { Extension } from '@tiptap/core';
2
+ import '@tiptap/extension-text-style';
3
+
4
+ export type FontSizeOptions = {
5
+ types: string[];
6
+ };
7
+
8
+ declare module '@tiptap/core' {
9
+ interface Commands<ReturnType> {
10
+ fontSize: {
11
+ /**
12
+ * Set the font size
13
+ */
14
+ setFontSize: (fontSize: string) => ReturnType;
15
+ /**
16
+ * Unset the font size
17
+ */
18
+ unsetFontSize: () => ReturnType;
19
+ };
20
+ }
21
+ }
22
+
23
+ export const FontSize = Extension.create<FontSizeOptions>({
24
+ name: 'fontSize',
25
+
26
+ addOptions() {
27
+ return {
28
+ types: ['textStyle'],
29
+ };
30
+ },
31
+
32
+ addGlobalAttributes() {
33
+ return [
34
+ {
35
+ types: this.options.types,
36
+ attributes: {
37
+ fontSize: {
38
+ default: null,
39
+ parseHTML: (element) =>
40
+ element.style.fontSize.replace(/['"]+/g, ''),
41
+ renderHTML: (attributes) => {
42
+ if (!attributes.fontSize) {
43
+ return {};
44
+ }
45
+
46
+ return {
47
+ style: `font-size: ${attributes.fontSize}`,
48
+ };
49
+ },
50
+ },
51
+ },
52
+ },
53
+ ];
54
+ },
55
+
56
+ addCommands() {
57
+ return {
58
+ setFontSize:
59
+ (fontSize) =>
60
+ ({ chain }) => {
61
+ return chain().setMark('textStyle', { fontSize }).run();
62
+ },
63
+ unsetFontSize:
64
+ () =>
65
+ ({ chain }) => {
66
+ return chain()
67
+ .setMark('textStyle', { fontSize: null })
68
+ .removeEmptyTextStyle()
69
+ .run();
70
+ },
71
+ };
72
+ },
73
+ });
@@ -0,0 +1,85 @@
1
+ import { Extension } from '@tiptap/core';
2
+
3
+ export type LineHeightOptions = {
4
+ /**
5
+ * Node / mark types that should carry the `lineHeight` attribute.
6
+ * Defaults to `['textStyle']` to piggy‑back on TipTap’s built‑in mark.
7
+ */
8
+ types: string[];
9
+ };
10
+
11
+ declare module '@tiptap/core' {
12
+ interface Commands<ReturnType> {
13
+ lineHeight: {
14
+ /**
15
+ * Apply a line‑height to the current selection (or stored marks).
16
+ */
17
+ setLineHeight: (heightValue: string) => ReturnType;
18
+ /**
19
+ * Remove any line‑height attribute.
20
+ */
21
+ unsetLineHeight: () => ReturnType;
22
+ /**
23
+ * Read the current line‑height from the selection / stored marks.
24
+ */
25
+ getLineHeight: () => ReturnType;
26
+ };
27
+ }
28
+ }
29
+
30
+ export const LineHeight = Extension.create<LineHeightOptions>({
31
+ name: 'lineHeight',
32
+
33
+ addOptions() {
34
+ return {
35
+ types: ['textStyle'],
36
+ };
37
+ },
38
+
39
+ addGlobalAttributes() {
40
+ return [
41
+ {
42
+ types: this.options.types,
43
+ attributes: {
44
+ lineHeight: {
45
+ default: null,
46
+ parseHTML: (element) =>
47
+ (element.style.lineHeight || '').replace(/['"]+/g, ''),
48
+ renderHTML: (attributes) => {
49
+ if (!attributes.lineHeight) {
50
+ return {};
51
+ }
52
+
53
+ return { style: `line-height: ${attributes.lineHeight}` };
54
+ },
55
+ },
56
+ },
57
+ },
58
+ ];
59
+ },
60
+
61
+ addCommands() {
62
+ return {
63
+ setLineHeight:
64
+ (lineHeight) =>
65
+ ({ chain }) => {
66
+ return chain().setMark('textStyle', { lineHeight }).run();
67
+ },
68
+
69
+ unsetLineHeight:
70
+ () =>
71
+ ({ chain }) => {
72
+ return chain()
73
+ .setMark('textStyle', { lineHeight: null })
74
+ .removeEmptyTextStyle()
75
+ .run();
76
+ },
77
+
78
+ getLineHeight:
79
+ () =>
80
+ ({ editor }) => {
81
+ return editor.getAttributes('textStyle').lineHeight;
82
+ },
83
+ };
84
+ },
85
+ });
@@ -8,7 +8,18 @@ import {
8
8
  watch,
9
9
  } from 'vue';
10
10
  import { Editor } from '@tiptap/vue-3';
11
- import { getRteExtensions } from './getRteExtensions';
11
+ import StarterKit from '@tiptap/starter-kit';
12
+ import Underline from '@tiptap/extension-underline';
13
+ import TextStyle from '@tiptap/extension-text-style';
14
+ import TextAlign from '@tiptap/extension-text-align';
15
+ import FontFamily from '@tiptap/extension-font-family';
16
+ import ColorExtension from '@tiptap/extension-color';
17
+ import { FontSize } from './extensions/FontSize';
18
+ import { LetterSpacing } from './extensions/LetterSpacing';
19
+ import { LineHeight } from './extensions/LineHeight';
20
+ import Highlight from '@tiptap/extension-highlight';
21
+ import Placeholder from '@tiptap/extension-placeholder';
22
+ import Link from '@tiptap/extension-link';
12
23
  import type { StyleTextFontFamilyOption } from '@shared/style-settings/models';
13
24
  import { createFormattingActions } from './formatting';
14
25
  import { useTextAlign } from './textAlign';
@@ -20,7 +31,7 @@ import {
20
31
  emToPx,
21
32
  isStyleChanged,
22
33
  pxToEm,
23
- sanitizeRteJson,
34
+ sanitiseClientSideRTE,
24
35
  skipStyleDetection,
25
36
  } from '@brand-page-maker/utils';
26
37
  import type { HyperlinkType, TextStyles } from '@brand-page';
@@ -38,7 +49,9 @@ import {
38
49
  import { useUiContentSettingsStore } from '@shared/content-settings/store';
39
50
  import { useHistoryStore } from '@brand-page-maker/stores/history/history-store';
40
51
  import { AllSelection } from 'prosemirror-state';
41
- import { useHyperlink } from './hyperlink';
52
+ import { MaxLength } from './extensions/MaxLength';
53
+ import { AutoLink } from './extensions/AutoLink';
54
+ import { isHrefPageId, useHyperlink } from './hyperlink';
42
55
  import { useBubbleMenu } from './bubbleMenu';
43
56
  import {
44
57
  hasLogoColorVar,
@@ -79,7 +92,7 @@ interface UseRichTextEditorReturn {
79
92
  hyperlinkValue: ComputedRef<{ id: string; name: string; value: string }>;
80
93
  setHyperlinkUrlValue: (url: string) => void;
81
94
  shouldShow: ({ editor, state }: { editor: any; state: any }) => boolean;
82
- bubbleMenuOptions: Record<string, any>;
95
+ tippyOptions: Record<string, any>;
83
96
  handleAdminColorChange: (
84
97
  color: {
85
98
  id: string;
@@ -116,7 +129,44 @@ export const useExternalRichTextEditor = (): {
116
129
  } => {
117
130
  const externalEditor = new Editor({
118
131
  editable: false,
119
- extensions: getRteExtensions(),
132
+ extensions: [
133
+ StarterKit,
134
+ Underline,
135
+ TextStyle.configure({ mergeNestedSpanStyles: true }),
136
+ TextAlign.configure({
137
+ types: ['heading', 'paragraph'],
138
+ }),
139
+ FontFamily.configure({
140
+ types: ['textStyle'],
141
+ }),
142
+ FontSize,
143
+ LetterSpacing,
144
+ LineHeight,
145
+ ColorExtension.configure({
146
+ types: ['textStyle'],
147
+ }),
148
+ Highlight.configure({
149
+ multicolor: true,
150
+ }),
151
+ Link.configure({
152
+ openOnClick: false,
153
+ defaultProtocol: 'https',
154
+ autolink: true,
155
+ HTMLAttributes: (attrs) => {
156
+ const href = attrs.href;
157
+ const isExternal =
158
+ href && (href.startsWith('http://') || href.startsWith('https://'));
159
+
160
+ return {
161
+ rel: 'noopener noreferrer',
162
+ target: isExternal ? '_blank' : null,
163
+ };
164
+ },
165
+ isAllowedUri: (url, ctx) =>
166
+ ctx.defaultValidate(url) || isHrefPageId(url),
167
+ }),
168
+ AutoLink,
169
+ ],
120
170
  });
121
171
 
122
172
  return {
@@ -164,13 +214,54 @@ export const useRichTextEditor = (
164
214
  const editor = new Editor({
165
215
  content: model.value ?? '',
166
216
  editable: !props.readOnly,
167
- extensions: getRteExtensions({
168
- restrictStarterKit: true,
169
- editing: {
217
+ extensions: [
218
+ StarterKit.configure({
219
+ bulletList: false,
220
+ orderedList: false,
221
+ listItem: false,
222
+ horizontalRule: false,
223
+ codeBlock: false,
224
+ code: false,
225
+ }),
226
+ Underline,
227
+ TextStyle.configure({ mergeNestedSpanStyles: true }),
228
+ TextAlign.configure({
229
+ types: ['heading', 'paragraph'],
230
+ }),
231
+ FontFamily.configure({
232
+ types: ['textStyle'],
233
+ }),
234
+ FontSize,
235
+ LetterSpacing,
236
+ LineHeight,
237
+ ColorExtension.configure({
238
+ types: ['textStyle'],
239
+ }),
240
+ Highlight.configure({
241
+ multicolor: true,
242
+ }),
243
+ Placeholder.configure({
170
244
  placeholder: props.placeholder,
171
- maxLength: props.maxLength,
172
- },
173
- }),
245
+ }),
246
+ MaxLength.configure({ maxLength: props.maxLength }),
247
+ Link.configure({
248
+ openOnClick: false,
249
+ defaultProtocol: 'https',
250
+ autolink: true,
251
+ HTMLAttributes: (attrs) => {
252
+ const href = attrs.href;
253
+ const isExternal =
254
+ href && (href.startsWith('http://') || href.startsWith('https://'));
255
+
256
+ return {
257
+ target: isExternal ? '_blank' : null,
258
+ };
259
+ },
260
+ isAllowedUri: (url, ctx) =>
261
+ ctx.defaultValidate(url) || isHrefPageId(url),
262
+ }),
263
+ AutoLink,
264
+ ],
174
265
  editorProps: {
175
266
  handleDOMEvents: {
176
267
  drop(view, event: DragEvent) {
@@ -349,7 +440,7 @@ export const useRichTextEditor = (
349
440
  applyHyperlinkToEditor,
350
441
  } = useHyperlink(editor);
351
442
 
352
- const { shouldShow, bubbleMenuOptions } = useBubbleMenu(
443
+ const { shouldShow, tippyOptions } = useBubbleMenu(
353
444
  editor,
354
445
  isHyperlinkPopupOpen,
355
446
  );
@@ -381,14 +472,8 @@ export const useRichTextEditor = (
381
472
 
382
473
  const isExternalMutation = updateRichTextEditor.value || appliedSinceLast;
383
474
 
384
- // Compare in the stored (JSON) format so a JSON model value isn't always
385
- // seen as "changed" against the editor's HTML (which would re-run
386
- // setContent and reset the user's just-applied styles).
387
- const hasChanged =
388
- JSON.stringify(editor.getJSON()) !== JSON.stringify(newValue);
389
-
390
- if (isExternalMutation && hasChanged) {
391
- editor.commands.setContent(newValue, { emitUpdate: false });
475
+ if (isExternalMutation && editor.getHTML() !== newValue) {
476
+ editor.commands.setContent(newValue, false);
392
477
  updateRichTextEditor.value = false;
393
478
  //text style type is not switching when reordering
394
479
  textStyleTypeOptions.forEach((item) => {
@@ -418,13 +503,12 @@ export const useRichTextEditor = (
418
503
  }
419
504
  });
420
505
 
421
- // Update model when editor content changes. RTE content is stored as Tiptap
422
- // JSON (BP-5787). getJSON() is already in hand (the editor holds the doc) and
423
- // is sanitised natively — no getHTML(), no DOMPurify, no extra editor — so
424
- // the per-keystroke path allocates nothing heavy.
506
+ // Update model when editor content changes
425
507
  editor.on('update', () => {
508
+ const sanitizedData = sanitiseClientSideRTE(editor.getHTML());
509
+
426
510
  // An update fired while the editor is unfocused is TipTap rewriting the
427
- // model back into its canonical form during mount rehydration or a
511
+ // model back into its canonical HTML during mount rehydration or a
428
512
  // programmatic setContent — not a user edit. Tag the emit so the write
429
513
  // folds into the current undo head instead of pushing a phantom entry
430
514
  // (BP-5810). The flag travels WITH the value through the debounced
@@ -438,7 +522,7 @@ export const useRichTextEditor = (
438
522
  // the RTE only mounts inside an already-open settings drawer and the user
439
523
  // focuses it before formatting, so this doesn't occur — revisit if a
440
524
  // consumer ever drives toolbar actions on an unfocused editor.
441
- emit('update:modelValue', sanitizeRteJson(editor.getJSON()), {
525
+ emit('update:modelValue', sanitizedData.sanitizedHtml, {
442
526
  isHydrationWrite: !editor.isFocused,
443
527
  });
444
528
  });
@@ -965,7 +1049,7 @@ export const useRichTextEditor = (
965
1049
  hyperlinkValue,
966
1050
  setHyperlinkUrlValue,
967
1051
  shouldShow,
968
- bubbleMenuOptions,
1052
+ tippyOptions,
969
1053
  handleAdminColorChange,
970
1054
  };
971
1055
  };
@@ -840,6 +840,15 @@ export const useBrandPageContainerStore = defineStore('brandPageContainer', {
840
840
  prepurchaseExportData.shopConfigData = shopConfigData
841
841
  ? JSON.stringify(shopConfigData)
842
842
  : undefined;
843
+
844
+ // Carry the AI content key through the save→sign-in→reload cycle. The key lives in the
845
+ // store, not the URL, so appendPreservedQueryParams (URL-only) can't carry it — the save
846
+ // must send it explicitly, or logging in inside the editor drops the key and the AI
847
+ // templates/content disappear on reload. The upsert appends these to the post-login returnUrl.
848
+ prepurchaseExportData.contentKey = uiStore.userPromptContentKey;
849
+ prepurchaseExportData.contentKeyType = uiStore.userPromptContentKey
850
+ ? 'UserPrompt'
851
+ : undefined;
843
852
  }
844
853
 
845
854
  let uniqueLogoDraftTokens = [];
@@ -515,14 +515,22 @@ export const useBrandPageMakerStore = defineStore('brandPageMaker', {
515
515
  }));
516
516
  }
517
517
 
518
- if (uiStore.personalisationData) {
518
+ // Search content when searching, else the page-load personalisation. Must resolve to the
519
+ // same content the tile previews used (template-settings-store resolves the equivalent from
520
+ // the fresh response). Guard on length: an empty [] is truthy but would make the content
521
+ // index NaN and crash the apply below (matches the sibling guard in template-settings-store).
522
+ const activePersonalisation =
523
+ templateSettingsStore.searchPersonalisationData ??
524
+ uiStore.personalisationData;
525
+
526
+ if (activePersonalisation?.length) {
519
527
  const personalisationIndex =
520
528
  await getAiContentIndexByTemplateTokenAndNumberOfVariations(
521
529
  this.selectedTemplateId,
522
- uiStore.personalisationData.length,
530
+ activePersonalisation.length,
523
531
  );
524
532
  const { model } = await applyUserPersonalisationToBrandPageModelAsync(
525
- uiStore.personalisationData[personalisationIndex].userPersonalisation,
533
+ activePersonalisation[personalisationIndex].userPersonalisation,
526
534
  new BrandPageModel(brandPageModel),
527
535
  );
528
536
 
@@ -76,7 +76,7 @@ export const useUiStore = defineStore('ui', {
76
76
  setUserPersonalisationData({
77
77
  personalisationData,
78
78
  }: {
79
- personalisationData: UserPersonalisationContent[];
79
+ personalisationData: UserPersonalisationContent[] | undefined;
80
80
  }) {
81
81
  this.personalisationData = personalisationData;
82
82
  },
@@ -7,8 +7,6 @@ export * from './url.util';
7
7
  export * from './slug.util';
8
8
  export * from './text.util';
9
9
  export * from './rich-text-editor.util';
10
- export * from './rte-render.util';
11
- export * from './rte-sanitize.util';
12
10
 
13
11
  export const isColorMatched = (color: string | null | undefined): boolean => {
14
12
  if (!color) return false;
@@ -8,7 +8,6 @@ import {
8
8
  getUpdatedHTMLFromEditor,
9
9
  sanitiseClientSideRTE,
10
10
  getRTEColor,
11
- getRteTextLength,
12
11
  } from './rich-text-editor.util';
13
12
  import type { TextStyleDeclaration } from '@brand-page';
14
13
 
@@ -76,65 +75,8 @@ describe('hasHTML', () => {
76
75
  });
77
76
  });
78
77
 
79
- describe('getRteTextLength', () => {
80
- it('returns the raw length for a plain string (unchanged legacy behaviour)', () => {
81
- expect(getRteTextLength('hello')).toBe(5);
82
- expect(getRteTextLength('<p>hello</p>')).toBe('<p>hello</p>'.length);
83
- expect(getRteTextLength('')).toBe(0);
84
- });
85
-
86
- it('returns visible-text length for a Tiptap JSON doc (not undefined/throw)', () => {
87
- const doc = {
88
- type: 'doc',
89
- content: [
90
- {
91
- type: 'paragraph',
92
- content: [
93
- { type: 'text', text: 'hello ' },
94
- { type: 'text', marks: [{ type: 'bold' }], text: 'world' },
95
- ],
96
- },
97
- ],
98
- };
99
-
100
- // "hello world" — measured from the text nodes, not the JSON wrapper.
101
- expect(getRteTextLength(doc)).toBe(11);
102
- });
103
-
104
- it('counts adjacent split nodes with no phantom separator (BP-5787)', () => {
105
- // Hyperlinking (or bolding) a word splits its text node. Length must match
106
- // the editor's textContent — "clickhere" = 9 — NOT a space-joined
107
- // "click here" = 10, which would fire the max-length indicator one char
108
- // early and flicker as content settles.
109
- const doc = {
110
- type: 'doc',
111
- content: [
112
- {
113
- type: 'paragraph',
114
- content: [
115
- { type: 'text', text: 'click' },
116
- {
117
- type: 'text',
118
- text: 'here',
119
- marks: [{ type: 'link', attrs: { href: 'https://x.com' } }],
120
- },
121
- ],
122
- },
123
- ],
124
- };
125
-
126
- expect(getRteTextLength(doc)).toBe(9);
127
- });
128
-
129
- it('returns 0 for null/undefined and an empty doc', () => {
130
- expect(getRteTextLength(null)).toBe(0);
131
- expect(getRteTextLength(undefined)).toBe(0);
132
- expect(getRteTextLength({ type: 'doc', content: [] })).toBe(0);
133
- });
134
- });
135
-
136
78
  describe('getUpdatedHTMLFromEditor', () => {
137
- it('applies style commands and returns updated JSON', () => {
79
+ it('applies style commands and returns updated HTML', () => {
138
80
  // Mock editor
139
81
  const runMock = vi.fn();
140
82
  const chain = {
@@ -177,7 +119,7 @@ describe('getUpdatedHTMLFromEditor', () => {
177
119
  },
178
120
  },
179
121
  chain: () => chain as any,
180
- getJSON: () => ({ type: 'doc', content: [{ type: 'paragraph' }] }),
122
+ getHTML: () => '<div>styled</div>',
181
123
  } as unknown as Editor;
182
124
 
183
125
  const newStyle = {
@@ -195,8 +137,7 @@ describe('getUpdatedHTMLFromEditor', () => {
195
137
 
196
138
  expect(externalEditor.commands.setContent).toHaveBeenCalledWith('foo');
197
139
  expect(runMock).toHaveBeenCalled();
198
- // BP-5787: returns the JSON form (consistent with JSON storage).
199
- expect(result).toEqual({ type: 'doc', content: [{ type: 'paragraph' }] });
140
+ expect(result).toBe('<div>styled</div>');
200
141
  });
201
142
  });
202
143