@designcrowd/library.brand-page 6.1.1-json-prereq-3 → 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-hj_ievMc.js → CreatedOnBrandCrowd-de-de-B5B5ThJ6.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-jAOSpfCw.js → CreatedOnBrandCrowd-en-us-a48CaORy.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-DZMVc-y3.js → CreatedOnBrandCrowd-es-es-xpuZ317-.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-DvB54H38.js → CreatedOnBrandCrowd-fr-ca-V-iUMArW.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-DvB54H38.js → CreatedOnBrandCrowd-fr-fr-V-iUMArW.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-bf-x1xhi.js → CreatedOnBrandCrowd-pt-br-DZ0NKSAy.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-bf-x1xhi.js → CreatedOnBrandCrowd-pt-pt-DZ0NKSAy.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-B-NY1B44.js → CreatedOnDcom-de-de-_950cgGp.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-Cd4C6KQf.js → CreatedOnDcom-en-us-DogKzH-g.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-DONlsQsm.js → CreatedOnDcom-es-es-CmGM3IPX.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-Dsv3FRpZ.js → CreatedOnDcom-fr-ca-BonEDkWZ.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-DOgyvY5C.js → CreatedOnDcom-fr-fr-DQHtKrd_.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-DX66MhJS.js → CreatedOnDcom-pt-br-CtYJSBjs.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-COe2G0bU.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 -28
  54. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +5 -48
  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-scDtk-Pi.js → lib-DtalJKI3.js} +72445 -75920
  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 -37
  157. package/src/brand-page-maker/utils/rich-text-editor.util.ts +11 -66
  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
