@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
@@ -14,6 +14,10 @@ import { FooterBuilder } from './footer-builder';
14
14
  * The fix routes copyright style writes through `applyDetachedStyleOverride`,
15
15
  * which resolves the preset in and clears `textStyleType` so the per-element
16
16
  * override sticks.
17
+ *
18
+ * BP-5603: social link mutations converted from index-based to id-based
19
+ * (item_id / move_before_item_id / _remove_social_link_ids), mirroring the
20
+ * section-level move_before_content_id contract one level deeper.
17
21
  */
18
22
 
19
23
  const textStyleOptions = {
@@ -42,6 +46,37 @@ const makeModel = (copyrightStyle: Record<string, unknown>): BrandPageModel =>
42
46
  pageFooterMobile: { copyrightStyle: { ...copyrightStyle } },
43
47
  }) as unknown as BrandPageModel;
44
48
 
49
+ const makeModelWithLinks = (
50
+ links: { id?: string; imageUrl: string; url: string }[],
51
+ ): BrandPageModel =>
52
+ ({
53
+ styles: { textStyles: { textStyleOptions } },
54
+ pageFooterDesktop: {
55
+ copyrightStyle: {},
56
+ socialLinks: links.map((l) => ({
57
+ id: l.id,
58
+ imageUrl: l.imageUrl,
59
+ url: l.url,
60
+ imageTitle: '',
61
+ width: 0,
62
+ height: 0,
63
+ imageLink: '',
64
+ })),
65
+ },
66
+ pageFooterMobile: {
67
+ copyrightStyle: {},
68
+ socialLinks: links.map((l) => ({
69
+ id: l.id,
70
+ imageUrl: l.imageUrl,
71
+ url: l.url,
72
+ imageTitle: '',
73
+ width: 0,
74
+ height: 0,
75
+ imageLink: '',
76
+ })),
77
+ },
78
+ }) as unknown as BrandPageModel;
79
+
45
80
  const run = (args: UpdateFooterArguments, model: BrandPageModel) =>
46
81
  new FooterBuilder({
47
82
  id: 'f1',
@@ -49,6 +84,10 @@ const run = (args: UpdateFooterArguments, model: BrandPageModel) =>
49
84
  payload: args,
50
85
  }).buildAsync(model);
51
86
 
