@designcrowd/library.brand-page 6.0.6-json-prereq-7 → 6.0.6-tracker-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 (99) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-SPVLPi8n.js → CreatedOnBrandCrowd-de-de-DD0OBnij.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-CvtrCPHd.js → CreatedOnBrandCrowd-en-us-DysrpsmM.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-BSsac7jB.js → CreatedOnBrandCrowd-es-es-B6twGr9h.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-BauENV2a.js → CreatedOnBrandCrowd-fr-ca-yxl0ih_4.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-BauENV2a.js → CreatedOnBrandCrowd-fr-fr-yxl0ih_4.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-Be0hawmr.js → CreatedOnBrandCrowd-pt-br-BtXDrIcx.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-Be0hawmr.js → CreatedOnBrandCrowd-pt-pt-BtXDrIcx.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-CTjE2Tmn.js → CreatedOnDcom-de-de-ho_ROy1L.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-DnZRp5Pg.js → CreatedOnDcom-en-us-C91p9kls.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-J-9qvjy8.js → CreatedOnDcom-es-es-B3FYmObE.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-BDNHqm4G.js → CreatedOnDcom-fr-ca-D40m7fPx.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-B_2-ceuO.js → CreatedOnDcom-fr-fr-Cd57oYyc.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-76lJVrAY.js → CreatedOnDcom-pt-br-Du57mWxG.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-TQp3CNcu.js → CreatedOnDcom-pt-pt-fYoPZWwr.js} +1 -1
  15. package/lib/src/brand-page/models/index.d.ts +26 -34
  16. package/lib/src/brand-page/utils/migration.util.js +0 -11
  17. package/lib/src/brand-page/utils/text.util.d.ts +1 -1
  18. package/lib/src/brand-page/utils/text.util.js +0 -32
  19. package/lib/src/brand-page/utils/vcard.util.js +4 -4
  20. package/lib/src/brand-page/utils/view-model.util.d.ts +0 -1
  21. package/lib/src/brand-page/utils/view-model.util.js +20 -68
  22. package/lib/src/brand-page/view-models.d.ts +3 -3
  23. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +99 -20
  24. package/lib/src/brand-page-maker/utils/index.d.ts +0 -2
  25. package/lib/src/brand-page-maker/utils/index.js +0 -2
  26. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +5 -19
  27. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +5 -35
  28. package/lib/src/brand-page-maker/validators/field.validator.js +2 -10
  29. package/lib/src/css/library.brand-page-brandCrowd.min.css +1 -1
  30. package/lib/src/css/library.brand-page-brandPage.min.css +1 -1
  31. package/lib/src/css/library.brand-page-designCom.min.css +1 -1
  32. package/lib/src/css/library.brand-page-preview.min.css +1 -1
  33. package/lib/src/index.d.ts +0 -1
  34. package/lib/src/index.mjs +137 -141
  35. package/lib/src/{lib-Bh9Y5_-x.js → lib-lMNy66QO.js} +43580 -44184
  36. package/lib/src/lib.js +0 -2
  37. package/lib/src/metadata-builder/builders/update-content-text-builder.js +15 -20
  38. package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +3 -5
  39. package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +1 -1
  40. package/lib/src/metadata-builder/helpers/rte.helpers.js +3 -6
  41. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.js +1 -14
  42. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +3 -12
  43. package/lib/src/tracker.d.ts +2 -0
  44. package/lib/src/tracker.js +12 -0
  45. package/lib/src/tracker.mjs +12 -0
  46. package/lib/src/useEventTracker-DmQ6AkNn.js +356 -0
  47. package/lib/src/utils/minify-brand-page-metadata.js +1 -4
  48. package/package.json +16 -1
  49. package/src/brand-page/components/text-wrapper/TextWrapper.vue +6 -17
  50. package/src/brand-page/models/index.ts +26 -35
  51. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -3
  52. package/src/brand-page/utils/migration.util.ts +0 -12
  53. package/src/brand-page/utils/style.util.test.ts +0 -51
  54. package/src/brand-page/utils/text.util.ts +4 -43
  55. package/src/brand-page/utils/vcard.util.ts +4 -6
  56. package/src/brand-page/utils/view-model.util.ts +26 -94
  57. package/src/brand-page/view-models.ts +2 -3
  58. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +106 -22
  59. package/src/brand-page-maker/utils/index.ts +0 -2
  60. package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +3 -4
  61. package/src/brand-page-maker/utils/rich-text-editor.util.ts +11 -50
  62. package/src/brand-page-maker/validators/field.validator.ts +2 -10
  63. package/src/lib.ts +0 -9
  64. package/src/metadata-builder/builders/update-content-text-builder.ts +18 -32
  65. package/src/metadata-builder/builders/update-content-text-style-builder.ts +4 -6
  66. package/src/metadata-builder/helpers/rte.helpers.ts +7 -11
  67. package/src/shared/content-settings/components/GenericTextInput.mixin.ts +1 -17
  68. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +3 -16
  69. package/src/tracker.ts +28 -0
  70. package/src/utils/minify-brand-page-metadata.ts +1 -4
  71. package/lib/src/brand-page/utils/migration/migration-18.d.ts +0 -2
  72. package/lib/src/brand-page/utils/migration/migration-18.js +0 -71
  73. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.d.ts +0 -22
  74. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.js +0 -22
  75. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.d.ts +0 -14
  76. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.js +0 -39
  77. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.d.ts +0 -28
  78. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.js +0 -66
  79. package/lib/src/brand-page-maker/utils/rte-render.util.d.ts +0 -33
  80. package/lib/src/brand-page-maker/utils/rte-render.util.js +0 -50
  81. package/lib/src/brand-page-maker/utils/rte-sanitize.util.d.ts +0 -6
  82. package/lib/src/brand-page-maker/utils/rte-sanitize.util.js +0 -158
  83. package/lib/src/brand-page-maker/utils/rte-serializer.d.ts +0 -5
  84. package/lib/src/brand-page-maker/utils/rte-serializer.js +0 -112
  85. package/src/brand-page/utils/migration/migration-18.test.ts +0 -72
  86. package/src/brand-page/utils/migration/migration-18.ts +0 -83
  87. package/src/brand-page/utils/text.util.test.ts +0 -47
  88. package/src/brand-page/utils/view-model.pageid-rte.test.ts +0 -104
  89. package/src/brand-page-maker/__fixtures__/complex-rte.fixture.ts +0 -23
  90. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +0 -52
  91. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.ts +0 -42
  92. package/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.ts +0 -113
  93. package/src/brand-page-maker/utils/__snapshots__/rte-render-parity.test.ts.snap +0 -200
  94. package/src/brand-page-maker/utils/rte-render-parity.test.ts +0 -156
  95. package/src/brand-page-maker/utils/rte-render.util.ts +0 -77
  96. package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +0 -158
  97. package/src/brand-page-maker/utils/rte-sanitize.util.ts +0 -195
  98. package/src/brand-page-maker/utils/rte-serializer.ts +0 -172
  99. package/src/brand-page-maker/validators/field.validator.test.ts +0 -55
