@designcrowd/library.brand-page 6.0.19 → 6.0.20

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 (97) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-DCwwdYPj.js → CreatedOnBrandCrowd-de-de-C52IyXZu.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-DTmZeDQ1.js → CreatedOnBrandCrowd-en-us-DhTy_dEV.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-Dbrz6TXv.js → CreatedOnBrandCrowd-es-es-BJelIAQ4.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-DLQhgn8T.js → CreatedOnBrandCrowd-fr-ca-DwJohy0T.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-DLQhgn8T.js → CreatedOnBrandCrowd-fr-fr-DwJohy0T.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-yAzzD2ik.js → CreatedOnBrandCrowd-pt-br-CMzKbxdG.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-yAzzD2ik.js → CreatedOnBrandCrowd-pt-pt-CMzKbxdG.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-CNmNNE5o.js → CreatedOnDcom-de-de-DAy1GHrw.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-BUmLFlS_.js → CreatedOnDcom-en-us-CgBDAkJN.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-DfygM_0q.js → CreatedOnDcom-es-es-Ds9d5rem.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-DIEft8xE.js → CreatedOnDcom-fr-ca-CZL0BQ-u.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-Cn3jWG2n.js → CreatedOnDcom-fr-fr-BVYh5Wvo.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-DXhgGwEh.js → CreatedOnDcom-pt-br-1hsWNXfq.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-BvE9ckw1.js → CreatedOnDcom-pt-pt-DvM0N-3M.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/rich-text-editor/useRichTextEditor.js +100 -21
  25. package/lib/src/brand-page-maker/utils/index.d.ts +0 -2
  26. package/lib/src/brand-page-maker/utils/index.js +0 -2
  27. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +5 -19
  28. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +5 -35
  29. package/lib/src/brand-page-maker/validators/field.validator.js +2 -10
  30. package/lib/src/css/library.brand-page-brandCrowd.min.css +1 -1
  31. package/lib/src/css/library.brand-page-brandPage.min.css +1 -1
  32. package/lib/src/css/library.brand-page-designCom.min.css +1 -1
  33. package/lib/src/css/library.brand-page-preview.min.css +1 -1
  34. package/lib/src/index.d.ts +0 -1
  35. package/lib/src/index.mjs +138 -143
  36. package/lib/src/{lib-Bm-fYap1.js → lib-CK2FPBv5.js} +55098 -55367
  37. package/lib/src/lib.js +0 -2
  38. package/lib/src/metadata-builder/builders/update-content-text-builder.js +15 -20
  39. package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +3 -8
  40. package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +2 -2
  41. package/lib/src/metadata-builder/helpers/rte.helpers.js +3 -6
  42. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.js +1 -14
  43. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +3 -12
  44. package/lib/src/utils/minify-brand-page-metadata.js +1 -4
  45. package/package.json +1 -1
  46. package/src/brand-page/components/contact-form/ContactForm.mixin.ts +2 -5
  47. package/src/brand-page/components/text-wrapper/TextWrapper.vue +6 -17
  48. package/src/brand-page/models/index.ts +26 -35
  49. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -3
  50. package/src/brand-page/utils/migration.util.ts +0 -12
  51. package/src/brand-page/utils/style.util.test.ts +0 -51
  52. package/src/brand-page/utils/text.util.ts +4 -43
  53. package/src/brand-page/utils/vcard.util.ts +4 -6
  54. package/src/brand-page/utils/view-model.util.ts +26 -94
  55. package/src/brand-page/view-models.ts +2 -3
  56. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +107 -23
  57. package/src/brand-page-maker/utils/index.ts +0 -2
  58. package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +3 -4
  59. package/src/brand-page-maker/utils/rich-text-editor.util.ts +11 -50
  60. package/src/brand-page-maker/validators/field.validator.ts +2 -10
  61. package/src/lib.ts +0 -9
  62. package/src/metadata-builder/builders/update-content-text-builder.ts +18 -32
  63. package/src/metadata-builder/builders/update-content-text-style-builder.ts +4 -14
  64. package/src/metadata-builder/helpers/rte.helpers.ts +7 -11
  65. package/src/shared/content-settings/components/GenericTextInput.mixin.ts +1 -17
  66. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +3 -16
  67. package/src/utils/minify-brand-page-metadata.ts +1 -4
  68. package/lib/src/brand-page/utils/migration/migration-19.d.ts +0 -2
  69. package/lib/src/brand-page/utils/migration/migration-19.js +0 -71
  70. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.d.ts +0 -22
  71. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.js +0 -22
  72. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.d.ts +0 -14
  73. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.js +0 -39
  74. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.d.ts +0 -28
  75. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.js +0 -66
  76. package/lib/src/brand-page-maker/utils/rte-render.util.d.ts +0 -33
  77. package/lib/src/brand-page-maker/utils/rte-render.util.js +0 -50
  78. package/lib/src/brand-page-maker/utils/rte-sanitize.util.d.ts +0 -6
  79. package/lib/src/brand-page-maker/utils/rte-sanitize.util.js +0 -158
  80. package/lib/src/brand-page-maker/utils/rte-serializer.d.ts +0 -5
  81. package/lib/src/brand-page-maker/utils/rte-serializer.js +0 -112
  82. package/src/brand-page/utils/migration/migration-19.test.ts +0 -72
  83. package/src/brand-page/utils/migration/migration-19.ts +0 -83
  84. package/src/brand-page/utils/text.util.test.ts +0 -47
  85. package/src/brand-page/utils/view-model.pageid-rte.test.ts +0 -104
  86. package/src/brand-page-maker/__fixtures__/complex-rte.fixture.ts +0 -23
  87. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +0 -52
  88. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.ts +0 -42
  89. package/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.ts +0 -113
  90. package/src/brand-page-maker/utils/__snapshots__/rte-render-parity.test.ts.snap +0 -200
  91. package/src/brand-page-maker/utils/rte-features.test.ts +0 -330
  92. package/src/brand-page-maker/utils/rte-render-parity.test.ts +0 -156
  93. package/src/brand-page-maker/utils/rte-render.util.ts +0 -77
  94. package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +0 -158
  95. package/src/brand-page-maker/utils/rte-sanitize.util.ts +0 -195
  96. package/src/brand-page-maker/utils/rte-serializer.ts +0 -172
  97. package/src/brand-page-maker/validators/field.validator.test.ts +0 -55
