@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
@@ -3,8 +3,15 @@ import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
3
3
  import { applyScalarFields, applyStyleFields, isValidUrl, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
4
4
  import { BaseSectionBuilder } from './base-section-builder';
5
5
  import { validateImagesSection } from '../section-rules/images-section.rules';
6
+ import { ensureItemIds, generateItemId, removeItemsByIds, repositionItemByAnchor, } from '../item-id-utils';
7
+ /**
8
+ * Full replacement: discard existing items and rebuild from the payload.
9
+ * Assigns a stable `id` to each item; `itemsId` is set positionally here
10
+ * but will be re-indexed at the end of buildAsync.
11
+ */
6
12
  const applyImageItems = (content, items) => {
7
13
  content.items = items.map((item, index) => ({
14
+ id: item.item_id || generateItemId(),
8
15
  imageUrl: isValidUrl(item.image_url) ? item.image_url : '',
9
16
  imageTitle: '',
10
17
  width: 0,
@@ -18,16 +25,28 @@ const applyImageItems = (content, items) => {
18
25
  colorReplacements: [],
19
26
  }));
20
27
  };
21
- const mergeImageItems = (content, items, replaceAll) => {
22
- const hasIndexedItems = items.some((item) => item.item_index !== undefined);
23
- if (hasIndexedItems) {
24
- for (const item of items) {
25
- const targetIndex = item.item_index;
26
- if (targetIndex === undefined ||
27
- targetIndex < 0 ||
28
- !content.items?.[targetIndex])
28
+ /**
29
+ * Apply a batch of item operations by id (BP-5603), mirroring the
30
+ * section-level id-based contract one level deeper:
31
+ * - item with `item_id` matching an existing item → update in place (and
32
+ * reposition if `move_before_item_id` is set);
33
+ * - item without `item_id` → add a new item,
34
+ * positioned by `move_before_item_id`;
35
+ * - `_remove_item_ids` → delete by id.
36
+ *
37
+ * Existing items are id-backfilled first so legacy sections become
38
+ * referenceable the moment the metadata builder touches them.
39
+ * Order-independent: there are no positional indices to shift between ops.
40
+ */
41
+ const mergeImageItems = (content, items, removeIds) => {
42
+ ensureItemIds(content.items);
43
+ content.items = removeItemsByIds(content.items, removeIds);
44
+ for (const item of items) {
45
+ if (item.item_id) {
46
+ const existing = content.items.find((i) => i.id === item.item_id);
47
+ // Unknown id — skip rather than guess at a position.
48
+ if (!existing)
29
49
  continue;
30
- const existing = content.items[targetIndex];
31
50
  if (isValidUrl(item.image_url))
32
51
  existing.imageUrl = item.image_url;
33
52
  if (item.heading !== undefined)
@@ -36,36 +55,26 @@ const mergeImageItems = (content, items, replaceAll) => {
36
55
  existing.paragraph = item.text;
37
56
  if (item.button_text !== undefined)
38
57
  existing.buttonText = item.button_text || false;
58
+ repositionItemByAnchor(content.items, existing, item.move_before_item_id);
59
+ }
60
+ else {
61
+ const newItem = {
62
+ id: generateItemId(),
63
+ imageUrl: isValidUrl(item.image_url) ? item.image_url : '',
64
+ imageTitle: '',
65
+ width: 0,
66
+ height: 0,
67
+ mediaType: 'image',
68
+ imageLink: '',
69
+ title: item.heading ?? '',
70
+ paragraph: item.text ?? '',
71
+ buttonText: item.button_text || false,
72
+ itemsId: 0, // re-indexed at the end of buildAsync
73
+ colorReplacements: [],
74
+ };
75
+ repositionItemByAnchor(content.items, newItem, item.move_before_item_id);
39
76
  }
40
- return;
41
- }
42
- const itemsToMerge = items.filter((item) => item.insert_at === undefined);
43
- const updatedItems = itemsToMerge.map((item, index) => {
44
- const existing = content.items?.[index];
45
- return {
46
- ...(existing || {}),
47
- imageUrl: isValidUrl(item.image_url)
48
- ? item.image_url
49
- : (existing?.imageUrl ?? ''),
50
- imageTitle: existing?.imageTitle ?? '',
51
- width: existing?.width ?? 0,
52
- height: existing?.height ?? 0,
53
- mediaType: existing?.mediaType ?? 'image',
54
- imageLink: existing?.imageLink ?? '',
55
- title: item.heading ?? existing?.title ?? '',
56
- paragraph: item.text ?? existing?.paragraph ?? '',
57
- buttonText: item.button_text ?? existing?.buttonText ?? '',
58
- itemsId: existing?.itemsId ?? index,
59
- colorReplacements: existing?.colorReplacements ?? [],
60
- };
61
- });
62
- if (!replaceAll &&
63
- content.items &&
64
- content.items.length > itemsToMerge.length) {
65
- const remaining = content.items.slice(itemsToMerge.length);
66
- updatedItems.push(...remaining);
67
77
  }
68
- content.items = updatedItems;
69
78
  };
70
79
  export class ImagesSectionBuilder extends BaseSectionBuilder {
71
80
  get sectionValidate() {
@@ -123,66 +132,22 @@ export class ImagesSectionBuilder extends BaseSectionBuilder {
123
132
  ])),
124
133
  };
125
134
  }
126
- if (args.items && Array.isArray(args.items)) {
127
- if (this.mode === 'create') {
128
- applyImageItems(content, args.items);
129
- }
130
- else {
131
- mergeImageItems(content, args.items, !!args._replace_items);
132
- }
133
- }
134
- // Phase 2: Item operations (remove, insert, reorder)
135
- if (args._remove_item_indices?.length && content.items) {
136
- const sorted = [...args._remove_item_indices].sort((a, b) => b - a);
137
- for (const idx of sorted) {
138
- if (idx >= 0 && idx < content.items.length) {
139
- content.items.splice(idx, 1);
140
- }
141
- }
135
+ if (args._replace_items && args.items && Array.isArray(args.items)) {
136
+ // Full replacement: discard existing items and rebuild from the payload.
137
+ applyImageItems(content, args.items);
142
138
  }
143
- if (args.items && Array.isArray(args.items)) {
144
- const insertItems = args.items.filter((item) => item.insert_at !== undefined && item.item_index === undefined);
145
- // Process inserts in reverse order of insert_at to preserve indices
146
- const sortedInserts = [...insertItems].sort((a, b) => (b.insert_at ?? 0) - (a.insert_at ?? 0));
147
- for (const item of sortedInserts) {
148
- if (!isValidUrl(item.image_url))
149
- continue;
150
- // Skip if this image_url already exists — defensive against duplicate
151
- // tool call application
152
- if (content.items?.some((existing) => existing.imageUrl === item.image_url)) {
153
- continue;
154
- }
155
- const pos = Math.max(0, Math.min(item.insert_at ?? 0, content.items?.length ?? 0));
156
- const newItem = {
157
- imageUrl: item.image_url,
158
- imageTitle: '',
159
- width: 0,
160
- height: 0,
161
- mediaType: 'image',
162
- imageLink: '',
163
- title: item.heading ?? '',
164
- paragraph: item.text ?? '',
165
- buttonText: item.button_text || '',
166
- itemsId: pos,
167
- colorReplacements: [],
168
- };
169
- content.items = content.items ?? [];
170
- content.items.splice(pos, 0, newItem);
139
+ else if (this.mode === 'create') {
140
+ if (args.items && Array.isArray(args.items)) {
141
+ applyImageItems(content, args.items);
171
142
  }
172
143
  }
173
- if (args._reorder_items?.length && content.items) {
174
- const newOrder = args._reorder_items;
175
- const unique = new Set(newOrder);
176
- if (unique.size === newOrder.length) {
177
- const reordered = newOrder
178
- .filter((idx) => idx >= 0 && idx < content.items.length)
179
- .map((idx) => content.items[idx]);
180
- if (reordered.length === content.items.length) {
181
- content.items = reordered;
182
- }
183
- }
144
+ else if ((args.items && Array.isArray(args.items)) ||
145
+ args._remove_item_ids?.length) {
146
+ // Update: id-based add / edit / move / remove in a single pass.
147
+ mergeImageItems(content, args.items ?? [], args._remove_item_ids);
184
148
  }
185
- // Re-index itemsId after any item operations
149
+ // Re-index itemsId after all id-based item operations (render concern —
150
+ // separate from the stable `id` used for targeting).
186
151
  if (content.items) {
187
152
  content.items.forEach((item, i) => {
188
153
  item.itemsId = i;
@@ -4,6 +4,7 @@ import { applyScalarFields, applyStyleFields, resolveGlobalPresetIntoStyle, } fr
4
4
  import { applyColorThemeToReplacements, resolveShapeColorReplacementsAsync, } from '../shape-color-utils';
5
5
  import { BaseSectionBuilder } from './base-section-builder';
6
6
  import { validateShapesSection } from '../section-rules/shapes-section.rules';
7
+ import { ensureItemIds, generateItemId, removeItemsByIds, repositionItemByAnchor, } from '../item-id-utils';
7
8
  const resolveItemColorsAsync = async (item, existing, prevImageUrl) => {
8
9
  let colors = existing ?? [];
9
10
  const newUrl = item.shape_url ?? item.icon_image_url;
@@ -15,8 +16,13 @@ const resolveItemColorsAsync = async (item, existing, prevImageUrl) => {
15
16
  }
16
17
  return colors;
17
18
  };
19
+ /**
20
+ * Full replacement: discard existing items and rebuild from the payload.
21
+ * Used for create mode and _replace_items: true.
22
+ */
18
23
  const applyShapeItemsAsync = async (content, items) => {
19
- content.items = await Promise.all(items.map(async (item, index) => ({
24
+ content.items = await Promise.all(items.map(async (item) => ({
25
+ id: item.item_id || generateItemId(),
20
26
  imageUrl: item.shape_url || item.icon_image_url || '',
21
27
  imageTitle: '',
22
28
  width: 0,
@@ -25,20 +31,36 @@ const applyShapeItemsAsync = async (content, items) => {
25
31
  imageLink: '',
26
32
  title: item.heading || '',
27
33
  paragraph: item.text || '',
28
- itemsId: index,
34
+ itemsId: 0,
29
35
  colorReplacements: await resolveItemColorsAsync(item, [], undefined),
30
36
  })));
37
+ // Re-index itemsId after build
38
+ content.items.forEach((item, i) => {
39
+ item.itemsId = i;
40
+ });
31
41
  };
32
- const mergeShapeItemsAsync = async (content, items, replaceAll) => {
33
- const hasIndexedItems = items.some((item) => item.item_index !== undefined);
34
- if (hasIndexedItems) {
35
- await Promise.all(items.map(async (item) => {
36
- const targetIndex = item.item_index;
37
- if (targetIndex === undefined ||
38
- targetIndex < 0 ||
39
- !content.items?.[targetIndex])
40
- return;
41
- const existing = content.items[targetIndex];
42
+ /**
43
+ * Apply a batch of item operations by id (BP-5603), mirroring the section-level
44
+ * id-based contract one level deeper:
45
+ * - item with `item_id` matching an existing item → update in place (and
46
+ * reposition if `move_before_item_id` is set);
47
+ * - item without `item_id` → add a new item,
48
+ * positioned by `move_before_item_id`;
49
+ * - `_remove_item_ids` → delete by id.
50
+ *
51
+ * Existing items are id-backfilled first so legacy sections become referenceable
52
+ * the moment the metadata builder touches them. Order-independent: there are no
53
+ * positional indices to shift between operations.
54
+ */
55
+ const mergeShapeItemsAsync = async (content, items, removeIds) => {
56
+ ensureItemIds(content.items);
57
+ content.items = removeItemsByIds(content.items, removeIds);
58
+ for (const item of items) {
59
+ if (item.item_id) {
60
+ const existing = content.items.find((i) => i.id === item.item_id);
61
+ // Unknown id — skip rather than guess at a position.
62
+ if (!existing)
63
+ continue;
42
64
  const prevImageUrl = existing.imageUrl;
43
65
  if (item.shape_url !== undefined || item.icon_image_url !== undefined) {
44
66
  existing.imageUrl =
@@ -51,34 +73,29 @@ const mergeShapeItemsAsync = async (content, items, replaceAll) => {
51
73
  if (item.shape_url !== undefined || item.color_theme !== undefined) {
52
74
  existing.colorReplacements = await resolveItemColorsAsync(item, existing.colorReplacements, prevImageUrl);
53
75
  }
54
- }));
55
- return;
56
- }
57
- const itemsToMerge = items.filter((item) => item.insert_at === undefined);
58
- const updatedItems = await Promise.all(itemsToMerge.map(async (item, index) => {
59
- const existing = content.items?.[index];
60
- const imageUrl = item.shape_url ?? item.icon_image_url ?? existing?.imageUrl ?? '';
61
- return {
62
- ...(existing || {}),
63
- imageUrl,
64
- imageTitle: existing?.imageTitle ?? '',
65
- width: existing?.width ?? 0,
66
- height: existing?.height ?? 0,
67
- mediaType: existing?.mediaType ?? 'shape',
68
- imageLink: existing?.imageLink ?? '',
69
- title: item.heading ?? existing?.title ?? '',
70
- paragraph: item.text ?? existing?.paragraph ?? '',
71
- itemsId: existing?.itemsId ?? index,
72
- colorReplacements: await resolveItemColorsAsync(item, existing?.colorReplacements, existing?.imageUrl),
73
- };
74
- }));
75
- if (!replaceAll &&
76
- content.items &&
77
- content.items.length > itemsToMerge.length) {
78
- const remaining = content.items.slice(itemsToMerge.length);
79
- updatedItems.push(...remaining);
76
+ repositionItemByAnchor(content.items, existing, item.move_before_item_id);
77
+ }
78
+ else {
79
+ const newItem = {
80
+ id: generateItemId(),
81
+ imageUrl: item.shape_url ?? item.icon_image_url ?? '',
82
+ imageTitle: '',
83
+ width: 0,
84
+ height: 0,
85
+ mediaType: 'shape',
86
+ imageLink: '',
87
+ title: item.heading ?? '',
88
+ paragraph: item.text ?? '',
89
+ itemsId: 0,
90
+ colorReplacements: await resolveItemColorsAsync(item, [], undefined),
91
+ };
92
+ repositionItemByAnchor(content.items, newItem, item.move_before_item_id);
93
+ }
80
94
  }
81
- content.items = updatedItems;
95
+ // Re-index itemsId after all mutations
96
+ content.items.forEach((item, i) => {
97
+ item.itemsId = i;
98
+ });
82
99
  };
83
100
  export class ShapesSectionBuilder extends BaseSectionBuilder {
84
101
  get sectionValidate() {
@@ -136,69 +153,19 @@ export class ShapesSectionBuilder extends BaseSectionBuilder {
136
153
  ])),
137
154
  };
138
155
  }
139
- if (args.items && Array.isArray(args.items)) {
140
- if (this.mode === 'create') {
141
- await applyShapeItemsAsync(content, args.items);
142
- }
143
- else {
144
- await mergeShapeItemsAsync(content, args.items, !!args._replace_items);
145
- }
146
- }
147
- // Phase 2: Item operations (remove, insert, reorder)
148
- if (args._remove_item_indices?.length && content.items) {
149
- const sorted = [...args._remove_item_indices].sort((a, b) => b - a);
150
- for (const idx of sorted) {
151
- if (idx >= 0 && idx < content.items.length) {
152
- content.items.splice(idx, 1);
153
- }
154
- }
156
+ if (args._replace_items && args.items && Array.isArray(args.items)) {
157
+ // Full replacement: discard existing items and rebuild from the payload.
158
+ await applyShapeItemsAsync(content, args.items);
155
159
  }
156
- if (args.items && Array.isArray(args.items)) {
157
- const insertItems = args.items.filter((item) => item.insert_at !== undefined && item.item_index === undefined);
158
- // Process inserts in reverse order of insert_at to preserve indices
159
- const sortedInserts = [...insertItems].sort((a, b) => (b.insert_at ?? 0) - (a.insert_at ?? 0));
160
- for (const item of sortedInserts) {
161
- const insertUrl = item.shape_url ?? item.icon_image_url ?? '';
162
- // Skip if this shape/icon already exists — defensive against duplicate
163
- // tool call application
164
- if (insertUrl &&
165
- content.items?.some((existing) => existing.imageUrl === insertUrl)) {
166
- continue;
167
- }
168
- const pos = Math.max(0, Math.min(item.insert_at ?? 0, content.items?.length ?? 0));
169
- const newItem = {
170
- imageUrl: insertUrl,
171
- imageTitle: '',
172
- width: 0,
173
- height: 0,
174
- mediaType: 'shape',
175
- imageLink: '',
176
- title: item.heading ?? '',
177
- paragraph: item.text ?? '',
178
- itemsId: pos,
179
- colorReplacements: await resolveItemColorsAsync(item, [], undefined),
180
- };
181
- content.items = content.items ?? [];
182
- content.items.splice(pos, 0, newItem);
183
- }
184
- }
185
- if (args._reorder_items?.length && content.items) {
186
- const newOrder = args._reorder_items;
187
- const unique = new Set(newOrder);
188
- if (unique.size === newOrder.length) {
189
- const reordered = newOrder
190
- .filter((idx) => idx >= 0 && idx < content.items.length)
191
- .map((idx) => content.items[idx]);
192
- if (reordered.length === content.items.length) {
193
- content.items = reordered;
194
- }
160
+ else if (this.mode === 'create') {
161
+ if (args.items && Array.isArray(args.items)) {
162
+ await applyShapeItemsAsync(content, args.items);
195
163
  }
196
164
  }
197
- // Re-index itemsId after any item operations
198
- if (content.items) {
199
- content.items.forEach((item, i) => {
200
- item.itemsId = i;
201
- });
165
+ else if ((args.items && Array.isArray(args.items)) ||
166
+ args._remove_item_ids?.length) {
167
+ // Update: id-based add / edit / move / remove in a single pass.
168
+ await mergeShapeItemsAsync(content, args.items ?? [], args._remove_item_ids);
202
169
  }
203
170
  return { request: this.request, model };
204
171
  }
@@ -1,8 +1,15 @@
1
1
  import { applyScalarFields, applyStyleFields } from '../section-field-utils';
2
2
  import { BaseSectionBuilder } from './base-section-builder';
3
3
  import { validateSocialLinks } from '../section-rules/social-links.rules';
4
+ import { ensureItemIds, generateItemId, removeItemsByIds, repositionItemByAnchor, } from '../item-id-utils';
5
+ /**
6
+ * Create path: build the links array from scratch, assigning stable ids.
7
+ * `link.platform` is stored as `imageUrl` — the renderer resolves it to the
8
+ * actual SVG at display time.
9
+ */
4
10
  const applySocialLinks = (content, links) => {
5
11
  content.links = links.map((link) => ({
12
+ id: link.item_id || generateItemId(),
6
13
  imageUrl: link.platform || '',
7
14
  imageTitle: '',
8
15
  width: 0,
@@ -11,19 +18,46 @@ const applySocialLinks = (content, links) => {
11
18
  url: link.url || '',
12
19
  }));
13
20
  };
14
- const mergeSocialLinks = (content, links, replaceAll) => {
15
- const updatedLinks = links.map((link, index) => {
16
- const existing = content.links?.[index];
17
- return {
18
- ...(existing || {}),
19
- imageUrl: link.platform ?? existing?.imageUrl ?? '',
20
- url: link.url ?? existing?.url ?? '',
21
- };
22
- });
23
- if (!replaceAll && content.links && content.links.length > links.length) {
24
- updatedLinks.push(...content.links.slice(links.length));
21
+ /**
22
+ * Merge path: id-based add / edit / move / remove in a single pass (BP-5603).
23
+ *
24
+ * - Existing items are id-backfilled first so legacy sections gain stable ids
25
+ * the moment the metadata builder touches them.
26
+ * - `_remove_item_ids` deletes by id before the edit/add loop.
27
+ * - An item with `item_id` matching an existing link is updated in place and
28
+ * optionally repositioned via `move_before_item_id`.
29
+ * - An item without `item_id` is added as a new link, positioned by
30
+ * `move_before_item_id` (or appended when omitted).
31
+ * - An item with an unknown `item_id` is skipped rather than guessing position.
32
+ */
33
+ const mergeSocialLinks = (content, links, removeIds) => {
34
+ ensureItemIds(content.links);
35
+ content.links = removeItemsByIds(content.links, removeIds);
36
+ for (const link of links) {
37
+ if (link.item_id) {
38
+ const existing = content.links.find((l) => l.id === link.item_id);
39
+ // Unknown id — skip rather than guess at a position.
40
+ if (!existing)
41
+ continue;
42
+ if (link.platform !== undefined)
43
+ existing.imageUrl = link.platform;
44
+ if (link.url !== undefined)
45
+ existing.url = link.url;
46
+ repositionItemByAnchor(content.links, existing, link.move_before_item_id);
47
+ }
48
+ else {
49
+ const newLink = {
50
+ id: generateItemId(),
51
+ imageUrl: link.platform ?? '',
52
+ imageTitle: '',
53
+ width: 0,
54
+ height: 0,
55
+ imageLink: '',
56
+ url: link.url ?? '',
57
+ };
58
+ repositionItemByAnchor(content.links, newLink, link.move_before_item_id);
59
+ }
25
60
  }
26
- content.links = updatedLinks;
27
61
  };
28
62
  export class SocialLinksBuilder extends BaseSectionBuilder {
29
63
  get sectionValidate() {
@@ -42,13 +76,19 @@ export class SocialLinksBuilder extends BaseSectionBuilder {
42
76
  applyScalarFields(content, this.args, this.sectionType);
43
77
  applyStyleFields(content, this.args, this.sectionType, this.getGlobalTextStyles(model));
44
78
  const args = this.args;
45
- if (args.links && Array.isArray(args.links)) {
46
- if (this.mode === 'create') {
79
+ if (args._replace_items && args.links && Array.isArray(args.links)) {
80
+ // Full replacement: discard existing links and rebuild from the payload.
81
+ applySocialLinks(content, args.links);
82
+ }
83
+ else if (this.mode === 'create') {
84
+ if (args.links && Array.isArray(args.links)) {
47
85
  applySocialLinks(content, args.links);
48
86
  }
49
- else {
50
- mergeSocialLinks(content, args.links, !!args._replace_items);
51
- }
87
+ }
88
+ else if ((args.links && Array.isArray(args.links)) ||
89
+ args._remove_item_ids?.length) {
90
+ // Update: id-based add / edit / move / remove in a single pass.
91
+ mergeSocialLinks(content, args.links ?? [], args._remove_item_ids);
52
92
  }
53
93
  return { request: this.request, model };
54
94
  }