@designcrowd/library.brand-page 5.17.28 → 5.18.0

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 (380) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-CJZpKPNH.js → CreatedOnBrandCrowd-de-de-xmkupPus.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-DkIa7iEb.js → CreatedOnBrandCrowd-en-us-Ck7EdTtb.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-CE2p6XHz.js → CreatedOnBrandCrowd-es-es-BC8sJuDH.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-BBjOZS3M.js → CreatedOnBrandCrowd-fr-ca-CN-UBe1q.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-BBjOZS3M.js → CreatedOnBrandCrowd-fr-fr-CN-UBe1q.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-Dayzl4BZ.js → CreatedOnBrandCrowd-pt-br-CajQyNkx.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-Dayzl4BZ.js → CreatedOnBrandCrowd-pt-pt-CajQyNkx.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-CFzDqfCb.js → CreatedOnDcom-de-de-BRCNMmDq.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-C2jIPBDY.js → CreatedOnDcom-en-us-BzQDtZhc.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-qejpzqho.js → CreatedOnDcom-es-es-D-qAM4Cd.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-DMO9Ld7K.js → CreatedOnDcom-fr-ca-CZ1irFOc.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-BFWN9qTP.js → CreatedOnDcom-fr-fr-Gb5BCF0N.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-CneD3ghF.js → CreatedOnDcom-pt-br-CAQV0q6o.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-Cg1B7s0o.js → CreatedOnDcom-pt-pt-D6tQdQFh.js} +1 -1
  15. package/lib/src/brand-page/components/image-gallery/ImageGalleryModal.mixin.js +2 -2
  16. package/lib/src/brand-page/models/brand-page-context.model.d.ts +0 -2
  17. package/lib/src/brand-page/models/brand-page-context.model.js +1 -11
  18. package/lib/src/brand-page/models/index.d.ts +1 -0
  19. package/lib/src/brand-page/models/index.js +1 -0
  20. package/lib/src/brand-page/utils/color.util.js +23 -3
  21. package/lib/src/brand-page/utils/raygun.util.d.ts +5 -1
  22. package/lib/src/brand-page/utils/raygun.util.js +7 -2
  23. package/lib/src/brand-page/utils/svg-color.util.js +1 -2
  24. package/lib/src/brand-page/utils/view-model.util.js +1 -1
  25. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +1 -31
  26. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +0 -2
  27. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +2 -31
  28. package/lib/src/brand-page-maker/components/preview/Preview.mixin.d.ts +0 -14
  29. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +0 -28
  30. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +0 -28
  31. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +0 -28
  32. package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +0 -28
  33. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +0 -28
  34. package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +0 -28
  35. package/lib/src/brand-page-maker/composables/ai-chat/aiErrorCopy.d.ts +35 -0
  36. package/lib/src/brand-page-maker/composables/ai-chat/aiErrorCopy.js +108 -0
  37. package/lib/src/brand-page-maker/composables/ai-chat/{useAiChat.d.ts → types.d.ts} +5 -13
  38. package/lib/src/brand-page-maker/composables/ai-chat/types.js +1 -0
  39. package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.d.ts +19 -0
  40. package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.js +1 -0
  41. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.d.ts +1 -1
  42. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +488 -82
  43. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +5 -5
  44. package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +6 -16
  45. package/lib/src/brand-page-maker/composables/useEventTracker.js +19 -21
  46. package/lib/src/brand-page-maker/constants/sidebar-constant.d.ts +0 -1
  47. package/lib/src/brand-page-maker/constants/sidebar-constant.js +7 -15
  48. package/lib/src/brand-page-maker/convertors/image-aspect-ratio.convertor.js +2 -2
  49. package/lib/src/brand-page-maker/convertors/text-size-value.convertor.js +3 -1
  50. package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +30660 -0
  51. package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.js +34 -0
  52. package/lib/src/brand-page-maker/stores/ai-thread/ai-website-builder-thread-store.d.ts +1 -1
  53. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +0 -28
  54. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +42 -2
  55. package/lib/src/brand-page-maker/stores/index.d.ts +1 -1
  56. package/lib/src/brand-page-maker/stores/index.js +1 -1
  57. package/lib/src/brand-page-maker/stores/user/user-store.js +0 -1
  58. package/lib/src/bundled-translations-CnareLWC.js +175 -0
  59. package/lib/src/bundled-translations.de-DE-2Ys7epLO.js +175 -0
  60. package/lib/src/bundled-translations.es-ES-Cjp-G8rT.js +175 -0
  61. package/lib/src/bundled-translations.fr-CA-CsaW4Oae.js +175 -0
  62. package/lib/src/bundled-translations.fr-FR-DIT2dd8J.js +175 -0
  63. package/lib/src/bundled-translations.pt-BR-BamoBpDW.js +175 -0
  64. package/lib/src/bundled-translations.pt-PT-C9A4hY_e.js +175 -0
  65. package/lib/src/bundles/bundled-translations.de-DE.json +1 -20
  66. package/lib/src/bundles/bundled-translations.es-ES.json +1 -20
  67. package/lib/src/bundles/bundled-translations.fr-CA.json +1 -20
  68. package/lib/src/bundles/bundled-translations.fr-FR.json +1 -20
  69. package/lib/src/bundles/bundled-translations.json +1 -20
  70. package/lib/src/bundles/bundled-translations.pt-BR.json +1 -20
  71. package/lib/src/bundles/bundled-translations.pt-PT.json +1 -20
  72. package/lib/src/clients/ai-assistant-client.d.ts +30 -29
  73. package/lib/src/clients/ai-assistant-client.js +16 -16
  74. package/lib/src/clients/design-template-client.d.ts +0 -2
  75. package/lib/src/clients/design-template-client.js +0 -38
  76. package/lib/src/clients/websocket-gateway-client.d.ts +9 -1
  77. package/lib/src/composables/useTranslate.d.ts +2 -1
  78. package/lib/src/composables/useTranslate.js +2 -1
  79. package/lib/src/constants/ui.d.ts +0 -1
  80. package/lib/src/css/library.brand-page-brandCrowd.min.css +225 -134
  81. package/lib/src/css/library.brand-page-brandPage.min.css +227 -121
  82. package/lib/src/css/library.brand-page-designCom.min.css +225 -134
  83. package/lib/src/css/library.brand-page-preview.min.css +225 -118
  84. package/lib/src/index.d.ts +1 -1
  85. package/lib/src/index.mjs +1 -1
  86. package/lib/src/{lib-33w_DBny.js → lib-CSRUPpSo.js} +30887 -29395
  87. package/lib/src/metadata-builder/ai-metadata-executor.d.ts +21 -0
  88. package/lib/src/metadata-builder/ai-metadata-executor.js +56 -3
  89. package/lib/src/metadata-builder/builders/add-page-builder.d.ts +2 -4
  90. package/lib/src/metadata-builder/builders/add-page-builder.js +5 -8
  91. package/lib/src/metadata-builder/builders/footer-builder.d.ts +11 -0
  92. package/lib/src/metadata-builder/builders/footer-builder.js +151 -0
  93. package/lib/src/metadata-builder/builders/global-styles-builder.d.ts +15 -0
  94. package/lib/src/metadata-builder/builders/global-styles-builder.js +129 -0
  95. package/lib/src/metadata-builder/builders/header-builder.d.ts +11 -0
  96. package/lib/src/metadata-builder/builders/header-builder.js +167 -0
  97. package/lib/src/metadata-builder/builders/rename-page-builder.d.ts +12 -0
  98. package/lib/src/metadata-builder/builders/rename-page-builder.js +49 -0
  99. package/lib/src/metadata-builder/builders/section-builders/about-section-builder.d.ts +2 -0
  100. package/lib/src/metadata-builder/builders/section-builders/about-section-builder.js +44 -4
  101. package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.d.ts +2 -0
  102. package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.js +70 -6
  103. package/lib/src/metadata-builder/builders/section-builders/base-section-builder.d.ts +36 -4
  104. package/lib/src/metadata-builder/builders/section-builders/base-section-builder.js +102 -12
  105. package/lib/src/metadata-builder/builders/section-builders/booking-section-builder.d.ts +8 -0
  106. package/lib/src/metadata-builder/builders/section-builders/booking-section-builder.js +50 -0
  107. package/lib/src/metadata-builder/builders/section-builders/button-section-builder.d.ts +2 -0
  108. package/lib/src/metadata-builder/builders/section-builders/button-section-builder.js +5 -1
  109. package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.d.ts +2 -0
  110. package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.js +47 -3
  111. package/lib/src/metadata-builder/builders/section-builders/delete-section-builder.d.ts +2 -1
  112. package/lib/src/metadata-builder/builders/section-builders/delete-section-builder.js +5 -2
  113. package/lib/src/metadata-builder/builders/section-builders/divider-section-builder.d.ts +2 -0
  114. package/lib/src/metadata-builder/builders/section-builders/divider-section-builder.js +11 -1
  115. package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.d.ts +2 -0
  116. package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.js +52 -13
  117. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.d.ts +2 -0
  118. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +47 -17
  119. package/lib/src/metadata-builder/builders/section-builders/index.d.ts +1 -0
  120. package/lib/src/metadata-builder/builders/section-builders/index.js +1 -0
  121. package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.d.ts +2 -0
  122. package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.js +20 -2
  123. package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.d.ts +2 -0
  124. package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.js +20 -2
  125. package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.d.ts +2 -0
  126. package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.js +36 -2
  127. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.d.ts +2 -0
  128. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +70 -27
  129. package/lib/src/metadata-builder/builders/section-builders/social-links-builder.d.ts +2 -0
  130. package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +7 -2
  131. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.d.ts +2 -0
  132. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +91 -26
  133. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.d.ts +2 -0
  134. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +69 -31
  135. package/lib/src/metadata-builder/builders/section-content-factory.d.ts +2 -2
  136. package/lib/src/metadata-builder/builders/section-content-factory.js +8 -2
  137. package/lib/src/metadata-builder/builders/section-field-maps.js +30 -2
  138. package/lib/src/metadata-builder/builders/section-field-utils.d.ts +46 -7
  139. package/lib/src/metadata-builder/builders/section-field-utils.js +86 -62
  140. package/lib/src/metadata-builder/builders/section-rules/about-section.rules.d.ts +2 -0
  141. package/lib/src/metadata-builder/builders/section-rules/about-section.rules.js +20 -0
  142. package/lib/src/metadata-builder/builders/section-rules/add-page.rules.d.ts +7 -0
  143. package/lib/src/metadata-builder/builders/section-rules/add-page.rules.js +8 -0
  144. package/lib/src/metadata-builder/builders/section-rules/banner-section.rules.d.ts +2 -0
  145. package/lib/src/metadata-builder/builders/section-rules/banner-section.rules.js +27 -0
  146. package/lib/src/metadata-builder/builders/section-rules/booking-section.rules.d.ts +2 -0
  147. package/lib/src/metadata-builder/builders/section-rules/booking-section.rules.js +16 -0
  148. package/lib/src/metadata-builder/builders/section-rules/button-section.rules.d.ts +2 -0
  149. package/lib/src/metadata-builder/builders/section-rules/button-section.rules.js +13 -0
  150. package/lib/src/metadata-builder/builders/section-rules/contact-form.rules.d.ts +2 -0
  151. package/lib/src/metadata-builder/builders/section-rules/contact-form.rules.js +16 -0
  152. package/lib/src/metadata-builder/builders/section-rules/divider-section.rules.d.ts +2 -0
  153. package/lib/src/metadata-builder/builders/section-rules/divider-section.rules.js +7 -0
  154. package/lib/src/metadata-builder/builders/section-rules/footer.rules.d.ts +4 -0
  155. package/lib/src/metadata-builder/builders/section-rules/footer.rules.js +5 -0
  156. package/lib/src/metadata-builder/builders/section-rules/global-styles.rules.d.ts +6 -0
  157. package/lib/src/metadata-builder/builders/section-rules/global-styles.rules.js +26 -0
  158. package/lib/src/metadata-builder/builders/section-rules/header.rules.d.ts +4 -0
  159. package/lib/src/metadata-builder/builders/section-rules/header.rules.js +41 -0
  160. package/lib/src/metadata-builder/builders/section-rules/image-gallery.rules.d.ts +2 -0
  161. package/lib/src/metadata-builder/builders/section-rules/image-gallery.rules.js +10 -0
  162. package/lib/src/metadata-builder/builders/section-rules/images-section.rules.d.ts +2 -0
  163. package/lib/src/metadata-builder/builders/section-rules/images-section.rules.js +31 -0
  164. package/lib/src/metadata-builder/builders/section-rules/mailing-list.rules.d.ts +2 -0
  165. package/lib/src/metadata-builder/builders/section-rules/mailing-list.rules.js +11 -0
  166. package/lib/src/metadata-builder/builders/section-rules/profile.rules.d.ts +2 -0
  167. package/lib/src/metadata-builder/builders/section-rules/profile.rules.js +14 -0
  168. package/lib/src/metadata-builder/builders/section-rules/rename-page.rules.d.ts +7 -0
  169. package/lib/src/metadata-builder/builders/section-rules/rename-page.rules.js +8 -0
  170. package/lib/src/metadata-builder/builders/section-rules/shapes-section.rules.d.ts +2 -0
  171. package/lib/src/metadata-builder/builders/section-rules/shapes-section.rules.js +27 -0
  172. package/lib/src/metadata-builder/builders/section-rules/social-links.rules.d.ts +2 -0
  173. package/lib/src/metadata-builder/builders/section-rules/social-links.rules.js +9 -0
  174. package/lib/src/metadata-builder/builders/section-rules/testimonial-section.rules.d.ts +7 -0
  175. package/lib/src/metadata-builder/builders/section-rules/testimonial-section.rules.js +26 -0
  176. package/lib/src/metadata-builder/builders/section-rules/text-section.rules.d.ts +2 -0
  177. package/lib/src/metadata-builder/builders/section-rules/text-section.rules.js +14 -0
  178. package/lib/src/metadata-builder/builders/shape-color-utils.d.ts +26 -0
  179. package/lib/src/metadata-builder/builders/shape-color-utils.js +52 -0
  180. package/lib/src/metadata-builder/builders/undo-builder.d.ts +48 -0
  181. package/lib/src/metadata-builder/builders/undo-builder.js +58 -0
  182. package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +5 -1
  183. package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +15 -4
  184. package/lib/src/metadata-builder/helpers/rte.helpers.js +24 -10
  185. package/lib/src/metadata-builder/metadata-executor.d.ts +2 -1
  186. package/lib/src/metadata-builder/metadata-executor.js +32 -5
  187. package/lib/src/metadata-builder/models.d.ts +26 -1
  188. package/lib/src/metadata-builder/models.js +14 -1
  189. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +144 -0
  190. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +17 -0
  191. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +113 -24
  192. package/lib/src/metadata-builder/parsers/section-tool-arguments.js +5 -1
  193. package/lib/src/metadata-builder/sanitizers/rte-schema-check.d.ts +22 -0
  194. package/lib/src/metadata-builder/sanitizers/rte-schema-check.js +84 -0
  195. package/lib/src/metadata-builder/sanitizers/sanitizer-rules.d.ts +47 -0
  196. package/lib/src/metadata-builder/sanitizers/sanitizer-rules.js +60 -0
  197. package/lib/src/metadata-builder/sanitizers/section-args-validator.d.ts +41 -0
  198. package/lib/src/metadata-builder/sanitizers/section-args-validator.js +274 -0
  199. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +2 -1
  200. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.js +2 -16
  201. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.stories.js +0 -1
  202. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +2 -1
  203. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +2 -3
  204. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +0 -4
  205. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +6 -8
  206. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +0 -28
  207. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +2 -1
  208. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +0 -28
  209. package/lib/src/shared/navigation-style-settings/store/index.js +1 -0
  210. package/lib/src/shared/page-navigation/store/page-navigation-store.js +1 -20
  211. package/lib/src/shared/page-navigation/utils/nav-order-utils.d.ts +3 -0
  212. package/lib/src/shared/page-navigation/utils/nav-order-utils.js +20 -0
  213. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +2 -30
  214. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +0 -28
  215. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +2 -43
  216. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +0 -30
  217. package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +2 -3
  218. package/lib/src/shared/side-bar/store/side-bar-store.d.ts +0 -12
  219. package/lib/src/shared/side-bar/store/side-bar-store.js +0 -7
  220. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +2 -29
  221. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +2 -1
  222. package/lib/src/utils/build-asset-manifest.d.ts +26 -0
  223. package/lib/src/utils/build-asset-manifest.js +114 -0
  224. package/lib/src/utils/minify-brand-page-metadata.d.ts +49 -1
  225. package/lib/src/utils/minify-brand-page-metadata.js +216 -129
  226. package/package.json +1 -1
  227. package/src/brand-page/components/banner-section/BannerSection.vue +1 -1
  228. package/src/brand-page/components/contact-form/ContactForm.vue +29 -26
  229. package/src/brand-page/components/image-gallery/ImageGalleryModal.mixin.ts +6 -2
  230. package/src/brand-page/components/text-media-section/TextMediaSection.vue +1 -5
  231. package/src/brand-page/models/brand-page-context.model.ts +1 -25
  232. package/src/brand-page/models/index.ts +1 -0
  233. package/src/brand-page/utils/color.util.test.ts +48 -0
  234. package/src/brand-page/utils/color.util.ts +29 -4
  235. package/src/brand-page/utils/raygun.util.ts +11 -2
  236. package/src/brand-page/utils/svg-color.util.ts +1 -3
  237. package/src/brand-page/utils/view-model.util.ts +1 -4
  238. package/src/brand-page-maker/components/ai-text-edit/AiChatbox.vue +2 -2
  239. package/src/brand-page-maker/components/ai-text-edit/AiProgressFeed.vue +162 -0
  240. package/src/brand-page-maker/components/ai-text-edit/TextBubble.vue +57 -5
  241. package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.de-DE.json +0 -20
  242. package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.es-ES.json +0 -20
  243. package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.fr-CA.json +0 -20
  244. package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.fr-FR.json +0 -20
  245. package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.json +0 -20
  246. package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.pt-BR.json +0 -20
  247. package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.pt-PT.json +0 -20
  248. package/src/brand-page-maker/components/ai-website-builder/AiWebsiteBuilder.vue +1 -2
  249. package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.de-DE.json +3 -0
  250. package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.es-ES.json +3 -0
  251. package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.fr-CA.json +3 -0
  252. package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.fr-FR.json +3 -0
  253. package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.json +3 -0
  254. package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.pt-BR.json +3 -0
  255. package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.pt-PT.json +3 -0
  256. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.vue +3 -28
  257. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +0 -2
  258. package/src/brand-page-maker/composables/ai-chat/aiErrorCopy.test.ts +229 -0
  259. package/src/brand-page-maker/composables/ai-chat/aiErrorCopy.ts +164 -0
  260. package/src/brand-page-maker/composables/ai-chat/types.ts +49 -0
  261. package/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.ts +20 -0
  262. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +651 -94
  263. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +9 -9
  264. package/src/brand-page-maker/composables/useEventTracker.ts +31 -25
  265. package/src/brand-page-maker/constants/sidebar-constant.ts +7 -15
  266. package/src/brand-page-maker/convertors/image-aspect-ratio.convertor.ts +2 -2
  267. package/src/brand-page-maker/convertors/text-size-value.convertor.ts +3 -3
  268. package/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.ts +54 -0
  269. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +76 -0
  270. package/src/brand-page-maker/stores/index.ts +1 -1
  271. package/src/brand-page-maker/stores/user/user-store.ts +0 -1
  272. package/src/bundles/bundled-translations.de-DE.json +1 -20
  273. package/src/bundles/bundled-translations.es-ES.json +1 -20
  274. package/src/bundles/bundled-translations.fr-CA.json +1 -20
  275. package/src/bundles/bundled-translations.fr-FR.json +1 -20
  276. package/src/bundles/bundled-translations.json +1 -20
  277. package/src/bundles/bundled-translations.pt-BR.json +1 -20
  278. package/src/bundles/bundled-translations.pt-PT.json +1 -20
  279. package/src/clients/ai-assistant-client.ts +59 -46
  280. package/src/clients/design-template-client.ts +0 -66
  281. package/src/clients/websocket-gateway-client.ts +17 -1
  282. package/src/composables/useTranslate.ts +3 -0
  283. package/src/constants/ui.ts +0 -1
  284. package/src/lib.ts +1 -0
  285. package/src/metadata-builder/ai-metadata-executor.ts +94 -1
  286. package/src/metadata-builder/builders/add-page-builder.ts +9 -13
  287. package/src/metadata-builder/builders/footer-builder.test.ts +76 -0
  288. package/src/metadata-builder/builders/footer-builder.ts +226 -0
  289. package/src/metadata-builder/builders/global-styles-builder.ts +187 -0
  290. package/src/metadata-builder/builders/header-builder.test.ts +106 -0
  291. package/src/metadata-builder/builders/header-builder.ts +254 -0
  292. package/src/metadata-builder/builders/rename-page-builder.ts +79 -0
  293. package/src/metadata-builder/builders/section-builders/about-section-builder.ts +73 -5
  294. package/src/metadata-builder/builders/section-builders/banner-section-builder.ts +112 -7
  295. package/src/metadata-builder/builders/section-builders/base-section-builder.ts +149 -11
  296. package/src/metadata-builder/builders/section-builders/booking-section-builder.ts +87 -0
  297. package/src/metadata-builder/builders/section-builders/button-section-builder.ts +6 -1
  298. package/src/metadata-builder/builders/section-builders/contact-form-builder.ts +74 -4
  299. package/src/metadata-builder/builders/section-builders/delete-section-builder.ts +15 -1
  300. package/src/metadata-builder/builders/section-builders/divider-section-builder.ts +13 -1
  301. package/src/metadata-builder/builders/section-builders/image-gallery-builder.ts +91 -31
  302. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +102 -48
  303. package/src/metadata-builder/builders/section-builders/index.ts +1 -0
  304. package/src/metadata-builder/builders/section-builders/mailing-list-builder.ts +38 -2
  305. package/src/metadata-builder/builders/section-builders/mailing-list-section-builder.ts +38 -2
  306. package/src/metadata-builder/builders/section-builders/profile-section-builder.ts +55 -3
  307. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +170 -77
  308. package/src/metadata-builder/builders/section-builders/social-links-builder.test.ts +119 -0
  309. package/src/metadata-builder/builders/section-builders/social-links-builder.ts +13 -2
  310. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +178 -57
  311. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +130 -30
  312. package/src/metadata-builder/builders/section-content-factory.ts +15 -2
  313. package/src/metadata-builder/builders/section-field-maps.ts +34 -1
  314. package/src/metadata-builder/builders/section-field-utils.ts +131 -72
  315. package/src/metadata-builder/builders/section-rules/about-section.rules.ts +38 -0
  316. package/src/metadata-builder/builders/section-rules/add-page.rules.ts +17 -0
  317. package/src/metadata-builder/builders/section-rules/banner-section.rules.ts +49 -0
  318. package/src/metadata-builder/builders/section-rules/booking-section.rules.ts +34 -0
  319. package/src/metadata-builder/builders/section-rules/button-section.rules.ts +22 -0
  320. package/src/metadata-builder/builders/section-rules/contact-form.rules.ts +30 -0
  321. package/src/metadata-builder/builders/section-rules/divider-section.rules.ts +17 -0
  322. package/src/metadata-builder/builders/section-rules/footer.rules.ts +21 -0
  323. package/src/metadata-builder/builders/section-rules/global-styles.rules.ts +44 -0
  324. package/src/metadata-builder/builders/section-rules/header.rules.ts +75 -0
  325. package/src/metadata-builder/builders/section-rules/image-gallery.rules.ts +22 -0
  326. package/src/metadata-builder/builders/section-rules/images-section.rules.ts +55 -0
  327. package/src/metadata-builder/builders/section-rules/mailing-list.rules.ts +21 -0
  328. package/src/metadata-builder/builders/section-rules/profile.rules.ts +27 -0
  329. package/src/metadata-builder/builders/section-rules/rename-page.rules.ts +17 -0
  330. package/src/metadata-builder/builders/section-rules/shapes-section.rules.ts +51 -0
  331. package/src/metadata-builder/builders/section-rules/social-links.rules.ts +27 -0
  332. package/src/metadata-builder/builders/section-rules/testimonial-section.rules.ts +48 -0
  333. package/src/metadata-builder/builders/section-rules/text-section.rules.ts +28 -0
  334. package/src/metadata-builder/builders/shape-color-utils.test.ts +115 -0
  335. package/src/metadata-builder/builders/shape-color-utils.ts +75 -0
  336. package/src/metadata-builder/builders/undo-builder.ts +105 -0
  337. package/src/metadata-builder/builders/update-content-text-style-builder.ts +4 -1
  338. package/src/metadata-builder/helpers/rte.helpers.ts +27 -13
  339. package/src/metadata-builder/metadata-executor.ts +42 -2
  340. package/src/metadata-builder/models.ts +39 -1
  341. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +152 -0
  342. package/src/metadata-builder/parsers/section-tool-arguments.ts +138 -24
  343. package/src/metadata-builder/sanitizers/__tests__/section-args-validator.test.ts +659 -0
  344. package/src/metadata-builder/sanitizers/rte-schema-check.ts +99 -0
  345. package/src/metadata-builder/sanitizers/sanitizer-rules.ts +97 -0
  346. package/src/metadata-builder/sanitizers/section-args-validator.ts +445 -0
  347. package/src/shared/content-settings/components/content-settings/ContentSettings.mixin.ts +10 -16
  348. package/src/shared/content-settings/components/content-settings/ContentSettings.stories.ts +0 -1
  349. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +6 -10
  350. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.vue +1 -23
  351. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.vue +0 -15
  352. package/src/shared/content-settings/components/text-items-field/TextItemsField.vue +0 -15
  353. package/src/shared/navigation-style-settings/store/index.ts +5 -0
  354. package/src/shared/page-navigation/store/page-navigation-store.ts +4 -31
  355. package/src/shared/page-navigation/utils/nav-order-utils.ts +32 -0
  356. package/src/shared/side-bar/components/SideBar.vue +14 -1
  357. package/src/shared/side-bar/store/side-bar-store.ts +0 -20
  358. package/src/utils/build-asset-manifest.ts +153 -0
  359. package/src/utils/minify-brand-page-metadata.ts +362 -130
  360. package/lib/src/brand-page-maker/composables/ai-chat/useAiChat.js +0 -368
  361. package/lib/src/brand-page-maker/composables/ai-chat/useAiTextEditPrompts.d.ts +0 -18
  362. package/lib/src/brand-page-maker/composables/ai-chat/useAiTextEditPrompts.js +0 -144
  363. package/lib/src/brand-page-maker/stores/ai-thread/ai-thread-store.d.ts +0 -155
  364. package/lib/src/brand-page-maker/stores/ai-thread/ai-thread-store.js +0 -25
  365. package/lib/src/brand-page-maker/utils/mixpanel.util.d.ts +0 -1
  366. package/lib/src/brand-page-maker/utils/mixpanel.util.js +0 -29
  367. package/lib/src/bundled-translations-CNK7l6_v.js +0 -173
  368. package/lib/src/bundled-translations.de-DE-BLE4_y0H.js +0 -173
  369. package/lib/src/bundled-translations.es-ES-thO9osbg.js +0 -173
  370. package/lib/src/bundled-translations.fr-CA-BZkhiNgU.js +0 -173
  371. package/lib/src/bundled-translations.fr-FR-bhDLPKgZ.js +0 -173
  372. package/lib/src/bundled-translations.pt-BR-CI9a5Bq1.js +0 -173
  373. package/lib/src/bundled-translations.pt-PT-DBoErBaq.js +0 -173
  374. package/src/brand-page-maker/components/ai-text-edit/AiTextEdit.vue +0 -154
  375. package/src/brand-page-maker/composables/ai-chat/useAiChat.ts +0 -562
  376. package/src/brand-page-maker/composables/ai-chat/useAiTextEditPrompts.ts +0 -196
  377. package/src/brand-page-maker/stores/ai-thread/ai-thread-store.ts +0 -31
  378. package/src/brand-page-maker/utils/mixpanel.util.ts +0 -42
  379. package/src/shared/content-settings/components/generate-ai-button/GenerateAiButton.vue +0 -114
  380. package/src/shared/content-settings/components/generate-ai-modal/GenerateAiModal.vue +0 -761
