@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
@@ -1,20 +1,34 @@
1
1
  import { ref, computed, watch, nextTick, onUnmounted } from 'vue';
2
- import { getInstance as getAiAssistantClient, AI_ASSISTANT_AGENT_TYPES, buildWebsiteBuilderMessage, } from '../../../clients/ai-assistant-client';
2
+ import { getInstance as getAiAssistantClient, AI_ASSISTANT_AGENT_TYPES, } from '../../../clients/ai-assistant-client';
3
+ import { FabricRenderClient } from '../../../clients/fabric-render-client';
4
+ import { parseLogoThemeToCssStyles } from '../../../brand-page/utils/style.util';
5
+ import { AI_ASSISTANT_TOOL_NAMES } from '../../../metadata-builder/parsers/ai-toolcall-parser';
3
6
  import { WebSocketGatewayClient, } from '../../../clients/websocket-gateway-client';
4
7
  import { minifyBrandPageMetadata } from '../../../utils/minify-brand-page-metadata';
5
- import { useSnapshotStore, useAiWebsiteBuilderThreadStore } from '../../stores';
8
+ import { buildAssetManifest } from '../../../utils/build-asset-manifest';
9
+ import { usePageNavigationStore } from '../../../shared/page-navigation/store/page-navigation-store';
10
+ import { navigateToPageByPageId } from '../../routing';
11
+ import { useSnapshotStore, useAiWebsiteBuilderThreadStore, useAiCheckpointStore, useBrandPageContainerStore, useUiStore, } from '../../stores';
6
12
  import { useBrandPageMakerStore } from '../../stores';
13
+ import { useBrandPageMakerUserStore } from '../../stores/user/user-store';
7
14
  import { useAiMetadataBuilder } from './useAiMetadataBuilder';
15
+ import { pickErrorMessage, pickWsErrorMessage, buildSubmitErrorReport, } from './aiErrorCopy';
16
+ import raygunUtil from '../../../brand-page/utils/raygun.util';
17
+ import { useEventTracker, AI_CTA_LOCATIONS } from '../useEventTracker';
8
18
  import { Logger } from '../../../metadata-builder/logger';
9
19
  const BUILD_TIMEOUT_MS = 600000;
10
20
  const aiLogger = new Logger();
11
21
  let activeBuildWsClient = null;
12
- const MAX_TOOL_CALL_ITERATIONS = 5;
13
22
  export function useAiWebsiteBuilderChat() {
14
23
  const aiThreadStore = useAiWebsiteBuilderThreadStore();
15
24
  const snapshotStore = useSnapshotStore();
16
25
  const brandPageMakerStore = useBrandPageMakerStore();
26
+ const containerStore = useBrandPageContainerStore();
27
+ const userStore = useBrandPageMakerUserStore();
28
+ const uiStore = useUiStore();
17
29
  const aiMetadataBuilder = useAiMetadataBuilder(aiLogger);
30
+ const checkpointStore = useAiCheckpointStore();
31
+ const eventTracker = useEventTracker();
18
32
  const hasStoredMessages = aiThreadStore.messages.length > 0;
19
33
  const threadItems = ref(aiThreadStore.messages.length > 0
20
34
  ? aiThreadStore.messages
@@ -27,6 +41,45 @@ export function useAiWebsiteBuilderChat() {
27
41
  timestamp: new Date(),
28
42
  },
29
43
  ]);
