@designcrowd/library.brand-page 6.0.3 → 6.0.4

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 (383) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-BctSP4tp.js → CreatedOnBrandCrowd-de-de-Ruu5Az32.js} +3 -3
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-BeNytcxt.js → CreatedOnBrandCrowd-en-us-DGUJtvld.js} +3 -3
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-hY12f5TN.js → CreatedOnBrandCrowd-es-es-Dg-1uOfV.js} +3 -3
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-C4SCU39P.js → CreatedOnBrandCrowd-fr-ca-CRKraZl8.js} +3 -3
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-C4SCU39P.js → CreatedOnBrandCrowd-fr-fr-CRKraZl8.js} +3 -3
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-C5J5kWiD.js → CreatedOnBrandCrowd-pt-br-bZ9VlEAu.js} +3 -3
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-C5J5kWiD.js → CreatedOnBrandCrowd-pt-pt-bZ9VlEAu.js} +3 -3
  8. package/lib/src/{CreatedOnDcom-de-de-DXCeZZLg.js → CreatedOnDcom-de-de-CJYfwZx8.js} +3 -3
  9. package/lib/src/{CreatedOnDcom-en-us-GeBTaofv.js → CreatedOnDcom-en-us-rzA1lVxH.js} +3 -3
  10. package/lib/src/{CreatedOnDcom-es-es-BiPmv1q-.js → CreatedOnDcom-es-es-CNwGPjik.js} +3 -3
  11. package/lib/src/{CreatedOnDcom-fr-ca-Cns_iX4-.js → CreatedOnDcom-fr-ca-DKw7OGIz.js} +3 -3
  12. package/lib/src/{CreatedOnDcom-fr-fr-CC-f_hUR.js → CreatedOnDcom-fr-fr-WzOEaAZG.js} +3 -3
  13. package/lib/src/{CreatedOnDcom-pt-br-DnhSuPKo.js → CreatedOnDcom-pt-br-DkualVhm.js} +3 -3
  14. package/lib/src/{CreatedOnDcom-pt-pt-BCgbUsqY.js → CreatedOnDcom-pt-pt-w4Nnsdzq.js} +3 -3
  15. package/lib/src/brand-page/clients/brand-page.client.d.ts +9 -9
  16. package/lib/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.d.ts +2 -1
  17. package/lib/src/brand-page/components/navigation/hamburger/HamburgerNavigation.mixin.d.ts +2 -4
  18. package/lib/src/brand-page/components/navigation/hamburger/HamburgerNavigation.mixin.js +0 -1
  19. package/lib/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.d.ts +2 -4
  20. package/lib/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.js +4 -5
  21. package/lib/src/brand-page/components/page/Page.mixin.d.ts +1 -1
  22. package/lib/src/brand-page/components/page/PageComponent.mixin.d.ts +1 -1
  23. package/lib/src/brand-page/components/payment/Payment.mixin.d.ts +3 -3
  24. package/lib/src/brand-page/components/payment/__fixtures__/request-payment.d.ts +13 -13
  25. package/lib/src/brand-page/components/payment/__fixtures__/support-me.d.ts +13 -13
  26. package/lib/src/brand-page/constants/sections/footer-display.constants.js +1 -1
  27. package/lib/src/brand-page/models/index.d.ts +13 -2
  28. package/lib/src/brand-page/models/index.js +0 -3
  29. package/lib/src/brand-page/models/payment.model.d.ts +1 -1
  30. package/lib/src/brand-page/utils/personalisation.util.js +14 -72
  31. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +19 -21
  32. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.js +0 -1
  33. package/lib/src/brand-page-maker/components/admin-background-settings/AdminBackgroundSettings.mixin.d.ts +10 -16
  34. package/lib/src/brand-page-maker/components/admin-background-settings/ShapePositionSlider.mixin.d.ts +1 -1
  35. package/lib/src/brand-page-maker/components/admin-logo-settings/AdminLogoSettings.mixin.d.ts +3 -3
  36. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +170 -181
  37. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +0 -3
  38. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.js +0 -1
  39. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.mixin.d.ts +12 -12
  40. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.stories.js +2 -2
  41. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-payment.js +7 -0
  42. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +149 -148
  43. package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.d.ts +21 -21
  44. package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.js +1 -1
  45. package/lib/src/brand-page-maker/components/color-picker/components/ColorModeSelect.mixin.d.ts +2 -4
  46. package/lib/src/brand-page-maker/components/color-picker/components/ColorModeSelect.mixin.js +0 -1
  47. package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSlider.mixin.d.ts +2 -4
  48. package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSlider.mixin.js +0 -1
  49. package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSliderHandler.mixin.d.ts +2 -5
  50. package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSliderHandler.mixin.js +0 -1
  51. package/lib/src/brand-page-maker/components/color-picker/components/GradientTypeSelector.mixin.d.ts +2 -4
  52. package/lib/src/brand-page-maker/components/color-picker/components/GradientTypeSelector.mixin.js +0 -1
  53. package/lib/src/brand-page-maker/components/device/Device.mixin.d.ts +2 -4
  54. package/lib/src/brand-page-maker/components/device/Device.mixin.js +0 -1
  55. package/lib/src/brand-page-maker/components/navigation/Navigation.mixin.d.ts +2 -4
  56. package/lib/src/brand-page-maker/components/navigation/Navigation.mixin.js +0 -1
  57. package/lib/src/brand-page-maker/components/preview/Preview.mixin.d.ts +3 -3
  58. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +11 -11
  59. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +25 -27
  60. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.js +0 -1
  61. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +27 -27
  62. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.js +1 -1
  63. package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +12 -17
  64. package/lib/src/brand-page-maker/components/social-share/SocialShare.mixin.d.ts +12 -12
  65. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +32 -30
  66. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.js +5 -16
  67. package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +11 -11
  68. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +6 -3
  69. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +1 -1
  70. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +6 -6
  71. package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +2 -2
  72. package/lib/src/brand-page-maker/constants/config-settings-constant.d.ts +6 -6
  73. package/lib/src/brand-page-maker/constants/config-settings-constant.js +3 -3
  74. package/lib/src/brand-page-maker/convertors/text-image-display-style.convertor.d.ts +1 -1
  75. package/lib/src/brand-page-maker/convertors/text-image-display-style.convertor.js +1 -1
  76. package/lib/src/brand-page-maker/models/content-settings.d.ts +1 -1
  77. package/lib/src/brand-page-maker/models/navigation-style-configs.js +1 -1
  78. package/lib/src/brand-page-maker/routing/brand-page-maker-router.d.ts +2 -1
  79. package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +378 -366
  80. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +4 -5
  81. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.js +5 -8
  82. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.d.ts +1 -3
  83. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +3 -7
  84. package/lib/src/brand-page-maker/stores/history/history-store.d.ts +15 -15
  85. package/lib/src/brand-page-maker/stores/ui/ui-store.d.ts +0 -6
  86. package/lib/src/brand-page-maker/stores/ui/ui-store.js +0 -8
  87. package/lib/src/brand-page-maker/utils/content-settings.util.js +1 -1
  88. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +1 -1
  89. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +4 -4
  90. package/lib/src/clients/ai-assistant-client.d.ts +0 -7
  91. package/lib/src/clients/brand-page-client.d.ts +15 -16
  92. package/lib/src/clients/design-template-client.d.ts +0 -3
  93. package/lib/src/clients/media-client.d.ts +2 -2
  94. package/lib/src/clients/models.d.ts +0 -2
  95. package/lib/src/composables/useSnapshot.js +18 -18
  96. package/lib/src/css/library.brand-page-brandCrowd.min.css +324 -342
  97. package/lib/src/css/library.brand-page-brandPage.min.css +324 -620
  98. package/lib/src/css/library.brand-page-designCom.min.css +324 -342
  99. package/lib/src/css/library.brand-page-preview.min.css +324 -598
  100. package/lib/src/index.d.ts +1 -5
  101. package/lib/src/index.mjs +106 -110
  102. package/lib/src/{lib-Cw6L6CfQ.js → lib-Dy7MYyqz.js} +33240 -36457
  103. package/lib/src/lib.js +1 -11
  104. package/lib/src/metadata-builder/builders/footer-builder.js +62 -105
  105. package/lib/src/metadata-builder/builders/item-id-utils.d.ts +54 -0
  106. package/lib/src/metadata-builder/builders/item-id-utils.js +96 -0
  107. package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.js +57 -68
  108. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +58 -93
  109. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +66 -99
  110. package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +57 -17
  111. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +51 -85
  112. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +49 -74
  113. package/lib/src/metadata-builder/builders/section-field-utils.d.ts +0 -7
  114. package/lib/src/metadata-builder/builders/section-field-utils.js +1 -1
  115. package/lib/src/metadata-builder/builders/shape-color-utils.js +1 -1
  116. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +16 -7
  117. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +66 -48
  118. package/lib/src/plugins/google-maps-plugin/google-maps-plugin.d.ts +1 -1
  119. package/lib/src/plugins/google-maps-plugin/google-maps-plugin.js +8 -8
  120. package/lib/src/shared/background-settings/components/background-shapes/BackgroundShapeSettings.mixin.d.ts +13 -13
  121. package/lib/src/shared/background-settings/store/background-settings-store.d.ts +3 -3
  122. package/lib/src/shared/color-picker/components/ColorPicker.mixin.d.ts +2 -4
  123. package/lib/src/shared/color-picker/components/ColorPicker.mixin.js +2 -2
  124. package/lib/src/shared/color-picker/components/ColorPickerSwatch.mixin.d.ts +2 -4
  125. package/lib/src/shared/color-picker/components/ColorPickerSwatch.mixin.js +0 -1
  126. package/lib/src/shared/confirm-modal/ConfirmModal.mixin.d.ts +2 -5
  127. package/lib/src/shared/confirm-modal/ConfirmModal.mixin.js +0 -1
  128. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.d.ts +1 -0
  129. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +129 -131
  130. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.js +1 -2
  131. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +110 -111
  132. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +81 -84
  133. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.js +0 -5
  134. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +109 -117
  135. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +14 -1
  136. package/lib/src/shared/content-settings/components/email-field/EmailField.mixin.d.ts +1 -0
  137. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.d.ts +70 -72
  138. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.js +3 -4
  139. package/lib/src/shared/content-settings/components/form-fields-field/FormFieldsField.mixin.d.ts +4 -4
  140. package/lib/src/shared/content-settings/components/icon-library/IconLibrary.mixin.d.ts +1 -3
  141. package/lib/src/shared/content-settings/components/icon-library/IconLibrary.mixin.js +0 -1
  142. package/lib/src/shared/content-settings/components/images-field/ImagesField.mixin.d.ts +2 -2
  143. package/lib/src/shared/content-settings/components/items-field/ItemsField.mixin.d.ts +3 -4
  144. package/lib/src/shared/content-settings/components/items-field/ItemsField.mixin.js +1 -1
  145. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +40 -40
  146. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigOptionField.mixin.d.ts +6 -5
  147. package/lib/src/shared/content-settings/components/shape-field/ShapeField.mixin.d.ts +1 -0
  148. package/lib/src/shared/content-settings/components/slider-field/SliderField.mixin.d.ts +2 -2
  149. package/lib/src/shared/content-settings/components/tel-field/TelField.mixin.d.ts +1 -0
  150. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.d.ts +2 -1
  151. package/lib/src/shared/content-settings/components/text-field/TextField.mixin.d.ts +1 -0
  152. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.d.ts +2 -1
  153. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.js +1 -1
  154. package/lib/src/shared/content-settings/components/text-style-field/TextStyleField.mixin.d.ts +28 -28
  155. package/lib/src/shared/content-settings/components/textarea-field/TextareaField.mixin.d.ts +1 -0
  156. package/lib/src/shared/content-settings/components/toggle-field/ToggleField.mixin.d.ts +2 -2
  157. package/lib/src/shared/content-settings/components/url-field/UrlField.mixin.d.ts +1 -0
  158. package/lib/src/shared/content-settings/components/youtube-video-field/YoutubeVideoField.mixin.d.ts +1 -0
  159. package/lib/src/shared/content-settings/store/content-settings-store.d.ts +39 -39
  160. package/lib/src/shared/content-settings/utils.js +1 -0
  161. package/lib/src/shared/font-family-selector/FontFamilySelector.mixin.d.ts +4 -6
  162. package/lib/src/shared/font-family-selector/FontFamilySelector.mixin.js +0 -1
  163. package/lib/src/shared/image-gallery-style-settings/ImageGalleryStyleSettings.mixin.d.ts +2 -4
  164. package/lib/src/shared/image-gallery-style-settings/ImageGalleryStyleSettings.mixin.js +0 -1
  165. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +21 -29
  166. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.js +1 -1
  167. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +51 -50
  168. package/lib/src/shared/logo-settings/components/LogoSettingsHeader.mixin.d.ts +4 -6
  169. package/lib/src/shared/logo-settings/components/LogoSettingsHeader.mixin.js +0 -1
  170. package/lib/src/shared/logo-settings/store/editable-logo-store.js +1 -1
  171. package/lib/src/shared/logo-settings/store/logo-settings-store.d.ts +1 -1
  172. package/lib/src/shared/logo-versions/LogoVersions.mixin.d.ts +5 -13
  173. package/lib/src/shared/logo-versions/LogoVersions.mixin.js +0 -1
  174. package/lib/src/shared/models.d.ts +0 -2
  175. package/lib/src/shared/my-designs/MyDesigns.mixin.d.ts +6 -14
  176. package/lib/src/shared/my-designs/MyDesigns.mixin.js +0 -1
  177. package/lib/src/shared/my-uploads/MyMedia.mixin.d.ts +5 -14
  178. package/lib/src/shared/my-uploads/MyMedia.mixin.js +0 -1
  179. package/lib/src/shared/my-uploads/MyUploads.mixin.d.ts +5 -14
  180. package/lib/src/shared/my-uploads/MyUploads.mixin.js +0 -1
  181. package/lib/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.d.ts +3 -5
  182. package/lib/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.js +0 -1
  183. package/lib/src/shared/navigation-style-settings/store/index.d.ts +2 -2
  184. package/lib/src/shared/page-navigation/store/page-navigation-store.d.ts +2 -2
  185. package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.d.ts +28 -27
  186. package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.js +2 -0
  187. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +179 -188
  188. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +177 -181
  189. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.js +1 -1
  190. package/lib/src/shared/page-settings/store/page-settings-store.d.ts +9 -9
  191. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +39 -39
  192. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.js +1 -0
  193. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +18 -18
  194. package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +6 -6
  195. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +60 -66
  196. package/lib/src/shared/style-settings/store/style-settings-store.d.ts +1 -1
  197. package/lib/src/shared/style-settings/store/style-settings-store.js +1 -1
  198. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +450 -20
  199. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.js +2 -2
  200. package/lib/src/shared/template-settings/models.d.ts +0 -1
  201. package/lib/src/shared/template-settings/store/template-settings-store.d.ts +0 -4
  202. package/lib/src/shared/template-settings/store/template-settings-store.js +11 -79
  203. package/lib/src/shared/unsplash-library/UnsplashLibrary.mixin.d.ts +4 -9
  204. package/lib/src/utils/build-asset-manifest.d.ts +0 -4
  205. package/lib/src/utils/build-asset-manifest.js +0 -8
  206. package/lib/src/utils/minify-brand-page-metadata.d.ts +2 -0
  207. package/lib/src/utils/minify-brand-page-metadata.js +17 -2
  208. package/package.json +56 -60
  209. package/src/brand-page/components/about-section/AboutSection.vue.test.ts +5 -7
  210. package/src/brand-page/components/brand-page-footer/BrandPageFooter.vue +1 -1
  211. package/src/brand-page/components/image/Image.mixin.ts +1 -1
  212. package/src/brand-page/components/image/Image.test.ts +1 -1
  213. package/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.ts +2 -1
  214. package/src/brand-page/components/images-section/ImagesSection.vue +1 -0
  215. package/src/brand-page/components/logo/LogoRender.vue +188 -188
  216. package/src/brand-page/components/navigation/hamburger/HamburgerNavigation.mixin.ts +0 -1
  217. package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.ts +6 -15
  218. package/src/brand-page/components/page-footer/PageFooter.vue +0 -1
  219. package/src/brand-page/components/page-header/HeaderLayout.vue +6 -12
  220. package/src/brand-page/components/page-header/PageHeader.vue +0 -1
  221. package/src/brand-page/components/shapes-section/ShapesSection.vue +1 -1
  222. package/src/brand-page/components/shop/CartModal.vue +3 -1
  223. package/src/brand-page/components/shop/FeaturedDisplay.vue +2 -2
  224. package/src/brand-page/components/shop/PaymentModal.vue +5 -1
  225. package/src/brand-page/components/shop/ProductDetailModal.vue +1 -1
  226. package/src/brand-page/components/shop/Shop.vue +1 -1
  227. package/src/brand-page/components/shop/TextDisplay.vue +1 -1
  228. package/src/brand-page/components/shop/ThumbnailDisplay.vue +2 -2
  229. package/src/brand-page/components/testimonial-section/TestimonialSection.vue +3 -3
  230. package/src/brand-page/components/text-wrapper/TextWrapper.vue +1 -3
  231. package/src/brand-page/constants/sections/footer-display.constants.ts +1 -1
  232. package/src/brand-page/models/index.ts +12 -4
  233. package/src/brand-page/models/payment.model.ts +1 -2
  234. package/src/brand-page/models/shop.model.ts +2 -0
  235. package/src/brand-page/models/user-personalisation.model.ts +2 -1
  236. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +0 -3
  237. package/src/brand-page/utils/__snapshots__/migration2.util.test.ts.snap +0 -1
  238. package/src/brand-page/utils/__snapshots__/model.util.test.ts.snap +0 -2
  239. package/src/brand-page/utils/migration/migration-01_1.ts +1 -1
  240. package/src/brand-page/utils/migration/migration-02.ts +1 -0
  241. package/src/brand-page/utils/migration/migration-03.ts +1 -0
  242. package/src/brand-page/utils/migration/migration-04.ts +1 -0
  243. package/src/brand-page/utils/migration/migration-05.ts +5 -1
  244. package/src/brand-page/utils/migration/migration-05_2.ts +1 -1
  245. package/src/brand-page/utils/migration/migration-08.test.ts +1 -1
  246. package/src/brand-page/utils/personalisation.util.test.ts +0 -35
  247. package/src/brand-page/utils/personalisation.util.ts +23 -119
  248. package/src/brand-page/view-models.ts +56 -38
  249. package/src/brand-page-maker/assessors/banner-section-description-visibility.assessor.ts +6 -6
  250. package/src/brand-page-maker/assessors/color-replacement-icon-visibility.assessor.ts +2 -2
  251. package/src/brand-page-maker/assessors/color-replacement-profile-visibility.assessor.ts +2 -2
  252. package/src/brand-page-maker/assessors/color-replacement-visibility.assessor.ts +2 -2
  253. package/src/brand-page-maker/assessors/contact-form-visibility.assessor.ts +14 -14
  254. package/src/brand-page-maker/components/action-bar/ActionBar.mixin.ts +0 -1
  255. package/src/brand-page-maker/components/ai-text-edit/TextBubble.vue +0 -1
  256. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +1 -5
  257. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.ts +0 -1
  258. package/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.stories.ts +5 -2
  259. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +5 -0
  260. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-payment.ts +8 -0
  261. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-site-template.examples.ts +2 -0
  262. package/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.vue +0 -1
  263. package/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.ts +1 -1
  264. package/src/brand-page-maker/components/color-palette/BpColorPalette.vue +2 -1
  265. package/src/brand-page-maker/components/color-picker/components/ColorModeSelect.mixin.ts +0 -1
  266. package/src/brand-page-maker/components/color-picker/components/GradientColorSlider.mixin.ts +0 -1
  267. package/src/brand-page-maker/components/color-picker/components/GradientColorSliderHandler.mixin.ts +0 -1
  268. package/src/brand-page-maker/components/color-picker/components/GradientTypeSelector.mixin.ts +0 -1
  269. package/src/brand-page-maker/components/device/Device.mixin.ts +0 -1
  270. package/src/brand-page-maker/components/navigation/Navigation.mixin.ts +0 -1
  271. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +1 -3
  272. package/src/brand-page-maker/components/sensitive-content/SensitiveContent.vue +4 -1
  273. package/src/brand-page-maker/components/seo-settings/DeleteDomainModal.vue +1 -1
  274. package/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.ts +0 -1
  275. package/src/brand-page-maker/components/seo-settings/SeoPageSettings.vue +1 -1
  276. package/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.ts +1 -0
  277. package/src/brand-page-maker/components/seo-settings/UrlSettings.vue +1 -0
  278. package/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.vue +5 -1
  279. package/src/brand-page-maker/components/shop/AddOrEditProductContainer.vue +1 -1
  280. package/src/brand-page-maker/components/shop/ReplaceImageSettingsForShop.vue +1 -1
  281. package/src/brand-page-maker/components/social-share/SocialShare.vue +1 -1
  282. package/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.ts +8 -22
  283. package/src/brand-page-maker/components/template-preview/TemplatePreview.vue +10 -5
  284. package/src/brand-page-maker/components/video-play/VideoPlayButton.vue +1 -1
  285. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +7 -3
  286. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +6 -6
  287. package/src/brand-page-maker/constants/config-settings-constant.ts +5 -5
  288. package/src/brand-page-maker/convertors/text-image-display-style.convertor.ts +1 -1
  289. package/src/brand-page-maker/models/button-styles-config.ts +2 -4
  290. package/src/brand-page-maker/models/content-settings-configs.ts +2 -4
  291. package/src/brand-page-maker/models/navigation-style-configs.ts +6 -5
  292. package/src/brand-page-maker/stores/admin-logo-settings/admin-logo-settings-store.ts +1 -0
  293. package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.ts +5 -8
  294. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.test.ts +3 -1
  295. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +4 -12
  296. package/src/brand-page-maker/stores/ui/ui-store.ts +0 -16
  297. package/src/brand-page-maker/utils/content-settings.util.ts +1 -1
  298. package/src/brand-page-maker/utils/rich-text-editor.util.ts +4 -4
  299. package/src/clients/ai-assistant-client.ts +0 -7
  300. package/src/clients/design-template-client.ts +0 -3
  301. package/src/clients/models.ts +0 -2
  302. package/src/composables/useSnapshot.ts +22 -19
  303. package/src/lib.ts +0 -13
  304. package/src/metadata-builder/builders/footer-builder.test.ts +229 -130
  305. package/src/metadata-builder/builders/footer-builder.ts +90 -155
  306. package/src/metadata-builder/builders/item-id-utils.test.ts +184 -0
  307. package/src/metadata-builder/builders/item-id-utils.ts +117 -0
  308. package/src/metadata-builder/builders/section-builders/image-gallery-builder.ts +62 -88
  309. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +66 -129
  310. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +105 -160
  311. package/src/metadata-builder/builders/section-builders/social-links-builder.ts +65 -20
  312. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +83 -146
  313. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +70 -122
  314. package/src/metadata-builder/builders/section-field-utils.ts +1 -1
  315. package/src/metadata-builder/builders/shape-color-utils.ts +2 -2
  316. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +16 -7
  317. package/src/metadata-builder/parsers/section-tool-arguments.ts +66 -48
  318. package/src/plugins/google-maps-plugin/google-maps-plugin.ts +8 -8
  319. package/src/shared/background-settings/components/BackgroundSettings.vue +1 -1
  320. package/src/shared/background-settings/components/background-color/BackgroundColorSettings.vue +1 -1
  321. package/src/shared/color-picker/components/ColorPicker.mixin.ts +7 -2
  322. package/src/shared/color-picker/components/ColorPickerSwatch.mixin.ts +0 -1
  323. package/src/shared/common/NumberInput.vue +0 -1
  324. package/src/shared/common/SideBarSettingsHeader.vue +2 -2
  325. package/src/shared/confirm-modal/ConfirmModal.mixin.ts +0 -1
  326. package/src/shared/content-settings/components/booking-url-field/BookingUrlField.vue +0 -1
  327. package/src/shared/content-settings/components/button-configuration-field/ButtonConfigurationField.vue +0 -1
  328. package/src/shared/content-settings/components/color-field/ColorField.vue +10 -3
  329. package/src/shared/content-settings/components/content-settings/ContentSettings.mixin.ts +1 -1
  330. package/src/shared/content-settings/components/content-settings/ContentSettingsLockedButton.vue +0 -1
  331. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.ts +0 -5
  332. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +14 -1
  333. package/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.ts +6 -4
  334. package/src/shared/content-settings/components/icon-library/IconLibrary.mixin.ts +0 -1
  335. package/src/shared/content-settings/components/items-field/ItemsField.mixin.ts +1 -1
  336. package/src/shared/content-settings/components/logo-selection-field/LogoSelectionField.vue +1 -1
  337. package/src/shared/content-settings/components/logo-size-field/LogoSizeSliderField.vue +1 -1
  338. package/src/shared/content-settings/components/page-dropdown-field/PageDropdownField.vue +1 -1
  339. package/src/shared/content-settings/components/personalisation-role-field/PersonalisationRoleField.vue +4 -4
  340. package/src/shared/content-settings/components/text-image-field/TextImageField.vue +8 -2
  341. package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.ts +1 -1
  342. package/src/shared/content-settings/models.ts +2 -3
  343. package/src/shared/content-settings/utils.ts +1 -0
  344. package/src/shared/dropzone-image-uploader/components/DropzoneImageUploader.vue +0 -1
  345. package/src/shared/ecommerce-settings/store/cart-store.test.ts +1 -1
  346. package/src/shared/font-family-selector/FontFamilySelector.mixin.ts +0 -1
  347. package/src/shared/image-gallery-style-settings/ImageGalleryStyleSettings.mixin.ts +0 -1
  348. package/src/shared/logo-settings/components/EditLogoSettings.mixin.ts +1 -1
  349. package/src/shared/logo-settings/components/LogoSettings.mixin.ts +1 -0
  350. package/src/shared/logo-settings/components/LogoSettingsHeader.mixin.ts +0 -1
  351. package/src/shared/logo-settings/components/LogoStyleSettingsWithState.vue +2 -2
  352. package/src/shared/logo-settings/components/LogoTemplateSearch.vue +1 -1
  353. package/src/shared/logo-settings/store/editable-logo-store.ts +1 -1
  354. package/src/shared/logo-settings/store/logo-settings-store.test.ts +4 -4
  355. package/src/shared/logo-versions/LogoVersions.mixin.ts +0 -1
  356. package/src/shared/models.ts +0 -6
  357. package/src/shared/my-designs/MyDesigns.mixin.ts +0 -1
  358. package/src/shared/my-uploads/MyMedia.mixin.ts +0 -1
  359. package/src/shared/my-uploads/MyMediaSelectionItem.vue +1 -2
  360. package/src/shared/my-uploads/MyUploads.mixin.ts +0 -1
  361. package/src/shared/my-uploads/models.ts +2 -1
  362. package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.ts +0 -1
  363. package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.vue +1 -1
  364. package/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.ts +3 -0
  365. package/src/shared/page-settings/components/page-settings/NestedDraggable.vue +1 -1
  366. package/src/shared/page-settings/components/page-settings/PageSettings.vue +2 -1
  367. package/src/shared/page-settings/components/page-settings-section-styles/HeaderDisplayStyle.vue +1 -1
  368. package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.ts +1 -1
  369. package/src/shared/page-settings/store/page-settings-store.test.ts +8 -4
  370. package/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.ts +1 -0
  371. package/src/shared/shape-library/ShapeLibrary.vue +2 -2
  372. package/src/shared/style-settings/store/style-settings-store.ts +1 -1
  373. package/src/shared/template-settings/components/TemplatesSettings.mixin.ts +2 -2
  374. package/src/shared/template-settings/models.ts +0 -1
  375. package/src/shared/template-settings/store/template-settings-store.ts +18 -105
  376. package/src/shared/unsplash-library/UnsplashLibrary.vue +6 -2
  377. package/src/shared/video-library/VideoLibrary.vue +1 -1
  378. package/src/utils/build-asset-manifest.ts +0 -12
  379. package/src/utils/minify-brand-page-metadata.ts +27 -7
  380. package/lib/src/brand-page/components/banner-section/BannerSection.mixin.d.ts +0 -103
  381. package/lib/src/brand-page/components/banner-section/BannerSection.mixin.js +0 -178
  382. package/src/brand-page/components/banner-section/BannerSection.mixin.ts +0 -210
  383. package/src/brand-page-maker/components/template-preview/TemplatePreviewAiBadge.vue +0 -12