@@ -0,0 +1,659 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { TiptapDocument } from '../../parsers/section-tool-arguments';
3
+ import { validateBannerSection } from '../../builders/section-rules/banner-section.rules';
4
+ import { validateImagesSection } from '../../builders/section-rules/images-section.rules';
5
+ import { validateTestimonialSection } from '../../builders/section-rules/testimonial-section.rules';
6
+ import { validateSocialLinks } from '../../builders/section-rules/social-links.rules';
7
+ import { validateTextSection } from '../../builders/section-rules/text-section.rules';
8
+ import { validateMailingList } from '../../builders/section-rules/mailing-list.rules';
9
+
10
+ // ─── helpers ─────────────────────────────────────────────────────────────────
11
+
12
+ const str = (len: number) => 'a'.repeat(len);
13
+
14
+ const tiptap = (text: string): TiptapDocument => ({
15
+ type: 'doc',
16
+ content: [
17
+ {
18
+ type: 'paragraph',
19
+ content: [{ type: 'text', text }],
20
+ },
21
+ ],
22
+ });
23
+
24
+ // ─── text field limits ────────────────────────────────────────────────────────
25
+
26
+ describe('text field limits', () => {
27
+ it('passes when field is exactly at the limit', () => {
28
+ const errors = validateBannerSection({ title: str(200) } as any, {});
29
+
30
+ expect(errors).toHaveLength(0);
31
+ });
32
+
33
+ it('errors when field exceeds the limit', () => {
34
+ const errors = validateBannerSection({ title: str(201) } as any, {});
35
+
36
+ expect(errors).toHaveLength(1);
37
+ expect(errors[0]).toMatch(/"title"/);
38
+ expect(errors[0]).toMatch(/200/);
39
+ expect(errors[0]).toMatch(/201/);
40
+ });
41
+
42
+ it('skips fields that are null or undefined', () => {
43
+ const errors = validateBannerSection(
44
+ { title: null, description: undefined } as any,
45
+ {},
46
+ );
47
+
48
+ expect(errors).toHaveLength(0);
49
+ });
50
+
51
+ it('checks multiple fields and reports all violations', () => {
52
+ const errors = validateBannerSection(
53
+ { title: str(201), description: str(1001) } as any,
54
+ {},
55
+ );
56
+
57
+ expect(errors).toHaveLength(2);
58
+ });
59
+ });
60
+
61
+ // ─── tiptap document text extraction ─────────────────────────────────────────
62
+
63
+ describe('TiptapDocument text extraction', () => {
64
+ it('extracts plain text and checks against the limit', () => {
65
+ const errors = validateBannerSection(
66
+ { title: tiptap(str(201)) } as any,
67
+ {},
68
+ );
69
+
70
+ expect(errors).toHaveLength(1);
71
+ expect(errors[0]).toMatch(/"title"/);
72
+ });
73
+
74
+ it('passes when tiptap text is within limit', () => {
75
+ const errors = validateBannerSection(
76
+ { title: tiptap(str(200)) } as any,
77
+ {},
78
+ );
79
+
80
+ expect(errors).toHaveLength(0);
81
+ });
82
+
83
+ it('measures text content — not the JSON serialisation size', () => {
84
+ // The tiptap JSON wrapper is much larger than the text payload.
85
+ // Validation must count only the extracted text characters.
86
+ const shortText = 'hello';
87
+ const errors = validateBannerSection(
88
+ { title: tiptap(shortText) } as any,
89
+ {},
90
+ );
91
+
92
+ expect(errors).toHaveLength(0);
93
+ });
94
+ });
95
+
96
+ // ─── item array text field limits ────────────────────────────────────────────
97
+
98
+ describe('item array text field limits', () => {
99
+ it('errors when an item field exceeds the limit and includes the index', () => {
100
+ const errors = validateImagesSection(
101
+ { items: [{ heading: str(200) }, { heading: str(201) }] } as any,
102
+ {},
103
+ );
104
+
105
+ expect(errors).toHaveLength(1);
106
+ expect(errors[0]).toMatch(/items\[1\]\.heading/);
107
+ });
108
+
109
+ it('passes when all item fields are within limits', () => {
110
+ const errors = validateTestimonialSection(
111
+ {
112
+ items: [{ testimonial: str(1000), name: str(200), position: str(200) }],
113
+ } as any,
114
+ {},
115
+ );
116
+
117
+ expect(errors).toHaveLength(0);
118
+ });
119
+
120
+ it('skips the item array check when the field is absent', () => {
121
+ const errors = validateImagesSection({} as any, {});
122
+
123
+ expect(errors).toHaveLength(0);
124
+ });
125
+ });
126
+
127
+ // ─── field enum validation ────────────────────────────────────────────────────
128
+
129
+ describe('field enum validation', () => {
130
+ it('errors on an invalid display_style', () => {
131
+ const errors = validateBannerSection(
132
+ { display_style: 'invalid-style' } as any,
133
+ {},
134
+ );
135
+
136
+ expect(errors).toHaveLength(1);
137
+ expect(errors[0]).toMatch(/"display_style"/);
138
+ expect(errors[0]).toMatch(/invalid-style/);
139
+ expect(errors[0]).toMatch(/Valid values:/);
140
+ });
141
+
142
+ it('passes on a valid display_style', () => {
143
+ const errors = validateBannerSection(
144
+ { display_style: 'centered' } as any,
145
+ {},
146
+ );
147
+
148
+ expect(errors).toHaveLength(0);
149
+ });
150
+
151
+ it('validates icon_style for social-links', () => {
152
+ const errors = validateSocialLinks(
153
+ { icon_style: 'not-a-real-style' } as any,
154
+ {},
155
+ );
156
+
157
+ expect(errors).toHaveLength(1);
158
+ expect(errors[0]).toMatch(/"icon_style"/);
159
+ });
160
+
161
+ it('validates column_style for text sections', () => {
162
+ const errors = validateTextSection({ column_style: '5' } as any, {});
163
+
164
+ expect(errors).toHaveLength(1);
165
+ expect(errors[0]).toMatch(/"column_style"/);
166
+ expect(errors[0]).toMatch(/Valid values: 1, 2, 3, 4/);
167
+ });
168
+
169
+ it('validates aspect_ratio for images-section', () => {
170
+ const errors = validateImagesSection(
171
+ { aspect_ratio: 'fake-ratio' } as any,
172
+ {},
173
+ );
174
+
175
+ expect(errors).toHaveLength(1);
176
+ expect(errors[0]).toMatch(/"aspect_ratio"/);
177
+ });
178
+
179
+ it('validates accepted_fields for mailing-list', () => {
180
+ const errors = validateMailingList(
181
+ { accepted_fields: 'phoneAndEmail' } as any,
182
+ {},
183
+ );
184
+
185
+ expect(errors).toHaveLength(1);
186
+ expect(errors[0]).toMatch(/"accepted_fields"/);
187
+ expect(errors[0]).toMatch(/emailOnly, nameAndEmail/);
188
+ });
189
+
190
+ it('skips enum check when field is null', () => {
191
+ const errors = validateBannerSection({ display_style: null } as any, {});
192
+
193
+ expect(errors).toHaveLength(0);
194
+ });
195
+ });
196
+
197
+ // ─── empty args ───────────────────────────────────────────────────────────────
198
+
199
+ describe('empty args', () => {
200
+ it('returns no errors when no args are provided', () => {
201
+ const errors = validateBannerSection({}, {});
202
+
203
+ expect(errors).toHaveLength(0);
204
+ });
205
+ });
206
+
207
+ // ─── style field validation ───────────────────────────────────────────────────
208
+
209
+ describe('style field validation', () => {
210
+ describe('fontSize unit enforcement', () => {
211
+ it('errors when fontSize uses px units', () => {
212
+ const errors = validateBannerSection(
213
+ { title_style: { fontSize: '16px' } } as any,
214
+ {},
215
+ );
216
+
217
+ expect(errors).toHaveLength(1);
218
+ expect(errors[0]).toMatch(/"title_style\.fontSize"/);
219
+ expect(errors[0]).toMatch(/em/);
220
+ });
221
+
222
+ it('errors when fontSize uses rem units', () => {
223
+ const errors = validateBannerSection(
224
+ { title_style: { fontSize: '1rem' } } as any,
225
+ {},
226
+ );
227
+
228
+ expect(errors).toHaveLength(1);
229
+ expect(errors[0]).toMatch(/"title_style\.fontSize"/);
230
+ });
231
+
232
+ it('passes when fontSize uses em units within bounds', () => {
233
+ const errors = validateBannerSection(
234
+ { title_style: { fontSize: '1em' } } as any,
235
+ {},
236
+ );
237
+
238
+ expect(errors).toHaveLength(0);
239
+ });
240
+
241
+ it('errors when fontSize em value is below the minimum', () => {
242
+ const errors = validateBannerSection(
243
+ { title_style: { fontSize: '0.1em' } } as any,
244
+ {},
245
+ );
246
+
247
+ expect(errors).toHaveLength(1);
248
+ expect(errors[0]).toMatch(/out of bounds/);
249
+ });
250
+
251
+ it('errors when fontSize em value exceeds the maximum', () => {
252
+ const errors = validateBannerSection(
253
+ { title_style: { fontSize: '100em' } } as any,
254
+ {},
255
+ );
256
+
257
+ expect(errors).toHaveLength(1);
258
+ expect(errors[0]).toMatch(/out of bounds/);
259
+ });
260
+
261
+ it('skips fontSize validation when value is null', () => {
262
+ const errors = validateBannerSection(
263
+ { title_style: { fontSize: null } } as any,
264
+ {},
265
+ );
266
+
267
+ expect(errors).toHaveLength(0);
268
+ });
269
+ });
270
+
271
+ describe('lineHeight bounds', () => {
272
+ it('errors when lineHeight is below minimum', () => {
273
+ const errors = validateBannerSection(
274
+ { title_style: { lineHeight: 0.5 } } as any,
275
+ {},
276
+ );
277
+
278
+ expect(errors).toHaveLength(1);
279
+ expect(errors[0]).toMatch(/"title_style\.lineHeight"/);
280
+ expect(errors[0]).toMatch(/out of bounds/);
281
+ });
282
+
283
+ it('errors when lineHeight exceeds maximum', () => {
284
+ const errors = validateBannerSection(
285
+ { title_style: { lineHeight: 3 } } as any,
286
+ {},
287
+ );
288
+
289
+ expect(errors).toHaveLength(1);
290
+ expect(errors[0]).toMatch(/out of bounds/);
291
+ });
292
+
293
+ it('passes for a valid lineHeight', () => {
294
+ const errors = validateBannerSection(
295
+ { title_style: { lineHeight: 1.5 } } as any,
296
+ {},
297
+ );
298
+
299
+ expect(errors).toHaveLength(0);
300
+ });
301
+ });
302
+
303
+ describe('letterSpacing bounds', () => {
304
+ it('errors when letterSpacing is below minimum', () => {
305
+ const errors = validateBannerSection(
306
+ { title_style: { letterSpacing: -1 } } as any,
307
+ {},
308
+ );
309
+
310
+ expect(errors).toHaveLength(1);
311
+ expect(errors[0]).toMatch(/"title_style\.letterSpacing"/);
312
+ });
313
+
314
+ it('errors when letterSpacing exceeds maximum', () => {
315
+ const errors = validateBannerSection(
316
+ { title_style: { letterSpacing: 101 } } as any,
317
+ {},
318
+ );
319
+
320
+ expect(errors).toHaveLength(1);
321
+ });
322
+
323
+ it('passes for a valid letterSpacing', () => {
324
+ const errors = validateBannerSection(
325
+ { title_style: { letterSpacing: 50 } } as any,
326
+ {},
327
+ );
328
+
329
+ expect(errors).toHaveLength(0);
330
+ });
331
+ });
332
+
333
+ describe('fontFamily validity', () => {
334
+ it('errors when fontFamily is not a recognized name', () => {
335
+ const errors = validateBannerSection(
336
+ { title_style: { fontFamily: 'Comic Sans' } } as any,
337
+ {},
338
+ );
339
+
340
+ expect(errors).toHaveLength(1);
341
+ expect(errors[0]).toMatch(/"title_style\.fontFamily"/);
342
+ expect(errors[0]).toMatch(/not a recognized font family/);
343
+ });
344
+
345
+ it('skips fontFamily validation when value is null', () => {
346
+ const errors = validateBannerSection(
347
+ { title_style: { fontFamily: null } } as any,
348
+ {},
349
+ );
350
+
351
+ expect(errors).toHaveLength(0);
352
+ });
353
+ });
354
+
355
+ it('skips style validation when the style field is not an object', () => {
356
+ const errors = validateBannerSection({ title_style: null } as any, {});
357
+
358
+ expect(errors).toHaveLength(0);
359
+ });
360
+ });
361
+
362
+ // ─── item cap rules ───────────────────────────────────────────────────────────
363
+
364
+ describe('item cap rules', () => {
365
+ const makeItems = (count: number) =>
366
+ Array.from({ length: count }, (_, i) => ({
367
+ image_url: `https://example.com/${i}.jpg`,
368
+ }));
369
+
370
+ describe('create mode', () => {
371
+ it('errors when items exceed the brandSite cap', () => {
372
+ const errors = validateImagesSection({ items: makeItems(6) } as any, {
373
+ brandPageType: 'brandSite',
374
+ mode: 'create',
375
+ });
376
+
377
+ expect(errors).toHaveLength(1);
378
+ expect(errors[0]).toMatch(/"items"/);
379
+ expect(errors[0]).toMatch(/6 items/);
380
+ expect(errors[0]).toMatch(/maximum of 5/);
381
+ });
382
+
383
+ it('passes when items are exactly at the cap', () => {
384
+ const errors = validateImagesSection({ items: makeItems(5) } as any, {
385
+ brandPageType: 'brandSite',
386
+ mode: 'create',
387
+ });
388
+
389
+ expect(errors).toHaveLength(0);
390
+ });
391
+
392
+ it('errors when items exceed the brandPage cap (lower than brandSite)', () => {
393
+ const errors = validateImagesSection({ items: makeItems(3) } as any, {
394
+ brandPageType: 'default',
395
+ mode: 'create',
396
+ });
397
+
398
+ expect(errors).toHaveLength(1);
399
+ expect(errors[0]).toMatch(/maximum of 2/);
400
+ });
401
+ });
402
+
403
+ describe('update + _replace_items', () => {
404
+ it('errors when replacement items exceed the cap', () => {
405
+ const errors = validateTestimonialSection(
406
+ { items: makeItems(4), _replace_items: true } as any,
407
+ { brandPageType: 'brandSite', mode: 'update' },
408
+ );
409
+
410
+ expect(errors).toHaveLength(1);
411
+ expect(errors[0]).toMatch(/maximum of 3/);
412
+ });
413
+
414
+ it('skips cap check on update without _replace_items', () => {
415
+ const errors = validateTestimonialSection(
416
+ { items: makeItems(10) } as any,
417
+ { brandPageType: 'brandSite', mode: 'update' },
418
+ );
419
+
420
+ expect(errors).toHaveLength(0);
421
+ });
422
+ });
423
+
424
+ it('checks the correct array field (links for social-links)', () => {
425
+ const errors = validateSocialLinks({ links: makeItems(5) } as any, {
426
+ brandPageType: 'brandSite',
427
+ mode: 'create',
428
+ });
429
+
430
+ expect(errors).toHaveLength(1);
431
+ expect(errors[0]).toMatch(/"links"/);
432
+ expect(errors[0]).toMatch(/maximum of 4/);
433
+ });
434
+
435
+ it('skips cap check when brandPageType is not provided', () => {
436
+ const errors = validateImagesSection({ items: makeItems(99) } as any, {
437
+ mode: 'create',
438
+ });
439
+
440
+ expect(errors).toHaveLength(0);
441
+ });
442
+
443
+ it('skips cap check for sections with no cap rule', () => {
444
+ const errors = validateBannerSection({ items: makeItems(99) } as any, {
445
+ brandPageType: 'brandSite',
446
+ mode: 'create',
447
+ });
448
+
449
+ expect(errors).toHaveLength(0);
450
+ });
451
+ });
452
+
453
+ // ─── RTE inline mark style validation ────────────────────────────────────────
454
+
455
+ const rteDoc = (
456
+ marks: { type: string; attrs?: Record<string, unknown> }[],
457
+ ): TiptapDocument => ({
458
+ type: 'doc',
459
+ content: [
460
+ {
461
+ type: 'paragraph',
462
+ content: [{ type: 'text', text: 'hello', marks } as any],
463
+ },
464
+ ],
465
+ });
466
+
467
+ const textStyleMark = (attrs: Record<string, unknown>) => ({
468
+ type: 'textStyle',
469
+ attrs,
470
+ });
471
+
472
+ describe('RTE inline mark style validation', () => {
473
+ it('passes when the field is a plain string (not a TiptapDocument)', () => {
474
+ const errors = validateBannerSection({ title: 'plain string' } as any, {});
475
+
476
+ expect(errors).toHaveLength(0);
477
+ });
478
+
479
+ it('passes when TiptapDocument has no textStyle marks', () => {
480
+ const errors = validateBannerSection(
481
+ { title: rteDoc([{ type: 'bold' }]) } as any,
482
+ {},
483
+ );
484
+
485
+ expect(errors).toHaveLength(0);
486
+ });
487
+
488
+ it('passes when all textStyle mark attrs are valid', () => {
489
+ const errors = validateBannerSection(
490
+ {
491
+ title: rteDoc([
492
+ textStyleMark({
493
+ fontSize: '1em',
494
+ fontFamily: 'Playfair Display',
495
+ lineHeight: '1.5',
496
+ letterSpacing: '0.05em',
497
+ }),
498
+ ]),
499
+ } as any,
500
+ {},
501
+ );
502
+
503
+ expect(errors).toHaveLength(0);
504
+ });
505
+
506
+ it('passes when textStyle mark has no attrs', () => {
507
+ const errors = validateBannerSection(
508
+ { title: rteDoc([{ type: 'textStyle' }]) } as any,
509
+ {},
510
+ );
511
+
512
+ expect(errors).toHaveLength(0);
513
+ });
514
+
515
+ describe('fontSize in textStyle mark', () => {
516
+ it('errors when fontSize uses px units', () => {
517
+ const errors = validateBannerSection(
518
+ { title: rteDoc([textStyleMark({ fontSize: '16px' })]) } as any,
519
+ {},
520
+ );
521
+
522
+ expect(errors).toHaveLength(1);
523
+ expect(errors[0]).toMatch(/fontSize/);
524
+ expect(errors[0]).toMatch(/em/);
525
+ });
526
+
527
+ it('errors when fontSize is out of bounds', () => {
528
+ const errors = validateBannerSection(
529
+ { title: rteDoc([textStyleMark({ fontSize: '100em' })]) } as any,
530
+ {},
531
+ );
532
+
533
+ expect(errors).toHaveLength(1);
534
+ expect(errors[0]).toMatch(/out of bounds/);
535
+ });
536
+
537
+ it('passes when fontSize is valid em within bounds', () => {
538
+ const errors = validateBannerSection(
539
+ { title: rteDoc([textStyleMark({ fontSize: '1.5em' })]) } as any,
540
+ {},
541
+ );
542
+
543
+ expect(errors).toHaveLength(0);
544
+ });
545
+ });
546
+
547
+ describe('fontFamily in textStyle mark', () => {
548
+ it('errors when fontFamily is not in the library', () => {
549
+ const errors = validateBannerSection(
550
+ {
551
+ title: rteDoc([textStyleMark({ fontFamily: 'Comic Sans MS' })]),
552
+ } as any,
553
+ {},
554
+ );
555
+
556
+ expect(errors).toHaveLength(1);
557
+ expect(errors[0]).toMatch(/fontFamily/);
558
+ expect(errors[0]).toMatch(/not a recognized font family/);
559
+ });
560
+
561
+ it('passes when fontFamily is a valid library font', () => {
562
+ const errors = validateBannerSection(
563
+ {
564
+ title: rteDoc([textStyleMark({ fontFamily: 'Playfair Display' })]),
565
+ } as any,
566
+ {},
567
+ );
568
+
569
+ expect(errors).toHaveLength(0);
570
+ });
571
+ });
572
+
573
+ describe('lineHeight in textStyle mark', () => {
574
+ it('errors when lineHeight has units (e.g. "1.5em")', () => {
575
+ const errors = validateBannerSection(
576
+ { title: rteDoc([textStyleMark({ lineHeight: '1.5em' })]) } as any,
577
+ {},
578
+ );
579
+
580
+ expect(errors).toHaveLength(1);
581
+ expect(errors[0]).toMatch(/lineHeight/);
582
+ expect(errors[0]).toMatch(/unitless/);
583
+ });
584
+
585
+ it('errors when lineHeight is out of bounds', () => {
586
+ const errors = validateBannerSection(
587
+ { title: rteDoc([textStyleMark({ lineHeight: '3' })]) } as any,
588
+ {},
589
+ );
590
+
591
+ expect(errors).toHaveLength(1);
592
+ expect(errors[0]).toMatch(/out of bounds/);
593
+ });
594
+
595
+ it('passes when lineHeight is a valid unitless number', () => {
596
+ const errors = validateBannerSection(
597
+ { title: rteDoc([textStyleMark({ lineHeight: '1.5' })]) } as any,
598
+ {},
599
+ );
600
+
601
+ expect(errors).toHaveLength(0);
602
+ });
603
+ });
604
+
605
+ describe('letterSpacing in textStyle mark', () => {
606
+ it('errors when letterSpacing uses % units instead of em', () => {
607
+ const errors = validateBannerSection(
608
+ { title: rteDoc([textStyleMark({ letterSpacing: '5%' })]) } as any,
609
+ {},
610
+ );
611
+
612
+ expect(errors).toHaveLength(1);
613
+ expect(errors[0]).toMatch(/letterSpacing/);
614
+ expect(errors[0]).toMatch(/em/);
615
+ });
616
+
617
+ it('errors when letterSpacing is out of bounds', () => {
618
+ const errors = validateBannerSection(
619
+ { title: rteDoc([textStyleMark({ letterSpacing: '2em' })]) } as any,
620
+ {},
621
+ );
622
+
623
+ expect(errors).toHaveLength(1);
624
+ expect(errors[0]).toMatch(/out of bounds/);
625
+ });
626
+
627
+ it('passes when letterSpacing is valid em within bounds', () => {
628
+ const errors = validateBannerSection(
629
+ { title: rteDoc([textStyleMark({ letterSpacing: '0.05em' })]) } as any,
630
+ {},
631
+ );
632
+
633
+ expect(errors).toHaveLength(0);
634
+ });
635
+
636
+ it('passes when letterSpacing is "0" (unitless zero is valid CSS)', () => {
637
+ const errors = validateBannerSection(
638
+ { title: rteDoc([textStyleMark({ letterSpacing: '0' })]) } as any,
639
+ {},
640
+ );
641
+
642
+ expect(errors).toHaveLength(0);
643
+ });
644
+ });
645
+
646
+ it('errors when an item array text field contains an invalid textStyle mark', () => {
647
+ const errors = validateImagesSection(
648
+ {
649
+ items: [{ heading: rteDoc([textStyleMark({ fontSize: '99em' })]) }],
650
+ } as any,
651
+ {},
652
+ );
653
+
654
+ expect(errors).toHaveLength(1);
655
+ expect(errors[0]).toMatch(/items\[0\]\.heading/);
656
+ expect(errors[0]).toMatch(/fontSize/);
657
+ expect(errors[0]).toMatch(/out of bounds/);
658
+ });
659
+ });