44
+ // ---------------------------------------------------------------------------
45
+ // Undo snapshot lifecycle (WL9) — reuses the existing `checkpointStore`,
46
+ // which stores pre-prompt BrandPageModel snapshots keyed by user message id.
47
+ //
48
+ // Capture rule: one checkpoint per user prompt, but ONLY when the response
49
+ // contains at least one non-undo tool call. Conversational responses and
50
+ // pure-undo responses leave checkpoint state untouched. This is handled
51
+ // inside `applyToolCallsAsync`, which is idempotent per user prompt thanks
52
+ // to the `hasCheckpoint(userMessageId)` guard — so multi-turn retry flows
53
+ // (where `applyToolCallsAsync` may run multiple times for one user prompt)
54
+ // only create the checkpoint on the first change-producing response.
55
+ //
56
+ // Restore rule: the UndoBuilder calls `undoHandler` which reverts to the
57
+ // latest checkpoint. `checkpointStore.revertToCheckpoint` splices away the
58
+ // target plus all subsequent entries in one step — exactly the pop-and-
59
+ // cleanup semantics we want.
60
+ //
61
+ // User-facing feedback comes from the AI pipeline's AIMessage content
62
+ // (e.g., "Reverted your last change.", server-side Gate 1 refusals for
63
+ // empty history) — NOT from this handler. Server-side Gate 1 already
64
+ // refuses undo when the conversation has no prior change-producing action,
65
+ // so a null return here is a defensive no-op rather than a normal path.
66
+ // ---------------------------------------------------------------------------
67
+ /**
68
+ * Resolver invoked by the UndoBuilder at execute time.
69
+ *
70
+ * Pops the latest checkpoint and returns its model; returns `null` when
71
+ * no checkpoint exists (defensive — server-side Gate 1 should prevent
72
+ * undo from reaching this handler on an empty history). The UndoBuilder
73
+ * surfaces the null case as a tool error, which the caller may log; no
74
+ * thread-level UI bubble is appended here.
75
+ */
76
+ const undoHandler = () => {
77
+ const { checkpoints } = checkpointStore;
78
+ if (checkpoints.length === 0)
79
+ return null;
80
+ const latest = checkpoints[checkpoints.length - 1];
81
+ return checkpointStore.revertToCheckpoint(latest.userMessageId);
82
+ };
30
83
  const isProcessing = computed({
31
84
  get: () => aiThreadStore.isProcessing,
32
85
  set: (value) => {
@@ -63,34 +116,187 @@ export function useAiWebsiteBuilderChat() {
63
116
  const wsClient = new WebSocketGatewayClient();
64
117
  const connectionId = await wsClient.connect();
65
118
  wsClient.onDisconnect(() => {
119
+ // Only report when a build was actively in flight. Idle disconnects
120
+ // (page reload, server restart with no user-visible build) would
121
+ // otherwise flood Raygun with non-actionable noise.
122
+ const wasBuildInFlight = aiThreadStore.isBuildInProgress;
66
123
  activeBuildWsClient = null;
67
124
  addServiceUnavailableBubble();
125
+ if (wasBuildInFlight) {
126
+ const err = new Error('There was a disruption in the connection while building your website. Please try again.');
127
+ raygunUtil.safeSendRaygunError(err, {
128
+ tags: [
129
+ 'feature:ai-website-builder',
130
+ 'failure:websocket_disconnect',
131
+ 'stage:in_flight_build',
132
+ ],
133
+ customData: {
134
+ build_id: aiThreadStore.buildId ?? null,
135
+ thread_id: aiThreadStore.threadId ?? null,
136
+ },
137
+ });
138
+ }
68
139
  });
69
140
  activeBuildWsClient = wsClient;
70
141
  return connectionId;
71
142
  };
72
- const buildMessage = (userPrompt) => {
73
- const minified = minifyBrandPageMetadata(brandPageMakerStore.brandPageData);
74
- return buildWebsiteBuilderMessage(userPrompt, minified);
143
+ const USER_PROMPT_PARAM_MAX_LENGTH = 100;
144
+ const appendUserPromptParam = (rawUrl, userPrompt) => {
145
+ try {
146
+ const url = new URL(rawUrl);
147
+ const trimmed = userPrompt.trim().slice(0, USER_PROMPT_PARAM_MAX_LENGTH);
148
+ if (trimmed) {
149
+ url.searchParams.set('user_prompt', trimmed);
150
+ }
151
+ return url.toString();
152
+ }
153
+ catch {
154
+ return rawUrl;
155
+ }
156
+ };
157
+ const fetchLogoContext = async (logoDraftToken) => {
158
+ if (!logoDraftToken)
159
+ return undefined;
160
+ const client = FabricRenderClient.getInstance();
161
+ if (!client)
162
+ return undefined;
163
+ const attributes = await client.getLogoAttributesByLogoDraftTokenAsync(logoDraftToken);
164
+ if (!attributes?.logoTheme)
165
+ return undefined;
166
+ const { logoTextColor, logoBackgroundColor } = parseLogoThemeToCssStyles(attributes.logoTheme);
167
+ const context = {};
168
+ if (logoTextColor.rgb)
169
+ context.logoTextColor = logoTextColor.rgb;
170
+ if (logoBackgroundColor.rgb)
171
+ context.logoBackgroundColor = logoBackgroundColor.rgb;
172
+ return Object.keys(context).length > 0 ? context : undefined;
173
+ };
174
+ const buildMessagePayload = async (userPrompt) => {
175
+ const brandPageData = brandPageMakerStore.brandPageData;
176
+ const headerLogoDraftToken = brandPageData.pageHeaderDesktop?.logoImage?.logoDraftToken ??
177
+ uiStore.logoDraftToken;
178
+ const [assets, logoContext] = await Promise.all([
179
+ buildAssetManifest(brandPageData),
180
+ fetchLogoContext(headerLogoDraftToken),
181
+ ]);
182
+ const pageNavigationStore = usePageNavigationStore();
183
+ const currentPageId = pageNavigationStore.activePageId;
184
+ const websiteContext = {
185
+ metadata: minifyBrandPageMetadata(brandPageData, assets),
186
+ ...(assets.length > 0 && { assets }),
187
+ websiteTemplateToken: containerStore.designTemplateToken,
188
+ ...(containerStore.designDraftToken && {
189
+ websiteDraftToken: containerStore.designDraftToken,
190
+ }),
191
+ ...(currentPageId && { activeContext: { currentPageId } }),
192
+ ...(brandPageMakerStore.businessName && {
193
+ businessName: brandPageMakerStore.businessName,
194
+ }),
195
+ ...(logoContext && { logoContext }),
196
+ };
197
+ return { message: userPrompt, websiteContext };
75
198
  };
76
- const applyToolCallsAsync = async (response) => {
199
+ const applyToolCallsAsync = async (response, userMessageId, vacantSlots) => {
77
200
  let aiMessage = response.message || '';
78
201
  const modelToUpdate = brandPageMakerStore.brandPageData;
79
- const updateResult = await aiMetadataBuilder.applyAiToolCallsAsync(modelToUpdate, response.toolCalls || []);
80
- const toolResults = updateResult.results.map((x) => {
202
+ const preCallModel = modelToUpdate;
203
+ // Conditional per-prompt checkpoint creation. Guarded by:
204
+ // - has at least one non-undo tool call (skip conversational + pure-undo
205
+ // responses — nothing worth reverting to),
206
+ // - no checkpoint exists yet for this user prompt (idempotent across the
207
+ // multi-turn retry flow, which calls applyToolCallsAsync multiple
208
+ // times for the same userMessageId).
209
+ const hasChangeProducingToolCall = (response.toolCalls ?? []).some((tc) => tc.function !== AI_ASSISTANT_TOOL_NAMES.UNDO);
210
+ if (hasChangeProducingToolCall &&
211
+ !checkpointStore.hasCheckpoint(userMessageId)) {
212
+ checkpointStore.createCheckpoint(userMessageId, preCallModel);
213
+ }
214
+ // PRE-PASS: for each create-mode section tool call on a page with a
215
+ // pending anchored vacant slot, splice `move_before_content_id` into
216
+ // the wire arguments so the executor places the section at the planned
217
+ // index instead of the end of the page. We rewrite the JSON string
218
+ // because parseToolCalls owns the parse step downstream.
219
+ const enrichedToolCalls = (response.toolCalls ?? []).map((tc) => {
220
+ try {
221
+ const args = JSON.parse(tc.arguments);
222
+ const mode = args.mode;
223
+ const pageId = args.page_id;
224
+ if (mode === 'create' &&
225
+ pageId &&
226
+ !args.move_before_content_id &&
227
+ (vacantSlots.get(pageId)?.length ?? 0) > 0) {
228
+ const slots = vacantSlots.get(pageId);
229
+ const anchorIdx = slots.findIndex((a) => a !== null);
230
+ if (anchorIdx !== -1) {
231
+ const anchor = slots[anchorIdx];
232
+ slots.splice(anchorIdx, 1);
233
+ args.move_before_content_id = anchor;
234
+ return { ...tc, arguments: JSON.stringify(args) };
235
+ }
236
+ }
237
+ }
238
+ catch {
239
+ // Malformed arguments — leave as-is; parseToolCalls will surface the error.
240
+ }
241
+ return tc;
242
+ });
243
+ const updateResult = await aiMetadataBuilder.applyAiToolCallsAsync(modelToUpdate, enrichedToolCalls, {
244
+ undoHandler,
245
+ eventTracker,
246
+ });
247
+ const toolResults = updateResult.results.map((x, i) => {
81
248
  return {
82
249
  toolId: x.toolId,
83
250
  success: x.errors && x.errors.length > 0 ? 'failed' : 'success',
251
+ name: enrichedToolCalls[i]?.function,
252
+ errorMessage: x.errors?.length ? x.errors.join('; ') : undefined,
84
253
  };
85
254
  });
255
+ // POST-PASS: walk this batch's create-mode results in plan order.
256
+ // - Failure → append `null` to the page's vacant queue. Anchor unknown
257
+ // until a later successful CREATE on the same page binds it.
258
+ // - Success → if the page has any pending `null` placeholders from
259
+ // earlier failures in this batch, bind the FIRST one to this new
260
+ // content id. The retry batch will pop it in plan order, which
261
+ // matches the backend's ascending-`order` retry emission.
262
+ updateResult.parsedToolCalls.forEach((parsed, i) => {
263
+ const args = parsed.arguments;
264
+ if (args.mode !== 'create' || !args.page_id)
265
+ return;
266
+ const result = updateResult.results[i];
267
+ const pageId = args.page_id;
268
+ const slots = vacantSlots.get(pageId) ?? [];
269
+ if (result.errors && result.errors.length > 0) {
270
+ slots.push(null);
271
+ }
272
+ else if (result.contentId) {
273
+ const nullIdx = slots.indexOf(null);
274
+ if (nullIdx !== -1) {
275
+ slots[nullIdx] = result.contentId;
276
+ }
277
+ }
278
+ vacantSlots.set(pageId, slots);
279
+ });
86
280
  updateResult.parsedToolCalls.forEach((toolCall) => {
87
281
  const args = toolCall.arguments;
88
282
  if (args.message) {
89
283
  aiMessage = aiMessage ? `${aiMessage} ${args.message}` : args.message;
90
284
  }
91
285
  });
92
- console.log('updateResult.model', JSON.parse(JSON.stringify(updateResult.model)));
93
286
  await brandPageMakerStore.initialiseStoresByBrandPageModelAsync(updateResult.model);
287
+ // AI tool calls can regenerate the active page's slug (e.g. rename_page
288
+ // recomputes pageSlug from the new title). The in-memory maker router's
289
+ // currentRoute still holds the pre-change slug, so the preview's
290
+ // slug-keyed page lookup (findNavItemByRelativePath) misses and silently
291
+ // falls back to pages[0]. Re-push by the stable pageId — which survives
292
+ // any rename — to refresh routeStore.currentRoute.params to the new slug
293
+ // and keep the preview on the page the user was editing. Unconditional:
294
+ // a push to an unchanged slug is a no-op, and an empty activePageId (e.g.
295
+ // initial load) is a safe early-return inside navigateToPageByPageId.
296
+ const pageNavigationStore = usePageNavigationStore();
297
+ if (pageNavigationStore.activePageId) {
298
+ navigateToPageByPageId(pageNavigationStore.activePageId);
299
+ }
94
300
  await nextTick();
95
301
  // Re-capture snapshot so subsequent messages reflect the updated page
96
302
  await snapshotStore.refreshSnapshotAsync();
@@ -100,27 +306,33 @@ export function useAiWebsiteBuilderChat() {
100
306
  aiMessage,
101
307
  };
102
308
  };
103
- const callApiBuildAsync = async (userPrompt) => {
309
+ const callApiBuildAsync = async (userPrompt, userMessageId) => {
104
310
  const connectionId = await ensureConnectedAsync();
105
311
  const wsClient = activeBuildWsClient;
106
312
  try {
107
313
  const client = getAiAssistantClient();
108
- const message = buildMessage(userPrompt);
109
- wsClient.sendHeartbeat();
314
+ const { message, websiteContext } = await buildMessagePayload(userPrompt);
315
+ const userPlan = userStore.hasPremiumSubscription
316
+ ? 'premium'
317
+ : 'free';
110
318
  let buildResponse;
111
319
  if (!aiThreadStore.threadId) {
112
320
  buildResponse = await client.createWebsiteThreadBuildAsync({
113
321
  message,
322
+ websiteContext,
114
323
  websiteSnapshot: snapshotStore.snapshotBase64 || undefined,
115
324
  agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
325
+ userPlan,
116
326
  connectionId,
117
327
  });
118
328
  }
119
329
  else {
120
330
  buildResponse = await client.sendWebsiteMessageBuildAsync(aiThreadStore.threadId, {
121
331
  message,
332
+ websiteContext,
122
333
  websiteSnapshot: snapshotStore.snapshotBase64 || undefined,
123
334
  agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
335
+ userPlan,
124
336
  connectionId,
125
337
  });
126
338
  }
@@ -128,12 +340,34 @@ export function useAiWebsiteBuilderChat() {
128
340
  aiThreadStore.buildId = buildResponse.buildId;
129
341
  aiThreadStore.isBuildInProgress = true;
130
342
  aiThreadStore.buildSectionsCompleted = 0;
131
- const langChainUrl = buildResponse.langChainUrl;
132
343
  wsClient.setBuildActive(true);
133
344
  wsClient.startResponseHeartbeat();
345
+ const withTypingMessage = (fn) => {
346
+ const typingId = currentTypingMessageId.value;
347
+ if (!typingId)
348
+ return;
349
+ const idx = threadItems.value.findIndex((m) => m.id === typingId);
350
+ if (idx !== -1)
351
+ fn(threadItems.value[idx]);
352
+ };
353
+ // Surface the thread URL on the agent bubble as soon as the API returns
354
+ // it, so the button is available while the build is still streaming and
355
+ // is preserved if the build later errors or times out.
356
+ if (buildResponse.langChainUrl) {
357
+ const url = appendUserPromptParam(buildResponse.langChainUrl, userPrompt);
358
+ withTypingMessage((m) => {
359
+ m.langChainUrl = url;
360
+ });
361
+ }
134
362
  return new Promise((resolve, reject) => {
135
363
  let settled = false;
364
+ let retryCount = 0;
365
+ let lastAiMessage = null;
136
366
  let handler = null;
367
+ // Vacant-slot queue scoped to this build session. Persists across
368
+ // retry rounds so a failed CREATE returns to its planned slot via
369
+ // a synthesised `move_before_content_id`.
370
+ const vacantSlots = new Map();
137
371
  const finishBuild = () => {
138
372
  aiThreadStore.isBuildInProgress = false;
139
373
  wsClient.setBuildActive(false);
@@ -151,53 +385,202 @@ export function useAiWebsiteBuilderChat() {
151
385
  handler = (msg) => {
152
386
  if (settled)
153
387
  return;
154
- if (msg.buildId !== aiThreadStore.buildId)
155
- return;
156
- if (isStopped.value) {
157
- settled = true;
158
- clearTimeout(timeout);
159
- finishBuild();
160
- resolve({ text: null });
161
- return;
162
- }
163
- if (msg.type === 'build_complete') {
164
- settled = true;
165
- clearTimeout(timeout);
166
- const toolCalls = (msg.toolCalls || []);
167
- const aiResponse = {
168
- threadId: aiThreadStore.threadId || '',
169
- toolCalls,
170
- message: msg.message || null,
171
- type: 'tool_calls',
172
- totalPaidToolCalls: 0,
173
- timestamp: Date.now(),
174
- };
175
- (async () => {
176
- try {
177
- if (toolCalls.length > 0) {
178
- await applyToolCallsAsync(aiResponse);
388
+ try {
389
+ if (msg.buildId !== aiThreadStore.buildId)
390
+ return;
391
+ if (isStopped.value) {
392
+ settled = true;
393
+ clearTimeout(timeout);
394
+ finishBuild();
395
+ resolve({ text: null });
396
+ return;
397
+ }
398
+ if (msg.type === 'chat_started' && msg.message) {
399
+ withTypingMessage((m) => {
400
+ m.initialMessage = msg.message;
401
+ });
402
+ return;
403
+ }
404
+ if (msg.type === 'plan_announced' && msg.items) {
405
+ withTypingMessage((m) => {
406
+ if (!m.planItems)
407
+ m.planItems = msg.items;
408
+ });
409
+ return;
410
+ }
411
+ if (msg.type === 'item_complete' && msg.itemId) {
412
+ const itemId = msg.itemId;
413
+ withTypingMessage((m) => {
414
+ if (!m.completedItemIds?.includes(itemId)) {
415
+ m.completedItemIds = [
416
+ ...(m.completedItemIds ?? []),
417
+ itemId,
418
+ ];
419
+ }
420
+ });
421
+ return;
422
+ }
423
+ if (msg.type === 'server_response') {
424
+ const toolCalls = (msg.toolCalls ||
425
+ []);
426
+ const aiResponse = {
427
+ threadId: aiThreadStore.threadId || '',
428
+ toolCalls,
429
+ message: msg.message || null,
430
+ type: 'tool_calls',
431
+ totalPaidToolCalls: 0,
432
+ timestamp: Date.now(),
433
+ };
434
+ (async () => {
435
+ try {
436
+ // No tool calls = build cycle complete (success, max retries, or no retryable sections)
437
+ if (toolCalls.length === 0) {
438
+ // Defence-in-depth: a server_response with no toolCalls AND
439
+ // no message text is a degenerate response — it means the
440
+ // backend graph reached END without producing anything
441
+ // actionable. The server-side fix in stream_with_push now
442
+ // converts this to a build_error frame, but if a future
443
+ // regression slips through we must NOT show "Your website
444
+ // has been updated" — that hides real failures from users
445
+ // and from monitoring.
446
+ const hasMeaningfulMessage = Boolean(aiResponse.message || lastAiMessage);
447
+ if (!hasMeaningfulMessage) {
448
+ raygunUtil.safeSendRaygunError(new Error('AI build server_response had empty toolCalls and empty message'), {
449
+ tags: [
450
+ 'feature:ai-website-builder',
451
+ 'failure:degenerate_server_response',
452
+ ],
453
+ customData: {
454
+ build_id: aiThreadStore.buildId ?? null,
455
+ thread_id: aiThreadStore.threadId ?? null,
456
+ },
457
+ });
458
+ settled = true;
459
+ clearTimeout(timeout);
460
+ finishBuild();
461
+ resolve({
462
+ text: 'Something went wrong while updating your website. Please try again.',
463
+ isError: true,
464
+ });
465
+ return;
466
+ }
467
+ settled = true;
468
+ clearTimeout(timeout);
469
+ finishBuild();
470
+ await snapshotStore.refreshSnapshotAsync();
471
+ resolve({
472
+ text: aiResponse.message || lastAiMessage || '',
473
+ });
474
+ return;
475
+ }
476
+ // Tool calls present — apply locally and submit results back
477
+ const { toolResults, aiMessage } = await applyToolCallsAsync(aiResponse, userMessageId, vacantSlots);
478
+ lastAiMessage = aiMessage;
479
+ // Submit all tool results (success + failure) so the AI pipeline
480
+ // persists ToolMessages matching every tool_call in the conversation.
481
+ const failedResults = toolResults.filter((tr) => tr.success !== 'success');
482
+ aiLogger.log('tool submission for results', {
483
+ total: toolResults.length,
484
+ failed: failedResults.length,
485
+ failedResults,
486
+ retryCount,
487
+ });
488
+ const submitResult = await client.submitToolResultsBuildAsync(aiThreadStore.threadId, {
489
+ toolCalls: toolResults,
490
+ agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
491
+ connectionId,
492
+ retryCount,
493
+ userPlan,
494
+ });
495
+ if (failedResults.length === 0) {
496
+ settled = true;
497
+ clearTimeout(timeout);
498
+ finishBuild();
499
+ resolve({
500
+ text: aiMessage || 'Your website has been updated.',
501
+ });
502
+ return;
503
+ }
504
+ // Update buildId so the handler matches subsequent WS messages
505
+ if (submitResult.buildId) {
506
+ aiThreadStore.buildId = submitResult.buildId;
507
+ }
508
+ // eslint-disable-next-line no-plusplus
509
+ retryCount++;
179
510
  }
180
- finishBuild();
181
- await snapshotStore.refreshSnapshotAsync();
182
- resolve({
183
- text: aiResponse.message || 'Your website has been updated.',
184
- langChainUrl,
185
- });
186
- }
187
- catch (err) {
188
- aiLogger.log('build_complete apply error', { err });
189
- finishBuild();
190
- resolve({ text: 'An error occurred applying changes.' });
191
- }
192
- })();
511
+ catch (err) {
512
+ aiLogger.log('server_response apply error', { err });
513
+ // Client-side apply failure (metadata builder, tool-call parser,
514
+ // missing field, etc.) — server thinks the build succeeded but we
515
+ // couldn't materialise the changes. Mark the chat bubble as an
516
+ // error so the user sees the real outcome, and report to Raygun
517
+ // so the failure is visible in monitoring (the server didn't see it).
518
+ raygunUtil.safeSendRaygunError(err instanceof Error
519
+ ? err
520
+ : new Error('AI apply failed: non-Error thrown'), {
521
+ tags: [
522
+ 'feature:ai-website-builder',
523
+ 'failure:apply_error',
524
+ 'stage:client_apply',
525
+ ],
526
+ customData: {
527
+ thread_id: aiThreadStore.threadId ?? null,
528
+ build_id: aiThreadStore.buildId ?? null,
529
+ },
530
+ });
531
+ settled = true;
532
+ clearTimeout(timeout);
533
+ finishBuild();
534
+ resolve({
535
+ text: "I couldn't apply those changes to your website. Please try again.",
536
+ isError: true,
537
+ });
538
+ }
539
+ })();
540
+ return;
541
+ }
542
+ if (msg.type === 'server_error' || msg.type === 'build_error') {
543
+ settled = true;
544
+ clearTimeout(timeout);
545
+ finishBuild();
546
+ // Backend already reports the underlying pipeline failure to
547
+ // Raygun via its structured handler (stage:run_build /
548
+ // stage:section_builder / etc. with full Python traceback).
549
+ // We report the frontend's RECEIPT of the error frame as a
550
+ // separate signal — distinct tag, distinct grouping. This
551
+ // gives triangulation: both sides have entry → normal crash;
552
+ // frontend only → backend Raygun outage; backend only → WS
553
+ // frame didn't reach client (different bug shape).
554
+ const wsMsg = msg;
555
+ raygunUtil.safeSendRaygunError(new Error(`AI build ${wsMsg.type}: ${wsMsg.code ?? wsMsg.error ?? 'unknown'}`), {
556
+ tags: [
557
+ 'feature:ai-website-builder',
558
+ 'failure:websocket_error_received',
559
+ `frame_type:${wsMsg.type}`,
560
+ ],
561
+ customData: {
562
+ code: wsMsg.code ?? null,
563
+ message: wsMsg.message ?? null,
564
+ error: wsMsg.error ?? null,
565
+ build_id: wsMsg.buildId ?? null,
566
+ thread_id: aiThreadStore.threadId ?? null,
567
+ },
568
+ });
569
+ resolve({ text: pickWsErrorMessage(msg), isError: true });
570
+ return;
571
+ }
193
572
  }
194
- if (msg.type === 'build_error') {
195
- settled = true;
196
- clearTimeout(timeout);
197
- finishBuild();
198
- resolve({
199
- text: msg.error || 'An error occurred during the build.',
573
+ catch (err) {
574
+ aiLogger.log('Unexpected error in WS message handler', {
575
+ err,
576
+ type: msg.type,
200
577
  });
578
+ if (!settled) {
579
+ settled = true;
580
+ clearTimeout(timeout);
581
+ finishBuild();
582
+ resolve({ text: 'An error occurred during the build.' });
583
+ }
201
584
  }
202
585
  };
203
586
  wsClient.onMessage(handler);
@@ -214,6 +597,7 @@ export function useAiWebsiteBuilderChat() {
214
597
  const { text, images } = messageOptions;
215
598
  if (!text?.trim() && (!images || images.length === 0))
216
599
  return;
600
+ eventTracker.sendAiSendPromptEvent(AI_CTA_LOCATIONS.AI_BUILDER_TAB);
217
601
  isStopped.value = false;
218
602
  aiThreadStore.isProcessing = true;
219
603
  const userMessage = images
@@ -233,6 +617,11 @@ export function useAiWebsiteBuilderChat() {
233
617
  timestamp: new Date(),
234
618
  };
235
619
  threadItems.value.push(userMessage);
620
+ // Checkpoint creation is now conditional and happens inside
621
+ // `applyToolCallsAsync` — it fires only when the response carries at
622
+ // least one change-producing tool call, and only once per user prompt
623
+ // (idempotent across the multi-turn retry flow). See the "Undo snapshot
624
+ // lifecycle (WL9)" block near the top of this composable.
236
625
  const agentMessageId = (Date.now() + 1).toString();
237
626
  currentTypingMessageId.value = agentMessageId;
238
627
  const agentMessage = {
@@ -247,7 +636,7 @@ export function useAiWebsiteBuilderChat() {
247
636
  try {
248
637
  aiLogger.startGroup();
249
638
  aiLogger.log('sendMessageAsync', { messageOptions });
250
- const result = await callApiBuildAsync(text || '');
639
+ const result = await callApiBuildAsync(text || '', userMessage.id);
251
640
  if (isStopped.value)
252
641
  return;
253
642
  const messageIndex = threadItems.value.findIndex((msg) => msg.id === agentMessageId);
@@ -255,7 +644,12 @@ export function useAiWebsiteBuilderChat() {
255
644
  if (result.text) {
256
645
  threadItems.value[messageIndex].text = result.text;
257
646
  threadItems.value[messageIndex].isTyping = false;
258
- threadItems.value[messageIndex].langChainUrl = result.langChainUrl;
647
+ // Honour the isError flag set by error paths in callApiBuildAsync
648
+ // (apply-failure catch, server_error / build_error WS frames).
649
+ // Without this, error text would render as a normal AI reply.
650
+ if (result.isError) {
651
+ threadItems.value[messageIndex].isError = true;
652
+ }
259
653
  }
260
654
  else {
261
655
  threadItems.value[messageIndex].text =
@@ -274,11 +668,20 @@ export function useAiWebsiteBuilderChat() {
274
668
  if (messageIndex !== -1) {
275
669
  threadItems.value[messageIndex].text = isConnectionError
276
670
  ? SERVICE_UNAVAILABLE_MESSAGE
277
- : error?.response?.data?.error ||
278
- 'An error occurred. Please try again.';
671
+ : pickErrorMessage(error);
279
672
  threadItems.value[messageIndex].isTyping = false;
280
673
  threadItems.value[messageIndex].isError = true;
281
674
  }
675
+ // REL 1 item (4): report request-path failures to Raygun so we can
676
+ // see network / proxy / upstream errors separately from pipeline
677
+ // failures (which are reported server-side via build_error).
678
+ const report = buildSubmitErrorReport(error, { stage: 'submit' });
679
+ if (report) {
680
+ raygunUtil.safeSendRaygunError(report.error, {
681
+ tags: report.tags,
682
+ customData: report.customData,
683
+ });
684
+ }
282
685
  }
283
686
  finally {
284
687
  currentTypingMessageId.value = null;
@@ -287,7 +690,6 @@ export function useAiWebsiteBuilderChat() {
287
690
  // was destroyed (tab switch) and the writable computed setter is inert.
288
691
  aiThreadStore.isProcessing = false;
289
692
  }
290
- console.log('aiLogger', aiLogger);
291
693
  };
292
694
  const deleteMessage = (messageId) => {
293
695
  const index = threadItems.value.findIndex((msg) => msg.id === messageId);
@@ -296,6 +698,7 @@ export function useAiWebsiteBuilderChat() {
296
698
  }
297
699
  };
298
700
  const stopGenerationAsync = async () => {
701
+ eventTracker.sendAiStopGenerationEvent(AI_CTA_LOCATIONS.AI_BUILDER_TAB);
299
702
  isStopped.value = true;
300
703
  aiThreadStore.isBuildInProgress = false;
301
704
  if (activeBuildWsClient) {
@@ -310,29 +713,32 @@ export function useAiWebsiteBuilderChat() {
310
713
  aiThreadStore.isProcessing = false;
311
714
  };
312
715
  aiThreadStore.stopGeneration = stopGenerationAsync;
313
- onUnmounted(() => {
314
- if (aiThreadStore.stopGeneration === stopGenerationAsync) {
315
- aiThreadStore.stopGeneration = null;
316
- }
317
- });
318
716
  const formattedMessages = computed(() => {
319
717
  const initialMessageCount = hasStoredMessages
320
718
  ? aiThreadStore.messages.length
321
719
  : 1;
322
- return threadItems.value.map((msg, index) => ({
323
- type: msg.type,
324
- props: {
325
- ...(msg.type === 'text' && { text: msg.text }),
326
- ...(msg.type === 'image' && { images: msg.images, text: msg.text }),
327
- isUser: msg.role === 'user',
328
- isAi: msg.role === 'agent',
329
- showIcon: msg.role === 'agent',
330
- isTyping: msg.isTyping,
331
- isResponse: index >= 1,
332
- disableAnimation: index < initialMessageCount,
333
- langChainUrl: msg.langChainUrl,
334
- },
335
- }));
720
+ return threadItems.value.map((msg, index) => {
721
+ return {
722
+ type: msg.type,
723
+ props: {
724
+ ...(msg.type === 'text' && { text: msg.text }),
725
+ ...(msg.type === 'image' && { images: msg.images, text: msg.text }),
726
+ isUser: msg.role === 'user',
727
+ isAi: msg.role === 'agent',
728
+ showIcon: msg.role === 'agent',
729
+ isTyping: msg.isTyping,
730
+ isResponse: index >= 1,
731
+ disableAnimation: index < initialMessageCount,
732
+ langChainUrl: msg.langChainUrl,
733
+ ...(msg.type === 'text' &&
734
+ msg.isTyping && {
735
+ initialMessage: msg.initialMessage,
736
+ planItems: msg.planItems ?? [],
737
+ completedItemIds: msg.completedItemIds ?? [],
738
+ }),
739
+ },
740
+ };
741
+ });
336
742
  });
337
743
  return {
338
744
  threadItems,