@designcrowd/library.brand-page 6.0.26-json-prereq-2 → 6.0.27-websitev2-dev-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 (186) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-CKBqjDH3.js → CreatedOnBrandCrowd-de-de-BpFKOKIo.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-CRXiimvT.js → CreatedOnBrandCrowd-en-us-USGAnC_n.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-DCYdY6sj.js → CreatedOnBrandCrowd-es-es-CvVx3h4H.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-Dk7LOhxE.js → CreatedOnBrandCrowd-fr-ca-BCwamW5a.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-Dk7LOhxE.js → CreatedOnBrandCrowd-fr-fr-BCwamW5a.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-Cp4ufHJP.js → CreatedOnBrandCrowd-pt-br-Bt72hMrQ.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-Cp4ufHJP.js → CreatedOnBrandCrowd-pt-pt-Bt72hMrQ.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-ZSLkWMwM.js → CreatedOnDcom-de-de-BxUDrgG9.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-BgdIGfT3.js → CreatedOnDcom-en-us-wjVp5eOs.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-D0KemkCZ.js → CreatedOnDcom-es-es-CwFL2q5h.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-BuwNjNbC.js → CreatedOnDcom-fr-ca-BPZ2_PNq.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-DCQcsFPf.js → CreatedOnDcom-fr-fr-D0vLOikZ.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-DD-oLAS1.js → CreatedOnDcom-pt-br-CTwbxm9H.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-D8qLq2wW.js → CreatedOnDcom-pt-pt-Cfqf8LaM.js} +1 -1
  15. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +2 -5
  16. package/lib/src/brand-page/models/index.d.ts +26 -34
  17. package/lib/src/brand-page/utils/migration.util.js +0 -11
  18. package/lib/src/brand-page/utils/text.util.d.ts +1 -1
  19. package/lib/src/brand-page/utils/text.util.js +0 -32
  20. package/lib/src/brand-page/utils/vcard.util.js +4 -4
  21. package/lib/src/brand-page/utils/view-model.util.d.ts +0 -1
  22. package/lib/src/brand-page/utils/view-model.util.js +20 -68
  23. package/lib/src/brand-page/view-models.d.ts +3 -3
  24. package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.d.ts +1 -0
  25. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +7 -3
  26. package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.d.ts +9 -2
  27. package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.js +10 -6
  28. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.d.ts +20 -0
  29. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.js +44 -0
  30. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.d.ts +27 -0
  31. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.js +44 -0
  32. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +1 -1
  33. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +103 -24
  34. package/lib/src/brand-page-maker/utils/index.d.ts +0 -2
  35. package/lib/src/brand-page-maker/utils/index.js +0 -2
  36. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +5 -28
  37. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +5 -48
  38. package/lib/src/brand-page-maker/validators/field.validator.js +2 -10
  39. package/lib/src/clients/ai-assistant-client.d.ts +1 -0
  40. package/lib/src/clients/ai-assistant-client.js +1 -0
  41. package/lib/src/css/library.brand-page-brandCrowd.min.css +39 -39
  42. package/lib/src/css/library.brand-page-brandPage.min.css +39 -39
  43. package/lib/src/css/library.brand-page-designCom.min.css +39 -39
  44. package/lib/src/css/library.brand-page-preview.min.css +39 -39
  45. package/lib/src/index.d.ts +0 -1
  46. package/lib/src/index.mjs +138 -143
  47. package/lib/src/{lib-Db6j9fQZ.js → lib-Cg4BExY4.js} +72135 -75458
  48. package/lib/src/lib.js +0 -2
  49. package/lib/src/metadata-builder/ai-metadata-executor.d.ts +1 -0
  50. package/lib/src/metadata-builder/ai-metadata-executor.js +19 -0
  51. package/lib/src/metadata-builder/builders/global-font-propagation.d.ts +28 -0
  52. package/lib/src/metadata-builder/builders/global-font-propagation.js +125 -0
  53. package/lib/src/metadata-builder/builders/global-styles-builder.js +16 -0
  54. package/lib/src/metadata-builder/builders/section-builders/about-section-builder.js +3 -2
  55. package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.js +3 -2
  56. package/lib/src/metadata-builder/builders/section-builders/booking-section-builder.js +3 -2
  57. package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.js +4 -3
  58. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +3 -2
  59. package/lib/src/metadata-builder/builders/section-builders/index.d.ts +1 -0
  60. package/lib/src/metadata-builder/builders/section-builders/index.js +1 -0
  61. package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.js +2 -1
  62. package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.js +2 -1
  63. package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.js +4 -3
  64. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +3 -2
  65. package/lib/src/metadata-builder/builders/section-builders/support-me-builder.d.ts +8 -0
  66. package/lib/src/metadata-builder/builders/section-builders/support-me-builder.js +25 -0
  67. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +2 -1
  68. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +16 -17
  69. package/lib/src/metadata-builder/builders/section-field-maps.js +15 -0
  70. package/lib/src/metadata-builder/builders/section-field-utils.d.ts +6 -1
  71. package/lib/src/metadata-builder/builders/section-field-utils.js +48 -12
  72. package/lib/src/metadata-builder/builders/section-rules/support-me.rules.d.ts +2 -0
  73. package/lib/src/metadata-builder/builders/section-rules/support-me.rules.js +17 -0
  74. package/lib/src/metadata-builder/builders/update-content-text-builder.d.ts +17 -0
  75. package/lib/src/metadata-builder/builders/update-content-text-builder.js +103 -0
  76. package/lib/src/metadata-builder/builders/update-content-text-style-builder.d.ts +16 -0
  77. package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +216 -0
  78. package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +13 -11
  79. package/lib/src/metadata-builder/helpers/rte.helpers.js +73 -14
  80. package/lib/src/metadata-builder/metadata-executor.js +7 -1
  81. package/lib/src/metadata-builder/models.d.ts +4 -0
  82. package/lib/src/metadata-builder/models.js +4 -0
  83. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +20 -0
  84. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +8 -0
  85. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +14 -2
  86. package/lib/src/metadata-builder/sanitizers/rte-schema-check.d.ts +8 -7
  87. package/lib/src/metadata-builder/sanitizers/rte-schema-check.js +52 -21
  88. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.js +1 -14
  89. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +3 -12
  90. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.d.ts +2 -3
  91. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.js +3 -10
  92. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.d.ts +1 -2
  93. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.js +1 -4
  94. package/lib/src/utils/minify-brand-page-metadata.js +8 -4
  95. package/package.json +15 -17
  96. package/src/brand-page/components/contact-form/ContactForm.mixin.ts +2 -5
  97. package/src/brand-page/components/text-wrapper/TextWrapper.vue +6 -17
  98. package/src/brand-page/models/index.ts +26 -35
  99. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -3
  100. package/src/brand-page/utils/migration.util.ts +0 -12
  101. package/src/brand-page/utils/style.util.test.ts +0 -51
  102. package/src/brand-page/utils/text.util.ts +4 -43
  103. package/src/brand-page/utils/vcard.util.ts +4 -6
  104. package/src/brand-page/utils/view-model.util.ts +26 -94
  105. package/src/brand-page/view-models.ts +2 -3
  106. package/src/brand-page-maker/components/ai-website-builder/AiWebsiteBuilder.vue +1 -1
  107. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +3 -4
  108. package/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.ts +1 -0
  109. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +7 -3
  110. package/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.ts +10 -6
  111. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.ts +73 -0
  112. package/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.ts +85 -0
  113. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +111 -27
  114. package/src/brand-page-maker/utils/index.ts +0 -2
  115. package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +3 -37
  116. package/src/brand-page-maker/utils/rich-text-editor.util.ts +11 -66
  117. package/src/brand-page-maker/validators/field.validator.ts +2 -10
  118. package/src/clients/ai-assistant-client.ts +1 -0
  119. package/src/lib.ts +0 -9
  120. package/src/metadata-builder/ai-metadata-executor.ts +26 -0
  121. package/src/metadata-builder/builders/global-font-propagation.test.ts +269 -0
  122. package/src/metadata-builder/builders/global-font-propagation.ts +205 -0
  123. package/src/metadata-builder/builders/global-styles-builder.ts +21 -0
  124. package/src/metadata-builder/builders/section-builders/about-section-builder.ts +3 -2
  125. package/src/metadata-builder/builders/section-builders/banner-section-builder.ts +3 -2
  126. package/src/metadata-builder/builders/section-builders/booking-section-builder.ts +5 -2
  127. package/src/metadata-builder/builders/section-builders/contact-form-builder.ts +4 -3
  128. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +3 -2
  129. package/src/metadata-builder/builders/section-builders/index.ts +1 -0
  130. package/src/metadata-builder/builders/section-builders/mailing-list-builder.ts +2 -1
  131. package/src/metadata-builder/builders/section-builders/mailing-list-section-builder.ts +2 -1
  132. package/src/metadata-builder/builders/section-builders/profile-section-builder.ts +6 -3
  133. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +3 -2
  134. package/src/metadata-builder/builders/section-builders/support-me-builder.test.ts +177 -0
  135. package/src/metadata-builder/builders/section-builders/support-me-builder.ts +43 -0
  136. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +2 -1
  137. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +18 -21
  138. package/src/metadata-builder/builders/section-field-maps.ts +18 -0
  139. package/src/metadata-builder/builders/section-field-utils.test.ts +83 -0
  140. package/src/metadata-builder/builders/section-field-utils.ts +69 -18
  141. package/src/metadata-builder/builders/section-rules/support-me.rules.ts +39 -0
  142. package/src/metadata-builder/builders/update-content-text-builder.ts +148 -0
  143. package/src/metadata-builder/builders/update-content-text-style-builder.ts +352 -0
  144. package/src/metadata-builder/helpers/rte.helpers.ts +101 -15
  145. package/src/metadata-builder/metadata-executor.ts +8 -0
  146. package/src/metadata-builder/models.ts +5 -0
  147. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +31 -0
  148. package/src/metadata-builder/parsers/section-tool-arguments.ts +14 -1
  149. package/src/metadata-builder/sanitizers/rte-schema-check.ts +53 -24
  150. package/src/shared/content-settings/components/GenericTextInput.mixin.ts +1 -17
  151. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +3 -16
  152. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.ts +4 -13
  153. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.vue +4 -6
  154. package/src/shared/content-settings/components/text-image-field/TextImageField.vue +3 -6
  155. package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.ts +1 -6
  156. package/src/shared/content-settings/components/text-items-field/TextItemsField.vue +1 -1
  157. package/src/utils/minify-brand-page-metadata.test.ts +61 -0
  158. package/src/utils/minify-brand-page-metadata.ts +9 -4
  159. package/lib/src/brand-page/utils/migration/migration-20.d.ts +0 -2
  160. package/lib/src/brand-page/utils/migration/migration-20.js +0 -66
  161. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.d.ts +0 -22
  162. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.js +0 -22
  163. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.d.ts +0 -14
  164. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.js +0 -39
  165. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.d.ts +0 -28
  166. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.js +0 -90
  167. package/lib/src/brand-page-maker/utils/rte-render.util.d.ts +0 -63
  168. package/lib/src/brand-page-maker/utils/rte-render.util.js +0 -95
  169. package/lib/src/brand-page-maker/utils/rte-sanitize.util.d.ts +0 -6
  170. package/lib/src/brand-page-maker/utils/rte-sanitize.util.js +0 -158
  171. package/src/brand-page/utils/migration/migration-20.server.test.ts +0 -125
  172. package/src/brand-page/utils/migration/migration-20.test.ts +0 -72
  173. package/src/brand-page/utils/migration/migration-20.ts +0 -87
  174. package/src/brand-page/utils/text.util.test.ts +0 -47
  175. package/src/brand-page/utils/view-model.pageid-rte.test.ts +0 -104
  176. package/src/brand-page-maker/__fixtures__/complex-rte.fixture.ts +0 -23
  177. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +0 -52
  178. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.ts +0 -42
  179. package/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.ts +0 -137
  180. package/src/brand-page-maker/utils/__snapshots__/rte-render-parity.test.ts.snap +0 -202
  181. package/src/brand-page-maker/utils/rte-features.test.ts +0 -351
  182. package/src/brand-page-maker/utils/rte-render-parity.test.ts +0 -157
  183. package/src/brand-page-maker/utils/rte-render.util.ts +0 -165
  184. package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +0 -158
  185. package/src/brand-page-maker/utils/rte-sanitize.util.ts +0 -195
  186. package/src/brand-page-maker/validators/field.validator.test.ts +0 -55
