@designcrowd/library.brand-page 6.0.12 → 6.0.14-5804-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 (127) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-BfQcnhof.js → CreatedOnBrandCrowd-de-de-BYABvnQX.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-B86JfNzV.js → CreatedOnBrandCrowd-en-us-CU6aE_W2.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-Dy70BV7X.js → CreatedOnBrandCrowd-es-es-Pfuc4Nfl.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-CQ8UhGRG.js → CreatedOnBrandCrowd-fr-ca-DyQ1RyZN.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-CQ8UhGRG.js → CreatedOnBrandCrowd-fr-fr-DyQ1RyZN.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-c257u4q-.js → CreatedOnBrandCrowd-pt-br-DXMSa1wa.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-c257u4q-.js → CreatedOnBrandCrowd-pt-pt-DXMSa1wa.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-cRZ_Lx3_.js → CreatedOnDcom-de-de-kGnTiAMq.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-CrfHKR-1.js → CreatedOnDcom-en-us-CKrgHXUY.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-BFjhMPTR.js → CreatedOnDcom-es-es-CVxOQoOy.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-CZimjPcn.js → CreatedOnDcom-fr-ca-BduB1A0z.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-V1z3-WzE.js → CreatedOnDcom-fr-fr-LGhd51py.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-NrmWKrvi.js → CreatedOnDcom-pt-br-BLqHx-RQ.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-CKLASXM9.js → CreatedOnDcom-pt-pt-52goGQ1s.js} +1 -1
  15. package/lib/src/brand-page/components/image-gallery/ImageGallery.stories.js +3 -0
  16. package/lib/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.d.ts +1 -1
  17. package/lib/src/brand-page/components/images-section/ImagesSection.stories.js +2 -1
  18. package/lib/src/brand-page/components/social-links/SocialLinks.stories.js +3 -0
  19. package/lib/src/brand-page/components/testimonial-section/__fixtures__/data.js +3 -0
  20. package/lib/src/brand-page/components/text/Text.stories.js +2 -0
  21. package/lib/src/brand-page/models/index.d.ts +13 -0
  22. package/lib/src/brand-page/models/index.js +4 -0
  23. package/lib/src/brand-page/utils/migration/migration-01.js +2 -0
  24. package/lib/src/brand-page/utils/migration/migration-18.d.ts +14 -0
  25. package/lib/src/brand-page/utils/migration/migration-18.js +17 -0
  26. package/lib/src/brand-page/utils/migration.util.js +6 -0
  27. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +1 -0
  28. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +5 -0
  29. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.js +1 -0
  30. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +1 -0
  31. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +1 -1
  32. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.js +4 -6
  33. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +5 -0
  34. package/lib/src/brand-page-maker/composables/useQuickLinkBarVisibility.d.ts +17 -0
  35. package/lib/src/brand-page-maker/composables/useQuickLinkBarVisibility.js +23 -0
  36. package/lib/src/brand-page-maker/constants/config-settings-constant.js +4 -0
  37. package/lib/src/brand-page-maker/models/content-settings.js +4 -0
  38. package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +18 -0
  39. package/lib/src/css/library.brand-page-brandCrowd.min.css +29 -32
  40. package/lib/src/css/library.brand-page-brandPage.min.css +29 -32
  41. package/lib/src/css/library.brand-page-designCom.min.css +29 -32
  42. package/lib/src/css/library.brand-page-preview.min.css +29 -32
  43. package/lib/src/index.mjs +1 -1
  44. package/lib/src/{lib-CHfUKgt-.js → lib-ClM5iHnZ.js} +19076 -18948
  45. package/lib/src/metadata-builder/ai-metadata-executor.js +24 -1
  46. package/lib/src/metadata-builder/builders/footer-builder.js +32 -41
  47. package/lib/src/metadata-builder/builders/item-id-utils.d.ts +54 -0
  48. package/lib/src/metadata-builder/builders/item-id-utils.js +96 -0
  49. package/lib/src/metadata-builder/builders/move-page-builder.d.ts +32 -0
  50. package/lib/src/metadata-builder/builders/move-page-builder.js +137 -0
  51. package/lib/src/metadata-builder/builders/nav-utils.d.ts +26 -0
  52. package/lib/src/metadata-builder/builders/nav-utils.js +95 -0
  53. package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.js +57 -68
  54. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +58 -93
  55. package/lib/src/metadata-builder/builders/section-builders/move-section-builder.d.ts +13 -1
  56. package/lib/src/metadata-builder/builders/section-builders/move-section-builder.js +40 -30
  57. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +66 -99
  58. package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +57 -17
  59. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +51 -85
  60. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +50 -74
  61. package/lib/src/metadata-builder/builders/section-field-maps.js +17 -0
  62. package/lib/src/metadata-builder/builders/set-page-visibility-builder.d.ts +23 -0
  63. package/lib/src/metadata-builder/builders/set-page-visibility-builder.js +83 -0
  64. package/lib/src/metadata-builder/metadata-executor.js +4 -0
  65. package/lib/src/metadata-builder/models.d.ts +2 -0
  66. package/lib/src/metadata-builder/models.js +2 -0
  67. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +31 -7
  68. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +2 -0
  69. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +84 -48
  70. package/lib/src/shared/content-settings/components/links-field/LinksField.mixin.js +2 -0
  71. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +2 -0
  72. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +2 -0
  73. package/lib/src/utils/minify-brand-page-metadata.d.ts +2 -0
  74. package/lib/src/utils/minify-brand-page-metadata.js +8 -1
  75. package/package.json +1 -1
  76. package/src/brand-page/components/button/Button.vue +20 -0
  77. package/src/brand-page/components/button/Button.vue.test.ts +175 -0
  78. package/src/brand-page/components/image-gallery/ImageGallery.stories.ts +3 -0
  79. package/src/brand-page/components/image-gallery/ImageGallery.test.ts +1 -0
  80. package/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.ts +2 -1
  81. package/src/brand-page/components/images-section/ImagesSection.stories.ts +2 -1
  82. package/src/brand-page/components/social-links/SocialLinks.stories.ts +3 -0
  83. package/src/brand-page/components/social-links/SocialLinks.vue.test.ts +5 -0
  84. package/src/brand-page/components/testimonial-section/__fixtures__/data.ts +3 -0
  85. package/src/brand-page/components/text/Text.stories.ts +2 -0
  86. package/src/brand-page/models/index.ts +17 -0
  87. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +23 -3
  88. package/src/brand-page/utils/__snapshots__/migration2.util.test.ts.snap +6 -0
  89. package/src/brand-page/utils/__snapshots__/model.util.test.ts.snap +12 -0
  90. package/src/brand-page/utils/migration/migration-01.ts +2 -0
  91. package/src/brand-page/utils/migration/migration-18.test.ts +131 -0
  92. package/src/brand-page/utils/migration/migration-18.ts +22 -0
  93. package/src/brand-page/utils/migration.util.ts +7 -0
  94. package/src/brand-page/utils/personalisation.util.test.ts +4 -0
  95. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +5 -0
  96. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.ts +1 -0
  97. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.vue +2 -2
  98. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +1 -0
  99. package/src/brand-page-maker/components/device/Device.vue +0 -1
  100. package/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.ts +4 -7
  101. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +6 -0
  102. package/src/brand-page-maker/composables/useQuickLinkBarVisibility.test.ts +81 -0
  103. package/src/brand-page-maker/composables/useQuickLinkBarVisibility.ts +25 -0
  104. package/src/brand-page-maker/constants/config-settings-constant.ts +4 -0
  105. package/src/brand-page-maker/models/content-settings.ts +4 -0
  106. package/src/metadata-builder/ai-metadata-executor.ts +31 -1
  107. package/src/metadata-builder/builders/footer-builder.test.ts +335 -28
  108. package/src/metadata-builder/builders/footer-builder.ts +40 -57
  109. package/src/metadata-builder/builders/item-id-utils.test.ts +184 -0
  110. package/src/metadata-builder/builders/item-id-utils.ts +117 -0
  111. package/src/metadata-builder/builders/move-page-builder.ts +227 -0
  112. package/src/metadata-builder/builders/nav-utils.ts +141 -0
  113. package/src/metadata-builder/builders/section-builders/image-gallery-builder.ts +62 -88
  114. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +66 -129
  115. package/src/metadata-builder/builders/section-builders/move-section-builder.ts +58 -35
  116. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +105 -160
  117. package/src/metadata-builder/builders/section-builders/social-links-builder.ts +65 -20
  118. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +83 -146
  119. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +71 -122
  120. package/src/metadata-builder/builders/section-field-maps.ts +18 -0
  121. package/src/metadata-builder/builders/set-page-visibility-builder.ts +122 -0
  122. package/src/metadata-builder/metadata-executor.ts +4 -0
  123. package/src/metadata-builder/models.ts +2 -0
  124. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +32 -7
  125. package/src/metadata-builder/parsers/section-tool-arguments.ts +84 -48
  126. package/src/shared/content-settings/components/links-field/LinksField.mixin.ts +2 -0
  127. package/src/utils/minify-brand-page-metadata.ts +14 -2
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CHfUKgt-.js";
2
+ import { _ as t } from "./lib-ClM5iHnZ.js";
3
3
  const i = {}, h = {
4
4
  width: "275",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CHfUKgt-.js";
2
+ import { _ as t } from "./lib-ClM5iHnZ.js";
3
3
  const i = {}, h = {
4
4
  width: "251",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CHfUKgt-.js";
2
+ import { _ as t } from "./lib-ClM5iHnZ.js";
3
3
  const i = {}, h = {
4
4
  width: "250",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CHfUKgt-.js";
2
+ import { _ as t } from "./lib-ClM5iHnZ.js";
3
3
  const i = {}, h = {
4
4
  width: "237",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CHfUKgt-.js";
2
+ import { _ as t } from "./lib-ClM5iHnZ.js";
3
3
  const i = {}, h = {
4
4
  width: "237",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CHfUKgt-.js";
2
+ import { _ as t } from "./lib-ClM5iHnZ.js";
3
3
  const i = {}, h = {
4
4
  width: "263",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as e, createElementBlock as l, createStaticVNode as a } from "vue";
2
- import { _ as t } from "./lib-CHfUKgt-.js";
2
+ import { _ as t } from "./lib-ClM5iHnZ.js";
3
3
  const i = {}, h = {
4
4
  width: "263",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-CHfUKgt-.js";
2
+ import { _ as e } from "./lib-ClM5iHnZ.js";
3
3
  const M = {}, Z = {
4
4
  width: "242",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-CHfUKgt-.js";
2
+ import { _ as e } from "./lib-ClM5iHnZ.js";
3
3
  const i = {}, M = {
4
4
  width: "231",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-CHfUKgt-.js";
2
+ import { _ as e } from "./lib-ClM5iHnZ.js";
3
3
  const M = {}, Z = {
4
4
  width: "230",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-CHfUKgt-.js";
2
+ import { _ as e } from "./lib-ClM5iHnZ.js";
3
3
  const i = {}, l = {
4
4
  width: "216",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as e } from "./lib-CHfUKgt-.js";
2
+ import { _ as e } from "./lib-ClM5iHnZ.js";
3
3
  const i = {}, l = {
4
4
  width: "216",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as M } from "./lib-CHfUKgt-.js";
2
+ import { _ as M } from "./lib-ClM5iHnZ.js";
3
3
  const Z = {}, e = {
4
4
  width: "232",
5
5
  height: "36",
@@ -1,5 +1,5 @@
1
1
  import { openBlock as H, createElementBlock as V, createStaticVNode as t } from "vue";
2
- import { _ as M } from "./lib-CHfUKgt-.js";
2
+ import { _ as M } from "./lib-ClM5iHnZ.js";
3
3
  const Z = {}, e = {
4
4
  width: "232",
5
5
  height: "36",
@@ -44,6 +44,7 @@ export const Carousel = () => {
44
44
  content.galleryStyles = undefined;
45
45
  content.images = new Array(10).fill(0).map((_, i) => {
46
46
  return {
47
+ id: `gallery-carousel-item-${i}`,
47
48
  imageUrl: `https://picsum.photos/${300 * ((i + 1) / 4)}/${300 * ((i + 1) / 4)}?random=${i}`,
48
49
  width: 300,
49
50
  height: 300,
@@ -71,6 +72,7 @@ export const Masonry = () => {
71
72
  const content = createInitialImageGalleryContent();
72
73
  content.images = new Array(10).fill(0).map((_, i) => {
73
74
  return {
75
+ id: `gallery-masonry-item-${i}`,
74
76
  imageUrl: `https://picsum.photos/${300 * ((i + 1) / 4)}/${300 * ((i + 1) / 4)}?random=${i}`,
75
77
  width: 564,
76
78
  height: 416,
@@ -98,6 +100,7 @@ export const Grid = () => {
98
100
  const content = createInitialImageGalleryContent();
99
101
  content.images = new Array(10).fill(0).map((_, i) => {
100
102
  return {
103
+ id: `gallery-grid-item-${i}`,
101
104
  imageUrl: `https://picsum.photos/${300 * ((i + 1) / 4)}/${300 * ((i + 1) / 4)}?random=${i}`,
102
105
  width: 564,
103
106
  height: 416,
@@ -2,7 +2,7 @@ import type { PropType } from 'vue';
2
2
  import type { ImageGalleryViewModel } from '../../../view-models';
3
3
  import type { DebounceHandle } from 'lodash.debounce';
4
4
  import type { BrandPageContext, ImageGalleryItem } from '../../../models';
5
- interface IndexedImageGalleryItem extends ImageGalleryItem {
5
+ interface IndexedImageGalleryItem extends Omit<ImageGalleryItem, 'id'> {
6
6
  id: number;
7
7
  }
8
8
  declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
@@ -41,8 +41,9 @@ export const Images = () => {
41
41
  },
42
42
  data() {
43
43
  const content = createInitialImagesSectionContent();
44
- content.items = new Array(2).fill(0).map(() => {
44
+ content.items = new Array(2).fill(0).map((_, i) => {
45
45
  return {
46
+ id: `images-section-item-${i}`,
46
47
  imageUrl: 'https://bc-user-uploads.brandcrowd.com/public/media-int/6b3a4d19-e95c-4d59-aa22-ac033a2cec2f/1116870a-c672-4306-b515-32f8d5d6e10c',
47
48
  width: 564,
48
49
  height: 416,
@@ -11,6 +11,7 @@ const viewModel = {
11
11
  title: 'Follow us on',
12
12
  links: [
13
13
  {
14
+ id: 'social-link-facebook',
14
15
  imageUrl: 'facebook',
15
16
  url: '',
16
17
  imageTitle: '',
@@ -21,6 +22,7 @@ const viewModel = {
21
22
  imageLink: '',
22
23
  },
23
24
  {
25
+ id: 'social-link-instagram',
24
26
  imageUrl: 'instagram',
25
27
  url: 'https://www.instagram.com',
26
28
  imageTitle: '',
@@ -31,6 +33,7 @@ const viewModel = {
31
33
  imageLink: '',
32
34
  },
33
35
  {
36
+ id: 'social-link-empty',
34
37
  imageUrl: '',
35
38
  url: '',
36
39
  imageTitle: '',
@@ -9,6 +9,7 @@ export const viewModel = {
9
9
  displayStyle: 'centered',
10
10
  items: [
11
11
  {
12
+ id: 'testimonial-1',
12
13
  testimonials: faker.lorem.paragraphs(1),
13
14
  imageUrl: unsplashImage,
14
15
  name: '1',
@@ -17,6 +18,7 @@ export const viewModel = {
17
18
  colorReplacements: [],
18
19
  },
19
20
  {
21
+ id: 'testimonial-2',
20
22
  testimonials: faker.lorem.paragraphs(3),
21
23
  imageUrl: unsplashImage,
22
24
  name: '2',
@@ -25,6 +27,7 @@ export const viewModel = {
25
27
  colorReplacements: [],
26
28
  },
27
29
  {
30
+ id: 'testimonial-3',
28
31
  testimonials: faker.lorem.paragraphs(5),
29
32
  imageUrl: 'https://source.unsplash.com/random/200x200?3',
30
33
  name: '3',
@@ -31,6 +31,7 @@ const BSviewModel = {
31
31
  text: 'asdasjdlkasjmdlkmsad',
32
32
  textItems: [
33
33
  {
34
+ id: 'text-item-1',
34
35
  text: 'lorem ipsum',
35
36
  textStyle: {
36
37
  fontSize: '1rem',
@@ -39,6 +40,7 @@ const BSviewModel = {
39
40
  },
40
41
  },
41
42
  {
43
+ id: 'text-item-2',
42
44
  text: 'lorem ipsum',
43
45
  textStyle: {
44
46
  fontSize: '1rem',
@@ -110,6 +110,13 @@ export interface ImageContent extends Content {
110
110
  height?: number;
111
111
  }
112
112
  export interface ImageItem {
113
+ /**
114
+ * Stable per-item identifier. Mirrors the section-level `Content.id` one
115
+ * level deeper so AI tool calls can reference items by id rather than by
116
+ * positional index. Backfilled on load for persisted data that predates
117
+ * item ids (see ensureItemIds), so it is always present at runtime.
118
+ */
119
+ id: string;
113
120
  imageUrl: string;
114
121
  imageTitle: string;
115
122
  width: number;
@@ -311,6 +318,8 @@ export interface ShapeItem extends ImageItem {
311
318
  paragraphOverride?: string;
312
319
  }
313
320
  export interface TestimonialItems {
321
+ /** Stable per-item identifier. See ImageItem.id. */
322
+ id: string;
314
323
  imageUrl: string;
315
324
  name: string;
316
325
  position: string;
@@ -325,6 +334,8 @@ export interface TestimonialItems {
325
334
  positionOverride?: string;
326
335
  }
327
336
  export interface TextItem {
337
+ /** Stable per-item identifier. See ImageItem.id. */
338
+ id: string;
328
339
  text: string;
329
340
  textStyle: TextStyleDeclaration;
330
341
  textOverride?: string;
@@ -385,6 +396,8 @@ export interface AboutSectionContent extends Content {
385
396
  size?: string;
386
397
  }
387
398
  export interface LinksItem {
399
+ /** Stable per-item identifier. See ImageItem.id. */
400
+ id: string;
388
401
  imageUrl: string;
389
402
  url: string;
390
403
  size?: string;
@@ -11,6 +11,7 @@ import { NavigationDesktop, NavigationMobile } from './navigation-styles.model';
11
11
  import { TEXT_STYLE_TYPES, TextStyles } from './styles.model';
12
12
  import { EMPTY_LOGO_IMAGE_MODEL, LogoStyles } from './logo.model';
13
13
  import cloneDeep from 'lodash.clonedeep';
14
+ import * as uuid from 'uuid';
14
15
  export * from './brand-page-context.model';
15
16
  export * from './payment.model';
16
17
  export * from './content.model';
@@ -331,6 +332,7 @@ export class AbstractPageFooterStyles {
331
332
  this.logoImageWidth = options.logoImageWidth || DEFAULT_LOGO_WIDTH;
332
333
  this.socialLinks = options.socialLinks || [
333
334
  {
335
+ id: uuid.v4(),
334
336
  width: 0,
335
337
  height: 0,
336
338
  imageUrl: 'facebook',
@@ -340,6 +342,7 @@ export class AbstractPageFooterStyles {
340
342
  imageLink: '',
341
343
  },
342
344
  {
345
+ id: uuid.v4(),
343
346
  width: 0,
344
347
  height: 0,
345
348
  imageUrl: 'instagram',
@@ -349,6 +352,7 @@ export class AbstractPageFooterStyles {
349
352
  imageLink: '',
350
353
  },
351
354
  {
355
+ id: uuid.v4(),
352
356
  width: 0,
353
357
  height: 0,
354
358
  imageUrl: 'x',
@@ -1,3 +1,4 @@
1
+ import * as uuid from 'uuid';
1
2
  import { getLogoVariationIndexFromLogoImageUrl, getLogoDraftTokenFromLogoImageUrl, safeGetLogoImageFromLogoImageUrl, } from '../logo.util';
2
3
  import { DEFAULT_LETTER_SPACING, FOOTER_DISPLAY, NavigationDesktop, NavigationMobile, PageFooterDesktop, PageFooterMobile, PageHeaderDesktop, PageHeaderMobile, } from '../../';
3
4
  import { ALIGN_CARD_CENTER_IMAGE, ALIGN_CARD_LEFT_IMAGE, ALIGN_CENTERED, ALIGN_LEFT_BIG_IMAGE, BIG_IMAGE, BRAND_PAGE_TYPES, DEFAULT_FONT_FAMILY_KEY, DEFAULT_LOGO_HEIGHT, DEFAULT_LOGO_WIDTH, DEFAULT_TEXT_ALIGN, FONT_FAMILY_CONFIGS, HEADER_DISPLAY, NAVIGATION_DISPLAY, LINE_HEIGHT_BODY, LINE_HEIGHT_HEADINGS, LINE_HEIGHT_TITLE, PAYMENT_OPTION_DISPLAY_TYPES, } from '../../';
@@ -25,6 +26,7 @@ export const applyTextContentTextToTextItemsMigration = (brandPageModel) => {
25
26
  };
26
27
  }
27
28
  const textItem = {
29
+ id: uuid.v4(),
28
30
  text: item.text,
29
31
  textStyle,
30
32
  };
@@ -0,0 +1,14 @@
1
+ import type { MigrationData } from '../migration.util';
2
+ /**
3
+ * BP-5603: backfill a stable uuid `id` onto every nested item (gallery images,
4
+ * images/shapes/testimonial items, text items, section + footer social links)
5
+ * that predates the id-based edit model.
6
+ *
7
+ * The item `id` field is now non-nullable in the model, so every load path must
8
+ * guarantee it — not just the AI chat flow, which already stamps ids before
9
+ * minifying (see ensureBrandPageItemIds in useAiWebsiteBuilderChat). This
10
+ * migration is that guarantee for persisted data. Idempotent — existing ids are
11
+ * preserved, so re-running (e.g. render-only sites that never re-save) is a
12
+ * no-op beyond generating ids for items that still lack one.
13
+ */
14
+ export declare const migration18: (migrationResult: MigrationData) => Promise<MigrationData>;
@@ -0,0 +1,17 @@
1
+ import { ensureBrandPageItemIds } from '../../../metadata-builder/builders/item-id-utils';
2
+ /**
3
+ * BP-5603: backfill a stable uuid `id` onto every nested item (gallery images,
4
+ * images/shapes/testimonial items, text items, section + footer social links)
5
+ * that predates the id-based edit model.
6
+ *
7
+ * The item `id` field is now non-nullable in the model, so every load path must
8
+ * guarantee it — not just the AI chat flow, which already stamps ids before
9
+ * minifying (see ensureBrandPageItemIds in useAiWebsiteBuilderChat). This
10
+ * migration is that guarantee for persisted data. Idempotent — existing ids are
11
+ * preserved, so re-running (e.g. render-only sites that never re-save) is a
12
+ * no-op beyond generating ids for items that still lack one.
13
+ */
14
+ export const migration18 = async (migrationResult) => {
15
+ ensureBrandPageItemIds(migrationResult.brandPageModel);
16
+ return migrationResult;
17
+ };
@@ -20,6 +20,7 @@ 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';
23
24
  const MIGRATION_TABLE = [
24
25
  {
25
26
  version: 1,
@@ -132,6 +133,11 @@ const MIGRATION_TABLE = [
132
133
  runAlways: true,
133
134
  description: '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)',
134
135
  },
136
+ {
137
+ version: 18,
138
+ migrateFn: migration18,
139
+ description: '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',
140
+ },
135
141
  ];
136
142
  export const GetLatestModelVersion = () => Math.max(...MIGRATION_TABLE.map((x) => x.version));
137
143
  export const migrateBrandPageModel = async (brandPageModel, options) => {
@@ -50,6 +50,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
50
50
  isSaving: import("vue").ComputedRef<boolean>;
51
51
  isPublishing: import("vue").ComputedRef<boolean>;
52
52
  isPublished: import("vue").ComputedRef<boolean>;
53
+ shouldShowQuickLinkBar: import("vue").ComputedRef<boolean>;
53
54
  isBrandPageOrIsBrandContact: import("vue").ComputedRef<boolean>;
54
55
  brandPageViewModel: import("vue").ComputedRef<import("@brand-page").BrandPageViewModel>;
55
56
  backUrl: import("vue").ComputedRef<string>;
@@ -13,6 +13,7 @@ import { UI_DEVICE_MODES, UI_SCREENSIZE_MODES, } from '@shared/models';
13
13
  import { useBrandPageMakerStore, useBrandPageMakerUserStore, useLocalisedRouteStore, useUiStore, useSnapshotStore, useVideoStore, } from '@brand-page-maker/stores';
14
14
  import { useHistoryStore } from '@brand-page-maker/stores/history/history-store';
15
15
  import { useUndoRedoFlag } from '@brand-page-maker/composables/useUndoRedoFlag';
16
+ import { useQuickLinkBarVisibility } from '@brand-page-maker/composables/useQuickLinkBarVisibility';
16
17
  import { useUiContentSettingsStore } from '@shared/content-settings/store';
17
18
  import { useUiTemplateSettingsStore } from '@shared/template-settings/store';
18
19
  import { useUiPageSettingsStore } from '@shared/page-settings/store';
@@ -73,6 +74,10 @@ export default defineComponent({
73
74
  isSaving: computed(() => brandPageMakerStore.isSaving),
74
75
  isPublishing: computed(() => brandPageMakerStore.isPublishing),
75
76
  isPublished: computed(() => brandPageMakerStore.isPublished),
77
+ // Whether the QuickLinkBar renders — used to offset the preview (Device)
78
+ // and SideBar clear of the fixed bar on mobile. Must match the bar's own
79
+ // render condition, so it shares the same predicate.
80
+ shouldShowQuickLinkBar: useQuickLinkBarVisibility(),
76
81
  isBrandPageOrIsBrandContact: computed(() => brandPageMakerStore.isBrandPageOrIsBrandContact),
77
82
  brandPageViewModel: computed(() => brandPageMakerStore.brandPageViewModel),
78
83
  backUrl: computed(() => localisedRouteStore.backToWebsiteManageUrlPath),
@@ -1281,6 +1281,7 @@ export const SampleImageGallery = () => {
1281
1281
  const width = 300;
1282
1282
  const height = 300;
1283
1283
  imageGalleryContent.images.push({
1284
+ id: `gallery-item-${i}`,
1284
1285
  width,
1285
1286
  height,
1286
1287
  imageUrl: `https://images.unsplash.com/photo-1659535915214-e7cbac112038?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjQzOTV8MXwxfHNlYXJjaHw4fHxnYW1pbmd8ZW58MHx8fHwxNjY5MDc4ODE1&ixlib=rb-4.0.3&q=80&w=1080`,
@@ -2212,6 +2212,7 @@ export const BRAND_PAGE_PERSONALISED = {
2212
2212
  displayStyle: 'centered',
2213
2213
  items: [
2214
2214
  {
2215
+ id: 'shapes-section-item-1',
2215
2216
  colorReplacements: [],
2216
2217
  height: -1,
2217
2218
  imageUrl: 'https://bcassetcdn.com/shapes/prod/00019160-9144-4bb3-af48-394a52845c37.svg',
@@ -292,11 +292,11 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
292
292
  pushManageWebPageUnpublishClickEvent(): Promise<void>;
293
293
  }>;
294
294
  undoRedoEnabled: import("vue").ComputedRef<boolean>;
295
+ shouldShowBar: import("vue").ComputedRef<boolean>;
295
296
  }, {
296
297
  copied: boolean;
297
298
  }, {
298
299
  isPublished(): boolean;
299
- shouldShowBar(): boolean;
300
300
  brandPageUrl(): string;
301
301
  copyButtonLabel(): string;
302
302
  }, {
@@ -2,6 +2,7 @@ import { defineComponent } from 'vue';
2
2
  import { CUSTOM_DOMAIN_STATUSES } from '@brand-page-maker/constants';
3
3
  import { useBrandPageContainerStore, useEventManagerStore, } from '@brand-page-maker/stores';
4
4
  import { useUndoRedoFlag } from '@brand-page-maker/composables/useUndoRedoFlag';
5
+ import { useQuickLinkBarVisibility } from '@brand-page-maker/composables/useQuickLinkBarVisibility';
5
6
  import { quickLinkBarTr } from '@src/composables/useTranslate';
6
7
  export default defineComponent({
7
8
  props: {
@@ -19,6 +20,9 @@ export default defineComponent({
19
20
  brandPageContainerStore: useBrandPageContainerStore(),
20
21
  eventManagerStore: useEventManagerStore(),
21
22
  undoRedoEnabled: useUndoRedoFlag(),
23
+ // Single source of truth for whether the bar renders — shared with the
24
+ // layout components (Device, SideBar) that offset content clear of it.
25
+ shouldShowBar: useQuickLinkBarVisibility(),
22
26
  };
23
27
  },
24
28
  data() {
@@ -30,12 +34,6 @@ export default defineComponent({
30
34
  isPublished() {
31
35
  return this.brandPageContainerStore.isPublished;
32
36
  },
33
- // Only render the bar when at least one inner cluster has something to
34
- // show. Otherwise we'd render an empty white strip — e.g. on an
35
- // unpublished page with the undo flag off.
36
- shouldShowBar() {
37
- return Boolean(this.undoRedoEnabled) || this.isPublished;
38
- },
39
37
  brandPageUrl() {
40
38
  if (this.brandPageContainerStore.isCustomDomainEnabled &&
41
39
  this.brandPageContainerStore.customDomain &&
@@ -5,6 +5,7 @@ import { parseLogoThemeToCssStyles } from '../../../brand-page/utils/style.util'
5
5
  import { AI_ASSISTANT_TOOL_NAMES } from '../../../metadata-builder/parsers/ai-toolcall-parser';
6
6
  import { WebSocketGatewayClient, } from '../../../clients/websocket-gateway-client';
7
7
  import { minifyBrandPageMetadata } from '../../../utils/minify-brand-page-metadata';
8
+ import { ensureBrandPageItemIds } from '../../../metadata-builder/builders/item-id-utils';
8
9
  import { buildAssetManifest } from '../../../utils/build-asset-manifest';
9
10
  import cloneDeep from 'lodash.clonedeep';
10
11
  import { usePageNavigationStore } from '../../../shared/page-navigation/store/page-navigation-store';
@@ -179,6 +180,10 @@ export function useAiWebsiteBuilderChat() {
179
180
  };
180
181
  const buildMessagePayload = async (userPrompt) => {
181
182
  const brandPageData = brandPageMakerStore.brandPageData;
183
+ // BP-5603: stamp stable uuid ids onto every nested item BEFORE minifying,
184
+ // so the id the AI is shown (and returns as `item_id`) is the exact id the
185
+ // executor matches when applying tool calls to this same live model.
186
+ ensureBrandPageItemIds(brandPageData);
182
187
  const headerLogoDraftToken = brandPageData.pageHeaderDesktop?.logoImage?.logoDraftToken ??
183
188
  uiStore.logoDraftToken;
184
189
  const [assets, logoContext] = await Promise.all([
@@ -0,0 +1,17 @@
1
+ import { type ComputedRef } from 'vue';
2
+ /**
3
+ * Whether the QuickLinkBar is rendered in the maker.
4
+ *
5
+ * The bar shows when at least one of its inner clusters has something to
6
+ * display: the undo/redo controls (flag on, non-admin — see
7
+ * {@link useUndoRedoFlag}) OR the published-URL cluster (page published).
8
+ *
9
+ * This is the single source of truth shared by the bar itself
10
+ * (`QuickLinkBar`) and the layout components that offset their content to sit
11
+ * clear of the fixed bar on mobile (`Device`, `SideBar`). Keeping the offset
12
+ * bound to the same predicate the bar renders on prevents the two from
13
+ * drifting — a drift where the bar showed for the undo/redo reason but the
14
+ * offset only accounted for the published reason caused the bar to overlap
15
+ * the mobile preview.
16
+ */
17
+ export declare function useQuickLinkBarVisibility(): ComputedRef<boolean>;
@@ -0,0 +1,23 @@
1
+ import { computed } from 'vue';
2
+ import { useBrandPageContainerStore } from '@brand-page-maker/stores';
3
+ import { useUndoRedoFlag } from './useUndoRedoFlag';
4
+ /**
5
+ * Whether the QuickLinkBar is rendered in the maker.
6
+ *
7
+ * The bar shows when at least one of its inner clusters has something to
8
+ * display: the undo/redo controls (flag on, non-admin — see
9
+ * {@link useUndoRedoFlag}) OR the published-URL cluster (page published).
10
+ *
11
+ * This is the single source of truth shared by the bar itself
12
+ * (`QuickLinkBar`) and the layout components that offset their content to sit
13
+ * clear of the fixed bar on mobile (`Device`, `SideBar`). Keeping the offset
14
+ * bound to the same predicate the bar renders on prevents the two from
15
+ * drifting — a drift where the bar showed for the undo/redo reason but the
16
+ * offset only accounted for the published reason caused the bar to overlap
17
+ * the mobile preview.
18
+ */
19
+ export function useQuickLinkBarVisibility() {
20
+ const containerStore = useBrandPageContainerStore();
21
+ const undoRedoEnabled = useUndoRedoFlag();
22
+ return computed(() => undoRedoEnabled.value || containerStore.isPublished);
23
+ }
@@ -1,5 +1,6 @@
1
1
  import { SOCIAL_LINKS_ICON_DISPLAY_STYLES, BRAND_PAGE_TYPES, DIVIDER_STYLES, MEDIA_TYPES, BUTTON_LINK_TYPE, FONT_SIZE_BODY, LINE_HEIGHT_BODY, MIN_TEXT_SIZE_PX, MAX_TEXT_SIZE_PX, TEXT_COLUMNS, DEFAULT_SHAPE_SIZE, PAYMENT_OPTION_DISPLAY_TYPES, PAYMENT_OPTION_IMAGE_TYPES, GettyVideoMedia, } from '@brand-page';
2
2
  import { createInitialButtonContent, createInitialImageContent, createInitialProfileContent, createInitialTextContent, createInitialVideoContent, createInitialLogoContent, createInitialMapContent, createInitialImageGalleryContent, createInitialGeneralLinkContent, createInitialMailingListContent, createInitialRequestPaymentContent, createInitialSupportMeContent, createInitialContactFormContent, CONTENT_SETTINGS_DISPLAY_MODES, createInitialBannerSectionContent, createInitialSocialLinksContent, createInitialImagesSectionContent, createInitialTestimonialSectionContent, createInitialAboutSectionContent, createInitialDividerSectionContent, createInitialShapesSectionContent, VARIANT_DISPLAY_MODES, createInitialShopContent, createInitialBookingSectionContent, } from '../models';
3
+ import * as uuid from 'uuid';
3
4
  import { colorConvertor, backgroundColorConvertor, borderColorConvertor, displayStyleConvertor, galleryTypeConvertor, imageAspectRatioConvertor, textAlignmentOptionsConvertor, videoContentStateConvertor, fillColorConvertor, bannerAlignDisplayStyleConvertor, aboutDisplayStyleConvertor, dividerStyleConvertor, shapeSizeOptionsConvertor, buttonLinkStyleConvertor, textSizeValueConvertor, textColumnStyleConvertor, contactLayoutStyleConvertor, paymentLinkDisplayStyleConvertor, shopDisplayConvertor, shopAspectRatioConvertor, } from '../convertors';
4
5
  import { ALIGN_CENTERED, DISPLAY_STYLE_COLLAPSED, DISPLAY_STYLE_PREVIEW, } from '@brand-page';
5
6
  import { imageFieldVisibilityAssessor, imageOptionVisibilityAssessor, imageGalleryFieldVisibilityAssessor, cardFillVisibilityAssessor, mediaSectionItemsFieldTypeAssessor, dividerToggleFieldVisibilityAssessor, shapeSectionItemsFieldTypeAssessor, colorReplacementVisibilityTypeAssessor, colorReplacementProfileVisibilityTypeAssessor, colorReplacementIconVisibilityTypeAssessor, imagesSectionCardStyleVisibilityAssessor, contactCardDisplayTypeVisibilityAssessor, imageUrlVisibilityAssessor, displayTypeVisibilityAssessor, logoFieldVisibilityAssessor, shapesSectionCardStyleVisibilityAssessor, shopImageOptionVisibilityAssessor, shopItemColorVisibility, personalisationOverrideVisibility, } from '../assessors';
@@ -167,6 +168,7 @@ const imageGalleryReplaceImageSettings = () => ({
167
168
  content.images = [];
168
169
  }
169
170
  content.images.push({
171
+ id: uuid.v4(),
170
172
  imageUrl: value.imageUrl,
171
173
  imageTitle: value.imageTitle,
172
174
  height: value.height,
@@ -372,6 +374,7 @@ const shapesImageReplaceImageSettings = () => ({
372
374
  setContentMedia: (content, value, index) => {
373
375
  if (index === undefined || index === null) {
374
376
  content.items?.push({
377
+ id: uuid.v4(),
375
378
  imageUrl: value.imageUrl,
376
379
  imageTitle: '',
377
380
  paragraph: '',
@@ -402,6 +405,7 @@ const textImageReplaceImageSettings = () => ({
402
405
  setContentMedia: (content, value, index) => {
403
406
  if (index === undefined || index === null) {
404
407
  content.items?.push({
408
+ id: uuid.v4(),
405
409
  imageUrl: value.imageUrl,
406
410
  imageTitle: value.imageTitle,
407
411
  paragraph: '',
@@ -153,6 +153,7 @@ export const createInitialTextContent = (options) => {
153
153
  textItems: options?.brandPageType === 'brandSite'
154
154
  ? [
155
155
  {
156
+ id: uuid.v4(),
156
157
  text: contentSettingsTr(`defaultTextValue`),
157
158
  textStyle: {
158
159
  applyAutoScale: options?.brandPageType === BRAND_PAGE_TYPES.brandSite,
@@ -706,6 +707,7 @@ export const createInitialSocialLinksContent = (options) => {
706
707
  title: contentSettingsTr(`followUsOnTitle`),
707
708
  links: [
708
709
  {
710
+ id: uuid.v4(),
709
711
  imageUrl: 'facebook',
710
712
  url: 'https://www.facebook.com',
711
713
  imageTitle: '',
@@ -716,6 +718,7 @@ export const createInitialSocialLinksContent = (options) => {
716
718
  imageLink: '',
717
719
  },
718
720
  {
721
+ id: uuid.v4(),
719
722
  imageUrl: 'instagram',
720
723
  url: 'https://www.instagram.com',
721
724
  imageTitle: '',
@@ -726,6 +729,7 @@ export const createInitialSocialLinksContent = (options) => {
726
729
  imageLink: '',
727
730
  },
728
731
  {
732
+ id: uuid.v4(),
729
733
  imageUrl: 'x',
730
734
  url: 'https://www.x.com',
731
735
  imageTitle: '',