@@ -25,6 +25,12 @@ import {
25
25
  validateTestimonialSection,
26
26
  TESTIMONIAL_ITEM_CAP,
27
27
  } from '../section-rules/testimonial-section.rules';
28
+ import {
29
+ ensureItemIds,
30
+ generateItemId,
31
+ removeItemsByIds,
32
+ repositionItemByAnchor,
33
+ } from '../item-id-utils';
28
34
 
29
35
  const resolveItemColorsAsync = async (
30
36
  item: TestimonialItem,
@@ -50,6 +56,7 @@ const applyTestimonialItemsAsync = async (
50
56
  ): Promise<void> => {
51
57
  content.items = await Promise.all(
52
58
  items.map(async (item) => ({
59
+ id: item.item_id || generateItemId(),
53
60
  imageUrl: item.avatar_shape_url || item.avatar_image_url || '',
54
61
  name: item.name || '',
55
62
  position: item.position || '',
@@ -60,101 +67,77 @@ const applyTestimonialItemsAsync = async (
60
67
  );
61
68
  };
62
69
 
70
+ /**
71
+ * Apply a batch of item operations by id (BP-5603), mirroring the section-level
72
+ * id-based contract one level deeper:
73
+ * - item with `item_id` matching an existing item → update in place (and
74
+ * reposition if `move_before_item_id` is set);
75
+ * - item without `item_id` → add a new item,
76
+ * positioned by `move_before_item_id`;
77
+ * - `_remove_item_ids` → delete by id.
78
+ *
79
+ * Existing items are id-backfilled first so legacy sections become referenceable
80
+ * the moment the metadata builder touches them. Order-independent: there are no
81
+ * positional indices to shift between operations.
82
+ */
63
83
  const mergeTestimonialItemsAsync = async (
64
84
  content: TestimonialSectionContent,
65
85
  items: TestimonialItem[],
66
- replaceAll: boolean,
86
+ removeIds: string[] | undefined,
67
87
  ): Promise<void> => {
68
- const hasIndexedItems = items.some((item) => item.item_index !== undefined);
69
-
70
- if (hasIndexedItems) {
71
- await Promise.all(
72
- items.map(async (item) => {
73
- const targetIndex = item.item_index;
74
-
75
- if (
76
- targetIndex === undefined ||
77
- targetIndex < 0 ||
78
- !content.items?.[targetIndex]
79
- )
80
- return;
81
-
82
- const existing = content.items[targetIndex];
83
- const prevImageUrl = existing.imageUrl;
84
-
85
- if (item.avatar_shape_url !== undefined) {
86
- existing.imageUrl = item.avatar_shape_url;
87
- existing.mediaType = 'shape';
88
- } else if (item.avatar_image_url !== undefined) {
89
- existing.imageUrl = item.avatar_image_url;
90
- existing.mediaType = 'image';
91
- }
92
-
93
- if (item.name !== undefined) existing.name = item.name;
94
-
95
- if (item.position !== undefined) existing.position = item.position;
96
-
97
- if (item.testimonial !== undefined)
98
- existing.testimonials = item.testimonial;
99
-
100
- if (
101
- item.avatar_shape_url !== undefined ||
102
- item.color_theme !== undefined
103
- ) {
104
- existing.colorReplacements = await resolveItemColorsAsync(
105
- item,
106
- existing.colorReplacements,
107
- prevImageUrl,
108
- );
109
- }
110
- }),
111
- );
88
+ ensureItemIds(content.items);
89
+ content.items = removeItemsByIds(content.items, removeIds);
112
90
 
113
- return;
114
- }
91
+ for (const item of items) {
92
+ if (item.item_id) {
93
+ const existing = content.items.find((i) => i.id === item.item_id);
94
+
95
+ // Unknown id — skip rather than guess at a position.
96
+ if (!existing) continue;
115
97
 
116
- const itemsToMerge = items.filter((item) => item.insert_at === undefined);
98
+ const prevImageUrl = existing.imageUrl;
117
99
 
118
- const updatedItems = await Promise.all(
119
- itemsToMerge.map(async (item, index) => {
120
- const existing = content.items?.[index];
100
+ if (item.avatar_shape_url !== undefined) {
101
+ existing.imageUrl = item.avatar_shape_url;
102
+ existing.mediaType = 'shape';
103
+ } else if (item.avatar_image_url !== undefined) {
104
+ existing.imageUrl = item.avatar_image_url;
105
+ existing.mediaType = 'image';
106
+ }
121
107
 
122
- const imageUrl =
123
- item.avatar_shape_url ??
124
- item.avatar_image_url ??
125
- existing?.imageUrl ??
126
- '';
127
- const mediaType = item.avatar_shape_url
128
- ? 'shape'
129
- : item.avatar_image_url
130
- ? 'image'
131
- : (existing?.mediaType ?? 'image');
108
+ if (item.name !== undefined) existing.name = item.name;
132
109
 
133
- return {
134
- ...(existing || {}),
135
- imageUrl,
136
- name: item.name ?? existing?.name ?? '',
137
- position: item.position ?? existing?.position ?? '',
138
- testimonials: item.testimonial ?? existing?.testimonials ?? '',
139
- colorReplacements: await resolveItemColorsAsync(
110
+ if (item.position !== undefined) existing.position = item.position;
111
+
112
+ if (item.testimonial !== undefined)
113
+ existing.testimonials = item.testimonial;
114
+
115
+ if (
116
+ item.avatar_shape_url !== undefined ||
117
+ item.color_theme !== undefined
118
+ ) {
119
+ existing.colorReplacements = await resolveItemColorsAsync(
140
120
  item,
141
- existing?.colorReplacements,
142
- existing?.imageUrl,
143
- ),
144
- mediaType,
145
- };
146
- }),
147
- );
121
+ existing.colorReplacements,
122
+ prevImageUrl,
123
+ );
124
+ }
148
125
 
149
- if (
150
- !replaceAll &&
151
- content.items &&
152
- content.items.length > itemsToMerge.length
153
- ) {
154
- updatedItems.push(...content.items.slice(itemsToMerge.length));
126
+ repositionItemByAnchor(content.items, existing, item.move_before_item_id);
127
+ } else {
128
+ const newItem = {
129
+ id: generateItemId(),
130
+ imageUrl: item.avatar_shape_url ?? item.avatar_image_url ?? '',
131
+ name: item.name ?? '',
132
+ position: item.position ?? '',
133
+ testimonials: item.testimonial ?? '',
134
+ colorReplacements: await resolveItemColorsAsync(item, [], undefined),
135
+ mediaType: item.avatar_shape_url ? 'shape' : 'image',
136
+ } as TestimonialSectionContent['items'][number];
137
+
138
+ repositionItemByAnchor(content.items, newItem, item.move_before_item_id);
139
+ }
155
140
  }
156
-
157
- content.items = updatedItems;
158
141
  };
159
142
 
160
143
  export class TestimonialSectionBuilder extends BaseSectionBuilder<TestimonialSectionContent> {
@@ -228,75 +211,29 @@ export class TestimonialSectionBuilder extends BaseSectionBuilder<TestimonialSec
228
211
  };
229
212
  }
230
213
 
231
- if (args.items && Array.isArray(args.items)) {
232
- if (this.mode === 'create') {
214
+ if (args._replace_items && args.items && Array.isArray(args.items)) {
215
+ // Full replacement: discard existing items and rebuild from the payload.
216
+ await applyTestimonialItemsAsync(content, args.items);
217
+ } else if (this.mode === 'create') {
218
+ if (args.items && Array.isArray(args.items)) {
233
219
  await applyTestimonialItemsAsync(content, args.items);
234
- } else {
235
- await mergeTestimonialItemsAsync(
236
- content,
237
- args.items,
238
- !!args._replace_items,
239
- );
240
- }
241
- }
242
-
243
- // Phase 2: Item operations (remove, insert, reorder)
244
- if (args._remove_item_indices?.length && content.items) {
245
- const sorted = [...args._remove_item_indices].sort((a, b) => b - a);
246
-
247
- for (const idx of sorted) {
248
- if (idx >= 0 && idx < content.items.length) {
249
- content.items.splice(idx, 1);
250
- }
251
220
  }
252
- }
253
-
254
- if (args.items && Array.isArray(args.items)) {
255
- const insertItems = args.items.filter(
256
- (item) => item.insert_at !== undefined && item.item_index === undefined,
257
- );
258
- const sortedInserts = [...insertItems].sort(
259
- (a, b) => (b.insert_at ?? 0) - (a.insert_at ?? 0),
221
+ } else if (
222
+ (args.items && Array.isArray(args.items)) ||
223
+ args._remove_item_ids?.length
224
+ ) {
225
+ // Update: id-based add / edit / move / remove in a single pass.
226
+ await mergeTestimonialItemsAsync(
227
+ content,
228
+ args.items ?? [],
229
+ args._remove_item_ids,
260
230
  );
261
-
262
- for (const item of sortedInserts) {
263
- const pos = Math.max(
264
- 0,
265
- Math.min(item.insert_at ?? 0, content.items?.length ?? 0),
266
- );
267
- const newItem = {
268
- imageUrl: item.avatar_shape_url ?? item.avatar_image_url ?? '',
269
- name: item.name ?? '',
270
- position: item.position ?? '',
271
- testimonials: item.testimonial ?? '',
272
- colorReplacements: await resolveItemColorsAsync(item, [], undefined),
273
- mediaType: item.avatar_shape_url ? 'shape' : 'image',
274
- };
275
-
276
- content.items = content.items ?? [];
277
- content.items.splice(pos, 0, newItem as any);
278
- }
279
- }
280
-
281
- if (args._reorder_items?.length && content.items) {
282
- const newOrder = args._reorder_items;
283
- const unique = new Set(newOrder);
284
-
285
- if (unique.size === newOrder.length) {
286
- const reordered = newOrder
287
- .filter((idx) => idx >= 0 && idx < content.items!.length)
288
- .map((idx) => content.items![idx]);
289
-
290
- if (reordered.length === content.items.length) {
291
- content.items = reordered;
292
- }
293
- }
294
231
  }
295
232
 
296
233
  // Post-merge cap check. itemCapCheck only fires for create / update with
297
- // _replace_items, so insert_at on an already-full section silently bloats
298
- // the array. Surface as an error so the AI retry loop gets a signal and
299
- // can reissue with _remove_item_indices or fewer inserts.
234
+ // _replace_items, so adding new items to an already-full section silently
235
+ // bloats the array. Surface as an error so the AI retry loop gets a signal
236
+ // and can reissue with _remove_item_ids or fewer additions.
300
237
  // brandSite key mirrors base-section-builder's hardcoded brandPageType.
301
238
  const cap = TESTIMONIAL_ITEM_CAP.brandSite;
302
239
 
@@ -19,6 +19,12 @@ import {
19
19
  } from '../section-field-utils';
20
20
  import { BaseSectionBuilder } from './base-section-builder';
21
21
  import { validateTextSection } from '../section-rules/text-section.rules';
22
+ import {
23
+ ensureItemIds,
24
+ generateItemId,
25
+ removeItemsByIds,
26
+ repositionItemByAnchor,
27
+ } from '../item-id-utils';
22
28
 
23
29
  /** Resolve a RichTextField (string or TiptapDocument) to a plain HTML string. */
24
30
  const resolveText = (value: unknown): { html: string; wasTiptap: boolean } => {
@@ -52,6 +58,7 @@ const createTextItem = (
52
58
  const { html, wasTiptap } = resolveText(item.text);
53
59
 
54
60
  return {
61
+ id: item.item_id || generateItemId(),
55
62
  text: html,
56
63
  textStyle: clearTextStyleTypeIfTiptap(
57
64
  textStyle,
@@ -61,6 +68,10 @@ const createTextItem = (
61
68
  };
62
69
  };
63
70
 
71
+ /**
72
+ * Full replacement: discard existing items and rebuild from the payload,
73
+ * assigning a stable id to each new item.
74
+ */
64
75
  const applyTextItems = (
65
76
  content: TextContent,
66
77
  items: TextSectionItem[],
@@ -71,26 +82,33 @@ const applyTextItems = (
71
82
  );
72
83
  };
73
84
 
85
+ /**
86
+ * Id-based merge (BP-5603): mirrors the section-level id contract one level
87
+ * deeper.
88
+ * - item with `item_id` matching an existing item → update in place and
89
+ * reposition if `move_before_item_id` is set; unknown id is skipped.
90
+ * - item without `item_id` → add a new item,
91
+ * positioned by `move_before_item_id`.
92
+ * - `removeIds` → delete by id.
93
+ *
94
+ * Existing items are id-backfilled first so legacy sections without ids
95
+ * become referenceable the moment this builder processes them.
96
+ */
74
97
  const mergeTextItems = (
75
98
  content: TextContent,
76
99
  items: TextSectionItem[],
77
- replaceAll: boolean,
100
+ removeIds: string[] | undefined,
78
101
  textStyleOptions: TextStyleOptions | undefined,
79
102
  ): void => {
80
- const hasIndexedItems = items.some((item) => item.item_index !== undefined);
81
-
82
- if (hasIndexedItems) {
83
- for (const item of items) {
84
- const targetIndex = item.item_index;
103
+ ensureItemIds(content.textItems);
104
+ content.textItems = removeItemsByIds(content.textItems, removeIds);
85
105
 
86
- if (
87
- targetIndex === undefined ||
88
- targetIndex < 0 ||
89
- !content.textItems?.[targetIndex]
90
- )
91
- continue;
106
+ for (const item of items) {
107
+ if (item.item_id) {
108
+ const existing = content.textItems?.find((i) => i.id === item.item_id);
92
109
 
93
- const existing = content.textItems[targetIndex];
110
+ // Unknown id — skip rather than guess at a position.
111
+ if (!existing) continue;
94
112
 
95
113
  if (item.text !== undefined) {
96
114
  const { html, wasTiptap } = resolveText(item.text);
@@ -104,51 +122,32 @@ const mergeTextItems = (
104
122
  }
105
123
 
106
124
  if (item.text_style && typeof item.text_style === 'object') {
107
- const mergedStyle = (
108
- item.text_style
109
- ? { ...(existing.textStyle || {}), ...item.text_style }
110
- : existing.textStyle || {}
111
- ) as TextStyleDeclaration;
112
-
113
- existing.textStyle = mergedStyle;
125
+ existing.textStyle = {
126
+ ...(existing.textStyle || {}),
127
+ ...item.text_style,
128
+ } as TextStyleDeclaration;
114
129
  }
115
- }
116
-
117
- return;
118
- }
119
-
120
- const itemsToMerge = items.filter((item) => item.insert_at === undefined);
121
-
122
- const updatedItems = itemsToMerge.map((item, index) => {
123
- const existing = content.textItems?.[index];
124
- const baseStyle = existing?.textStyle || content.textStyle || {};
125
-
126
- const mergedStyle = (
127
- item.text_style ? { ...baseStyle, ...item.text_style } : baseStyle
128
- ) as TextStyleDeclaration;
129
130
 
130
- const resolved = item.text !== undefined ? resolveText(item.text) : null;
131
- const text = resolved ? resolved.html : (existing?.text ?? '');
132
-
133
- return {
134
- text,
135
- textStyle: clearTextStyleTypeIfTiptap(
136
- mergedStyle,
137
- resolved?.wasTiptap ?? false,
131
+ repositionItemByAnchor(
132
+ content.textItems!,
133
+ existing,
134
+ item.move_before_item_id,
135
+ );
136
+ } else {
137
+ const newItem = createTextItem(
138
+ item,
139
+ content.textStyle || {},
138
140
  textStyleOptions,
139
- ),
140
- };
141
- });
141
+ );
142
142
 
143
- if (
144
- !replaceAll &&
145
- content.textItems &&
146
- content.textItems.length > itemsToMerge.length
147
- ) {
148
- updatedItems.push(...content.textItems.slice(itemsToMerge.length));
143
+ content.textItems = content.textItems ?? [];
144
+ repositionItemByAnchor(
145
+ content.textItems,
146
+ newItem,
147
+ item.move_before_item_id,
148
+ );
149
+ }
149
150
  }
150
-
151
- content.textItems = updatedItems;
152
151
  };
153
152
 
154
153
  /**
@@ -236,82 +235,31 @@ export class TextSectionBuilder extends BaseSectionBuilder<TextContent> {
236
235
 
237
236
  const args = this.args as TextSectionArguments;
238
237
 
239
- if (args.items && Array.isArray(args.items)) {
240
- if (this.mode === 'create') {
238
+ if (args._replace_items && args.items && Array.isArray(args.items)) {
239
+ // Full replacement: discard existing items and rebuild from the payload.
240
+ applyTextItems(content, args.items, textStyleOptions);
241
+ } else if (this.mode === 'create') {
242
+ if (args.items && Array.isArray(args.items)) {
241
243
  applyTextItems(content, args.items, textStyleOptions);
242
244
  } else {
243
- mergeTextItems(
244
- content,
245
- args.items,
246
- !!args._replace_items,
247
- textStyleOptions,
248
- );
245
+ applySingleTextField(content, args, textStyleOptions);
249
246
  }
247
+ } else if (
248
+ (args.items && Array.isArray(args.items)) ||
249
+ args._remove_item_ids?.length
250
+ ) {
251
+ // Update: id-based add / edit / move / remove in a single pass.
252
+ mergeTextItems(
253
+ content,
254
+ args.items ?? [],
255
+ args._remove_item_ids,
256
+ textStyleOptions,
257
+ );
250
258
  } else {
259
+ // No items array: apply the single text/text_style shorthand fields.
251
260
  applySingleTextField(content, args, textStyleOptions);
252
261
  }
253
262
 
254
- // Phase 2: Item operations (remove, insert, reorder)
255
- if (args._remove_item_indices?.length && content.textItems) {
256
- const sorted = [...args._remove_item_indices].sort((a, b) => b - a);
257
-
258
- for (const idx of sorted) {
259
- if (idx >= 0 && idx < content.textItems.length) {
260
- content.textItems.splice(idx, 1);
261
- }
262
- }
263
- }
264
-
265
- if (args.items && Array.isArray(args.items)) {
266
- const insertItems = args.items.filter(
267
- (item) => item.insert_at !== undefined && item.item_index === undefined,
268
- );
269
- const sortedInserts = [...insertItems].sort(
270
- (a, b) => (b.insert_at ?? 0) - (a.insert_at ?? 0),
271
- );
272
-
273
- for (const item of sortedInserts) {
274
- const pos = Math.max(
275
- 0,
276
- Math.min(item.insert_at ?? 0, content.textItems?.length ?? 0),
277
- );
278
- const baseStyle = content.textStyle || {};
279
- const textStyle = (
280
- item.text_style
281
- ? { ...baseStyle, ...item.text_style }
282
- : { ...baseStyle }
283
- ) as TextStyleDeclaration;
284
-
285
- const { html, wasTiptap } = resolveText(item.text);
286
- const newItem: TextItem = {
287
- text: html,
288
- textStyle: clearTextStyleTypeIfTiptap(
289
- textStyle,
290
- wasTiptap,
291
- textStyleOptions,
292
- ),
293
- };
294
-
295
- content.textItems = content.textItems ?? [];
296
- content.textItems.splice(pos, 0, newItem);
297
- }
298
- }
299
-
300
- if (args._reorder_items?.length && content.textItems) {
301
- const newOrder = args._reorder_items;
302
- const unique = new Set(newOrder);
303
-
304
- if (unique.size === newOrder.length) {
305
- const reordered = newOrder
306
- .filter((idx) => idx >= 0 && idx < content.textItems!.length)
307
- .map((idx) => content.textItems![idx]);
308
-
309
- if (reordered.length === content.textItems.length) {
310
- content.textItems = reordered;
311
- }
312
- }
313
- }
314
-
315
263
  if (args.background_color !== undefined) {
316
264
  content.backgroundColor = {
317
265
  ...content.backgroundColor,
@@ -109,7 +109,7 @@ export const applyScalarFields = (
109
109
  * - null → '' (explicitly clear/reset the property)
110
110
  * - everything else passes through as-is
111
111
  */
112
- export const normalizeStyleValue = (key: string, value: unknown): unknown => {
112
+ const normalizeStyleValue = (key: string, value: unknown): unknown => {
113
113
  if (value === null) return '';
114
114
 
115
115
  if (value === undefined) return undefined;
@@ -68,8 +68,8 @@ export const applyColorThemeToReplacements = (
68
68
  replacementColor:
69
69
  replacements.length === 1
70
70
  ? colorTheme
71
- : (getNewColorBasedOnTargetColorTheme(
71
+ : getNewColorBasedOnTargetColorTheme(
72
72
  r.originalColor,
73
73
  colorTheme,
74
- )?.toString() ?? colorTheme),
74
+ )?.toString() ?? colorTheme,
75
75
  }));
@@ -4,7 +4,7 @@ import type {
4
4
  HeaderDisplayStyle,
5
5
  NavigationDisplayType,
6
6
  } from '../../brand-page/constants';
7
- import type { SectionType, TextStyleArguments } from './section-tool-arguments';
7
+ import type { SectionType } from './section-tool-arguments';
8
8
 
9
9
  // Tool names — must match the function names generated by the AI pipeline.
10
10
  // Text element tools use camelCase names.
@@ -202,8 +202,17 @@ export interface ReorderPagesArguments {
202
202
  export interface UndoArguments {}
203
203
 
204
204
  export interface FooterSocialLinkItem {
205
- item_index?: number;
206
- insert_at?: number;
205
+ /**
206
+ * Stable id of the existing link to update or move. Omit to ADD a new link.
207
+ * Mirrors the section-level item_id convention (BP-5603).
208
+ */
209
+ item_id?: string;
210
+ /**
211
+ * Id of an existing link to position this link before (insert for new links,
212
+ * move for existing ones). Use "__end__" for the end of the list. Omit to
213
+ * leave an existing link in place / append a new link.
214
+ */
215
+ move_before_item_id?: string;
207
216
  social_link_icon?: string;
208
217
  url?: string;
209
218
  }
@@ -215,6 +224,7 @@ export interface FooterSocialLinkItem {
215
224
  export interface ViewportFooterOverrides {
216
225
  display_style?: FooterDisplayStyle;
217
226
  background_color?: string | null;
227
+ copyright_text_color?: string;
218
228
  icon_color?: string;
219
229
  is_logo_visible?: boolean;
220
230
  logo_image_height?: string;
@@ -223,12 +233,11 @@ export interface ViewportFooterOverrides {
223
233
 
224
234
  export interface UpdateFooterArguments {
225
235
  copyright_text?: string;
226
- /** Copyright line text-style object (typography + colour) — same shape as a section's *_style. Global: applies to both viewports. */
227
- copyright_text_style?: TextStyleArguments;
236
+ copyright_text_font_family?: string;
228
237
  social_links?: FooterSocialLinkItem[];
229
238
  _replace_social_links?: boolean;
230
- _remove_social_link_indices?: number[];
231
- _reorder_social_links?: number[];
239
+ /** Stable ids of social links to remove. Order-independent (replaces index-based _remove_social_link_indices). */
240
+ _remove_social_link_ids?: string[];
232
241
  /** Desktop-only styling (display_style, background_color, icon_color, is_logo_visible, logo sizes). */
233
242
  desktop?: ViewportFooterOverrides;
234
243
  /** Mobile-only styling (display_style, background_color, icon_color, is_logo_visible, logo sizes). */