@@ -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,
@@ -1,3 +1,5 @@
1
+ import type { UserPersonalisationContent } from '@brand-page/models';
2
+
1
3
  export interface Paginated<T> {
2
4
  paginatedItems: T[];
3
5
  page: number;
@@ -5,6 +7,7 @@ export interface Paginated<T> {
5
7
  pageCount: number;
6
8
  totalResultCount: number;
7
9
  abTestValuesToBePushed?: Record<string, string>;
10
+ searchPersonalisation?: UserPersonalisationContent[];
8
11
  }
9
12
 
10
13
  export interface DesignTemplate {
package/src/lib.ts CHANGED
@@ -62,6 +62,7 @@ export {
62
62
  import LogoRender from './brand-page/components/logo/LogoRender.vue';
63
63
  export { LogoRender };
64
64
  export { default as Page } from './brand-page/components/page/Page.vue';
65
+ export { Loader } from '@designcrowd/fe-shared-lib';
65
66
 
66
67
  export { default as CookieConsent } from './brand-page/components/cookie-consent/CookieConsent.vue';
67
68
  export { default as SensitiveContentModal } from './brand-page/components/sensitive-content/SensitiveContentModal.vue';
@@ -85,15 +86,6 @@ export {
85
86
  recalculateHeaderLogoSizes,
86
87
  } from './brand-page/utils/logo.util';
87
88
  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
89
  export { getPageSlugFromPageTitle } from './brand-page-maker/utils/slug.util';
98
90
 
99
91
  // 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
 
@@ -3,6 +3,7 @@ import type {
3
3
  BrandPageModel,
4
4
  TextStyleDeclaration,
5
5
  } from '../../../brand-page/models';
6
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
6
7
  import type { BuilderResult } from '../../models';
7
8
  import {
8
9
  isTiptapDocument,
@@ -53,7 +54,7 @@ export class AboutSectionBuilder extends BaseSectionBuilder<AboutSectionContent>
53
54
 
54
55
  if (aboutArgs.title !== undefined) {
55
56
  if (isTiptapDocument(aboutArgs.title)) {
56
- resolved.content.title = aboutArgs.title;
57
+ resolved.content.title = tiptapJsonToHtml(aboutArgs.title);
57
58
  resolved.content.titleStyle = resolveGlobalPresetIntoStyle(
58
59
  resolved.content.titleStyle,
59
60
  textStyleOptions,
@@ -65,7 +66,7 @@ export class AboutSectionBuilder extends BaseSectionBuilder<AboutSectionContent>
65
66
 
66
67
  if (aboutArgs.paragraph !== undefined) {
67
68
  if (isTiptapDocument(aboutArgs.paragraph)) {
68
- resolved.content.paragraph = aboutArgs.paragraph;
69
+ resolved.content.paragraph = tiptapJsonToHtml(aboutArgs.paragraph);
69
70
  resolved.content.paragraphStyle = resolveGlobalPresetIntoStyle(
70
71
  resolved.content.paragraphStyle,
71
72
  textStyleOptions,
@@ -3,6 +3,7 @@ import type {
3
3
  BrandPageModel,
4
4
  TextStyleDeclaration,
5
5
  } from '../../../brand-page/models';
6
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
6
7
  import type { BuilderResult } from '../../models';
7
8
  import {
8
9
  isTiptapDocument,
@@ -90,7 +91,7 @@ export class BannerSectionBuilder extends BaseSectionBuilder<BannerSectionConten
90
91
 
91
92
  if (bannerArgs.title !== undefined) {
92
93
  if (isTiptapDocument(bannerArgs.title)) {
93
- resolved.content.title = bannerArgs.title;
94
+ resolved.content.title = tiptapJsonToHtml(bannerArgs.title);
94
95
  resolved.content.titleStyle = resolveGlobalPresetIntoStyle(
95
96
  resolved.content.titleStyle,
96
97
  textStyleOptions,
@@ -102,7 +103,7 @@ export class BannerSectionBuilder extends BaseSectionBuilder<BannerSectionConten
102
103
 
103
104
  if (bannerArgs.description !== undefined) {
104
105
  if (isTiptapDocument(bannerArgs.description)) {
105
- resolved.content.description = bannerArgs.description;
106
+ resolved.content.description = tiptapJsonToHtml(bannerArgs.description);
106
107
  resolved.content.descriptionStyle = resolveGlobalPresetIntoStyle(
107
108
  resolved.content.descriptionStyle,
108
109
  textStyleOptions,
@@ -3,6 +3,7 @@ import type {
3
3
  BrandPageModel,
4
4
  TextStyleDeclaration,
5
5
  } from '../../../brand-page/models';
6
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
6
7
  import type { BuilderResult } from '../../models';
7
8
  import {
8
9
  isTiptapDocument,
@@ -50,7 +51,7 @@ export class BookingSectionBuilder extends BaseSectionBuilder<BookingSectionCont
50
51
 
51
52
  if (bookingArgs.title !== undefined) {
52
53
  if (isTiptapDocument(bookingArgs.title)) {
53
- resolved.content.title = bookingArgs.title;
54
+ resolved.content.title = tiptapJsonToHtml(bookingArgs.title);
54
55
  resolved.content.titleStyle = resolveGlobalPresetIntoStyle(
55
56
  resolved.content.titleStyle,
56
57
  textStyleOptions,
@@ -62,7 +63,9 @@ export class BookingSectionBuilder extends BaseSectionBuilder<BookingSectionCont
62
63
 
63
64
  if (bookingArgs.description !== undefined) {
64
65
  if (isTiptapDocument(bookingArgs.description)) {
65
- resolved.content.description = bookingArgs.description;
66
+ resolved.content.description = tiptapJsonToHtml(
67
+ bookingArgs.description,
68
+ );
66
69
  resolved.content.descriptionStyle = resolveGlobalPresetIntoStyle(
67
70
  resolved.content.descriptionStyle,
68
71
  textStyleOptions,
@@ -3,6 +3,7 @@ import type {
3
3
  BrandPageModel,
4
4
  TextStyleDeclaration,
5
5
  } from '../../../brand-page/models';
6
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
6
7
  import {
7
8
  isTiptapDocument,
8
9
  type ContactFormSectionArguments,
@@ -49,7 +50,7 @@ export class ContactFormBuilder extends BaseSectionBuilder<ContactFormContent> {
49
50
 
50
51
  if (args.title !== undefined) {
51
52
  if (isTiptapDocument(args.title)) {
52
- content.title = args.title;
53
+ content.title = tiptapJsonToHtml(args.title);
53
54
  content.titleStyle = resolveGlobalPresetIntoStyle(
54
55
  content.titleStyle,
55
56
  textStyleOptions,
@@ -61,7 +62,7 @@ export class ContactFormBuilder extends BaseSectionBuilder<ContactFormContent> {
61
62
 
62
63
  if (args.description !== undefined) {
63
64
  if (isTiptapDocument(args.description)) {
64
- content.description = args.description;
65
+ content.description = tiptapJsonToHtml(args.description);
65
66
  content.descriptionStyle = resolveGlobalPresetIntoStyle(
66
67
  content.descriptionStyle,
67
68
  textStyleOptions,
@@ -73,7 +74,7 @@ export class ContactFormBuilder extends BaseSectionBuilder<ContactFormContent> {
73
74
 
74
75
  if (args.success_message !== undefined) {
75
76
  if (isTiptapDocument(args.success_message)) {
76
- content.successMessage = args.success_message;
77
+ content.successMessage = tiptapJsonToHtml(args.success_message);
77
78
  content.successMessageStyle = resolveGlobalPresetIntoStyle(
78
79
  content.successMessageStyle,
79
80
  textStyleOptions,
@@ -3,6 +3,7 @@ import type {
3
3
  BrandPageModel,
4
4
  TextStyleDeclaration,
5
5
  } from '../../../brand-page/models';
6
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
6
7
  import type { BuilderResult } from '../../models';
7
8
  import {
8
9
  isTiptapDocument,
@@ -141,7 +142,7 @@ export class ImagesSectionBuilder extends BaseSectionBuilder<ImagesSectionConten
141
142
 
142
143
  if (args.title !== undefined) {
143
144
  if (isTiptapDocument(args.title)) {
144
- content.title = args.title;
145
+ content.title = tiptapJsonToHtml(args.title);
145
146
  content.titleStyle = resolveGlobalPresetIntoStyle(
146
147
  content.titleStyle,
147
148
  textStyleOptions,
@@ -153,7 +154,7 @@ export class ImagesSectionBuilder extends BaseSectionBuilder<ImagesSectionConten
153
154
 
154
155
  if (args.description !== undefined) {
155
156
  if (isTiptapDocument(args.description)) {
156
- content.description = args.description;
157
+ content.description = tiptapJsonToHtml(args.description);
157
158
  content.descriptionStyle = resolveGlobalPresetIntoStyle(
158
159
  content.descriptionStyle,
159
160
  textStyleOptions,
@@ -4,6 +4,7 @@ import type {
4
4
  MailingListContent,
5
5
  TextStyleDeclaration,
6
6
  } from '../../../brand-page/models';
7
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
7
8
  import {
8
9
  isTiptapDocument,
9
10
  type MailingListSectionArguments,
@@ -49,7 +50,7 @@ export class MailingListBuilder extends BaseSectionBuilder<MailingListContent> {
49
50
 
50
51
  if (mailingListArgs.title !== undefined) {
51
52
  if (isTiptapDocument(mailingListArgs.title)) {
52
- content.title = mailingListArgs.title;
53
+ content.title = tiptapJsonToHtml(mailingListArgs.title);
53
54
  content.titleStyle = resolveGlobalPresetIntoStyle(
54
55
  content.titleStyle,
55
56
  textStyleOptions,
@@ -3,6 +3,7 @@ import type {
3
3
  BrandPageModel,
4
4
  TextStyleDeclaration,
5
5
  } from '../../../brand-page/models';
6
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
6
7
  import type { BuilderResult } from '../../models';
7
8
  import {
8
9
  isTiptapDocument,
@@ -47,7 +48,7 @@ export class MailingListSectionBuilder extends BaseSectionBuilder<MailingListCon
47
48
 
48
49
  if (mailingListArgs.title !== undefined) {
49
50
  if (isTiptapDocument(mailingListArgs.title)) {
50
- resolved.content.title = mailingListArgs.title;
51
+ resolved.content.title = tiptapJsonToHtml(mailingListArgs.title);
51
52
  resolved.content.titleStyle = resolveGlobalPresetIntoStyle(
52
53
  resolved.content.titleStyle,
53
54
  textStyleOptions,
@@ -3,6 +3,7 @@ import type {
3
3
  BrandPageModel,
4
4
  TextStyleDeclaration,
5
5
  } from '../../../brand-page/models';
6
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
6
7
  import type { BuilderResult } from '../../models';
7
8
  import {
8
9
  isTiptapDocument,
@@ -40,7 +41,7 @@ export class ProfileSectionBuilder extends BaseSectionBuilder<ProfileContent> {
40
41
 
41
42
  if (profileArgs.name !== undefined) {
42
43
  if (isTiptapDocument(profileArgs.name)) {
43
- resolved.content.name = profileArgs.name;
44
+ resolved.content.name = tiptapJsonToHtml(profileArgs.name);
44
45
  resolved.content.nameStyle = resolveGlobalPresetIntoStyle(
45
46
  resolved.content.nameStyle,
46
47
  textStyleOptions,
@@ -52,7 +53,7 @@ export class ProfileSectionBuilder extends BaseSectionBuilder<ProfileContent> {
52
53
 
53
54
  if (profileArgs.title !== undefined) {
54
55
  if (isTiptapDocument(profileArgs.title)) {
55
- resolved.content.title = profileArgs.title;
56
+ resolved.content.title = tiptapJsonToHtml(profileArgs.title);
56
57
  resolved.content.titleStyle = resolveGlobalPresetIntoStyle(
57
58
  resolved.content.titleStyle,
58
59
  textStyleOptions,
@@ -64,7 +65,9 @@ export class ProfileSectionBuilder extends BaseSectionBuilder<ProfileContent> {
64
65
 
65
66
  if (profileArgs.description !== undefined) {
66
67
  if (isTiptapDocument(profileArgs.description)) {
67
- resolved.content.description = profileArgs.description;
68
+ resolved.content.description = tiptapJsonToHtml(
69
+ profileArgs.description,
70
+ );
68
71
  resolved.content.descriptionStyle = resolveGlobalPresetIntoStyle(
69
72
  resolved.content.descriptionStyle,
70
73
  textStyleOptions,
@@ -5,6 +5,7 @@ import type {
5
5
  TextShapeItem,
6
6
  TextStyleDeclaration,
7
7
  } from '../../../brand-page/models';
8
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
8
9
  import type { BuilderResult } from '../../models';
9
10
  import {
10
11
  isTiptapDocument,
@@ -194,7 +195,7 @@ export class ShapesSectionBuilder extends BaseSectionBuilder<ShapesSectionConten
194
195
 
195
196
  if (args.title !== undefined) {
196
197
  if (isTiptapDocument(args.title)) {
197
- content.title = args.title;
198
+ content.title = tiptapJsonToHtml(args.title);
198
199
  content.titleStyle = resolveGlobalPresetIntoStyle(
199
200
  content.titleStyle,
200
201
  textStyleOptions,
@@ -206,7 +207,7 @@ export class ShapesSectionBuilder extends BaseSectionBuilder<ShapesSectionConten
206
207
 
207
208
  if (args.description !== undefined) {
208
209
  if (isTiptapDocument(args.description)) {
209
- content.description = args.description;
210
+ content.description = tiptapJsonToHtml(args.description);
210
211
  content.descriptionStyle = resolveGlobalPresetIntoStyle(
211
212
  content.descriptionStyle,
212
213
  textStyleOptions,
@@ -4,6 +4,7 @@ import type {
4
4
  BrandPageModel,
5
5
  TextStyleDeclaration,
6
6
  } from '../../../brand-page/models';
7
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
7
8
  import {
8
9
  isTiptapDocument,
9
10
  type TestimonialItem,
@@ -172,7 +173,7 @@ export class TestimonialSectionBuilder extends BaseSectionBuilder<TestimonialSec
172
173
 
173
174
  if (args.title !== undefined) {
174
175
  if (isTiptapDocument(args.title)) {
175
- content.title = args.title;
176
+ content.title = tiptapJsonToHtml(args.title);
176
177
  content.titleStyle = resolveGlobalPresetIntoStyle(
177
178
  content.titleStyle,
178
179
  textStyleOptions,
@@ -4,9 +4,9 @@ import type {
4
4
  TextItem,
5
5
  TextStyleDeclaration,
6
6
  BrandPageModel,
7
- RichTextValue,
8
7
  } from '../../../brand-page/models';
9
8
  import type { TextStyleOptions } from '../../../brand-page/models/styles.model';
9
+ import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
10
10
  import {
11
11
  isTiptapDocument,
12
12
  type TextSectionArguments,
@@ -26,18 +26,12 @@ import {
26
26
  repositionItemByAnchor,
27
27
  } from '../item-id-utils';
28
28
 
29
- /**
30
- * Resolve a RichTextField to its stored RTE value. BP-5787: Tiptap JSON is
31
- * stored as-is (no JSON→HTML round-trip — migration-20 is now a one-time
32
- * legacy conversion), plain strings pass through. `wasTiptap` drives the
33
- * text-style preset resolution for rich content.
34
- */
35
- const resolveText = (
36
- value: unknown,
37
- ): { value: RichTextValue; wasTiptap: boolean } => {
38
- if (isTiptapDocument(value)) return { value, wasTiptap: true };
29
+ /** Resolve a RichTextField (string or TiptapDocument) to a plain HTML string. */
30
+ const resolveText = (value: unknown): { html: string; wasTiptap: boolean } => {
31
+ if (isTiptapDocument(value))
32
+ return { html: tiptapJsonToHtml(value), wasTiptap: true };
39
33
 
40
- return { value: typeof value === 'string' ? value : '', wasTiptap: false };
34
+ return { html: typeof value === 'string' ? value : '', wasTiptap: false };
41
35
  };
42
36
 
43
37
  const clearTextStyleTypeIfTiptap = (
@@ -61,11 +55,11 @@ const createTextItem = (
61
55
  item.text_style ? { ...baseStyle, ...item.text_style } : { ...baseStyle }
62
56
  ) as TextStyleDeclaration;
63
57
 
64
- const { value, wasTiptap } = resolveText(item.text);
58
+ const { html, wasTiptap } = resolveText(item.text);
65
59
 
66
60
  return {
67
61
  id: item.item_id || generateItemId(),
68
- text: value,
62
+ text: html,
69
63
  textStyle: clearTextStyleTypeIfTiptap(
70
64
  textStyle,
71
65
  wasTiptap,
@@ -117,9 +111,9 @@ const mergeTextItems = (
117
111
  if (!existing) continue;
118
112
 
119
113
  if (item.text !== undefined) {
120
- const { value, wasTiptap } = resolveText(item.text);
114
+ const { html, wasTiptap } = resolveText(item.text);
121
115
 
122
- existing.text = value;
116
+ existing.text = html;
123
117
  existing.textStyle = clearTextStyleTypeIfTiptap(
124
118
  existing.textStyle || {},
125
119
  wasTiptap,
@@ -166,12 +160,12 @@ const applySingleTextField = (
166
160
  textStyleOptions: TextStyleOptions | undefined,
167
161
  ): void => {
168
162
  if (args.text !== undefined) {
169
- const { value, wasTiptap } = resolveText(args.text);
163
+ const { html, wasTiptap } = resolveText(args.text);
170
164
 
171
165
  if (Array.isArray(content.textItems) && content.textItems.length > 0) {
172
166
  content.textItems[0] = {
173
167
  ...content.textItems[0],
174
- text: value,
168
+ text: html,
175
169
  textStyle: clearTextStyleTypeIfTiptap(
176
170
  content.textItems[0].textStyle || {},
177
171
  wasTiptap,
@@ -182,7 +176,7 @@ const applySingleTextField = (
182
176
  content.textItems = [
183
177
  {
184
178
  id: generateItemId(),
185
- text: value,
179
+ text: html,
186
180
  textStyle: clearTextStyleTypeIfTiptap(
187
181
  { ...(content.textStyle || {}) },
188
182
  wasTiptap,
@@ -0,0 +1,148 @@
1
+ import { type BrandPageModel } from '../../brand-page/models';
2
+ import type { Logger } from '../logger';
3
+ import {
4
+ BaseMetadataBuilder,
5
+ BUILDER_TYPES,
6
+ type BuilderResult,
7
+ type MetadataBuilderRequest,
8
+ } from '../models';
9
+ import { hasRTEContent, replaceTextInRTE } from '../../brand-page-maker/utils';
10
+
11
+ export interface UpdateTextPayload {
12
+ contentId: string;
13
+ fieldName: string;
14
+ newText: string;
15
+ maxCharacters?: number;
16
+ }
17
+
18
+ export const buildUpdateTextRequest = (
19
+ id: string,
20
+ payload: UpdateTextPayload,
21
+ ): MetadataBuilderRequest<UpdateTextPayload> => {
22
+ return {
23
+ id,
24
+ type: BUILDER_TYPES.UPDATE_CONTENT_TEXT,
25
+ payload,
26
+ };
27
+ };
28
+
29
+ const updateTextField = (target: any, fieldName: string, newText: string) => {
30
+ // Check if this is an array item path like "textItems.0.text"
31
+ const match = fieldName.match(/^(\w+)\.(\d+)\.(\w+)$/);
32
+
33
+ if (match) {
34
+ const [, arrayName, index, property] = match;
35
+ const array = target[arrayName];
36
+
37
+ if (Array.isArray(array) && array[+index]) {
38
+ const existingText = array[+index][property];
39
+ const updatedValue =
40
+ existingText &&
41
+ typeof existingText === 'string' &&
42
+ hasRTEContent(existingText)
43
+ ? replaceTextInRTE(existingText, newText)
44
+ : newText;
45
+
46
+ // Create a new array with the updated item to trigger reactivity
47
+ const newArray = [...array];
48
+
49
+ newArray[+index] = {
50
+ ...newArray[+index],
51
+ [property]: updatedValue,
52
+ };
53
+ target[arrayName] = newArray;
54
+ }
55
+ } else {
56
+ // Simple field
57
+ const existingText = target[fieldName];
58
+
59
+ if (
60
+ existingText &&
61
+ typeof existingText === 'string' &&
62
+ hasRTEContent(existingText)
63
+ ) {
64
+ target[fieldName] = replaceTextInRTE(existingText, newText);
65
+ } else {
66
+ target[fieldName] = newText;
67
+ }
68
+ }
69
+ };
70
+
71
+ export class UpdateContentTextBuilder extends BaseMetadataBuilder<UpdateTextPayload> {
72
+ constructor(
73
+ request: MetadataBuilderRequest<UpdateTextPayload>,
74
+ logger?: Logger,
75
+ ) {
76
+ super(request, logger);
77
+ }
78
+
79
+ validateAsync(): Promise<{ errors: string[] }> {
80
+ const newText = this.request.payload?.newText ?? '';
81
+
82
+ const errors: string[] = [];
83
+
84
+ const maxChars = this.request.payload?.maxCharacters ?? 2000;
85
+
86
+ if (newText.length > maxChars) {
87
+ errors.push(`Text exceeds maximum allowed length of ${maxChars}`);
88
+ }
89
+
90
+ this.log({
91
+ message: `Validation called`,
92
+ errors,
93
+ });
94
+
95
+ return Promise.resolve({ errors: errors });
96
+ }
97
+
98
+ async buildAsync(model: BrandPageModel): Promise<BuilderResult> {
99
+ this.log(`Build called`);
100
+
101
+ const { contentId, fieldName, newText } = this.request.payload!;
102
+
103
+ const validation = await this.validateAsync();
104
+
105
+ if (validation.errors.length) {
106
+ return {
107
+ request: this.request,
108
+ model,
109
+ errors: validation.errors,
110
+ };
111
+ }
112
+
113
+ let updated = false;
114
+
115
+ for (const page of model.pages) {
116
+ for (const content of page.contents) {
117
+ // Use includes to handle potential ID format differences
118
+ if (content.id.includes(contentId) || contentId.includes(content.id)) {
119
+ this.log({
120
+ message: `Content found`,
121
+ model,
122
+ });
123
+
124
+ updateTextField(content, fieldName, newText);
125
+
126
+ this.log({
127
+ message: `Successfully updated text content.`,
128
+ model,
129
+ });
130
+
131
+ updated = true;
132
+
133
+ break;
134
+ }
135
+ }
136
+ }
137
+
138
+ return {
139
+ request: this.request,
140
+ model,
141
+ errors: updated
142
+ ? undefined
143
+ : [
144
+ `Could not update the text because content with ID "${contentId}" not found.`,
145
+ ],
146
+ };
147
+ }
148
+ }