@@ -29,15 +29,6 @@ import { EMPTY_LOGO_IMAGE_MODEL, LogoStyles } from './logo.model';
29
29
  import type { LogoImageModel, LogoTemplateImageModel } from './logo.model';
30
30
  import cloneDeep from 'lodash.clonedeep';
31
31
  import type { BrandPagePaymentConfig } from './payment.model';
32
- import type { JSONContent } from '@tiptap/vue-3';
33
-
34
- /**
35
- * BP-5787 — a rich-text field is either a legacy inline-HTML string or a
36
- * Tiptap JSON document. Typing RTE content fields as RichTextValue (instead of
37
- * `string`) forces every consumer to handle both forms — surfacing at compile
38
- * time any code that assumes a plain string (e.g. `.replace`, `.length`).
39
- */
40
- export type RichTextValue = string | JSONContent;
41
32
 
42
33
  export * from './brand-page-context.model';
43
34
  export * from './payment.model';
@@ -281,7 +272,7 @@ export interface TextContent extends Content {
281
272
  /**
282
273
  * For BrandPage/BrandContact only
283
274
  */
284
- text: RichTextValue;
275
+ text: string;
285
276
  /**
286
277
  * For BrandSite
287
278
  */
@@ -305,9 +296,9 @@ export interface TextContent extends Content {
305
296
 
306
297
  export interface ProfileContent extends Content {
307
298
  avatarImageUrl: string;
308
- name: RichTextValue;
309
- title: RichTextValue;
310
- description: RichTextValue;
299
+ name: string;
300
+ title: string;
301
+ description: string;
311
302
  backgroundColor: BrandPageCssStyle;
312
303
  colorReplacements: ColorReplacement[];
313
304
  mediaType?: string;
@@ -328,7 +319,7 @@ export interface MapContent extends Content {
328
319
 
329
320
  export interface MailingListContent extends Content {
330
321
  acceptedFields?: 'emailOnly' | 'nameAndEmail';
331
- title: RichTextValue;
322
+ title: string;
332
323
  buttonText: string;
333
324
  titleStyle: TextStyleDeclaration;
334
325
  buttonTextStyle: TextStyleDeclaration;
@@ -387,10 +378,10 @@ export interface FormField {
387
378
  export interface ContactFormContent extends Content {
388
379
  type: 'contact-form';
389
380
  displayStyle: string;
390
- title: RichTextValue;
391
- description: RichTextValue;
381
+ title: string;
382
+ description: string;
392
383
  buttonText: string;
393
- successMessage: RichTextValue;
384
+ successMessage: string;
394
385
  fields: FormField[];
395
386
  responseEmail: string;
396
387
  imageUrl?: string;
@@ -410,8 +401,8 @@ export interface ContactFormContent extends Content {
410
401
  export interface BannerSectionContent extends Content {
411
402
  type: 'banner-section';
412
403
  imageUrl: string;
413
- title: RichTextValue;
414
- description: RichTextValue;
404
+ title: string;
405
+ description: string;
415
406
  buttonText: string | false;
416
407
  url: string;
417
408
  email?: string;
@@ -441,8 +432,8 @@ export const MEDIA_TYPES = {
441
432
  export type MediaType = keyof typeof MEDIA_TYPES;
442
433
 
443
434
  export interface TextMediaItem extends ImageItem {
444
- title: RichTextValue;
445
- paragraph: RichTextValue;
435
+ title: string;
436
+ paragraph: string;
446
437
  itemsId: number;
447
438
 
448
439
  buttonText?: string | false;
@@ -466,8 +457,8 @@ export interface TextShapeItem extends TextMediaItem {
466
457
  export interface ShapeItem extends ImageItem {
467
458
  colorReplacements: ColorReplacement[];
468
459
  mediaType: MediaType;
469
- title?: RichTextValue;
470
- paragraph?: RichTextValue;
460
+ title?: string;
461
+ paragraph?: string;
471
462
 
472
463
  // overrides for personalisation
473
464
  titleOverride?: string;
@@ -476,9 +467,9 @@ export interface ShapeItem extends ImageItem {
476
467
 
477
468
  export interface TestimonialItems {
478
469
  imageUrl: string;
479
- name: RichTextValue;
480
- position: RichTextValue;
481
- testimonials: RichTextValue;
470
+ name: string;
471
+ position: string;
472
+ testimonials: string;
482
473
  colorReplacements: ColorReplacement[];
483
474
  mediaType: string;
484
475
  size?: string;
@@ -492,7 +483,7 @@ export interface TestimonialItems {
492
483
  }
493
484
 
494
485
  export interface TextItem {
495
- text: RichTextValue;
486
+ text: string;
496
487
  textStyle: TextStyleDeclaration;
497
488
 
498
489
  // overrides for personalisation
@@ -514,8 +505,8 @@ export interface ImagesSectionContent extends Content, MediaSection {
514
505
  }
515
506
 
516
507
  export interface MediaSection extends Content {
517
- title: RichTextValue;
518
- description: RichTextValue;
508
+ title: string;
509
+ description: string;
519
510
  displayStyle: ImagesSectionContentDisplayStyle;
520
511
  items: TextShapeItem[] | TextMediaItem[];
521
512
  titleStyle: TextStyleDeclaration;
@@ -534,7 +525,7 @@ export interface ColorReplacement {
534
525
 
535
526
  export interface TestimonialSectionContent extends Content {
536
527
  type: 'testimonial-section';
537
- title: RichTextValue;
528
+ title: string;
538
529
  displayStyle: string;
539
530
  items: TestimonialItems[];
540
531
  titleStyle: TextStyleDeclaration;
@@ -549,8 +540,8 @@ export interface TestimonialSectionContent extends Content {
549
540
  export interface AboutSectionContent extends Content {
550
541
  type: 'about-section';
551
542
  imageUrl: string;
552
- title: RichTextValue;
553
- paragraph: RichTextValue;
543
+ title: string;
544
+ paragraph: string;
554
545
  displayStyle: string;
555
546
  titleStyle: TextStyleDeclaration;
556
547
  paragraphStyle: TextStyleDeclaration;
@@ -577,7 +568,7 @@ export type SocialLinksIconDisplayStyle =
577
568
 
578
569
  export interface SocialLinksContent extends Content {
579
570
  type: 'social-links';
580
- title: RichTextValue;
571
+ title: string;
581
572
  links: LinksItem[];
582
573
  iconDisplayStyle: SocialLinksIconDisplayStyle;
583
574
  iconFillStyles: BrandPageCssStyle;
@@ -604,8 +595,8 @@ export interface DividerSectionContent extends Content {
604
595
 
605
596
  export interface BookingSectionContent extends Content {
606
597
  type: 'booking-section';
607
- title: RichTextValue;
608
- description: RichTextValue;
598
+ title: string;
599
+ description: string;
609
600
  buttonText: string;
610
601
  url: string;
611
602
  email?: string;
@@ -457,7 +457,7 @@ exports[`migration.util > Banner Contents - text Align and Font Family should no
457
457
  },
458
458
  },
459
459
  "userPromptContentKey": undefined,
460
- "v": 18,
460
+ "v": 17,
461
461
  },
462
462
  "useOldLogoRenderSize": true,
463
463
  }
@@ -903,7 +903,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
903
903
  },
904
904
  },
905
905
  "userPromptContentKey": undefined,
906
- "v": 18,
906
+ "v": 17,
907
907
  },
908
908
  "useOldLogoRenderSize": true,
909
909
  }
@@ -1356,7 +1356,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
1356
1356
  },
1357
1357
  },
1358
1358
  "userPromptContentKey": undefined,
1359
- "v": 18,
1359
+ "v": 17,
1360
1360
  },
1361
1361
  "useOldLogoRenderSize": true,
1362
1362
  }
@@ -20,7 +20,6 @@ import { migration14 } from './migration/migration-14';
20
20
  import { migration15 } from './migration/migration-15';
21
21
  import { migration16 } from './migration/migration-16';
22
22
  import { migration17 } from './migration/migration-17';
23
- import { migration18 } from './migration/migration-18';
24
23
 
25
24
  export interface MigrationData {
26
25
  brandPageModel: BrandPageModel;
@@ -161,17 +160,6 @@ const MIGRATION_TABLE = [
161
160
  description:
162
161
  'Padding Control backfill (BP-5699 Tier 2 + BP-5700 Store): backfill existing top/bottom spacing as explicit padding for text (12px), images/social links/shapes/booking (32px), store (16px), contact form (per layout), banner and about (per display style)',
163
162
  },
164
- {
165
- version: 18,
166
- migrateFn: migration18,
167
- // runAlways: normalise RTE to JSON on every load. Idempotent (only HTML
168
- // strings are converted; JSON is skipped), so it also catches content the
169
- // AI section builders still emit as HTML — converting it on the next read
170
- // without a new migration or changes to those builders.
171
- runAlways: true,
172
- description:
173
- '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)',
174
- },
175
163
  ] as MigrationTableItem[];
176
164
 
177
165
  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
  };
@@ -119,7 +119,7 @@ import {
119
119
  getRouteNameByPageIndex,
120
120
  getRouteParamsByPage,
121
121
  } from './route.util';
122
- import { hasRTEContent, isTiptapDocument } from '../../brand-page-maker/utils';
122
+ import { hasRTEContent } from '../../brand-page-maker/utils';
123
123
  import {
124
124
  getPageIdFromHref,
125
125
  isHrefPageId,
@@ -1518,86 +1518,6 @@ const mapPageIdReferencesToRelativePaths = (
1518
1518
  });
1519
1519
  };
1520
1520
 
1521
- // Resolve a `pageid:<id>` internal-link href to the published relative path.
1522
- // Returns the resolved href, or null when `href` is not an internal pageid link
1523
- // (external/absolute links are left untouched). Falls back to the site root when
1524
- // the pageid can't be matched to a nav route.
1525
- const resolvePageIdHref = (
1526
- href: string,
1527
- nav: NavViewModel,
1528
- options: ViewModelMappingOptions,
1529
- ): string | null => {
1530
- // Only process internal links (no protocol, doesn't start with /)
1531
- if (!href || href.match(/^(https?:\/\/|\/)/) || !isHrefPageId(href)) {
1532
- return null;
1533
- }
1534
-
1535
- const pageId = getPageIdFromHref(href);
1536
- const route = findNavItemByPageId(nav.navItems, pageId);
1537
- const brandPageSlugRoute = options.brandPageSlug
1538
- ? `/${options.brandPageSlug}`
1539
- : '/';
1540
-
1541
- if (route) {
1542
- if (route.isRoot) {
1543
- return brandPageSlugRoute;
1544
- }
1545
-
1546
- return `${brandPageSlugRoute}${brandPageSlugRoute === '/' ? '' : '/'}${route.relativePath}`;
1547
- }
1548
-
1549
- // fallback to root if pageid cannot be resolved.
1550
- return brandPageSlugRoute;
1551
- };
1552
-
1553
- // BP-5847: walk a Tiptap JSON doc and rewrite every link mark whose href is a
1554
- // `pageid:` reference into its published relative path. Returns a NEW doc when
1555
- // something changed (so the shared model doc is never mutated), else the same
1556
- // reference.
1557
- export const resolvePageIdsInRteJson = (
1558
- doc: unknown,
1559
- nav: NavViewModel,
1560
- options: ViewModelMappingOptions,
1561
- ): unknown => {
1562
- let changed = false;
1563
-
1564
- const walk = (node: any): any => {
1565
- if (!node || typeof node !== 'object') {
1566
- return node;
1567
- }
1568
-
1569
- let next = node;
1570
-
1571
- if (Array.isArray(node.marks)) {
1572
- const marks = node.marks.map((mark: any) => {
1573
- if (mark?.type === 'link' && typeof mark.attrs?.href === 'string') {
1574
- const resolved = resolvePageIdHref(mark.attrs.href, nav, options);
1575
-
1576
- if (resolved !== null) {
1577
- changed = true;
1578
-
1579
- return { ...mark, attrs: { ...mark.attrs, href: resolved } };
1580
- }
1581
- }
1582
-
1583
- return mark;
1584
- });
1585
-
1586
- next = { ...next, marks };
1587
- }
1588
-
1589
- if (Array.isArray(node.content)) {
1590
- next = { ...next, content: node.content.map(walk) };
1591
- }
1592
-
1593
- return next;
1594
- };
1595
-
1596
- const result = walk(doc);
1597
-
1598
- return changed ? result : doc;
1599
- };
1600
-
1601
1521
  const dynamicallyMapPageIdInHyperlinksToRelativePaths = (
1602
1522
  content: any,
1603
1523
  nav: NavViewModel,
@@ -1610,15 +1530,7 @@ const dynamicallyMapPageIdInHyperlinksToRelativePaths = (
1610
1530
  return;
1611
1531
  }
1612
1532
 
1613
- if (isTiptapDocument(value)) {
1614
- // BP-5847: RTE fields may now be Tiptap JSON. Resolve pageid link marks
1615
- // here too, or internal links render as raw `pageid:` and don't navigate.
1616
- (content as unknown as Record<string, unknown>)[key] =
1617
- resolvePageIdsInRteJson(value, nav, options);
1618
- } else if (
1619
- typeof value === 'object' &&
1620
- (key === 'items' || key === 'textItems')
1621
- ) {
1533
+ if (typeof value === 'object' && (key === 'items' || key === 'textItems')) {
1622
1534
  if (Array.isArray(value)) {
1623
1535
  value.forEach((item) => {
1624
1536
  dynamicallyMapPageIdInHyperlinksToRelativePaths(item, nav, options);
@@ -1630,11 +1542,31 @@ const dynamicallyMapPageIdInHyperlinksToRelativePaths = (
1630
1542
  (content as unknown as Record<string, unknown>)[key] = value.replace(
1631
1543
  /<a\s+([^>]*?)href="([^"]*?)"([^>]*?)>/gi,
1632
1544
  (match, existingAttrsStart, href, existingAttrsEnd) => {
1633
- const resolved = resolvePageIdHref(href, nav, options);
1545
+ // Only process internal links (no protocol, doesn't start with /)
1546
+ if (href && !href.match(/^(https?:\/\/|\/)/) && isHrefPageId(href)) {
1547
+ const pageId = getPageIdFromHref(href);
1548
+
1549
+ const route = findNavItemByPageId(nav.navItems, pageId);
1550
+
1551
+ const brandPageSlugRoute = options.brandPageSlug
1552
+ ? `/${options.brandPageSlug}`
1553
+ : '/';
1554
+
1555
+ if (route) {
1556
+ let href = `${brandPageSlugRoute}`;
1557
+
1558
+ if (!route.isRoot) {
1559
+ href = `${brandPageSlugRoute}${brandPageSlugRoute === '/' ? '' : '/'}${route.relativePath}`;
1560
+ }
1561
+
1562
+ return `<a ${existingAttrsStart}href="${href}"${existingAttrsEnd}>`;
1563
+ }
1564
+
1565
+ // fallback to root if pageid cannot be resolved.
1566
+ return `<a ${existingAttrsStart}href="${brandPageSlugRoute}"${existingAttrsEnd}>`;
1567
+ }
1634
1568
 
1635
- return resolved === null
1636
- ? match
1637
- : `<a ${existingAttrsStart}href="${resolved}"${existingAttrsEnd}>`;
1569
+ return match;
1638
1570
  },
1639
1571
  );
1640
1572
  }
@@ -41,7 +41,6 @@ import type {
41
41
  BackgroundShape,
42
42
  PersonalisationRole,
43
43
  SaveContactOptions,
44
- RichTextValue,
45
44
  } from './models';
46
45
  import type { ImagesSectionContentDisplayStyle } from './constants';
47
46
 
@@ -197,8 +196,8 @@ export interface TextImageSectionViewModel {
197
196
  id: string;
198
197
  type: 'images-section';
199
198
  role: ContentType;
200
- title: RichTextValue;
201
- description: RichTextValue;
199
+ title: string;
200
+ description: string;
202
201
  displayStyle: ImagesSectionContentDisplayStyle;
203
202
  aspectRatio: AspectRatio;
204
203
  areImagesFullWidth: boolean;