package/lib/src/lib.js CHANGED
@@ -30,8 +30,6 @@ export { minifyBrandPageMetadata, } from './utils/minify-brand-page-metadata';
30
30
  export * as brandPageUtils from './brand-page/utils';
31
31
  export { getOptimalLogoSize, calculateOptimizedLogoDimensions, applyLogoImageBase64UrlToBrandPageTemplateModel, recalculateLogoContentDimensions, recalculateHeaderLogoSizes, } from './brand-page/utils/logo.util';
32
32
  export { sanitizeRTEHtml } from './brand-page-maker/utils';
33
- // BP-5787: RTE JSON storage boundary helpers (render/normalise/sanitise/extract).
34
- export { renderRteToHtml, toRteJson, isTiptapDocument, sanitizeRteJson, getRteJsonPlainText, } from './brand-page-maker/utils';
35
33
  export { getPageSlugFromPageTitle } from './brand-page-maker/utils/slug.util';
36
34
  // AI-pipeline preview (longclaw per-turn snapshots): web-renderer's
37
35
  // /preview/ai-pipeline render page + manifest server route apply the agent's
@@ -1,23 +1,6 @@
1
1
  import {} from '../../brand-page/models';
2
2
  import { BaseMetadataBuilder, BUILDER_TYPES, } from '../models';
