@designcrowd/library.brand-page 6.0.26-json-prereq-2 → 6.0.26

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 (155) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-CKBqjDH3.js → CreatedOnBrandCrowd-de-de-DU6YEVcW.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-CRXiimvT.js → CreatedOnBrandCrowd-en-us-DNWWK6Tj.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-DCYdY6sj.js → CreatedOnBrandCrowd-es-es-DVFvV9Qp.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-Dk7LOhxE.js → CreatedOnBrandCrowd-fr-ca-B4ZkjNdw.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-Dk7LOhxE.js → CreatedOnBrandCrowd-fr-fr-B4ZkjNdw.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-Cp4ufHJP.js → CreatedOnBrandCrowd-pt-br-DP7AQGgW.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-Cp4ufHJP.js → CreatedOnBrandCrowd-pt-pt-DP7AQGgW.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-ZSLkWMwM.js → CreatedOnDcom-de-de-CaC3GtG9.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-BgdIGfT3.js → CreatedOnDcom-en-us-D16yT0-P.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-D0KemkCZ.js → CreatedOnDcom-es-es-DA5sHgD_.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-BuwNjNbC.js → CreatedOnDcom-fr-ca-CDv2SG1-.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-DCQcsFPf.js → CreatedOnDcom-fr-fr-Cygc7c5d.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-DD-oLAS1.js → CreatedOnDcom-pt-br-B_3RIzgF.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-D8qLq2wW.js → CreatedOnDcom-pt-pt-Bz5w-rrD.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/rich-text-editor/bubbleMenu.d.ts +9 -2
  26. package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.js +10 -6
  27. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.d.ts +20 -0
  28. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.js +44 -0
  29. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.d.ts +27 -0
  30. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.js +44 -0
  31. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +1 -1
  32. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +103 -24
  33. package/lib/src/brand-page-maker/utils/index.d.ts +0 -2
  34. package/lib/src/brand-page-maker/utils/index.js +0 -2
  35. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +5 -28
  36. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +5 -48
  37. package/lib/src/brand-page-maker/validators/field.validator.js +2 -10
  38. package/lib/src/css/library.brand-page-brandCrowd.min.css +39 -39
  39. package/lib/src/css/library.brand-page-brandPage.min.css +39 -39
  40. package/lib/src/css/library.brand-page-designCom.min.css +39 -39
  41. package/lib/src/css/library.brand-page-preview.min.css +39 -39
  42. package/lib/src/index.d.ts +0 -1
  43. package/lib/src/index.mjs +138 -143
  44. package/lib/src/{lib-Db6j9fQZ.js → lib-6_P6kXPu.js} +51709 -55194
  45. package/lib/src/lib.js +0 -2
  46. package/lib/src/metadata-builder/ai-metadata-executor.d.ts +1 -0
  47. package/lib/src/metadata-builder/ai-metadata-executor.js +19 -0
  48. package/lib/src/metadata-builder/builders/section-builders/about-section-builder.js +3 -2
  49. package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.js +3 -2
  50. package/lib/src/metadata-builder/builders/section-builders/booking-section-builder.js +3 -2
  51. package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.js +4 -3
  52. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +3 -2
  53. package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.js +2 -1
  54. package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.js +2 -1
  55. package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.js +4 -3
  56. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +3 -2
  57. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +2 -1
  58. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +11 -15
  59. package/lib/src/metadata-builder/builders/update-content-text-builder.d.ts +17 -0
  60. package/lib/src/metadata-builder/builders/update-content-text-builder.js +103 -0
  61. package/lib/src/metadata-builder/builders/update-content-text-style-builder.d.ts +16 -0
  62. package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +216 -0
  63. package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +13 -11
  64. package/lib/src/metadata-builder/helpers/rte.helpers.js +73 -14
  65. package/lib/src/metadata-builder/metadata-executor.js +4 -0
  66. package/lib/src/metadata-builder/models.d.ts +2 -0
  67. package/lib/src/metadata-builder/models.js +2 -0
  68. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +19 -0
  69. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +4 -0
  70. package/lib/src/metadata-builder/sanitizers/rte-schema-check.d.ts +8 -7
  71. package/lib/src/metadata-builder/sanitizers/rte-schema-check.js +52 -21
  72. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.js +1 -14
  73. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +3 -12
  74. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.d.ts +2 -3
  75. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.js +3 -10
  76. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.d.ts +1 -2
  77. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.js +1 -4
  78. package/lib/src/utils/minify-brand-page-metadata.js +1 -4
  79. package/package.json +15 -17
  80. package/src/brand-page/components/contact-form/ContactForm.mixin.ts +2 -5
  81. package/src/brand-page/components/text-wrapper/TextWrapper.vue +6 -17
  82. package/src/brand-page/models/index.ts +26 -35
  83. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -3
  84. package/src/brand-page/utils/migration.util.ts +0 -12
  85. package/src/brand-page/utils/style.util.test.ts +0 -51
  86. package/src/brand-page/utils/text.util.ts +4 -43
  87. package/src/brand-page/utils/vcard.util.ts +4 -6
  88. package/src/brand-page/utils/view-model.util.ts +26 -94
  89. package/src/brand-page/view-models.ts +2 -3
  90. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +3 -4
  91. package/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.ts +1 -0
  92. package/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.ts +10 -6
  93. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.ts +73 -0
  94. package/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.ts +85 -0
  95. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +111 -27
  96. package/src/brand-page-maker/utils/index.ts +0 -2
  97. package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +3 -37
  98. package/src/brand-page-maker/utils/rich-text-editor.util.ts +11 -66
  99. package/src/brand-page-maker/validators/field.validator.ts +2 -10
  100. package/src/lib.ts +0 -9
  101. package/src/metadata-builder/ai-metadata-executor.ts +26 -0
  102. package/src/metadata-builder/builders/section-builders/about-section-builder.ts +3 -2
  103. package/src/metadata-builder/builders/section-builders/banner-section-builder.ts +3 -2
  104. package/src/metadata-builder/builders/section-builders/booking-section-builder.ts +5 -2
  105. package/src/metadata-builder/builders/section-builders/contact-form-builder.ts +4 -3
  106. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +3 -2
  107. package/src/metadata-builder/builders/section-builders/mailing-list-builder.ts +2 -1
  108. package/src/metadata-builder/builders/section-builders/mailing-list-section-builder.ts +2 -1
  109. package/src/metadata-builder/builders/section-builders/profile-section-builder.ts +6 -3
  110. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +3 -2
  111. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +2 -1
  112. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +13 -19
  113. package/src/metadata-builder/builders/update-content-text-builder.ts +148 -0
  114. package/src/metadata-builder/builders/update-content-text-style-builder.ts +352 -0
  115. package/src/metadata-builder/helpers/rte.helpers.ts +101 -15
  116. package/src/metadata-builder/metadata-executor.ts +5 -0
  117. package/src/metadata-builder/models.ts +3 -0
  118. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +27 -0
  119. package/src/metadata-builder/sanitizers/rte-schema-check.ts +53 -24
  120. package/src/shared/content-settings/components/GenericTextInput.mixin.ts +1 -17
  121. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +3 -16
  122. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.ts +4 -13
  123. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.vue +4 -6
  124. package/src/shared/content-settings/components/text-image-field/TextImageField.vue +3 -6
  125. package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.ts +1 -6
  126. package/src/shared/content-settings/components/text-items-field/TextItemsField.vue +1 -1
  127. package/src/utils/minify-brand-page-metadata.ts +1 -4
  128. package/lib/src/brand-page/utils/migration/migration-20.d.ts +0 -2
  129. package/lib/src/brand-page/utils/migration/migration-20.js +0 -66
  130. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.d.ts +0 -22
  131. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.js +0 -22
  132. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.d.ts +0 -14
  133. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.js +0 -39
  134. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.d.ts +0 -28
  135. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.js +0 -90
  136. package/lib/src/brand-page-maker/utils/rte-render.util.d.ts +0 -63
  137. package/lib/src/brand-page-maker/utils/rte-render.util.js +0 -95
  138. package/lib/src/brand-page-maker/utils/rte-sanitize.util.d.ts +0 -6
  139. package/lib/src/brand-page-maker/utils/rte-sanitize.util.js +0 -158
  140. package/src/brand-page/utils/migration/migration-20.server.test.ts +0 -125
  141. package/src/brand-page/utils/migration/migration-20.test.ts +0 -72
  142. package/src/brand-page/utils/migration/migration-20.ts +0 -87
  143. package/src/brand-page/utils/text.util.test.ts +0 -47
  144. package/src/brand-page/utils/view-model.pageid-rte.test.ts +0 -104
  145. package/src/brand-page-maker/__fixtures__/complex-rte.fixture.ts +0 -23
  146. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +0 -52
  147. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.ts +0 -42
  148. package/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.ts +0 -137
  149. package/src/brand-page-maker/utils/__snapshots__/rte-render-parity.test.ts.snap +0 -202
  150. package/src/brand-page-maker/utils/rte-features.test.ts +0 -351
  151. package/src/brand-page-maker/utils/rte-render-parity.test.ts +0 -157
  152. package/src/brand-page-maker/utils/rte-render.util.ts +0 -165
  153. package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +0 -158
  154. package/src/brand-page-maker/utils/rte-sanitize.util.ts +0 -195
  155. package/src/brand-page-maker/validators/field.validator.test.ts +0 -55
@@ -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,
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
 
@@ -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,