@designcrowd/library.brand-page 5.19.1-error-html2canvas-2 → 5.19.1-id-based-items-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-DmKe7W8L.js → CreatedOnBrandCrowd-de-de-0PcGqy0N.js} +3 -3
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-D2EeiNep.js → CreatedOnBrandCrowd-en-us-DB8wcFMI.js} +3 -3
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-BqVwNY1D.js → CreatedOnBrandCrowd-es-es-DVWQ243C.js} +3 -3
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-C756a27-.js → CreatedOnBrandCrowd-fr-ca-D-M_L0Du.js} +3 -3
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-C756a27-.js → CreatedOnBrandCrowd-fr-fr-D-M_L0Du.js} +3 -3
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-FNHAnZUw.js → CreatedOnBrandCrowd-pt-br-CNpzknxU.js} +3 -3
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-FNHAnZUw.js → CreatedOnBrandCrowd-pt-pt-CNpzknxU.js} +3 -3
  8. package/lib/src/{CreatedOnDcom-de-de-8AZZFmDt.js → CreatedOnDcom-de-de-DMJoD5wE.js} +3 -3
  9. package/lib/src/{CreatedOnDcom-en-us-DizTP32i.js → CreatedOnDcom-en-us-B-F3Pmjv.js} +3 -3
  10. package/lib/src/{CreatedOnDcom-es-es-qnp9_n_A.js → CreatedOnDcom-es-es-DmLk-cA2.js} +3 -3
  11. package/lib/src/{CreatedOnDcom-fr-ca-jUs352uK.js → CreatedOnDcom-fr-ca-ntnsCzvA.js} +3 -3
  12. package/lib/src/{CreatedOnDcom-fr-fr-CTxXtHyk.js → CreatedOnDcom-fr-fr-DFcO0gHL.js} +3 -3
  13. package/lib/src/{CreatedOnDcom-pt-br-nQHj8P7d.js → CreatedOnDcom-pt-br-CtuJoVXW.js} +3 -3
  14. package/lib/src/{CreatedOnDcom-pt-pt-DJmVYQuL.js → CreatedOnDcom-pt-pt-B7shZVVn.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-logo-settings/AdminLogoSettings.mixin.d.ts +3 -3
  35. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +170 -181
  36. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +0 -3
  37. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.js +0 -1
  38. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.mixin.d.ts +12 -12
  39. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.stories.js +2 -2
  40. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-payment.js +7 -0
  41. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +149 -148
  42. package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.d.ts +21 -21
  43. package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.js +1 -1
  44. package/lib/src/brand-page-maker/components/color-picker/components/ColorModeSelect.mixin.d.ts +2 -4
  45. package/lib/src/brand-page-maker/components/color-picker/components/ColorModeSelect.mixin.js +0 -1
  46. package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSlider.mixin.d.ts +2 -4
  47. package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSlider.mixin.js +0 -1
  48. package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSliderHandler.mixin.d.ts +2 -5
  49. package/lib/src/brand-page-maker/components/color-picker/components/GradientColorSliderHandler.mixin.js +0 -1
  50. package/lib/src/brand-page-maker/components/color-picker/components/GradientTypeSelector.mixin.d.ts +2 -4
  51. package/lib/src/brand-page-maker/components/color-picker/components/GradientTypeSelector.mixin.js +0 -1
  52. package/lib/src/brand-page-maker/components/device/Device.mixin.d.ts +2 -4
  53. package/lib/src/brand-page-maker/components/device/Device.mixin.js +0 -1
  54. package/lib/src/brand-page-maker/components/navigation/Navigation.mixin.d.ts +2 -4
  55. package/lib/src/brand-page-maker/components/navigation/Navigation.mixin.js +0 -1
  56. package/lib/src/brand-page-maker/components/preview/Preview.mixin.d.ts +3 -3
  57. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +11 -11
  58. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +25 -27
  59. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.js +0 -1
  60. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +27 -27
  61. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.js +1 -1
  62. package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +12 -17
  63. package/lib/src/brand-page-maker/components/social-share/SocialShare.mixin.d.ts +12 -12
  64. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +32 -30
  65. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.js +5 -16
  66. package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +11 -11
  67. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +1 -1
  68. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +1 -1
  69. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +6 -6
  70. package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +2 -2
  71. package/lib/src/brand-page-maker/constants/config-settings-constant.d.ts +6 -6
  72. package/lib/src/brand-page-maker/constants/config-settings-constant.js +1 -1
  73. package/lib/src/brand-page-maker/convertors/text-image-display-style.convertor.d.ts +1 -1
  74. package/lib/src/brand-page-maker/convertors/text-image-display-style.convertor.js +1 -1
  75. package/lib/src/brand-page-maker/models/content-settings.d.ts +1 -1
  76. package/lib/src/brand-page-maker/models/navigation-style-configs.js +1 -1
  77. package/lib/src/brand-page-maker/routing/brand-page-maker-router.d.ts +2 -1
  78. package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +378 -366
  79. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +4 -5
  80. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.js +5 -8
  81. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.d.ts +1 -3
  82. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +3 -7
  83. package/lib/src/brand-page-maker/stores/history/history-store.d.ts +15 -15
  84. package/lib/src/brand-page-maker/stores/ui/ui-store.d.ts +0 -6
  85. package/lib/src/brand-page-maker/stores/ui/ui-store.js +0 -8
  86. package/lib/src/brand-page-maker/utils/content-settings.util.js +1 -1
  87. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +1 -1
  88. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +4 -4
  89. package/lib/src/clients/brand-page-client.d.ts +15 -16
  90. package/lib/src/clients/design-template-client.d.ts +0 -3
  91. package/lib/src/clients/media-client.d.ts +2 -2
  92. package/lib/src/clients/models.d.ts +0 -2
  93. package/lib/src/composables/useSnapshot.js +18 -18
  94. package/lib/src/css/library.brand-page-brandCrowd.min.css +324 -342
  95. package/lib/src/css/library.brand-page-brandPage.min.css +324 -620
  96. package/lib/src/css/library.brand-page-designCom.min.css +324 -342
  97. package/lib/src/css/library.brand-page-preview.min.css +324 -598
  98. package/lib/src/index.d.ts +1 -1
  99. package/lib/src/index.mjs +15 -16
  100. package/lib/src/{lib-DityivZa.js → lib-BQmJBVbS.js} +33238 -36458
  101. package/lib/src/lib.js +1 -1
  102. package/lib/src/metadata-builder/builders/footer-builder.js +56 -91
  103. package/lib/src/metadata-builder/builders/item-id-utils.d.ts +41 -0
  104. package/lib/src/metadata-builder/builders/item-id-utils.js +68 -0
  105. package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.js +57 -68
  106. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +58 -93
  107. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +66 -99
  108. package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +57 -17
  109. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +51 -85
  110. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +49 -74
  111. package/lib/src/metadata-builder/builders/shape-color-utils.js +1 -1
  112. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +13 -4
  113. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +66 -48
  114. package/lib/src/plugins/google-maps-plugin/google-maps-plugin.d.ts +1 -1
  115. package/lib/src/plugins/google-maps-plugin/google-maps-plugin.js +8 -8
  116. package/lib/src/shared/background-settings/components/background-shapes/BackgroundShapeSettings.mixin.d.ts +13 -13
  117. package/lib/src/shared/background-settings/store/background-settings-store.d.ts +3 -3
  118. package/lib/src/shared/color-picker/components/ColorPicker.mixin.d.ts +2 -4
  119. package/lib/src/shared/color-picker/components/ColorPicker.mixin.js +2 -2
  120. package/lib/src/shared/color-picker/components/ColorPickerSwatch.mixin.d.ts +2 -4
  121. package/lib/src/shared/color-picker/components/ColorPickerSwatch.mixin.js +0 -1
  122. package/lib/src/shared/confirm-modal/ConfirmModal.mixin.d.ts +2 -5
  123. package/lib/src/shared/confirm-modal/ConfirmModal.mixin.js +0 -1
  124. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.d.ts +1 -0
  125. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +129 -131
  126. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.js +1 -2
  127. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +110 -111
  128. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +81 -84
  129. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.js +0 -5
  130. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +109 -117
  131. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +14 -1
  132. package/lib/src/shared/content-settings/components/email-field/EmailField.mixin.d.ts +1 -0
  133. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.d.ts +70 -72
  134. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.js +3 -4
  135. package/lib/src/shared/content-settings/components/form-fields-field/FormFieldsField.mixin.d.ts +4 -4
  136. package/lib/src/shared/content-settings/components/icon-library/IconLibrary.mixin.d.ts +1 -3
  137. package/lib/src/shared/content-settings/components/icon-library/IconLibrary.mixin.js +0 -1
  138. package/lib/src/shared/content-settings/components/images-field/ImagesField.mixin.d.ts +2 -2
  139. package/lib/src/shared/content-settings/components/items-field/ItemsField.mixin.d.ts +3 -4
  140. package/lib/src/shared/content-settings/components/items-field/ItemsField.mixin.js +1 -1
  141. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +40 -40
  142. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigOptionField.mixin.d.ts +6 -5
  143. package/lib/src/shared/content-settings/components/shape-field/ShapeField.mixin.d.ts +1 -0
  144. package/lib/src/shared/content-settings/components/slider-field/SliderField.mixin.d.ts +2 -2
  145. package/lib/src/shared/content-settings/components/tel-field/TelField.mixin.d.ts +1 -0
  146. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.d.ts +2 -1
  147. package/lib/src/shared/content-settings/components/text-field/TextField.mixin.d.ts +1 -0
  148. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.d.ts +2 -1
  149. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.js +1 -1
  150. package/lib/src/shared/content-settings/components/text-style-field/TextStyleField.mixin.d.ts +28 -28
  151. package/lib/src/shared/content-settings/components/textarea-field/TextareaField.mixin.d.ts +1 -0
  152. package/lib/src/shared/content-settings/components/toggle-field/ToggleField.mixin.d.ts +2 -2
  153. package/lib/src/shared/content-settings/components/url-field/UrlField.mixin.d.ts +1 -0
  154. package/lib/src/shared/content-settings/components/youtube-video-field/YoutubeVideoField.mixin.d.ts +1 -0
  155. package/lib/src/shared/content-settings/store/content-settings-store.d.ts +39 -39
  156. package/lib/src/shared/content-settings/utils.js +1 -0
  157. package/lib/src/shared/font-family-selector/FontFamilySelector.mixin.d.ts +4 -6
  158. package/lib/src/shared/font-family-selector/FontFamilySelector.mixin.js +0 -1
  159. package/lib/src/shared/image-gallery-style-settings/ImageGalleryStyleSettings.mixin.d.ts +2 -4
  160. package/lib/src/shared/image-gallery-style-settings/ImageGalleryStyleSettings.mixin.js +0 -1
  161. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +21 -29
  162. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.js +1 -1
  163. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +51 -50
  164. package/lib/src/shared/logo-settings/components/LogoSettingsHeader.mixin.d.ts +4 -6
  165. package/lib/src/shared/logo-settings/components/LogoSettingsHeader.mixin.js +0 -1
  166. package/lib/src/shared/logo-settings/store/editable-logo-store.js +1 -1
  167. package/lib/src/shared/logo-settings/store/logo-settings-store.d.ts +1 -1
  168. package/lib/src/shared/logo-versions/LogoVersions.mixin.d.ts +5 -13
  169. package/lib/src/shared/logo-versions/LogoVersions.mixin.js +0 -1
  170. package/lib/src/shared/models.d.ts +0 -2
  171. package/lib/src/shared/my-designs/MyDesigns.mixin.d.ts +6 -14
  172. package/lib/src/shared/my-designs/MyDesigns.mixin.js +0 -1
  173. package/lib/src/shared/my-uploads/MyMedia.mixin.d.ts +5 -14
  174. package/lib/src/shared/my-uploads/MyMedia.mixin.js +0 -1
  175. package/lib/src/shared/my-uploads/MyUploads.mixin.d.ts +5 -14
  176. package/lib/src/shared/my-uploads/MyUploads.mixin.js +0 -1
  177. package/lib/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.d.ts +3 -5
  178. package/lib/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.js +0 -1
  179. package/lib/src/shared/navigation-style-settings/store/index.d.ts +2 -2
  180. package/lib/src/shared/page-navigation/store/page-navigation-store.d.ts +2 -2
  181. package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.d.ts +28 -27
  182. package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.js +2 -0
  183. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +179 -188
  184. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +177 -181
  185. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.js +1 -1
  186. package/lib/src/shared/page-settings/store/page-settings-store.d.ts +9 -9
  187. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +39 -39
  188. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.js +1 -0
  189. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +18 -18
  190. package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +6 -6
  191. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +60 -66
  192. package/lib/src/shared/style-settings/store/style-settings-store.d.ts +1 -1
  193. package/lib/src/shared/style-settings/store/style-settings-store.js +1 -1
  194. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +450 -20
  195. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.js +2 -2
  196. package/lib/src/shared/template-settings/models.d.ts +0 -1
  197. package/lib/src/shared/template-settings/store/template-settings-store.d.ts +0 -4
  198. package/lib/src/shared/template-settings/store/template-settings-store.js +11 -79
  199. package/lib/src/shared/unsplash-library/UnsplashLibrary.mixin.d.ts +4 -9
  200. package/package.json +57 -61
  201. package/src/brand-page/components/about-section/AboutSection.vue.test.ts +5 -7
  202. package/src/brand-page/components/brand-page-footer/BrandPageFooter.vue +1 -1
  203. package/src/brand-page/components/image/Image.mixin.ts +1 -1
  204. package/src/brand-page/components/image/Image.test.ts +1 -1
  205. package/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.ts +2 -1
  206. package/src/brand-page/components/images-section/ImagesSection.vue +1 -0
  207. package/src/brand-page/components/logo/LogoRender.vue +188 -188
  208. package/src/brand-page/components/navigation/hamburger/HamburgerNavigation.mixin.ts +0 -1
  209. package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.ts +6 -15
  210. package/src/brand-page/components/page-footer/PageFooter.vue +0 -1
  211. package/src/brand-page/components/page-header/HeaderLayout.vue +6 -12
  212. package/src/brand-page/components/page-header/PageHeader.vue +0 -1
  213. package/src/brand-page/components/shapes-section/ShapesSection.vue +1 -1
  214. package/src/brand-page/components/shop/CartModal.vue +3 -1
  215. package/src/brand-page/components/shop/FeaturedDisplay.vue +2 -2
  216. package/src/brand-page/components/shop/PaymentModal.vue +5 -1
  217. package/src/brand-page/components/shop/ProductDetailModal.vue +1 -1
  218. package/src/brand-page/components/shop/Shop.vue +1 -1
  219. package/src/brand-page/components/shop/TextDisplay.vue +1 -1
  220. package/src/brand-page/components/shop/ThumbnailDisplay.vue +2 -2
  221. package/src/brand-page/components/testimonial-section/TestimonialSection.vue +3 -3
  222. package/src/brand-page/components/text-wrapper/TextWrapper.vue +1 -3
  223. package/src/brand-page/constants/sections/footer-display.constants.ts +1 -1
  224. package/src/brand-page/models/index.ts +12 -4
  225. package/src/brand-page/models/payment.model.ts +1 -2
  226. package/src/brand-page/models/shop.model.ts +2 -0
  227. package/src/brand-page/models/user-personalisation.model.ts +2 -1
  228. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +0 -3
  229. package/src/brand-page/utils/__snapshots__/migration2.util.test.ts.snap +0 -1
  230. package/src/brand-page/utils/__snapshots__/model.util.test.ts.snap +0 -2
  231. package/src/brand-page/utils/migration/migration-01_1.ts +1 -1
  232. package/src/brand-page/utils/migration/migration-02.ts +1 -0
  233. package/src/brand-page/utils/migration/migration-03.ts +1 -0
  234. package/src/brand-page/utils/migration/migration-04.ts +1 -0
  235. package/src/brand-page/utils/migration/migration-05.ts +5 -1
  236. package/src/brand-page/utils/migration/migration-05_2.ts +1 -1
  237. package/src/brand-page/utils/migration/migration-08.test.ts +1 -1
  238. package/src/brand-page/utils/personalisation.util.test.ts +0 -35
  239. package/src/brand-page/utils/personalisation.util.ts +23 -119
  240. package/src/brand-page/view-models.ts +56 -38
  241. package/src/brand-page-maker/assessors/banner-section-description-visibility.assessor.ts +6 -6
  242. package/src/brand-page-maker/assessors/color-replacement-icon-visibility.assessor.ts +2 -2
  243. package/src/brand-page-maker/assessors/color-replacement-profile-visibility.assessor.ts +2 -2
  244. package/src/brand-page-maker/assessors/color-replacement-visibility.assessor.ts +2 -2
  245. package/src/brand-page-maker/assessors/contact-form-visibility.assessor.ts +14 -14
  246. package/src/brand-page-maker/components/action-bar/ActionBar.mixin.ts +0 -1
  247. package/src/brand-page-maker/components/ai-text-edit/TextBubble.vue +0 -1
  248. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +1 -5
  249. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.ts +0 -1
  250. package/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.stories.ts +5 -2
  251. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +5 -0
  252. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-payment.ts +8 -0
  253. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-site-template.examples.ts +2 -0
  254. package/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.vue +0 -1
  255. package/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.ts +1 -1
  256. package/src/brand-page-maker/components/color-palette/BpColorPalette.vue +2 -1
  257. package/src/brand-page-maker/components/color-picker/components/ColorModeSelect.mixin.ts +0 -1
  258. package/src/brand-page-maker/components/color-picker/components/GradientColorSlider.mixin.ts +0 -1
  259. package/src/brand-page-maker/components/color-picker/components/GradientColorSliderHandler.mixin.ts +0 -1
  260. package/src/brand-page-maker/components/color-picker/components/GradientTypeSelector.mixin.ts +0 -1
  261. package/src/brand-page-maker/components/device/Device.mixin.ts +0 -1
  262. package/src/brand-page-maker/components/navigation/Navigation.mixin.ts +0 -1
  263. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +1 -3
  264. package/src/brand-page-maker/components/sensitive-content/SensitiveContent.vue +4 -1
  265. package/src/brand-page-maker/components/seo-settings/DeleteDomainModal.vue +1 -1
  266. package/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.ts +0 -1
  267. package/src/brand-page-maker/components/seo-settings/SeoPageSettings.vue +1 -1
  268. package/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.ts +1 -0
  269. package/src/brand-page-maker/components/seo-settings/UrlSettings.vue +1 -0
  270. package/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.vue +5 -1
  271. package/src/brand-page-maker/components/shop/AddOrEditProductContainer.vue +1 -1
  272. package/src/brand-page-maker/components/shop/ReplaceImageSettingsForShop.vue +1 -1
  273. package/src/brand-page-maker/components/social-share/SocialShare.vue +1 -1
  274. package/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.ts +8 -22
  275. package/src/brand-page-maker/components/template-preview/TemplatePreview.vue +10 -5
  276. package/src/brand-page-maker/components/video-play/VideoPlayButton.vue +1 -1
  277. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +1 -1
  278. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +6 -6
  279. package/src/brand-page-maker/constants/config-settings-constant.ts +3 -3
  280. package/src/brand-page-maker/convertors/text-image-display-style.convertor.ts +1 -1
  281. package/src/brand-page-maker/models/button-styles-config.ts +2 -4
  282. package/src/brand-page-maker/models/content-settings-configs.ts +2 -4
  283. package/src/brand-page-maker/models/navigation-style-configs.ts +6 -5
  284. package/src/brand-page-maker/stores/admin-logo-settings/admin-logo-settings-store.ts +1 -0
  285. package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.ts +5 -8
  286. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.test.ts +3 -1
  287. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +4 -12
  288. package/src/brand-page-maker/stores/ui/ui-store.ts +0 -16
  289. package/src/brand-page-maker/utils/content-settings.util.ts +1 -1
  290. package/src/brand-page-maker/utils/rich-text-editor.util.ts +4 -4
  291. package/src/clients/design-template-client.ts +0 -3
  292. package/src/clients/models.ts +0 -2
  293. package/src/composables/useSnapshot.ts +22 -19
  294. package/src/lib.ts +0 -1
  295. package/src/metadata-builder/builders/footer-builder.test.ts +245 -84
  296. package/src/metadata-builder/builders/footer-builder.ts +81 -130
  297. package/src/metadata-builder/builders/item-id-utils.test.ts +118 -0
  298. package/src/metadata-builder/builders/item-id-utils.ts +85 -0
  299. package/src/metadata-builder/builders/section-builders/image-gallery-builder.ts +62 -88
  300. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +66 -129
  301. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +105 -160
  302. package/src/metadata-builder/builders/section-builders/social-links-builder.ts +65 -20
  303. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +83 -146
  304. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +70 -122
  305. package/src/metadata-builder/builders/shape-color-utils.ts +2 -2
  306. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +13 -4
  307. package/src/metadata-builder/parsers/section-tool-arguments.ts +66 -48
  308. package/src/plugins/google-maps-plugin/google-maps-plugin.ts +8 -8
  309. package/src/shared/background-settings/components/BackgroundSettings.vue +1 -1
  310. package/src/shared/background-settings/components/background-color/BackgroundColorSettings.vue +1 -1
  311. package/src/shared/color-picker/components/ColorPicker.mixin.ts +7 -2
  312. package/src/shared/color-picker/components/ColorPickerSwatch.mixin.ts +0 -1
  313. package/src/shared/common/NumberInput.vue +0 -1
  314. package/src/shared/common/SideBarSettingsHeader.vue +2 -2
  315. package/src/shared/confirm-modal/ConfirmModal.mixin.ts +0 -1
  316. package/src/shared/content-settings/components/booking-url-field/BookingUrlField.vue +0 -1
  317. package/src/shared/content-settings/components/button-configuration-field/ButtonConfigurationField.vue +0 -1
  318. package/src/shared/content-settings/components/color-field/ColorField.vue +10 -3
  319. package/src/shared/content-settings/components/content-settings/ContentSettings.mixin.ts +1 -1
  320. package/src/shared/content-settings/components/content-settings/ContentSettingsLockedButton.vue +0 -1
  321. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.ts +0 -5
  322. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +14 -1
  323. package/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.ts +6 -4
  324. package/src/shared/content-settings/components/icon-library/IconLibrary.mixin.ts +0 -1
  325. package/src/shared/content-settings/components/items-field/ItemsField.mixin.ts +1 -1
  326. package/src/shared/content-settings/components/logo-selection-field/LogoSelectionField.vue +1 -1
  327. package/src/shared/content-settings/components/logo-size-field/LogoSizeSliderField.vue +1 -1
  328. package/src/shared/content-settings/components/page-dropdown-field/PageDropdownField.vue +1 -1
  329. package/src/shared/content-settings/components/personalisation-role-field/PersonalisationRoleField.vue +4 -4
  330. package/src/shared/content-settings/components/text-image-field/TextImageField.vue +8 -2
  331. package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.ts +1 -1
  332. package/src/shared/content-settings/models.ts +2 -3
  333. package/src/shared/content-settings/utils.ts +1 -0
  334. package/src/shared/dropzone-image-uploader/components/DropzoneImageUploader.vue +0 -1
  335. package/src/shared/ecommerce-settings/store/cart-store.test.ts +1 -1
  336. package/src/shared/font-family-selector/FontFamilySelector.mixin.ts +0 -1
  337. package/src/shared/image-gallery-style-settings/ImageGalleryStyleSettings.mixin.ts +0 -1
  338. package/src/shared/logo-settings/components/EditLogoSettings.mixin.ts +1 -1
  339. package/src/shared/logo-settings/components/LogoSettings.mixin.ts +1 -0
  340. package/src/shared/logo-settings/components/LogoSettingsHeader.mixin.ts +0 -1
  341. package/src/shared/logo-settings/components/LogoStyleSettingsWithState.vue +2 -2
  342. package/src/shared/logo-settings/components/LogoTemplateSearch.vue +1 -1
  343. package/src/shared/logo-settings/store/editable-logo-store.ts +1 -1
  344. package/src/shared/logo-settings/store/logo-settings-store.test.ts +4 -4
  345. package/src/shared/logo-versions/LogoVersions.mixin.ts +0 -1
  346. package/src/shared/models.ts +0 -6
  347. package/src/shared/my-designs/MyDesigns.mixin.ts +0 -1
  348. package/src/shared/my-uploads/MyMedia.mixin.ts +0 -1
  349. package/src/shared/my-uploads/MyMediaSelectionItem.vue +1 -2
  350. package/src/shared/my-uploads/MyUploads.mixin.ts +0 -1
  351. package/src/shared/my-uploads/models.ts +2 -1
  352. package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.ts +0 -1
  353. package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.vue +1 -1
  354. package/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.ts +3 -0
  355. package/src/shared/page-settings/components/page-settings/NestedDraggable.vue +1 -1
  356. package/src/shared/page-settings/components/page-settings/PageSettings.vue +2 -1
  357. package/src/shared/page-settings/components/page-settings-section-styles/HeaderDisplayStyle.vue +1 -1
  358. package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.ts +1 -1
  359. package/src/shared/page-settings/store/page-settings-store.test.ts +8 -4
  360. package/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.ts +1 -0
  361. package/src/shared/shape-library/ShapeLibrary.vue +2 -2
  362. package/src/shared/style-settings/store/style-settings-store.ts +1 -1
  363. package/src/shared/template-settings/components/TemplatesSettings.mixin.ts +2 -2
  364. package/src/shared/template-settings/models.ts +0 -1
  365. package/src/shared/template-settings/store/template-settings-store.ts +18 -105
  366. package/src/shared/unsplash-library/UnsplashLibrary.vue +6 -2
  367. package/src/shared/video-library/VideoLibrary.vue +1 -1
  368. package/lib/src/brand-page/components/banner-section/BannerSection.mixin.d.ts +0 -103
  369. package/lib/src/brand-page/components/banner-section/BannerSection.mixin.js +0 -178
  370. package/src/brand-page/components/banner-section/BannerSection.mixin.ts +0 -210
  371. 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,
@@ -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
  }));
@@ -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
  }
@@ -227,8 +236,8 @@ export interface UpdateFooterArguments {
227
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). */