3
- import { hasRTEContent, replaceTextInRTE, isTiptapDocument, renderRteToHtml, toRteJson, } from '../../brand-page-maker/utils';
4
- /**
5
- * BP-5787 — replace the text of an RTE field while preserving formatting, for
6
- * either storage form. JSON is rendered to HTML, edited via the existing
7
- * HTML-preserving replace, then converted back to JSON; plain/legacy strings
8
- * keep the original behaviour.
9
- */
10
- const updateRteFieldValue = (existing, newText) => {
11
- const wasJson = isTiptapDocument(existing);
12
- const existingHtml = wasJson ? renderRteToHtml(existing) : existing;
13
- if (existingHtml &&
14
- typeof existingHtml === 'string' &&
15
- hasRTEContent(existingHtml)) {
16
- const updatedHtml = replaceTextInRTE(existingHtml, newText);
17
- return wasJson ? toRteJson(updatedHtml) : updatedHtml;
18
- }
19
- return newText;
20
- };
3
+ import { hasRTEContent, replaceTextInRTE } from '../../brand-page-maker/utils';
21
4
  export const buildUpdateTextRequest = (id, payload) => {
22
5
  return {
23
6
  id,
@@ -33,7 +16,11 @@ const updateTextField = (target, fieldName, newText) => {
33
16
  const array = target[arrayName];
34
17
  if (Array.isArray(array) && array[+index]) {
35
18
  const existingText = array[+index][property];
36
- const updatedValue = updateRteFieldValue(existingText, newText);
19
+ const updatedValue = existingText &&
20
+ typeof existingText === 'string' &&
21
+ hasRTEContent(existingText)
22
+ ? replaceTextInRTE(existingText, newText)
23
+ : newText;
37
24
  // Create a new array with the updated item to trigger reactivity
38
25
  const newArray = [...array];
39
26
  newArray[+index] = {
@@ -45,7 +32,15 @@ const updateTextField = (target, fieldName, newText) => {
45
32
  }
46
33
  else {
47
34
  // Simple field
48
- target[fieldName] = updateRteFieldValue(target[fieldName], newText);
35
+ const existingText = target[fieldName];
36
+ if (existingText &&
37
+ typeof existingText === 'string' &&
38
+ hasRTEContent(existingText)) {
39
+ target[fieldName] = replaceTextInRTE(existingText, newText);
40
+ }
41
+ else {
42
+ target[fieldName] = newText;
43
+ }
49
44
  }
50
45
  };
51
46
  export class UpdateContentTextBuilder extends BaseMetadataBuilder {
@@ -35,19 +35,14 @@ const mergeStyles = (currentStyle, stylesToUpdate, textStyleOptions) => {
35
35
  });
36
36
  return merged;
37
37
  };
38
- // BP-5787 — apply a style to RTE text natively. applyStyleToRTEText runs Tiptap
39
- // commands on the doc (string or JSON in) and returns JSON — no HTML round-trip.
40
38
  const updateRTEText = (text, style) => {
41
- // `text` arrives via dynamic content[field] access, so it's untyped here.
42
- // hasRTEContent runtime-narrows it to an actual RTE value (HTML string or
43
- // Tiptap JSON doc) — i.e. a RichTextValue — before we hand it to the editor.
44
- if (!hasRTEContent(text)) {
39
+ if (!text || typeof text !== 'string' || !hasRTEContent(text)) {
45
40
  return text;
46
41
  }
47
42
  const { externalEditor } = useExternalRichTextEditor();
48
- const updated = applyStyleToRTEText(externalEditor, text, style);
43
+ const updatedText = applyStyleToRTEText(externalEditor, text, style);
49
44
  externalEditor.destroy();
50
- return updated;
45
+ return updatedText;
51
46
  };
52
47
  const findStyleKey = (target, property) => {
53
48
  // Try exact match first (e.g., 'testimonialsStyle')
@@ -1,6 +1,6 @@
1
- import type { RichTextValue, TextStyleDeclaration } from '../../brand-page';
1
+ import type { TextStyleDeclaration } from '../../brand-page';
2
2
  import type { Editor } from '@tiptap/vue-3';
3
- export declare const applyStyleToRTEText: (externalEditor: Editor, contentText: RichTextValue, newContentStyle: TextStyleDeclaration) => string | import("@tiptap/vue-3").JSONContent;
3
+ export declare const applyStyleToRTEText: (externalEditor: Editor, contentText: string, newContentStyle: TextStyleDeclaration) => string;
4
4
  /**
5
5
  * Converts a Tiptap JSON document (ProseMirror document model) to an HTML
6
6
  * string using a headless Tiptap Editor instance.
@@ -7,11 +7,7 @@ export const applyStyleToRTEText = (externalEditor, contentText, newContentStyle
7
7
  if (!contentText) {
8
8
  return contentText;
9
9
  }
10
- // BP-5787: operate natively on the Tiptap doc. setContent accepts a JSON
11
- // document or an HTML string; the style is applied via editor commands and
12
- // the result is returned as JSON — no HTML round-trip.
13
- const content = typeof contentText === 'string' &&
14
- contentText.includes(ESCAPED_NEWLINE_CHARACTER)
10
+ const content = contentText.includes(ESCAPED_NEWLINE_CHARACTER)
15
11
  ? getRenderText(contentText)
16
12
  : contentText;
17
13
  externalEditor.commands.setContent(content);
@@ -57,7 +53,8 @@ export const applyStyleToRTEText = (externalEditor, contentText, newContentStyle
57
53
  ? chain.setItalic()
58
54
  : chain.unsetItalic();
59
55
  chain.run();
60
- return externalEditor.getJSON();
56
+ const html = externalEditor.getHTML();
57
+ return html;
61
58
  };
62
59
  /**
63
60
  * Converts a Tiptap JSON document (ProseMirror document model) to an HTML
@@ -158,20 +158,7 @@ export default defineComponent({
158
158
  },
159
159
  },
160
160
  watch: {
161
- value(newVal, oldVal) {
162
- // BP-5787: RTE values are Tiptap JSON objects. The parent (TextItemsField)
163
- // re-clones its items array (cloneDeep) on ANY change — including a sibling
164
- // field's style sync — yielding a NEW object reference with identical text
165
- // content. For objects Vue fires this watcher on reference change, so that
166
- // spurious re-clone would overwrite `_value` and clobber an in-flight edit
167
- // (a mark applied but not yet debounced to the store), silently dropping
168
- // the user's formatting. Strings never hit this (same content = same
169
- // reference = no fire), so mirror that: ignore re-clones whose content is
170
- // unchanged from the previous prop value.
171
- if (this.showRichTextEditor &&
172
- JSON.stringify(newVal) === JSON.stringify(oldVal)) {
173
- return;
174
- }
161
+ value(newVal) {
175
162
  this._value = newVal;
176
163
  if (this.showRichTextEditor) {
177
164
  const textValue = escapeContentText(this._value);
@@ -8,7 +8,7 @@ import { SIDE_BAR_TAB_NAMES } from '@brand-page-maker/constants';
8
8
  import { ITEM_FIELD_TYPES } from '@brand-page-maker/models';
9
9
  import { useUiPageSettingsStore } from '../../../page-settings/store';
10
10
  import { PAGE_SECTIONS } from '../../../page-settings/models';
11
- import { getPlainTextFromRTE, replaceTextInRTE, RTE_ALLOWED_SECTION_TYPES, isRTEManagedStyleField, isTiptapDocument, renderRteToHtml, toRteJson, } from '@brand-page-maker/utils';
11
+ import { getPlainTextFromRTE, replaceTextInRTE, RTE_ALLOWED_SECTION_TYPES, isRTEManagedStyleField, } from '@brand-page-maker/utils';
12
12
  import { isDefaultContentText } from '@shared/content-settings/utils';
13
13
  import { useUiLogoSettingsStore } from '../../../logo-settings/store';
14
14
  const RTE_DISALLOWED_FIELD_NAMES = ['buttonText'];
@@ -262,17 +262,8 @@ export default defineComponent({
262
262
  },
263
263
  onUseGeneratedText(generatedText) {
264
264
  let formattedText = generatedText;
265
- // BP-5787: preserve the field's formatting when dropping in AI text.
266
- // replaceTextInRTE is HTML-based, so JSON is rendered/parsed around it
267
- // (cold AI path, shared editor); string fields use it directly.
268
- if (this.showRichTextEditor) {
269
- if (isTiptapDocument(this.value)) {
270
- formattedText =
271
- toRteJson(replaceTextInRTE(renderRteToHtml(this.value), generatedText)) ?? generatedText;
272
- }
273
- else if (typeof this.value === 'string') {
274
- formattedText = replaceTextInRTE(this.value, generatedText);
275
- }
265
+ if (this.showRichTextEditor && typeof this.value === 'string') {
266
+ formattedText = replaceTextInRTE(this.value, generatedText);
276
267
  }
277
268
  this.contentSettingsStore.updateItemValue({
278
269
  id: this.id,
@@ -1,5 +1,5 @@
1
1
  import { BRAND_PAGE_TYPES, } from '../brand-page/models';
2
- import { hasRTEContent, isTiptapDocument } from '../brand-page-maker/utils';
2
+ import { hasRTEContent } from '../brand-page-maker/utils';
3
3
  import { useExternalRichTextEditor } from '../brand-page-maker/composables/rich-text-editor/useRichTextEditor';
4
4
  import { findAssetKey, resolveLogoData, } from './build-asset-manifest';
5
5
  const BUTTON_TYPES = [
@@ -17,9 +17,6 @@ const BUTTON_TYPES = [
17
17
  'tiktok',
18
18
  ];
19
19
  const convertText = (value, editor) => {
20
- // Already Tiptap JSON (BP-5787 storage) — keep as-is.
21
- if (isTiptapDocument(value))
22
- return value;
23
20
  if (typeof value !== 'string' || !value)
24
21
  return undefined;
25
22
  if (hasRTEContent(value)) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@designcrowd/library.brand-page",
3
3
  "description": "A collection of Maker/Designer applications",
4
- "version": "6.0.19",
4
+ "version": "6.0.20",
5
5
  "author": "Design.com Eng",
6
6
  "license": "UNLICENSED",
7
7
  "type": "module",
@@ -31,7 +31,7 @@ import {
31
31
  mutedTextForBackground,
32
32
  backgroundColorFromStyle,
33
33
  } from '@brand-page/utils';
34
- import { hasRTEContent, getPlainTextFromRTE } from '@brand-page-maker/utils';
34
+ import { hasRTEContent } from '@brand-page-maker/utils';
35
35
  import { useReCaptcha } from '../../composables/recaptcha.composable';
36
36
 
37
37
  export interface FieldValue {
@@ -170,10 +170,7 @@ export default defineComponent({
170
170
  },
171
171
 
172
172
  title(): string {
173
- // BP-5787: title may be RTE JSON (not a string), so `.trim()` would
174
- // throw — read the visible text via getPlainTextFromRTE to decide
175
- // whether to fall back to the default.
176
- return getPlainTextFromRTE(this.viewModel.title)
173
+ return this.viewModel.title && this.viewModel.title.trim() !== ''
177
174
  ? this.viewModel.title
178
175
  : 'Contact Us';
179
176
  },
@@ -21,13 +21,7 @@
21
21
  </div>
22
22
  </template>
23
23
  <script setup lang="ts">
24
- import {
25
- hasRTEContent,
26
- getPlainTextFromRTE,
27
- renderRteToHtml,
28
- isTiptapDocument,
29
- type RichTextValue,
30
- } from '@brand-page-maker/utils';
24
+ import { hasRTEContent, getPlainTextFromRTE } from '@brand-page-maker/utils';
31
25
  import {
32
26
  type FluidTextData,
33
27
  type TextStyleDeclaration,
@@ -43,8 +37,7 @@ import {
43
37
 
44
38
  interface Props {
45
39
  textStyle: TextStyleDeclaration | undefined;
46
- // BP-5787: an RTE field is a legacy inline-HTML string or a Tiptap JSON doc.
47
- text: RichTextValue;
40
+ text: string;
48
41
  element?: string;
49
42
  slug?: string;
50
43
  }
@@ -86,20 +79,16 @@ const textStyleRTE = computed(() => {
86
79
  };
87
80
  });
88
81
 
89
- // Render either form (legacy HTML string or Tiptap JSON) to final HTML.
90
- // For a string this returns it unchanged, so the legacy path is unaffected.
91
- const renderedHtml = computed(() => renderRteToHtml(props.text));
92
-
93
82
  const isRTEContent = computed(() => {
94
- return isTiptapDocument(props.text) || hasRTEContent(renderedHtml.value);
83
+ return hasRTEContent(props.text);
95
84
  });
96
85
 
97
86
  const sanitizeText = computed(() => {
98
- if (isRTEContent.value && !getPlainTextFromRTE(renderedHtml.value)) {
87
+ if (isRTEContent.value && !getPlainTextFromRTE(props.text)) {
99
88
  return '';
100
89
  }
101
90
 
102
- return renderedHtml.value;
91
+ return props.text;
103
92
  });
104
93
 
105
94
  const fontSize = computed<any>(() => props.textStyle.fontSize);
@@ -171,7 +160,7 @@ const applyScale = () => {
171
160
  }
172
161
 
173
162
  const fontSizeMatchInRTEText = [
174
- ...renderedHtml.value.matchAll(
163
+ ...props.text.matchAll(
175
164
  /style="(?:[^"]*?)\bfont-size\s*:\s*(?<fontSize>[^;"']+)[^"]*?"/gm,
176
165
  ),
177
166
  ].map((x) => parseFloat(x.groups?.fontSize || '1em'));
@@ -30,15 +30,6 @@ import type { LogoImageModel, LogoTemplateImageModel } from './logo.model';
30
30
  import cloneDeep from 'lodash.clonedeep';
31
31
  import * as uuid from 'uuid';
32
32
  import type { BrandPagePaymentConfig } from './payment.model';
33
- import type { JSONContent } from '@tiptap/vue-3';
34
-
35
- /**
36
- * BP-5787 — a rich-text field is either a legacy inline-HTML string or a
37
- * Tiptap JSON document. Typing RTE content fields as RichTextValue (instead of
38
- * `string`) forces every consumer to handle both forms — surfacing at compile
39
- * time any code that assumes a plain string (e.g. `.replace`, `.length`).
40
- */
41
- export type RichTextValue = string | JSONContent;
42
33
 
43
34
  export * from './brand-page-context.model';
44
35
  export * from './payment.model';
@@ -289,7 +280,7 @@ export interface TextContent extends Content {
289
280
  /**
290
281
  * For BrandPage/BrandContact only
291
282
  */
292
- text: RichTextValue;
283
+ text: string;
293
284
  /**
294
285
  * For BrandSite
295
286
  */
@@ -313,9 +304,9 @@ export interface TextContent extends Content {
313
304
 
314
305
  export interface ProfileContent extends Content {
315
306
  avatarImageUrl: string;
316
- name: RichTextValue;
317
- title: RichTextValue;
318
- description: RichTextValue;
307
+ name: string;
308
+ title: string;
309
+ description: string;
319
310
  backgroundColor: BrandPageCssStyle;
320
311
  colorReplacements: ColorReplacement[];
321
312
  mediaType?: string;
@@ -336,7 +327,7 @@ export interface MapContent extends Content {
336
327
 
337
328
  export interface MailingListContent extends Content {
338
329
  acceptedFields?: 'emailOnly' | 'nameAndEmail';
339
- title: RichTextValue;
330
+ title: string;
340
331
  buttonText: string;
341
332
  titleStyle: TextStyleDeclaration;
342
333
  buttonTextStyle: TextStyleDeclaration;
@@ -395,10 +386,10 @@ export interface FormField {
395
386
  export interface ContactFormContent extends Content {
396
387
  type: 'contact-form';
397
388
  displayStyle: string;
398
- title: RichTextValue;
399
- description: RichTextValue;
389
+ title: string;
390
+ description: string;
400
391
  buttonText: string;
401
- successMessage: RichTextValue;
392
+ successMessage: string;
402
393
  fields: FormField[];
403
394
  responseEmail: string;
404
395
  imageUrl?: string;
@@ -418,8 +409,8 @@ export interface ContactFormContent extends Content {
418
409
  export interface BannerSectionContent extends Content {
419
410
  type: 'banner-section';
420
411
  imageUrl: string;
421
- title: RichTextValue;
422
- description: RichTextValue;
412
+ title: string;
413
+ description: string;
423
414
  buttonText: string | false;
424
415
  url: string;
425
416
  email?: string;
@@ -449,8 +440,8 @@ export const MEDIA_TYPES = {
449
440
  export type MediaType = keyof typeof MEDIA_TYPES;
450
441
 
451
442
  export interface TextMediaItem extends ImageItem {
452
- title: RichTextValue;
453
- paragraph: RichTextValue;
443
+ title: string;
444
+ paragraph: string;
454
445
  itemsId: number;
455
446
 
456
447
  buttonText?: string | false;
@@ -474,8 +465,8 @@ export interface TextShapeItem extends TextMediaItem {
474
465
  export interface ShapeItem extends ImageItem {
475
466
  colorReplacements: ColorReplacement[];
476
467
  mediaType: MediaType;
477
- title?: RichTextValue;
478
- paragraph?: RichTextValue;
468
+ title?: string;
469
+ paragraph?: string;
479
470
 
480
471
  // overrides for personalisation
481
472
  titleOverride?: string;
@@ -486,9 +477,9 @@ export interface TestimonialItems {
486
477
  /** Stable per-item identifier. See ImageItem.id. */
487
478
  id: string;
488
479
  imageUrl: string;
489
- name: RichTextValue;
490
- position: RichTextValue;
491
- testimonials: RichTextValue;
480
+ name: string;
481
+ position: string;
482
+ testimonials: string;
492
483
  colorReplacements: ColorReplacement[];
493
484
  mediaType: string;
494
485
  size?: string;
@@ -504,7 +495,7 @@ export interface TestimonialItems {
504
495
  export interface TextItem {
505
496
  /** Stable per-item identifier. See ImageItem.id. */
506
497
  id: string;
507
- text: RichTextValue;
498
+ text: string;
508
499
  textStyle: TextStyleDeclaration;
509
500
 
510
501
  // overrides for personalisation
@@ -526,8 +517,8 @@ export interface ImagesSectionContent extends Content, MediaSection {
526
517
  }
527
518
 
528
519
  export interface MediaSection extends Content {
529
- title: RichTextValue;
530
- description: RichTextValue;
520
+ title: string;
521
+ description: string;
531
522
  displayStyle: ImagesSectionContentDisplayStyle;
532
523
  items: TextShapeItem[] | TextMediaItem[];
533
524
  titleStyle: TextStyleDeclaration;
@@ -546,7 +537,7 @@ export interface ColorReplacement {
546
537
 
547
538
  export interface TestimonialSectionContent extends Content {
548
539
  type: 'testimonial-section';
549
- title: RichTextValue;
540
+ title: string;
550
541
  displayStyle: string;
551
542
  items: TestimonialItems[];
552
543
  titleStyle: TextStyleDeclaration;
@@ -561,8 +552,8 @@ export interface TestimonialSectionContent extends Content {
561
552
  export interface AboutSectionContent extends Content {
562
553
  type: 'about-section';
563
554
  imageUrl: string;
564
- title: RichTextValue;
565
- paragraph: RichTextValue;
555
+ title: string;
556
+ paragraph: string;
566
557
  displayStyle: string;
567
558
  titleStyle: TextStyleDeclaration;
568
559
  paragraphStyle: TextStyleDeclaration;
@@ -591,7 +582,7 @@ export type SocialLinksIconDisplayStyle =
591
582
 
592
583
  export interface SocialLinksContent extends Content {
593
584
  type: 'social-links';
594
- title: RichTextValue;
585
+ title: string;
595
586
  links: LinksItem[];
596
587
  iconDisplayStyle: SocialLinksIconDisplayStyle;
597
588
  iconFillStyles: BrandPageCssStyle;
@@ -618,8 +609,8 @@ export interface DividerSectionContent extends Content {
618
609
 
619
610
  export interface BookingSectionContent extends Content {
620
611
  type: 'booking-section';
621
- title: RichTextValue;
622
- description: RichTextValue;
612
+ title: string;
613
+ description: string;
623
614
  buttonText: string;
624
615
  url: string;
625
616
  email?: string;
@@ -463,7 +463,7 @@ exports[`migration.util > Banner Contents - text Align and Font Family should no
463
463
  },
464
464
  },
465
465
  "userPromptContentKey": undefined,
466
- "v": 19,
466
+ "v": 18,
467
467
  },
468
468
  "useOldLogoRenderSize": true,
469
469
  }
@@ -916,7 +916,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
916
916
  },
917
917
  },
918
918
  "userPromptContentKey": undefined,
919
- "v": 19,
919
+ "v": 18,
920
920
  },
921
921
  "useOldLogoRenderSize": true,
922
922
  }
@@ -1376,7 +1376,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
1376
1376
  },
1377
1377
  },
1378
1378
  "userPromptContentKey": undefined,
1379
- "v": 19,
1379
+ "v": 18,
1380
1380
  },
1381
1381
  "useOldLogoRenderSize": true,
1382
1382
  }
@@ -21,7 +21,6 @@ import { migration15 } from './migration/migration-15';
21
21
  import { migration16 } from './migration/migration-16';
22
22
  import { migration17 } from './migration/migration-17';
23
23
  import { migration18 } from './migration/migration-18';
24
- import { migration19 } from './migration/migration-19';
25
24
 
26
25
  export interface MigrationData {
27
26
  brandPageModel: BrandPageModel;
@@ -168,17 +167,6 @@ const MIGRATION_TABLE = [
168
167
  description:
169
168
  'BP-5603: backfill stable item ids on all nested items (gallery/section/testimonial/text items, section + footer social links) — the item id is now non-nullable',
170
169
  },
171
- {
172
- version: 19,
173
- migrateFn: migration19,
174
- // runAlways: normalise RTE to JSON on every load. Idempotent (only HTML
175
- // strings are converted; JSON is skipped), so it also catches content the
176
- // AI section builders still emit as HTML — converting it on the next read
177
- // without a new migration or changes to those builders.
178
- runAlways: true,
179
- description:
180
- 'BP-5787: convert inline-HTML RTE content to Tiptap JSON on load (lazy migrate-on-read for web-renderer; persisted on next maker save; also normalises AI-built HTML)',
181
- },
182
170
  ] as MigrationTableItem[];
183
171
 
184
172
  export const GetLatestModelVersion = () =>
@@ -87,57 +87,6 @@ describe('style.util', () => {
87
87
  );
88
88
  });
89
89
 
90
- test('applyLogoThemeToBrandPageModel preserves var() token refs in JSON RTE content (BP-5787)', () => {
91
- // Arrange — title stored as Tiptap JSON with a theme-token colour mark.
92
- const logoTheme = logoWithFlatColorsTheme;
93
- const titleDoc = {
94
- type: 'doc',
95
- content: [
96
- {
97
- type: 'heading',
98
- attrs: { level: 2, textAlign: 'center' },
99
- content: [
100
- {
101
- type: 'text',
102
- marks: [
103
- {
104
- type: 'textStyle',
105
- attrs: { color: `var(--${STYLE_THEME_KEYS.LogoTextColor})` },
106
- },
107
- ],
108
- text: 'Themed',
109
- },
110
- ],
111
- },
112
- ],
113
- };
114
- const brandPageModel = new BrandPageModel({
115
- ...BRAND_SITE,
116
- pages: [
117
- {
118
- title: '1',
119
- slug: '1',
120
- contents: [
121
- {
122
- ...createInitialBannerSectionContent(),
123
- title: titleDoc,
124
- } as any,
125
- ],
126
- },
127
- ],
128
- });
129
-
130
- // Act
131
- const { model } = applyLogoThemeToBrandPageModel(logoTheme, brandPageModel);
132
-
133
- // Assert — JSON RTE content is an object, so the var() token is NOT
134
- // flattened to rgb(); it survives so a new theme can still propagate.
135
- const title = JSON.stringify((model.pages[0].contents[0] as any).title);
136
-
137
- expect(title).toContain(`var(--${STYLE_THEME_KEYS.LogoTextColor})`);
138
- expect(title).not.toContain(logoTheme.logoTextColor.rgb);
139
- });
140
-
141
90
  test('applyLogoThemeToBrandPageModel should apply logo theme color to content color when content color has a color matched theme key', () => {
142
91
  // Arrange
143
92
  const logoTheme = logoWithFlatColorsTheme;
@@ -1,56 +1,17 @@
1
1
  import { ESCAPED_NEWLINE_CHARACTER } from '../../constants/ui';
2
2
 
3
- /**
4
- * BP-5787 — a Tiptap JSON document is an object with `type: 'doc'`.
5
- * Inlined here (rather than importing from brand-page-maker) to avoid a
6
- * circular import: rich-text-editor.util imports getRenderText from this file.
7
- */
8
- const isTiptapDoc = (value: unknown): value is { type: 'doc' } =>
9
- typeof value === 'object' &&
10
- value !== null &&
11
- (value as { type?: string }).type === 'doc';
12
-
13
- const jsonDocPlainText = (doc: unknown): string => {
14
- const parts: string[] = [];
15
-
16
- const walk = (node: any): void => {
17
- if (typeof node?.text === 'string') parts.push(node.text);
18
-
19
- node?.content?.forEach(walk);
20
- };
21
-
22
- walk(doc);
23
-
24
- return parts.join(' ').replace(/\s+/g, ' ').trim();
25
- };
26
-
27
3
  export const getRenderText = (text: string): string => {
28
- // BP-5787: RTE fields may now be Tiptap JSON objects. Non-strings pass
29
- // through untouched so the renderer (TextWrapper) handles them via its
30
- // JSON-aware path, instead of throwing on `.replace`.
31
- if (typeof text !== 'string') {
32
- return text as unknown as string;
33
- }
34
-
35
4
  return text.replace(
36
5
  new RegExp(`(${ESCAPED_NEWLINE_CHARACTER}|\\n)`, 'g'),
37
6
  '<br/>',
38
7
  );
39
8
  };
40
9
 
41
- // Accepts a legacy inline-HTML string or a Tiptap JSON document (BP-5787):
42
- // for JSON it returns the concatenated visible text; for HTML it strips tags.
43
- export const escapeContentText = (text: unknown): string | undefined => {
10
+ export const escapeContentText = (
11
+ text: string | undefined,
12
+ ): string | undefined => {
44
13
  if (!text) {
45
- return text as undefined;
46
- }
47
-
48
- if (isTiptapDoc(text)) {
49
- return jsonDocPlainText(text);
50
- }
51
-
52
- if (typeof text !== 'string') {
53
- return undefined;
14
+ return text;
54
15
  }
55
16
 
56
17
  return text
@@ -235,11 +235,9 @@ const addProfileInfo = (
235
235
  profile: ProfileViewModel,
236
236
  vcardLines: string[],
237
237
  ): void => {
238
- const name = sanitizeForVCard(getPlainTextFromRTE(profile.name) || '');
239
- const title = sanitizeForVCard(getPlainTextFromRTE(profile.title) || '');
240
- const description = sanitizeForVCard(
241
- getPlainTextFromRTE(profile.description) || '',
242
- );
238
+ const name = sanitizeForVCard(profile.name || '');
239
+ const title = sanitizeForVCard(profile.title || '');
240
+ const description = sanitizeForVCard(profile.description || '');
243
241
 
244
242
  if (name) {
245
243
  const nameParts = name.trim().split(/\s+/);
@@ -318,7 +316,7 @@ export const saveContact = (brandPageData: {
318
316
  }): void => {
319
317
  const vcard = generateVCard(brandPageData);
320
318
  const profile = findProfileContent(brandPageData);
321
- const filename = generateVCardFilename(getPlainTextFromRTE(profile?.name));
319
+ const filename = generateVCardFilename(profile?.name);
322
320
 
323
321
  downloadVCard(vcard, filename);
324
322
  };