87
+ // ---------------------------------------------------------------------------
88
+ // Copyright font (BP-5420)
89
+ // ---------------------------------------------------------------------------
90
+
52
91
  describe('FooterBuilder — copyright font vs global style', () => {
53
92
  it('detaches from the global preset: applies the font, clears textStyleType, retains preset values', async () => {
54
93
  const model = makeModel({
@@ -57,7 +96,7 @@ describe('FooterBuilder — copyright font vs global style', () => {
57
96
  });
58
97
 
59
98
  const { model: out } = await run(
60
- { copyright_text_style: { fontFamily: 'Anton' } },
99
+ { copyright_text_font_family: 'Anton' },
61
100
  model,
62
101
  );
63
102
 
@@ -73,203 +112,263 @@ describe('FooterBuilder — copyright font vs global style', () => {
73
112
  expect(style.color).toBe('#2d0b3f');
74
113
  expect(style.textAlign).toBe('center');
75
114
  });
115
+ });
76
116
 
77
- it('applies copyright font size + weight via the style object, detaches, preserves the text', async () => {
78
- const model = makeModel({
79
- textStyleType: 'paragraphs',
80
- textAlign: 'center',
81
- fontWeight: 'bold',
82
- });
117
+ // ---------------------------------------------------------------------------
118
+ // Social links — id-based mutations (BP-5603)
119
+ // ---------------------------------------------------------------------------
120
+
121
+ describe('FooterBuilder — social links id-based mutations', () => {
122
+ it('backfills ids on legacy links that have none', async () => {
123
+ // Links without ids (legacy persisted data)
124
+ const model = makeModelWithLinks([
125
+ { imageUrl: 'facebook', url: 'https://fb.com' },
126
+ { imageUrl: 'instagram', url: 'https://ig.com' },
127
+ ]);
128
+
129
+ const { model: out } = await run({}, model);
130
+
131
+ const links = out.pageFooterDesktop!.socialLinks;
132
+
133
+ expect(links[0].id).toBeTruthy();
134
+ expect(links[1].id).toBeTruthy();
135
+ expect(links[0].id).not.toBe(links[1].id);
136
+ });
137
+
138
+ it('updates an existing link in place by item_id', async () => {
139
+ const model = makeModelWithLinks([
140
+ { id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
141
+ { id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
142
+ ]);
83
143
 
84
144
  const { model: out } = await run(
85
- { copyright_text_style: { fontSize: '0.875em', fontWeight: 'normal' } },
145
+ {
146
+ social_links: [
147
+ { item_id: 'link-1', url: 'https://facebook.com/mybrand' },
148
+ ],
149
+ },
86
150
  model,
87
151
  );
88
152
 
89
- const desktop = out.pageFooterDesktop!;
90
- const style = desktop.copyrightStyle as Record<string, unknown>;
153
+ const links = out.pageFooterDesktop!.socialLinks;
91
154
 
92
- expect(style.fontSize).toBe('0.875em');
93
- expect(style.fontWeight).toBe('normal');
94
- expect(style.textStyleType).toBeUndefined();
95
- // preset values resolved in, not lost
96
- expect(style.fontFamily).toBe('Inter');
97
- expect(style.textAlign).toBe('center');
98
- // style-only change must NOT touch the copyright text itself
99
- expect(desktop.copyrightText).toBeUndefined();
155
+ expect(links).toHaveLength(2);
156
+ expect(links[0].url).toBe('https://facebook.com/mybrand');
157
+ expect(links[0].id).toBe('link-1');
158
+ // untouched link unchanged
159
+ expect(links[1].id).toBe('link-2');
160
+ expect(links[1].url).toBe('https://ig.com');
100
161
  });
101
162
 
102
- it('applies copyright style to both viewports', async () => {
103
- const model = makeModel({ textStyleType: 'paragraphs' });
163
+ it('updates social_link_icon on an existing link', async () => {
164
+ const model = makeModelWithLinks([
165
+ { id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
166
+ ]);
104
167
 
105
168
  const { model: out } = await run(
106
- { copyright_text_style: { fontSize: '0.75em', fontWeight: 'normal' } },
169
+ {
170
+ social_links: [{ item_id: 'link-1', social_link_icon: 'facebook-v2' }],
171
+ },
107
172
  model,
108
173
  );
109
174
 
110
- for (const vp of [out.pageFooterDesktop!, out.pageFooterMobile!]) {
111
- const style = vp.copyrightStyle as Record<string, unknown>;
112
-
113
- expect(style.fontSize).toBe('0.75em');
114
- expect(style.fontWeight).toBe('normal');
115
- expect(style.textStyleType).toBeUndefined();
116
- }
175
+ expect(out.pageFooterDesktop!.socialLinks[0].imageUrl).toBe('facebook-v2');
117
176
  });
118
177
 
119
- it('applies copyright colour and normalises letterSpacing (int→em) via the style object', async () => {
120
- const model = makeModel({ textStyleType: 'paragraphs' });
178
+ it('skips unknown item_id silently', async () => {
179
+ const model = makeModelWithLinks([
180
+ { id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
181
+ ]);
121
182
 
122
183
  const { model: out } = await run(
123
- { copyright_text_style: { color: '#f5f5f5', letterSpacing: 50 } },
184
+ {
185
+ social_links: [{ item_id: 'no-such-id', url: 'https://nowhere.com' }],
186
+ },
124
187
  model,
125
188
  );
126
189
 
127
- const style = out.pageFooterDesktop!.copyrightStyle as Record<
128
- string,
129
- unknown
130
- >;
190
+ const links = out.pageFooterDesktop!.socialLinks;
131
191
 
132
- expect(style.color).toBe('#f5f5f5');
133
- // letterSpacing int 0–100 is normalised to an em string (50 → '0.5em'),
134
- // exactly as section *_style values are.
135
- expect(style.letterSpacing).toBe('0.5em');
136
- expect(style.textStyleType).toBeUndefined();
192
+ expect(links).toHaveLength(1);
193
+ expect(links[0].url).toBe('https://fb.com');
137
194
  });
138
- });
139
195
 
140
- type Link = { imageUrl: string; url: string };
141
-
142
- const link = (imageUrl: string): Link => ({
143
- imageUrl,
144
- url: `https://${imageUrl}.com`,
145
- });
196
+ it('inserts a new link (no item_id) appended by default', async () => {
197
+ const model = makeModelWithLinks([
198
+ { id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
199
+ ]);
146
200
 
147
- /**
148
- * Footers migrated from the legacy single `footerStyles` model
149
- * (migration-01) split into desktop + mobile via a shallow spread, leaving
150
- * both viewports' `socialLinks` pointing at ONE shared array. `cloneDeep`
151
- * preserves that shared reference. This factory reproduces that shape.
152
- */
153
- const makeSharedSocialModel = (links: Link[]): BrandPageModel => {
154
- const shared = links.map((l) => ({ ...l }));
201
+ const { model: out } = await run(
202
+ {
203
+ social_links: [
204
+ { social_link_icon: 'instagram', url: 'https://ig.com' },
205
+ ],
206
+ },
207
+ model,
208
+ );
155
209
 
156
- return {
157
- styles: { textStyles: { textStyleOptions } },
158
- pageFooterDesktop: { socialLinks: shared },
159
- pageFooterMobile: { socialLinks: shared },
160
- } as unknown as BrandPageModel;
161
- };
210
+ const links = out.pageFooterDesktop!.socialLinks;
162
211
 
163
- const makeIndependentSocialModel = (links: Link[]): BrandPageModel =>
164
- ({
165
- styles: { textStyles: { textStyleOptions } },
166
- pageFooterDesktop: { socialLinks: links.map((l) => ({ ...l })) },
167
- pageFooterMobile: { socialLinks: links.map((l) => ({ ...l })) },
168
- }) as unknown as BrandPageModel;
212
+ expect(links).toHaveLength(2);
213
+ expect(links[1].imageUrl).toBe('instagram');
214
+ expect(links[1].id).toBeTruthy();
215
+ });
169
216
 
170
- const socialIcons = (footer: { socialLinks?: Link[] } | undefined) =>
171
- (footer?.socialLinks ?? []).map((l) => l.imageUrl);
172
-
173
- describe('FooterBuilder social link removal (shared-array regression)', () => {
174
- // Reproduces the bug: a single requested removal index dropped TWO links
175
- // because both viewports shared one array and the builder spliced it twice.
176
- it('removes exactly one link per requested index even when viewports share the array', async () => {
177
- const model = makeSharedSocialModel([
178
- link('facebook'),
179
- link('instagram'),
180
- link('x'),
181
- link('youtube'),
217
+ it('inserts a new link before an anchor via move_before_item_id', async () => {
218
+ const model = makeModelWithLinks([
219
+ { id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
220
+ { id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
182
221
  ]);
183
222
 
184
223
  const { model: out } = await run(
185
- { _remove_social_link_indices: [2] },
224
+ {
225
+ social_links: [
226
+ {
227
+ social_link_icon: 'x',
228
+ url: 'https://x.com',
229
+ move_before_item_id: 'link-2',
230
+ },
231
+ ],
232
+ },
186
233
  model,
187
234
  );
188
235
 
189
- expect(socialIcons(out.pageFooterDesktop)).toEqual([
190
- 'facebook',
191
- 'instagram',
192
- 'youtube',
193
- ]);
194
- expect(socialIcons(out.pageFooterMobile)).toEqual([
195
- 'facebook',
196
- 'instagram',
197
- 'youtube',
198
- ]);
236
+ const links = out.pageFooterDesktop!.socialLinks;
237
+
238
+ expect(links).toHaveLength(3);
239
+ expect(links[0].id).toBe('link-1');
240
+ expect(links[1].imageUrl).toBe('x');
241
+ expect(links[2].id).toBe('link-2');
199
242
  });
200
243
 
201
- it('un-shares the viewport arrays so later in-place edits cannot cross-corrupt', async () => {
202
- const model = makeSharedSocialModel([
203
- link('facebook'),
204
- link('instagram'),
205
- link('x'),
244
+ it('inserts a new link at end via move_before_item_id "__end__"', async () => {
245
+ const model = makeModelWithLinks([
246
+ { id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
247
+ { id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
206
248
  ]);
207
249
 
208
250
  const { model: out } = await run(
209
- { _remove_social_link_indices: [0] },
251
+ {
252
+ social_links: [
253
+ {
254
+ social_link_icon: 'x',
255
+ url: 'https://x.com',
256
+ move_before_item_id: '__end__',
257
+ },
258
+ ],
259
+ },
210
260
  model,
211
261
  );
212
262
 
213
- expect(out.pageFooterDesktop!.socialLinks).not.toBe(
214
- out.pageFooterMobile!.socialLinks,
215
- );
263
+ const links = out.pageFooterDesktop!.socialLinks;
264
+
265
+ expect(links).toHaveLength(3);
266
+ expect(links[2].imageUrl).toBe('x');
216
267
  });
217
268
 
218
- it('handles multiple removal indices on a shared array (descending splice)', async () => {
219
- const model = makeSharedSocialModel([
220
- link('facebook'),
221
- link('instagram'),
222
- link('x'),
223
- link('youtube'),
269
+ it('moves an existing link before an anchor via move_before_item_id', async () => {
270
+ const model = makeModelWithLinks([
271
+ { id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
272
+ { id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
273
+ { id: 'link-3', imageUrl: 'x', url: 'https://x.com' },
224
274
  ]);
225
275
 
226
276
  const { model: out } = await run(
227
- { _remove_social_link_indices: [0, 2] },
277
+ {
278
+ social_links: [{ item_id: 'link-3', move_before_item_id: 'link-1' }],
279
+ },
228
280
  model,
229
281
  );
230
282
 
231
- expect(socialIcons(out.pageFooterDesktop)).toEqual([
232
- 'instagram',
233
- 'youtube',
234
- ]);
235
- expect(socialIcons(out.pageFooterMobile)).toEqual(['instagram', 'youtube']);
283
+ const links = out.pageFooterDesktop!.socialLinks;
284
+
285
+ expect(links.map((l) => l.id)).toEqual(['link-3', 'link-1', 'link-2']);
236
286
  });
237
287
 
238
- it('removes one link per index for independent viewport arrays (no regression)', async () => {
239
- const model = makeIndependentSocialModel([
240
- link('facebook'),
241
- link('instagram'),
242
- link('x'),
288
+ it('removes links by id', async () => {
289
+ const model = makeModelWithLinks([
290
+ { id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
291
+ { id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
292
+ { id: 'link-3', imageUrl: 'x', url: 'https://x.com' },
243
293
  ]);
244
294
 
245
295
  const { model: out } = await run(
246
- { _remove_social_link_indices: [1] },
296
+ { _remove_social_link_ids: ['link-1', 'link-3'] },
247
297
  model,
248
298
  );
249
299
 
250
- expect(socialIcons(out.pageFooterDesktop)).toEqual(['facebook', 'x']);
251
- expect(socialIcons(out.pageFooterMobile)).toEqual(['facebook', 'x']);
300
+ const links = out.pageFooterDesktop!.socialLinks;
301
+
302
+ expect(links).toHaveLength(1);
303
+ expect(links[0].id).toBe('link-2');
252
304
  });
253
305
 
254
- it('inserts exactly one link on a shared array (symmetric add guard)', async () => {
255
- const model = makeSharedSocialModel([link('facebook'), link('instagram')]);
306
+ it('_replace_social_links rebuilds the entire list and assigns fresh ids', async () => {
307
+ const model = makeModelWithLinks([
308
+ { id: 'old-1', imageUrl: 'facebook', url: 'https://fb.com' },
309
+ ]);
256
310
 
257
311
  const { model: out } = await run(
258
312
  {
259
- social_links: [{ insert_at: 1, social_link_icon: 'x', url: 'x.com' }],
313
+ _replace_social_links: true,
314
+ social_links: [
315
+ { social_link_icon: 'instagram', url: 'https://ig.com' },
316
+ { social_link_icon: 'x', url: 'https://x.com' },
317
+ ],
260
318
  },
261
319
  model,
262
320
  );
263
321
 
264
- expect(socialIcons(out.pageFooterDesktop)).toEqual([
265
- 'facebook',
266
- 'x',
267
- 'instagram',
322
+ const links = out.pageFooterDesktop!.socialLinks;
323
+
324
+ expect(links).toHaveLength(2);
325
+ expect(links[0].imageUrl).toBe('instagram');
326
+ expect(links[1].imageUrl).toBe('x');
327
+ // fresh ids assigned, not the old one
328
+ expect(links[0].id).toBeTruthy();
329
+ expect(links[0].id).not.toBe('old-1');
330
+ });
331
+
332
+ it('_replace_social_links with empty social_links clears all', async () => {
333
+ const model = makeModelWithLinks([
334
+ { id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
268
335
  ]);
269
- expect(socialIcons(out.pageFooterMobile)).toEqual([
270
- 'facebook',
271
- 'x',
272
- 'instagram',
336
+
337
+ const { model: out } = await run({ _replace_social_links: true }, model);
338
+
339
+ expect(out.pageFooterDesktop!.socialLinks).toHaveLength(0);
340
+ });
341
+
342
+ it('normalises mailto: and tel: prefixes on new links', async () => {
343
+ const model = makeModelWithLinks([]);
344
+
345
+ const { model: out } = await run(
346
+ {
347
+ social_links: [
348
+ { social_link_icon: 'email', url: 'mailto:me@example.com' },
349
+ { social_link_icon: 'phone', url: 'tel:+1234567890' },
350
+ ],
351
+ },
352
+ model,
353
+ );
354
+
355
+ const links = out.pageFooterDesktop!.socialLinks;
356
+
357
+ expect(links[0].url).toBe('me@example.com');
358
+ expect(links[1].url).toBe('+1234567890');
359
+ });
360
+
361
+ it('applies mutations to both desktop and mobile footers', async () => {
362
+ const model = makeModelWithLinks([
363
+ { id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
273
364
  ]);
365
+
366
+ const { model: out } = await run(
367
+ { _remove_social_link_ids: ['link-1'] },
368
+ model,
369
+ );
370
+
371
+ expect(out.pageFooterDesktop!.socialLinks).toHaveLength(0);
372
+ expect(out.pageFooterMobile!.socialLinks).toHaveLength(0);
274
373
  });
275
374
  });