@@ -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({ includeLinkRel: true }),
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
  };
@@ -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,40 +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('returns 0 for null/undefined and an empty doc', () => {
105
- expect(getRteTextLength(null)).toBe(0);
106
- expect(getRteTextLength(undefined)).toBe(0);
107
- expect(getRteTextLength({ type: 'doc', content: [] })).toBe(0);
108
- });
109
- });
110
-
111
78
  describe('getUpdatedHTMLFromEditor', () => {
112
- it('applies style commands and returns updated JSON', () => {
79
+ it('applies style commands and returns updated HTML', () => {
113
80
  // Mock editor
114
81
  const runMock = vi.fn();
115
82
  const chain = {
@@ -152,7 +119,7 @@ describe('getUpdatedHTMLFromEditor', () => {
152
119
  },
153
120
  },
154
121
  chain: () => chain as any,
155
- getJSON: () => ({ type: 'doc', content: [{ type: 'paragraph' }] }),
122
+ getHTML: () => '<div>styled</div>',
156
123
  } as unknown as Editor;
157
124
 
158
125
  const newStyle = {
@@ -170,8 +137,7 @@ describe('getUpdatedHTMLFromEditor', () => {
170
137
 
171
138
  expect(externalEditor.commands.setContent).toHaveBeenCalledWith('foo');
172
139
  expect(runMock).toHaveBeenCalled();
173
- // BP-5787: returns the JSON form (consistent with JSON storage).
174
- expect(result).toEqual({ type: 'doc', content: [{ type: 'paragraph' }] });
140
+ expect(result).toBe('<div>styled</div>');
175
141
  });
176
142
  });
177
143
 
@@ -1,4 +1,4 @@
1
- import type { ChainedCommands, Editor, JSONContent } from '@tiptap/vue-3';
1
+ import type { ChainedCommands, Editor } from '@tiptap/vue-3';
2
2
  import type { EditorState, Transaction, Selection } from 'prosemirror-state';
3
3
  import {
4
4
  STYLE_THEME_KEYS,
@@ -19,7 +19,6 @@ import {
19
19
  type RequestPaymentContent,
20
20
  type SupportMeContent,
21
21
  type ShopContent,
22
- type RichTextValue,
23
22
  } from '@brand-page';
24
23
  import type { PageSettingsState } from '@shared/page-settings/store';
25
24
  import type { StyleSettingsState } from '@shared/style-settings/store';
@@ -244,38 +243,8 @@ export const skipStyleDetection = (chain: ChainedCommands, marks?: Mark[]) =>
244
243
  return true;
245
244
  });
246
245
 
247
- /**
248
- * BP-5787 — canonical discriminator for the JSON RTE model: a ProseMirror
249
- * document is an object with `type: 'doc'`.
250
- */
251
- export const isTiptapDocument = (value: unknown): value is JSONContent =>
252
- typeof value === 'object' &&
253
- value !== null &&
254
- (value as JSONContent).type === 'doc';
255
-
256
- /**
257
- * Extract visible plain text from a Tiptap JSON document (concatenated text
258
- * nodes). Dependency-free — no editor instantiation.
259
- */
260
- export const getRteJsonPlainText = (doc: JSONContent): string => {
261
- const parts: string[] = [];
262
-
263
- const walk = (node: JSONContent): void => {
264
- if (typeof node.text === 'string') parts.push(node.text);
265
-
266
- node.content?.forEach(walk);
267
- };
268
-
269
- walk(doc);
270
-
271
- return parts.join(' ').replace(/\s+/g, ' ').trim();
272
- };
273
-
274
- // Accepts a legacy inline-HTML string or a Tiptap JSON document (BP-5787).
275
- export const hasRTEContent = (content: unknown): boolean => {
276
- if (isTiptapDocument(content)) return true;
277
-
278
- if (typeof content !== 'string' || content.length === 0) return false;
246
+ export const hasRTEContent = (content: string): boolean => {
247
+ if (!content || content.length === 0) return false;
279
248
 
280
249
  const trimmed = content.trim();
281
250
  const tagWrapRegex = /^<([A-Za-z][A-Za-z0-9-]*)\b[^>]*>[\s\S]*<\/\1>$/;
@@ -283,11 +252,7 @@ export const hasRTEContent = (content: unknown): boolean => {
283
252
  return tagWrapRegex.test(trimmed);
284
253
  };
285
254
 
286
- export const getPlainTextFromRTE = (content: unknown): string => {
287
- if (isTiptapDocument(content)) {
288
- return getRteJsonPlainText(content);
289
- }
290
-
255
+ export const getPlainTextFromRTE = (content: string): string => {
291
256
  if (typeof content !== 'string') {
292
257
  return '';
293
258
  }
@@ -309,22 +274,6 @@ export const getPlainTextFromRTE = (content: unknown): string => {
309
274
  return plainText.trim();
310
275
  };
311
276
 
312
- /**
313
- * BP-5787 — visible-text length of an RTE value, for the maker item-field
314
- * max-length / emptiness checks. A plain string keeps its raw `.length`, so the
315
- * pre-JSON behaviour is byte-for-byte identical; a Tiptap JSON doc measures its
316
- * concatenated visible text. Without this, `doc.length` is `undefined` (silently
317
- * disabling the check) and `doc.trim()` throws — the class of bug that survived
318
- * the `RichTextValue` widening because Vue Options-API `this`/item access is `any`.
319
- */
320
- export const getRteTextLength = (
321
- value: RichTextValue | null | undefined,
322
- ): number => {
323
- if (typeof value === 'string') return value.length;
324
-
325
- return value ? getRteJsonPlainText(value).length : 0;
326
- };
327
-
328
277
  export const replaceTextInRTE = (
329
278
  existingHtml: string,
330
279
  newText: string,
@@ -772,20 +721,16 @@ export const switchAllContentToRTE = (
772
721
 
773
722
  export const getUpdatedHTMLFromEditor = (
774
723
  externalEditor: Editor,
775
- contentText: RichTextValue,
724
+ contentText: string,
776
725
  newContentStyle: TextStyleDeclaration,
777
726
  ) => {
778
727
  if (!contentText) {
779
728
  return contentText;
780
729
  }
781
730
 
782
- // BP-5787: accept a string (legacy HTML) or a Tiptap JSON doc. setContent
783
- // handles both; only the newline-escaping shim applies to strings.
784
- const content =
785
- typeof contentText === 'string' &&
786
- contentText.includes(ESCAPED_NEWLINE_CHARACTER)
787
- ? getRenderText(contentText)
788
- : contentText;
731
+ const content = contentText.includes(ESCAPED_NEWLINE_CHARACTER)
732
+ ? getRenderText(contentText)
733
+ : contentText;
789
734
 
790
735
  externalEditor.commands.setContent(content);
791
736
 
@@ -843,9 +788,9 @@ export const getUpdatedHTMLFromEditor = (
843
788
  chain = newContentStyle?.fontStyle ? chain.setItalic() : chain.unsetItalic();
844
789
 
845
790
  chain.run();
791
+ const html = externalEditor.getHTML();
846
792
 
847
- // BP-5787: return the JSON form, consistent with how RTE is now stored.
848
- return externalEditor.getJSON();
793
+ return html;
849
794
  };
850
795
 
851
796
  // Helper to build stored marks from a style declaration
@@ -969,7 +914,7 @@ export const sanitiseClientSideRTE = (html: string): SanitizationResult => {
969
914
  /**
970
915
  * Validates if an href value is safe and returns a sanitized version
971
916
  */
972
- export const validateAndSanitizeHref = (href: string): string | null => {
917
+ const validateAndSanitizeHref = (href: string): string | null => {
973
918
  if (!href || typeof href !== 'string') return null;
974
919
 
975
920
  // Trim whitespace and decode entities
@@ -1,6 +1,6 @@
1
1
  import { isURL, isEmail, isTel } from '@utils/validator.util';
2
2
  import type { ItemFieldType, Validator } from '@brand-page-maker/models';
3
- import { YOUTUBE_URL_RE, isTiptapDocument } from '@brand-page-maker/utils';
3
+ import { YOUTUBE_URL_RE } from '@brand-page-maker/utils';
4
4
  import { validatorTr } from '@src/composables/useTranslate';
5
5
 
6
6
  const addressValueValidator: Validator = {
@@ -76,15 +76,7 @@ export const dummyValidator: Validator = {
76
76
 
77
77
  const nonEmptyStringValidator: Validator = {
78
78
  validate: (name, value: any) => {
79
- // BP-5787/BP-5857: match legacy (pre-JSON) validation semantics.
80
- // An RTE field's stored value used to be inline HTML — an empty editor was
81
- // "<p></p>", a NON-empty string, so the required check always passed for RTE
82
- // fields (it only ever failed for genuinely empty PLAIN inputs). Now RTE
83
- // fields are Tiptap JSON docs; treat any doc as "present" so behaviour
84
- // stays identical. A plain string is still required to be non-empty.
85
- const isValid = isTiptapDocument(value)
86
- ? true
87
- : typeof value === 'string' && value.length > 0;
79
+ const isValid = typeof value === 'string' && value.length > 0;
88
80
 
89
81
  return {
90
82
  isValid,
@@ -30,6 +30,7 @@ export interface WebsiteContext {
30
30
  // Agent types
31
31
  export const AI_ASSISTANT_AGENT_TYPES = {
32
32
  WEBSITE_BUILDER: 'website_builder',
33
+ WEBSITE_BUILDER_V2: 'website_builder_v2',
33
34
  } as const;
34
35
 
35
36
  export type AiAssistantAgentType =
package/src/lib.ts CHANGED
@@ -85,15 +85,6 @@ export {
85
85
  recalculateHeaderLogoSizes,
86
86
  } from './brand-page/utils/logo.util';
87
87
  export { sanitizeRTEHtml } from './brand-page-maker/utils';
88
- // BP-5787: RTE JSON storage boundary helpers (render/normalise/sanitise/extract).
89
- export {
90
- renderRteToHtml,
91
- toRteJson,
92
- isTiptapDocument,
93
- sanitizeRteJson,
94
- getRteJsonPlainText,
95
- type RichTextValue,
96
- } from './brand-page-maker/utils';
97
88
  export { getPageSlugFromPageTitle } from './brand-page-maker/utils/slug.util';
98
89
 
99
90
  // AI-pipeline preview (longclaw per-turn snapshots): web-renderer's
@@ -1,5 +1,7 @@
1
1
  import { type BrandPageModel } from '../brand-page/models';
2
2
  import type { EventTracker } from '../brand-page-maker/composables/useEventTracker';
3
+ import { buildUpdateTextRequest } from './builders/update-content-text-builder';
4
+ import { buildUpdateTextStyleRequest } from './builders/update-content-text-style-builder';
3
5
  import type { UndoHandler } from './builders/undo-builder';
4
6
  import type { Logger } from './logger';
5
7
  import { MetadataExecutor } from './metadata-executor';
@@ -13,6 +15,8 @@ import {
13
15
  AI_ASSISTANT_TOOL_NAMES,
14
16
  SECTION_TOOL_MAP,
15
17
  type ParsedToolCall,
18
+ type UpdateStyleToolArguments,
19
+ type UpdateTextToolArguments,
16
20
  type DeleteSectionArguments,
17
21
  type MoveSectionArguments,
18
22
  type AddPageArguments,
@@ -33,6 +37,7 @@ export const executeToolCallsAsync = async (
33
37
  toolcalls: ParsedToolCall[],
34
38
  options: {
35
39
  logger?: Logger;
40
+ maxCharacters?: number;
36
41
  /**
37
42
  * Callback invoked by the UndoBuilder at execution time to resolve the
38
43
  * pre-prompt snapshot to restore to. The library is agnostic about where
@@ -57,6 +62,27 @@ export const executeToolCallsAsync = async (
57
62
  const requests: MetadataBuilderRequest[] = toolcalls
58
63
  .map((toolcall): MetadataBuilderRequest | null => {
59
64
  switch (toolcall.function) {
65
+ case AI_ASSISTANT_TOOL_NAMES.UPDATE_TEXT: {
66
+ const arg = toolcall.arguments as unknown as UpdateTextToolArguments;
67
+
68
+ return buildUpdateTextRequest(toolcall.toolId, {
69
+ contentId: arg.contentId,
70
+ fieldName: arg.fieldName,
71
+ newText: arg.metadata.text,
72
+ maxCharacters: options.maxCharacters,
73
+ });
74
+ }
75
+
76
+ case AI_ASSISTANT_TOOL_NAMES.UPDATE_STYLE: {
77
+ const arg = toolcall.arguments as unknown as UpdateStyleToolArguments;
78
+
79
+ return buildUpdateTextStyleRequest(toolcall.toolId, {
80
+ contentId: arg.contentId,
81
+ fieldName: arg.fieldName,
82
+ newStyles: arg.metadata.style,
83
+ });
84
+ }
85
+
60
86
  case AI_ASSISTANT_TOOL_NAMES.DELETE_SECTION: {
61
87
  const arg = toolcall.arguments as unknown as DeleteSectionArguments;
62
88