@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.
- package/lib/src/{CreatedOnBrandCrowd-de-de-CJZpKPNH.js → CreatedOnBrandCrowd-de-de-xmkupPus.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-DkIa7iEb.js → CreatedOnBrandCrowd-en-us-Ck7EdTtb.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-CE2p6XHz.js → CreatedOnBrandCrowd-es-es-BC8sJuDH.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-BBjOZS3M.js → CreatedOnBrandCrowd-fr-ca-CN-UBe1q.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-BBjOZS3M.js → CreatedOnBrandCrowd-fr-fr-CN-UBe1q.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-Dayzl4BZ.js → CreatedOnBrandCrowd-pt-br-CajQyNkx.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-Dayzl4BZ.js → CreatedOnBrandCrowd-pt-pt-CajQyNkx.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-CFzDqfCb.js → CreatedOnDcom-de-de-BRCNMmDq.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-C2jIPBDY.js → CreatedOnDcom-en-us-BzQDtZhc.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-qejpzqho.js → CreatedOnDcom-es-es-D-qAM4Cd.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-DMO9Ld7K.js → CreatedOnDcom-fr-ca-CZ1irFOc.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-BFWN9qTP.js → CreatedOnDcom-fr-fr-Gb5BCF0N.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-CneD3ghF.js → CreatedOnDcom-pt-br-CAQV0q6o.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-Cg1B7s0o.js → CreatedOnDcom-pt-pt-D6tQdQFh.js} +1 -1
- package/lib/src/brand-page/components/image-gallery/ImageGalleryModal.mixin.js +2 -2
- package/lib/src/brand-page/models/brand-page-context.model.d.ts +0 -2
- package/lib/src/brand-page/models/brand-page-context.model.js +1 -11
- package/lib/src/brand-page/models/index.d.ts +1 -0
- package/lib/src/brand-page/models/index.js +1 -0
- package/lib/src/brand-page/utils/color.util.js +23 -3
- package/lib/src/brand-page/utils/raygun.util.d.ts +5 -1
- package/lib/src/brand-page/utils/raygun.util.js +7 -2
- package/lib/src/brand-page/utils/svg-color.util.js +1 -2
- package/lib/src/brand-page/utils/view-model.util.js +1 -1
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +1 -31
- package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +0 -2
- package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +2 -31
- package/lib/src/brand-page-maker/components/preview/Preview.mixin.d.ts +0 -14
- package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +0 -28
- package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +0 -28
- package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +0 -28
- package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +0 -28
- package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +0 -28
- package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +0 -28
- package/lib/src/brand-page-maker/composables/ai-chat/aiErrorCopy.d.ts +35 -0
- package/lib/src/brand-page-maker/composables/ai-chat/aiErrorCopy.js +108 -0
- package/lib/src/brand-page-maker/composables/ai-chat/{useAiChat.d.ts → types.d.ts} +5 -13
- package/lib/src/brand-page-maker/composables/ai-chat/types.js +1 -0
- package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.d.ts +19 -0
- package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.js +1 -0
- package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.d.ts +1 -1
- package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +488 -82
- package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +5 -5
- package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +6 -16
- package/lib/src/brand-page-maker/composables/useEventTracker.js +19 -21
- package/lib/src/brand-page-maker/constants/sidebar-constant.d.ts +0 -1
- package/lib/src/brand-page-maker/constants/sidebar-constant.js +7 -15
- package/lib/src/brand-page-maker/convertors/image-aspect-ratio.convertor.js +2 -2
- package/lib/src/brand-page-maker/convertors/text-size-value.convertor.js +3 -1
- package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +30660 -0
- package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.js +34 -0
- package/lib/src/brand-page-maker/stores/ai-thread/ai-website-builder-thread-store.d.ts +1 -1
- package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +0 -28
- package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +42 -2
- package/lib/src/brand-page-maker/stores/index.d.ts +1 -1
- package/lib/src/brand-page-maker/stores/index.js +1 -1
- package/lib/src/brand-page-maker/stores/user/user-store.js +0 -1
- package/lib/src/bundled-translations-CnareLWC.js +175 -0
- package/lib/src/bundled-translations.de-DE-2Ys7epLO.js +175 -0
- package/lib/src/bundled-translations.es-ES-Cjp-G8rT.js +175 -0
- package/lib/src/bundled-translations.fr-CA-CsaW4Oae.js +175 -0
- package/lib/src/bundled-translations.fr-FR-DIT2dd8J.js +175 -0
- package/lib/src/bundled-translations.pt-BR-BamoBpDW.js +175 -0
- package/lib/src/bundled-translations.pt-PT-C9A4hY_e.js +175 -0
- package/lib/src/bundles/bundled-translations.de-DE.json +1 -20
- package/lib/src/bundles/bundled-translations.es-ES.json +1 -20
- package/lib/src/bundles/bundled-translations.fr-CA.json +1 -20
- package/lib/src/bundles/bundled-translations.fr-FR.json +1 -20
- package/lib/src/bundles/bundled-translations.json +1 -20
- package/lib/src/bundles/bundled-translations.pt-BR.json +1 -20
- package/lib/src/bundles/bundled-translations.pt-PT.json +1 -20
- package/lib/src/clients/ai-assistant-client.d.ts +30 -29
- package/lib/src/clients/ai-assistant-client.js +16 -16
- package/lib/src/clients/design-template-client.d.ts +0 -2
- package/lib/src/clients/design-template-client.js +0 -38
- package/lib/src/clients/websocket-gateway-client.d.ts +9 -1
- package/lib/src/composables/useTranslate.d.ts +2 -1
- package/lib/src/composables/useTranslate.js +2 -1
- package/lib/src/constants/ui.d.ts +0 -1
- package/lib/src/css/library.brand-page-brandCrowd.min.css +225 -134
- package/lib/src/css/library.brand-page-brandPage.min.css +227 -121
- package/lib/src/css/library.brand-page-designCom.min.css +225 -134
- package/lib/src/css/library.brand-page-preview.min.css +225 -118
- package/lib/src/index.d.ts +1 -1
- package/lib/src/index.mjs +1 -1
- package/lib/src/{lib-33w_DBny.js → lib-CSRUPpSo.js} +30887 -29395
- package/lib/src/metadata-builder/ai-metadata-executor.d.ts +21 -0
- package/lib/src/metadata-builder/ai-metadata-executor.js +56 -3
- package/lib/src/metadata-builder/builders/add-page-builder.d.ts +2 -4
- package/lib/src/metadata-builder/builders/add-page-builder.js +5 -8
- package/lib/src/metadata-builder/builders/footer-builder.d.ts +11 -0
- package/lib/src/metadata-builder/builders/footer-builder.js +151 -0
- package/lib/src/metadata-builder/builders/global-styles-builder.d.ts +15 -0
- package/lib/src/metadata-builder/builders/global-styles-builder.js +129 -0
- package/lib/src/metadata-builder/builders/header-builder.d.ts +11 -0
- package/lib/src/metadata-builder/builders/header-builder.js +167 -0
- package/lib/src/metadata-builder/builders/rename-page-builder.d.ts +12 -0
- package/lib/src/metadata-builder/builders/rename-page-builder.js +49 -0
- package/lib/src/metadata-builder/builders/section-builders/about-section-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/about-section-builder.js +44 -4
- package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.js +70 -6
- package/lib/src/metadata-builder/builders/section-builders/base-section-builder.d.ts +36 -4
- package/lib/src/metadata-builder/builders/section-builders/base-section-builder.js +102 -12
- package/lib/src/metadata-builder/builders/section-builders/booking-section-builder.d.ts +8 -0
- package/lib/src/metadata-builder/builders/section-builders/booking-section-builder.js +50 -0
- package/lib/src/metadata-builder/builders/section-builders/button-section-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/button-section-builder.js +5 -1
- package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.js +47 -3
- package/lib/src/metadata-builder/builders/section-builders/delete-section-builder.d.ts +2 -1
- package/lib/src/metadata-builder/builders/section-builders/delete-section-builder.js +5 -2
- package/lib/src/metadata-builder/builders/section-builders/divider-section-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/divider-section-builder.js +11 -1
- package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.js +52 -13
- package/lib/src/metadata-builder/builders/section-builders/images-section-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +47 -17
- package/lib/src/metadata-builder/builders/section-builders/index.d.ts +1 -0
- package/lib/src/metadata-builder/builders/section-builders/index.js +1 -0
- package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.js +20 -2
- package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.js +20 -2
- package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.js +36 -2
- package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +70 -27
- package/lib/src/metadata-builder/builders/section-builders/social-links-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +7 -2
- package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +91 -26
- package/lib/src/metadata-builder/builders/section-builders/text-section-builder.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +69 -31
- package/lib/src/metadata-builder/builders/section-content-factory.d.ts +2 -2
- package/lib/src/metadata-builder/builders/section-content-factory.js +8 -2
- package/lib/src/metadata-builder/builders/section-field-maps.js +30 -2
- package/lib/src/metadata-builder/builders/section-field-utils.d.ts +46 -7
- package/lib/src/metadata-builder/builders/section-field-utils.js +86 -62
- package/lib/src/metadata-builder/builders/section-rules/about-section.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/about-section.rules.js +20 -0
- package/lib/src/metadata-builder/builders/section-rules/add-page.rules.d.ts +7 -0
- package/lib/src/metadata-builder/builders/section-rules/add-page.rules.js +8 -0
- package/lib/src/metadata-builder/builders/section-rules/banner-section.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/banner-section.rules.js +27 -0
- package/lib/src/metadata-builder/builders/section-rules/booking-section.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/booking-section.rules.js +16 -0
- package/lib/src/metadata-builder/builders/section-rules/button-section.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/button-section.rules.js +13 -0
- package/lib/src/metadata-builder/builders/section-rules/contact-form.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/contact-form.rules.js +16 -0
- package/lib/src/metadata-builder/builders/section-rules/divider-section.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/divider-section.rules.js +7 -0
- package/lib/src/metadata-builder/builders/section-rules/footer.rules.d.ts +4 -0
- package/lib/src/metadata-builder/builders/section-rules/footer.rules.js +5 -0
- package/lib/src/metadata-builder/builders/section-rules/global-styles.rules.d.ts +6 -0
- package/lib/src/metadata-builder/builders/section-rules/global-styles.rules.js +26 -0
- package/lib/src/metadata-builder/builders/section-rules/header.rules.d.ts +4 -0
- package/lib/src/metadata-builder/builders/section-rules/header.rules.js +41 -0
- package/lib/src/metadata-builder/builders/section-rules/image-gallery.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/image-gallery.rules.js +10 -0
- package/lib/src/metadata-builder/builders/section-rules/images-section.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/images-section.rules.js +31 -0
- package/lib/src/metadata-builder/builders/section-rules/mailing-list.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/mailing-list.rules.js +11 -0
- package/lib/src/metadata-builder/builders/section-rules/profile.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/profile.rules.js +14 -0
- package/lib/src/metadata-builder/builders/section-rules/rename-page.rules.d.ts +7 -0
- package/lib/src/metadata-builder/builders/section-rules/rename-page.rules.js +8 -0
- package/lib/src/metadata-builder/builders/section-rules/shapes-section.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/shapes-section.rules.js +27 -0
- package/lib/src/metadata-builder/builders/section-rules/social-links.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/social-links.rules.js +9 -0
- package/lib/src/metadata-builder/builders/section-rules/testimonial-section.rules.d.ts +7 -0
- package/lib/src/metadata-builder/builders/section-rules/testimonial-section.rules.js +26 -0
- package/lib/src/metadata-builder/builders/section-rules/text-section.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/text-section.rules.js +14 -0
- package/lib/src/metadata-builder/builders/shape-color-utils.d.ts +26 -0
- package/lib/src/metadata-builder/builders/shape-color-utils.js +52 -0
- package/lib/src/metadata-builder/builders/undo-builder.d.ts +48 -0
- package/lib/src/metadata-builder/builders/undo-builder.js +58 -0
- package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +5 -1
- package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +15 -4
- package/lib/src/metadata-builder/helpers/rte.helpers.js +24 -10
- package/lib/src/metadata-builder/metadata-executor.d.ts +2 -1
- package/lib/src/metadata-builder/metadata-executor.js +32 -5
- package/lib/src/metadata-builder/models.d.ts +26 -1
- package/lib/src/metadata-builder/models.js +14 -1
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +144 -0
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +17 -0
- package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +113 -24
- package/lib/src/metadata-builder/parsers/section-tool-arguments.js +5 -1
- package/lib/src/metadata-builder/sanitizers/rte-schema-check.d.ts +22 -0
- package/lib/src/metadata-builder/sanitizers/rte-schema-check.js +84 -0
- package/lib/src/metadata-builder/sanitizers/sanitizer-rules.d.ts +47 -0
- package/lib/src/metadata-builder/sanitizers/sanitizer-rules.js +60 -0
- package/lib/src/metadata-builder/sanitizers/section-args-validator.d.ts +41 -0
- package/lib/src/metadata-builder/sanitizers/section-args-validator.js +274 -0
- package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +2 -1
- package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.js +2 -16
- package/lib/src/shared/content-settings/components/content-settings/ContentSettings.stories.js +0 -1
- package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +2 -1
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +2 -3
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +0 -4
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +6 -8
- package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +0 -28
- package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +2 -1
- package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +0 -28
- package/lib/src/shared/navigation-style-settings/store/index.js +1 -0
- package/lib/src/shared/page-navigation/store/page-navigation-store.js +1 -20
- package/lib/src/shared/page-navigation/utils/nav-order-utils.d.ts +3 -0
- package/lib/src/shared/page-navigation/utils/nav-order-utils.js +20 -0
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +2 -30
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +0 -28
- package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +2 -43
- package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +0 -30
- package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +2 -3
- package/lib/src/shared/side-bar/store/side-bar-store.d.ts +0 -12
- package/lib/src/shared/side-bar/store/side-bar-store.js +0 -7
- package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +2 -29
- package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +2 -1
- package/lib/src/utils/build-asset-manifest.d.ts +26 -0
- package/lib/src/utils/build-asset-manifest.js +114 -0
- package/lib/src/utils/minify-brand-page-metadata.d.ts +49 -1
- package/lib/src/utils/minify-brand-page-metadata.js +216 -129
- package/package.json +1 -1
- package/src/brand-page/components/banner-section/BannerSection.vue +1 -1
- package/src/brand-page/components/contact-form/ContactForm.vue +29 -26
- package/src/brand-page/components/image-gallery/ImageGalleryModal.mixin.ts +6 -2
- package/src/brand-page/components/text-media-section/TextMediaSection.vue +1 -5
- package/src/brand-page/models/brand-page-context.model.ts +1 -25
- package/src/brand-page/models/index.ts +1 -0
- package/src/brand-page/utils/color.util.test.ts +48 -0
- package/src/brand-page/utils/color.util.ts +29 -4
- package/src/brand-page/utils/raygun.util.ts +11 -2
- package/src/brand-page/utils/svg-color.util.ts +1 -3
- package/src/brand-page/utils/view-model.util.ts +1 -4
- package/src/brand-page-maker/components/ai-text-edit/AiChatbox.vue +2 -2
- package/src/brand-page-maker/components/ai-text-edit/AiProgressFeed.vue +162 -0
- package/src/brand-page-maker/components/ai-text-edit/TextBubble.vue +57 -5
- package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.de-DE.json +0 -20
- package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.es-ES.json +0 -20
- package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.fr-CA.json +0 -20
- package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.fr-FR.json +0 -20
- package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.json +0 -20
- package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.pt-BR.json +0 -20
- package/src/brand-page-maker/components/ai-text-edit/i18n/generate-ai.pt-PT.json +0 -20
- package/src/brand-page-maker/components/ai-website-builder/AiWebsiteBuilder.vue +1 -2
- package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.de-DE.json +3 -0
- package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.es-ES.json +3 -0
- package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.fr-CA.json +3 -0
- package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.fr-FR.json +3 -0
- package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.json +3 -0
- package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.pt-BR.json +3 -0
- package/src/brand-page-maker/components/ai-website-builder/i18n/ai-website-builder.pt-PT.json +3 -0
- package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.vue +3 -28
- package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +0 -2
- package/src/brand-page-maker/composables/ai-chat/aiErrorCopy.test.ts +229 -0
- package/src/brand-page-maker/composables/ai-chat/aiErrorCopy.ts +164 -0
- package/src/brand-page-maker/composables/ai-chat/types.ts +49 -0
- package/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.ts +20 -0
- package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +651 -94
- package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +9 -9
- package/src/brand-page-maker/composables/useEventTracker.ts +31 -25
- package/src/brand-page-maker/constants/sidebar-constant.ts +7 -15
- package/src/brand-page-maker/convertors/image-aspect-ratio.convertor.ts +2 -2
- package/src/brand-page-maker/convertors/text-size-value.convertor.ts +3 -3
- package/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.ts +54 -0
- package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +76 -0
- package/src/brand-page-maker/stores/index.ts +1 -1
- package/src/brand-page-maker/stores/user/user-store.ts +0 -1
- package/src/bundles/bundled-translations.de-DE.json +1 -20
- package/src/bundles/bundled-translations.es-ES.json +1 -20
- package/src/bundles/bundled-translations.fr-CA.json +1 -20
- package/src/bundles/bundled-translations.fr-FR.json +1 -20
- package/src/bundles/bundled-translations.json +1 -20
- package/src/bundles/bundled-translations.pt-BR.json +1 -20
- package/src/bundles/bundled-translations.pt-PT.json +1 -20
- package/src/clients/ai-assistant-client.ts +59 -46
- package/src/clients/design-template-client.ts +0 -66
- package/src/clients/websocket-gateway-client.ts +17 -1
- package/src/composables/useTranslate.ts +3 -0
- package/src/constants/ui.ts +0 -1
- package/src/lib.ts +1 -0
- package/src/metadata-builder/ai-metadata-executor.ts +94 -1
- package/src/metadata-builder/builders/add-page-builder.ts +9 -13
- package/src/metadata-builder/builders/footer-builder.test.ts +76 -0
- package/src/metadata-builder/builders/footer-builder.ts +226 -0
- package/src/metadata-builder/builders/global-styles-builder.ts +187 -0
- package/src/metadata-builder/builders/header-builder.test.ts +106 -0
- package/src/metadata-builder/builders/header-builder.ts +254 -0
- package/src/metadata-builder/builders/rename-page-builder.ts +79 -0
- package/src/metadata-builder/builders/section-builders/about-section-builder.ts +73 -5
- package/src/metadata-builder/builders/section-builders/banner-section-builder.ts +112 -7
- package/src/metadata-builder/builders/section-builders/base-section-builder.ts +149 -11
- package/src/metadata-builder/builders/section-builders/booking-section-builder.ts +87 -0
- package/src/metadata-builder/builders/section-builders/button-section-builder.ts +6 -1
- package/src/metadata-builder/builders/section-builders/contact-form-builder.ts +74 -4
- package/src/metadata-builder/builders/section-builders/delete-section-builder.ts +15 -1
- package/src/metadata-builder/builders/section-builders/divider-section-builder.ts +13 -1
- package/src/metadata-builder/builders/section-builders/image-gallery-builder.ts +91 -31
- package/src/metadata-builder/builders/section-builders/images-section-builder.ts +102 -48
- package/src/metadata-builder/builders/section-builders/index.ts +1 -0
- package/src/metadata-builder/builders/section-builders/mailing-list-builder.ts +38 -2
- package/src/metadata-builder/builders/section-builders/mailing-list-section-builder.ts +38 -2
- package/src/metadata-builder/builders/section-builders/profile-section-builder.ts +55 -3
- package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +170 -77
- package/src/metadata-builder/builders/section-builders/social-links-builder.test.ts +119 -0
- package/src/metadata-builder/builders/section-builders/social-links-builder.ts +13 -2
- package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +178 -57
- package/src/metadata-builder/builders/section-builders/text-section-builder.ts +130 -30
- package/src/metadata-builder/builders/section-content-factory.ts +15 -2
- package/src/metadata-builder/builders/section-field-maps.ts +34 -1
- package/src/metadata-builder/builders/section-field-utils.ts +131 -72
- package/src/metadata-builder/builders/section-rules/about-section.rules.ts +38 -0
- package/src/metadata-builder/builders/section-rules/add-page.rules.ts +17 -0
- package/src/metadata-builder/builders/section-rules/banner-section.rules.ts +49 -0
- package/src/metadata-builder/builders/section-rules/booking-section.rules.ts +34 -0
- package/src/metadata-builder/builders/section-rules/button-section.rules.ts +22 -0
- package/src/metadata-builder/builders/section-rules/contact-form.rules.ts +30 -0
- package/src/metadata-builder/builders/section-rules/divider-section.rules.ts +17 -0
- package/src/metadata-builder/builders/section-rules/footer.rules.ts +21 -0
- package/src/metadata-builder/builders/section-rules/global-styles.rules.ts +44 -0
- package/src/metadata-builder/builders/section-rules/header.rules.ts +75 -0
- package/src/metadata-builder/builders/section-rules/image-gallery.rules.ts +22 -0
- package/src/metadata-builder/builders/section-rules/images-section.rules.ts +55 -0
- package/src/metadata-builder/builders/section-rules/mailing-list.rules.ts +21 -0
- package/src/metadata-builder/builders/section-rules/profile.rules.ts +27 -0
- package/src/metadata-builder/builders/section-rules/rename-page.rules.ts +17 -0
- package/src/metadata-builder/builders/section-rules/shapes-section.rules.ts +51 -0
- package/src/metadata-builder/builders/section-rules/social-links.rules.ts +27 -0
- package/src/metadata-builder/builders/section-rules/testimonial-section.rules.ts +48 -0
- package/src/metadata-builder/builders/section-rules/text-section.rules.ts +28 -0
- package/src/metadata-builder/builders/shape-color-utils.test.ts +115 -0
- package/src/metadata-builder/builders/shape-color-utils.ts +75 -0
- package/src/metadata-builder/builders/undo-builder.ts +105 -0
- package/src/metadata-builder/builders/update-content-text-style-builder.ts +4 -1
- package/src/metadata-builder/helpers/rte.helpers.ts +27 -13
- package/src/metadata-builder/metadata-executor.ts +42 -2
- package/src/metadata-builder/models.ts +39 -1
- package/src/metadata-builder/parsers/ai-toolcall-parser.ts +152 -0
- package/src/metadata-builder/parsers/section-tool-arguments.ts +138 -24
- package/src/metadata-builder/sanitizers/__tests__/section-args-validator.test.ts +659 -0
- package/src/metadata-builder/sanitizers/rte-schema-check.ts +99 -0
- package/src/metadata-builder/sanitizers/sanitizer-rules.ts +97 -0
- package/src/metadata-builder/sanitizers/section-args-validator.ts +445 -0
- package/src/shared/content-settings/components/content-settings/ContentSettings.mixin.ts +10 -16
- package/src/shared/content-settings/components/content-settings/ContentSettings.stories.ts +0 -1
- package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +6 -10
- package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.vue +1 -23
- package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.vue +0 -15
- package/src/shared/content-settings/components/text-items-field/TextItemsField.vue +0 -15
- package/src/shared/navigation-style-settings/store/index.ts +5 -0
- package/src/shared/page-navigation/store/page-navigation-store.ts +4 -31
- package/src/shared/page-navigation/utils/nav-order-utils.ts +32 -0
- package/src/shared/side-bar/components/SideBar.vue +14 -1
- package/src/shared/side-bar/store/side-bar-store.ts +0 -20
- package/src/utils/build-asset-manifest.ts +153 -0
- package/src/utils/minify-brand-page-metadata.ts +362 -130
- package/lib/src/brand-page-maker/composables/ai-chat/useAiChat.js +0 -368
- package/lib/src/brand-page-maker/composables/ai-chat/useAiTextEditPrompts.d.ts +0 -18
- package/lib/src/brand-page-maker/composables/ai-chat/useAiTextEditPrompts.js +0 -144
- package/lib/src/brand-page-maker/stores/ai-thread/ai-thread-store.d.ts +0 -155
- package/lib/src/brand-page-maker/stores/ai-thread/ai-thread-store.js +0 -25
- package/lib/src/brand-page-maker/utils/mixpanel.util.d.ts +0 -1
- package/lib/src/brand-page-maker/utils/mixpanel.util.js +0 -29
- package/lib/src/bundled-translations-CNK7l6_v.js +0 -173
- package/lib/src/bundled-translations.de-DE-BLE4_y0H.js +0 -173
- package/lib/src/bundled-translations.es-ES-thO9osbg.js +0 -173
- package/lib/src/bundled-translations.fr-CA-BZkhiNgU.js +0 -173
- package/lib/src/bundled-translations.fr-FR-bhDLPKgZ.js +0 -173
- package/lib/src/bundled-translations.pt-BR-CI9a5Bq1.js +0 -173
- package/lib/src/bundled-translations.pt-PT-DBoErBaq.js +0 -173
- package/src/brand-page-maker/components/ai-text-edit/AiTextEdit.vue +0 -154
- package/src/brand-page-maker/composables/ai-chat/useAiChat.ts +0 -562
- package/src/brand-page-maker/composables/ai-chat/useAiTextEditPrompts.ts +0 -196
- package/src/brand-page-maker/stores/ai-thread/ai-thread-store.ts +0 -31
- package/src/brand-page-maker/utils/mixpanel.util.ts +0 -42
- package/src/shared/content-settings/components/generate-ai-button/GenerateAiButton.vue +0 -114
- package/src/shared/content-settings/components/generate-ai-modal/GenerateAiModal.vue +0 -761
|
@@ -1,39 +1,66 @@
|
|
|
1
1
|
import { ref, computed, watch, nextTick, onUnmounted } from 'vue';
|
|
2
|
+
import type { BrandPageModel } from '../../../brand-page/models';
|
|
2
3
|
import {
|
|
3
4
|
getInstance as getAiAssistantClient,
|
|
4
5
|
AI_ASSISTANT_AGENT_TYPES,
|
|
5
|
-
buildWebsiteBuilderMessage,
|
|
6
6
|
type AiAssistantResponse,
|
|
7
7
|
type AiAssistantToolResult,
|
|
8
8
|
type AiAssistantToolCall,
|
|
9
|
+
type LogoContext,
|
|
10
|
+
type UserPlan,
|
|
9
11
|
} from '../../../clients/ai-assistant-client';
|
|
12
|
+
import { FabricRenderClient } from '../../../clients/fabric-render-client';
|
|
13
|
+
import { parseLogoThemeToCssStyles } from '../../../brand-page/utils/style.util';
|
|
14
|
+
import { AI_ASSISTANT_TOOL_NAMES } from '../../../metadata-builder/parsers/ai-toolcall-parser';
|
|
10
15
|
import {
|
|
11
16
|
WebSocketGatewayClient,
|
|
12
17
|
type WebSocketMessage,
|
|
18
|
+
type PlanItem,
|
|
13
19
|
} from '../../../clients/websocket-gateway-client';
|
|
14
20
|
import { minifyBrandPageMetadata } from '../../../utils/minify-brand-page-metadata';
|
|
15
|
-
import {
|
|
21
|
+
import { buildAssetManifest } from '../../../utils/build-asset-manifest';
|
|
22
|
+
import { usePageNavigationStore } from '../../../shared/page-navigation/store/page-navigation-store';
|
|
23
|
+
import { navigateToPageByPageId } from '../../routing';
|
|
24
|
+
import {
|
|
25
|
+
useSnapshotStore,
|
|
26
|
+
useAiWebsiteBuilderThreadStore,
|
|
27
|
+
useAiCheckpointStore,
|
|
28
|
+
useBrandPageContainerStore,
|
|
29
|
+
useUiStore,
|
|
30
|
+
} from '../../stores';
|
|
16
31
|
import { useBrandPageMakerStore } from '../../stores';
|
|
32
|
+
import { useBrandPageMakerUserStore } from '../../stores/user/user-store';
|
|
17
33
|
import { useAiMetadataBuilder } from './useAiMetadataBuilder';
|
|
34
|
+
import {
|
|
35
|
+
pickErrorMessage,
|
|
36
|
+
pickWsErrorMessage,
|
|
37
|
+
buildSubmitErrorReport,
|
|
38
|
+
} from './aiErrorCopy';
|
|
39
|
+
import raygunUtil from '../../../brand-page/utils/raygun.util';
|
|
40
|
+
import { useEventTracker, AI_CTA_LOCATIONS } from '../useEventTracker';
|
|
18
41
|
import { Logger } from '../../../metadata-builder/logger';
|
|
19
42
|
import type {
|
|
20
43
|
UseAiChatResult,
|
|
21
44
|
ThreadMessage,
|
|
22
45
|
TextThreadMessage,
|
|
23
46
|
SendMessageOptions,
|
|
24
|
-
} from './
|
|
47
|
+
} from './types';
|
|
25
48
|
|
|
26
49
|
const BUILD_TIMEOUT_MS = 600000;
|
|
27
50
|
|
|
28
51
|
const aiLogger = new Logger();
|
|
29
52
|
let activeBuildWsClient: WebSocketGatewayClient | null = null;
|
|
30
|
-
const MAX_TOOL_CALL_ITERATIONS = 5;
|
|
31
53
|
|
|
32
54
|
export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
33
55
|
const aiThreadStore = useAiWebsiteBuilderThreadStore();
|
|
34
56
|
const snapshotStore = useSnapshotStore();
|
|
35
57
|
const brandPageMakerStore = useBrandPageMakerStore();
|
|
58
|
+
const containerStore = useBrandPageContainerStore();
|
|
59
|
+
const userStore = useBrandPageMakerUserStore();
|
|
60
|
+
const uiStore = useUiStore();
|
|
36
61
|
const aiMetadataBuilder = useAiMetadataBuilder(aiLogger);
|
|
62
|
+
const checkpointStore = useAiCheckpointStore();
|
|
63
|
+
const eventTracker = useEventTracker();
|
|
37
64
|
|
|
38
65
|
const hasStoredMessages = aiThreadStore.messages.length > 0;
|
|
39
66
|
const threadItems = ref<ThreadMessage[]>(
|
|
@@ -49,6 +76,50 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
49
76
|
} as TextThreadMessage,
|
|
50
77
|
],
|
|
51
78
|
);
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Undo snapshot lifecycle (WL9) — reuses the existing `checkpointStore`,
|
|
82
|
+
// which stores pre-prompt BrandPageModel snapshots keyed by user message id.
|
|
83
|
+
//
|
|
84
|
+
// Capture rule: one checkpoint per user prompt, but ONLY when the response
|
|
85
|
+
// contains at least one non-undo tool call. Conversational responses and
|
|
86
|
+
// pure-undo responses leave checkpoint state untouched. This is handled
|
|
87
|
+
// inside `applyToolCallsAsync`, which is idempotent per user prompt thanks
|
|
88
|
+
// to the `hasCheckpoint(userMessageId)` guard — so multi-turn retry flows
|
|
89
|
+
// (where `applyToolCallsAsync` may run multiple times for one user prompt)
|
|
90
|
+
// only create the checkpoint on the first change-producing response.
|
|
91
|
+
//
|
|
92
|
+
// Restore rule: the UndoBuilder calls `undoHandler` which reverts to the
|
|
93
|
+
// latest checkpoint. `checkpointStore.revertToCheckpoint` splices away the
|
|
94
|
+
// target plus all subsequent entries in one step — exactly the pop-and-
|
|
95
|
+
// cleanup semantics we want.
|
|
96
|
+
//
|
|
97
|
+
// User-facing feedback comes from the AI pipeline's AIMessage content
|
|
98
|
+
// (e.g., "Reverted your last change.", server-side Gate 1 refusals for
|
|
99
|
+
// empty history) — NOT from this handler. Server-side Gate 1 already
|
|
100
|
+
// refuses undo when the conversation has no prior change-producing action,
|
|
101
|
+
// so a null return here is a defensive no-op rather than a normal path.
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Resolver invoked by the UndoBuilder at execute time.
|
|
106
|
+
*
|
|
107
|
+
* Pops the latest checkpoint and returns its model; returns `null` when
|
|
108
|
+
* no checkpoint exists (defensive — server-side Gate 1 should prevent
|
|
109
|
+
* undo from reaching this handler on an empty history). The UndoBuilder
|
|
110
|
+
* surfaces the null case as a tool error, which the caller may log; no
|
|
111
|
+
* thread-level UI bubble is appended here.
|
|
112
|
+
*/
|
|
113
|
+
const undoHandler = (): BrandPageModel | null => {
|
|
114
|
+
const { checkpoints } = checkpointStore;
|
|
115
|
+
|
|
116
|
+
if (checkpoints.length === 0) return null;
|
|
117
|
+
|
|
118
|
+
const latest = checkpoints[checkpoints.length - 1];
|
|
119
|
+
|
|
120
|
+
return checkpointStore.revertToCheckpoint(latest.userMessageId);
|
|
121
|
+
};
|
|
122
|
+
|
|
52
123
|
const isProcessing = computed({
|
|
53
124
|
get: () => aiThreadStore.isProcessing,
|
|
54
125
|
set: (value: boolean) => {
|
|
@@ -99,8 +170,31 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
99
170
|
const connectionId = await wsClient.connect();
|
|
100
171
|
|
|
101
172
|
wsClient.onDisconnect(() => {
|
|
173
|
+
// Only report when a build was actively in flight. Idle disconnects
|
|
174
|
+
// (page reload, server restart with no user-visible build) would
|
|
175
|
+
// otherwise flood Raygun with non-actionable noise.
|
|
176
|
+
const wasBuildInFlight = aiThreadStore.isBuildInProgress;
|
|
177
|
+
|
|
102
178
|
activeBuildWsClient = null;
|
|
103
179
|
addServiceUnavailableBubble();
|
|
180
|
+
|
|
181
|
+
if (wasBuildInFlight) {
|
|
182
|
+
const err = new Error(
|
|
183
|
+
'There was a disruption in the connection while building your website. Please try again.',
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
raygunUtil.safeSendRaygunError(err, {
|
|
187
|
+
tags: [
|
|
188
|
+
'feature:ai-website-builder',
|
|
189
|
+
'failure:websocket_disconnect',
|
|
190
|
+
'stage:in_flight_build',
|
|
191
|
+
],
|
|
192
|
+
customData: {
|
|
193
|
+
build_id: aiThreadStore.buildId ?? null,
|
|
194
|
+
thread_id: aiThreadStore.threadId ?? null,
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
}
|
|
104
198
|
});
|
|
105
199
|
|
|
106
200
|
activeBuildWsClient = wsClient;
|
|
@@ -108,30 +202,216 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
108
202
|
return connectionId;
|
|
109
203
|
};
|
|
110
204
|
|
|
111
|
-
const
|
|
112
|
-
|
|
205
|
+
const USER_PROMPT_PARAM_MAX_LENGTH = 100;
|
|
206
|
+
|
|
207
|
+
const appendUserPromptParam = (
|
|
208
|
+
rawUrl: string,
|
|
209
|
+
userPrompt: string,
|
|
210
|
+
): string => {
|
|
211
|
+
try {
|
|
212
|
+
const url = new URL(rawUrl);
|
|
213
|
+
const trimmed = userPrompt.trim().slice(0, USER_PROMPT_PARAM_MAX_LENGTH);
|
|
214
|
+
|
|
215
|
+
if (trimmed) {
|
|
216
|
+
url.searchParams.set('user_prompt', trimmed);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return url.toString();
|
|
220
|
+
} catch {
|
|
221
|
+
return rawUrl;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const fetchLogoContext = async (
|
|
226
|
+
logoDraftToken: string | undefined,
|
|
227
|
+
): Promise<LogoContext | undefined> => {
|
|
228
|
+
if (!logoDraftToken) return undefined;
|
|
113
229
|
|
|
114
|
-
|
|
230
|
+
const client = FabricRenderClient.getInstance();
|
|
231
|
+
|
|
232
|
+
if (!client) return undefined;
|
|
233
|
+
|
|
234
|
+
const attributes =
|
|
235
|
+
await client.getLogoAttributesByLogoDraftTokenAsync(logoDraftToken);
|
|
236
|
+
|
|
237
|
+
if (!attributes?.logoTheme) return undefined;
|
|
238
|
+
|
|
239
|
+
const { logoTextColor, logoBackgroundColor } = parseLogoThemeToCssStyles(
|
|
240
|
+
attributes.logoTheme,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const context: LogoContext = {};
|
|
244
|
+
|
|
245
|
+
if (logoTextColor.rgb) context.logoTextColor = logoTextColor.rgb;
|
|
246
|
+
|
|
247
|
+
if (logoBackgroundColor.rgb)
|
|
248
|
+
context.logoBackgroundColor = logoBackgroundColor.rgb;
|
|
249
|
+
|
|
250
|
+
return Object.keys(context).length > 0 ? context : undefined;
|
|
115
251
|
};
|
|
116
252
|
|
|
117
|
-
const
|
|
253
|
+
const buildMessagePayload = async (userPrompt: string) => {
|
|
254
|
+
const brandPageData = brandPageMakerStore.brandPageData;
|
|
255
|
+
const headerLogoDraftToken =
|
|
256
|
+
brandPageData.pageHeaderDesktop?.logoImage?.logoDraftToken ??
|
|
257
|
+
uiStore.logoDraftToken;
|
|
258
|
+
const [assets, logoContext] = await Promise.all([
|
|
259
|
+
buildAssetManifest(brandPageData),
|
|
260
|
+
fetchLogoContext(headerLogoDraftToken),
|
|
261
|
+
]);
|
|
262
|
+
const pageNavigationStore = usePageNavigationStore();
|
|
263
|
+
const currentPageId = pageNavigationStore.activePageId;
|
|
264
|
+
|
|
265
|
+
const websiteContext = {
|
|
266
|
+
metadata: minifyBrandPageMetadata(brandPageData, assets),
|
|
267
|
+
...(assets.length > 0 && { assets }),
|
|
268
|
+
websiteTemplateToken: containerStore.designTemplateToken,
|
|
269
|
+
...(containerStore.designDraftToken && {
|
|
270
|
+
websiteDraftToken: containerStore.designDraftToken,
|
|
271
|
+
}),
|
|
272
|
+
...(currentPageId && { activeContext: { currentPageId } }),
|
|
273
|
+
...(brandPageMakerStore.businessName && {
|
|
274
|
+
businessName: brandPageMakerStore.businessName,
|
|
275
|
+
}),
|
|
276
|
+
...(logoContext && { logoContext }),
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
return { message: userPrompt, websiteContext };
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Per-page vacant-slot queue for the current build session.
|
|
284
|
+
*
|
|
285
|
+
* When the AI pipeline emits a CREATE batch, sections are processed in
|
|
286
|
+
* planned order. If any CREATE fails validation, it returns through the
|
|
287
|
+
* retry loop as a fresh CREATE tool call carrying no `move_before_content_id`
|
|
288
|
+
* — the backend cannot supply one because it never sees the frontend-
|
|
289
|
+
* assigned content ids. Without a hint, the retried section is pushed to
|
|
290
|
+
* the bottom of the page (see BaseSectionBuilder.createContent).
|
|
291
|
+
*
|
|
292
|
+
* To preserve position, we track "vacant slots" per page: each failure
|
|
293
|
+
* records a `null` placeholder; the first subsequent successful CREATE on
|
|
294
|
+
* the same page anchors that placeholder by stamping its own content id.
|
|
295
|
+
* A retry CREATE on that page pops the oldest anchored placeholder and
|
|
296
|
+
* injects `move_before_content_id`, so the splice lands at the planned
|
|
297
|
+
* index. Multiple interleaved failures are handled FIFO, matching the
|
|
298
|
+
* planner-ascending order in which the retry batch is emitted.
|
|
299
|
+
*
|
|
300
|
+
* The map is build-scoped and reset at the start of every `callApiBuildAsync`.
|
|
301
|
+
*/
|
|
302
|
+
type VacantSlotMap = Map<string, (string | null)[]>;
|
|
303
|
+
|
|
304
|
+
const applyToolCallsAsync = async (
|
|
305
|
+
response: AiAssistantResponse,
|
|
306
|
+
userMessageId: string,
|
|
307
|
+
vacantSlots: VacantSlotMap,
|
|
308
|
+
) => {
|
|
118
309
|
let aiMessage: string | null = response.message || '';
|
|
119
310
|
const modelToUpdate = brandPageMakerStore.brandPageData;
|
|
311
|
+
const preCallModel = modelToUpdate;
|
|
312
|
+
|
|
313
|
+
// Conditional per-prompt checkpoint creation. Guarded by:
|
|
314
|
+
// - has at least one non-undo tool call (skip conversational + pure-undo
|
|
315
|
+
// responses — nothing worth reverting to),
|
|
316
|
+
// - no checkpoint exists yet for this user prompt (idempotent across the
|
|
317
|
+
// multi-turn retry flow, which calls applyToolCallsAsync multiple
|
|
318
|
+
// times for the same userMessageId).
|
|
319
|
+
const hasChangeProducingToolCall = (response.toolCalls ?? []).some(
|
|
320
|
+
(tc) => tc.function !== AI_ASSISTANT_TOOL_NAMES.UNDO,
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
if (
|
|
324
|
+
hasChangeProducingToolCall &&
|
|
325
|
+
!checkpointStore.hasCheckpoint(userMessageId)
|
|
326
|
+
) {
|
|
327
|
+
checkpointStore.createCheckpoint(userMessageId, preCallModel);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// PRE-PASS: for each create-mode section tool call on a page with a
|
|
331
|
+
// pending anchored vacant slot, splice `move_before_content_id` into
|
|
332
|
+
// the wire arguments so the executor places the section at the planned
|
|
333
|
+
// index instead of the end of the page. We rewrite the JSON string
|
|
334
|
+
// because parseToolCalls owns the parse step downstream.
|
|
335
|
+
const enrichedToolCalls = (response.toolCalls ?? []).map((tc) => {
|
|
336
|
+
try {
|
|
337
|
+
const args = JSON.parse(tc.arguments) as Record<string, unknown>;
|
|
338
|
+
const mode = args.mode;
|
|
339
|
+
const pageId = args.page_id as string | undefined;
|
|
340
|
+
|
|
341
|
+
if (
|
|
342
|
+
mode === 'create' &&
|
|
343
|
+
pageId &&
|
|
344
|
+
!args.move_before_content_id &&
|
|
345
|
+
(vacantSlots.get(pageId)?.length ?? 0) > 0
|
|
346
|
+
) {
|
|
347
|
+
const slots = vacantSlots.get(pageId)!;
|
|
348
|
+
const anchorIdx = slots.findIndex((a) => a !== null);
|
|
349
|
+
|
|
350
|
+
if (anchorIdx !== -1) {
|
|
351
|
+
const anchor = slots[anchorIdx];
|
|
352
|
+
|
|
353
|
+
slots.splice(anchorIdx, 1);
|
|
354
|
+
args.move_before_content_id = anchor;
|
|
355
|
+
|
|
356
|
+
return { ...tc, arguments: JSON.stringify(args) };
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
} catch {
|
|
360
|
+
// Malformed arguments — leave as-is; parseToolCalls will surface the error.
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return tc;
|
|
364
|
+
});
|
|
120
365
|
|
|
121
366
|
const updateResult = await aiMetadataBuilder.applyAiToolCallsAsync(
|
|
122
367
|
modelToUpdate,
|
|
123
|
-
|
|
368
|
+
enrichedToolCalls,
|
|
369
|
+
{
|
|
370
|
+
undoHandler,
|
|
371
|
+
eventTracker,
|
|
372
|
+
},
|
|
124
373
|
);
|
|
125
374
|
|
|
126
375
|
const toolResults: AiAssistantToolResult[] = updateResult.results.map(
|
|
127
|
-
(x) => {
|
|
376
|
+
(x, i) => {
|
|
128
377
|
return {
|
|
129
378
|
toolId: x.toolId,
|
|
130
379
|
success: x.errors && x.errors.length > 0 ? 'failed' : 'success',
|
|
380
|
+
name: enrichedToolCalls[i]?.function,
|
|
381
|
+
errorMessage: x.errors?.length ? x.errors.join('; ') : undefined,
|
|
131
382
|
};
|
|
132
383
|
},
|
|
133
384
|
);
|
|
134
385
|
|
|
386
|
+
// POST-PASS: walk this batch's create-mode results in plan order.
|
|
387
|
+
// - Failure → append `null` to the page's vacant queue. Anchor unknown
|
|
388
|
+
// until a later successful CREATE on the same page binds it.
|
|
389
|
+
// - Success → if the page has any pending `null` placeholders from
|
|
390
|
+
// earlier failures in this batch, bind the FIRST one to this new
|
|
391
|
+
// content id. The retry batch will pop it in plan order, which
|
|
392
|
+
// matches the backend's ascending-`order` retry emission.
|
|
393
|
+
updateResult.parsedToolCalls.forEach((parsed, i) => {
|
|
394
|
+
const args = parsed.arguments as { mode?: string; page_id?: string };
|
|
395
|
+
|
|
396
|
+
if (args.mode !== 'create' || !args.page_id) return;
|
|
397
|
+
|
|
398
|
+
const result = updateResult.results[i];
|
|
399
|
+
const pageId = args.page_id;
|
|
400
|
+
const slots = vacantSlots.get(pageId) ?? [];
|
|
401
|
+
|
|
402
|
+
if (result.errors && result.errors.length > 0) {
|
|
403
|
+
slots.push(null);
|
|
404
|
+
} else if (result.contentId) {
|
|
405
|
+
const nullIdx = slots.indexOf(null);
|
|
406
|
+
|
|
407
|
+
if (nullIdx !== -1) {
|
|
408
|
+
slots[nullIdx] = result.contentId;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
vacantSlots.set(pageId, slots);
|
|
413
|
+
});
|
|
414
|
+
|
|
135
415
|
updateResult.parsedToolCalls.forEach((toolCall) => {
|
|
136
416
|
const args = toolCall.arguments as any;
|
|
137
417
|
|
|
@@ -140,15 +420,25 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
140
420
|
}
|
|
141
421
|
});
|
|
142
422
|
|
|
143
|
-
console.log(
|
|
144
|
-
'updateResult.model',
|
|
145
|
-
JSON.parse(JSON.stringify(updateResult.model)),
|
|
146
|
-
);
|
|
147
|
-
|
|
148
423
|
await brandPageMakerStore.initialiseStoresByBrandPageModelAsync(
|
|
149
424
|
updateResult.model,
|
|
150
425
|
);
|
|
151
426
|
|
|
427
|
+
// AI tool calls can regenerate the active page's slug (e.g. rename_page
|
|
428
|
+
// recomputes pageSlug from the new title). The in-memory maker router's
|
|
429
|
+
// currentRoute still holds the pre-change slug, so the preview's
|
|
430
|
+
// slug-keyed page lookup (findNavItemByRelativePath) misses and silently
|
|
431
|
+
// falls back to pages[0]. Re-push by the stable pageId — which survives
|
|
432
|
+
// any rename — to refresh routeStore.currentRoute.params to the new slug
|
|
433
|
+
// and keep the preview on the page the user was editing. Unconditional:
|
|
434
|
+
// a push to an unchanged slug is a no-op, and an empty activePageId (e.g.
|
|
435
|
+
// initial load) is a safe early-return inside navigateToPageByPageId.
|
|
436
|
+
const pageNavigationStore = usePageNavigationStore();
|
|
437
|
+
|
|
438
|
+
if (pageNavigationStore.activePageId) {
|
|
439
|
+
navigateToPageByPageId(pageNavigationStore.activePageId);
|
|
440
|
+
}
|
|
441
|
+
|
|
152
442
|
await nextTick();
|
|
153
443
|
|
|
154
444
|
// Re-capture snapshot so subsequent messages reflect the updated page
|
|
@@ -163,23 +453,28 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
163
453
|
|
|
164
454
|
const callApiBuildAsync = async (
|
|
165
455
|
userPrompt: string,
|
|
166
|
-
|
|
456
|
+
userMessageId: string,
|
|
457
|
+
): Promise<{ text: string | null; isError?: boolean }> => {
|
|
167
458
|
const connectionId = await ensureConnectedAsync();
|
|
168
459
|
const wsClient = activeBuildWsClient!;
|
|
169
460
|
|
|
170
461
|
try {
|
|
171
462
|
const client = getAiAssistantClient();
|
|
172
|
-
const message =
|
|
463
|
+
const { message, websiteContext } = await buildMessagePayload(userPrompt);
|
|
173
464
|
|
|
174
|
-
|
|
465
|
+
const userPlan: UserPlan = userStore.hasPremiumSubscription
|
|
466
|
+
? 'premium'
|
|
467
|
+
: 'free';
|
|
175
468
|
|
|
176
469
|
let buildResponse;
|
|
177
470
|
|
|
178
471
|
if (!aiThreadStore.threadId) {
|
|
179
472
|
buildResponse = await client.createWebsiteThreadBuildAsync({
|
|
180
473
|
message,
|
|
474
|
+
websiteContext,
|
|
181
475
|
websiteSnapshot: snapshotStore.snapshotBase64 || undefined,
|
|
182
476
|
agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
|
|
477
|
+
userPlan,
|
|
183
478
|
connectionId,
|
|
184
479
|
});
|
|
185
480
|
} else {
|
|
@@ -187,8 +482,10 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
187
482
|
aiThreadStore.threadId,
|
|
188
483
|
{
|
|
189
484
|
message,
|
|
485
|
+
websiteContext,
|
|
190
486
|
websiteSnapshot: snapshotStore.snapshotBase64 || undefined,
|
|
191
487
|
agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
|
|
488
|
+
userPlan,
|
|
192
489
|
connectionId,
|
|
193
490
|
},
|
|
194
491
|
);
|
|
@@ -199,15 +496,45 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
199
496
|
aiThreadStore.isBuildInProgress = true;
|
|
200
497
|
aiThreadStore.buildSectionsCompleted = 0;
|
|
201
498
|
|
|
202
|
-
const langChainUrl = buildResponse.langChainUrl;
|
|
203
|
-
|
|
204
499
|
wsClient.setBuildActive(true);
|
|
205
500
|
wsClient.startResponseHeartbeat();
|
|
206
501
|
|
|
207
|
-
|
|
502
|
+
const withTypingMessage = (
|
|
503
|
+
fn: (msg: TextThreadMessage) => void,
|
|
504
|
+
): void => {
|
|
505
|
+
const typingId = currentTypingMessageId.value;
|
|
506
|
+
|
|
507
|
+
if (!typingId) return;
|
|
508
|
+
|
|
509
|
+
const idx = threadItems.value.findIndex((m) => m.id === typingId);
|
|
510
|
+
|
|
511
|
+
if (idx !== -1) fn(threadItems.value[idx] as TextThreadMessage);
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
// Surface the thread URL on the agent bubble as soon as the API returns
|
|
515
|
+
// it, so the button is available while the build is still streaming and
|
|
516
|
+
// is preserved if the build later errors or times out.
|
|
517
|
+
if (buildResponse.langChainUrl) {
|
|
518
|
+
const url = appendUserPromptParam(
|
|
519
|
+
buildResponse.langChainUrl,
|
|
520
|
+
userPrompt,
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
withTypingMessage((m) => {
|
|
524
|
+
m.langChainUrl = url;
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return new Promise<{ text: string | null; isError?: boolean }>(
|
|
208
529
|
(resolve, reject) => {
|
|
209
530
|
let settled = false;
|
|
531
|
+
let retryCount = 0;
|
|
532
|
+
let lastAiMessage: string | null = null;
|
|
210
533
|
let handler: ((msg: WebSocketMessage) => void) | null = null;
|
|
534
|
+
// Vacant-slot queue scoped to this build session. Persists across
|
|
535
|
+
// retry rounds so a failed CREATE returns to its planned slot via
|
|
536
|
+
// a synthesised `move_before_content_id`.
|
|
537
|
+
const vacantSlots: VacantSlotMap = new Map();
|
|
211
538
|
|
|
212
539
|
const finishBuild = () => {
|
|
213
540
|
aiThreadStore.isBuildInProgress = false;
|
|
@@ -228,59 +555,261 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
228
555
|
handler = (msg: WebSocketMessage) => {
|
|
229
556
|
if (settled) return;
|
|
230
557
|
|
|
231
|
-
|
|
558
|
+
try {
|
|
559
|
+
if (msg.buildId !== aiThreadStore.buildId) return;
|
|
232
560
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
561
|
+
if (isStopped.value) {
|
|
562
|
+
settled = true;
|
|
563
|
+
clearTimeout(timeout);
|
|
564
|
+
finishBuild();
|
|
565
|
+
resolve({ text: null });
|
|
238
566
|
|
|
239
|
-
|
|
240
|
-
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
241
569
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const toolCalls = (msg.toolCalls || []) as AiAssistantToolCall[];
|
|
247
|
-
const aiResponse: AiAssistantResponse = {
|
|
248
|
-
threadId: aiThreadStore.threadId || '',
|
|
249
|
-
toolCalls,
|
|
250
|
-
message: msg.message || null,
|
|
251
|
-
type: 'tool_calls',
|
|
252
|
-
totalPaidToolCalls: 0,
|
|
253
|
-
timestamp: Date.now(),
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
(async () => {
|
|
257
|
-
try {
|
|
258
|
-
if (toolCalls.length > 0) {
|
|
259
|
-
await applyToolCallsAsync(aiResponse);
|
|
260
|
-
}
|
|
570
|
+
if (msg.type === 'chat_started' && msg.message) {
|
|
571
|
+
withTypingMessage((m) => {
|
|
572
|
+
m.initialMessage = msg.message;
|
|
573
|
+
});
|
|
261
574
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
} catch (err) {
|
|
270
|
-
aiLogger.log('build_complete apply error', { err });
|
|
271
|
-
finishBuild();
|
|
272
|
-
resolve({ text: 'An error occurred applying changes.' });
|
|
273
|
-
}
|
|
274
|
-
})();
|
|
275
|
-
}
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (msg.type === 'plan_announced' && msg.items) {
|
|
579
|
+
withTypingMessage((m) => {
|
|
580
|
+
if (!m.planItems) m.planItems = msg.items as PlanItem[];
|
|
581
|
+
});
|
|
276
582
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (msg.type === 'item_complete' && msg.itemId) {
|
|
587
|
+
const itemId = msg.itemId;
|
|
588
|
+
|
|
589
|
+
withTypingMessage((m) => {
|
|
590
|
+
if (!m.completedItemIds?.includes(itemId)) {
|
|
591
|
+
m.completedItemIds = [
|
|
592
|
+
...(m.completedItemIds ?? []),
|
|
593
|
+
itemId,
|
|
594
|
+
];
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (msg.type === 'server_response') {
|
|
602
|
+
const toolCalls = (msg.toolCalls ||
|
|
603
|
+
[]) as AiAssistantToolCall[];
|
|
604
|
+
const aiResponse: AiAssistantResponse = {
|
|
605
|
+
threadId: aiThreadStore.threadId || '',
|
|
606
|
+
toolCalls,
|
|
607
|
+
message: msg.message || null,
|
|
608
|
+
type: 'tool_calls',
|
|
609
|
+
totalPaidToolCalls: 0,
|
|
610
|
+
timestamp: Date.now(),
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
(async () => {
|
|
614
|
+
try {
|
|
615
|
+
// No tool calls = build cycle complete (success, max retries, or no retryable sections)
|
|
616
|
+
if (toolCalls.length === 0) {
|
|
617
|
+
// Defence-in-depth: a server_response with no toolCalls AND
|
|
618
|
+
// no message text is a degenerate response — it means the
|
|
619
|
+
// backend graph reached END without producing anything
|
|
620
|
+
// actionable. The server-side fix in stream_with_push now
|
|
621
|
+
// converts this to a build_error frame, but if a future
|
|
622
|
+
// regression slips through we must NOT show "Your website
|
|
623
|
+
// has been updated" — that hides real failures from users
|
|
624
|
+
// and from monitoring.
|
|
625
|
+
const hasMeaningfulMessage = Boolean(
|
|
626
|
+
aiResponse.message || lastAiMessage,
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
if (!hasMeaningfulMessage) {
|
|
630
|
+
raygunUtil.safeSendRaygunError(
|
|
631
|
+
new Error(
|
|
632
|
+
'AI build server_response had empty toolCalls and empty message',
|
|
633
|
+
),
|
|
634
|
+
{
|
|
635
|
+
tags: [
|
|
636
|
+
'feature:ai-website-builder',
|
|
637
|
+
'failure:degenerate_server_response',
|
|
638
|
+
],
|
|
639
|
+
customData: {
|
|
640
|
+
build_id: aiThreadStore.buildId ?? null,
|
|
641
|
+
thread_id: aiThreadStore.threadId ?? null,
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
);
|
|
645
|
+
settled = true;
|
|
646
|
+
clearTimeout(timeout);
|
|
647
|
+
finishBuild();
|
|
648
|
+
resolve({
|
|
649
|
+
text: 'Something went wrong while updating your website. Please try again.',
|
|
650
|
+
isError: true,
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
settled = true;
|
|
657
|
+
clearTimeout(timeout);
|
|
658
|
+
finishBuild();
|
|
659
|
+
await snapshotStore.refreshSnapshotAsync();
|
|
660
|
+
resolve({
|
|
661
|
+
text: aiResponse.message || lastAiMessage || '',
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Tool calls present — apply locally and submit results back
|
|
668
|
+
const { toolResults, aiMessage } =
|
|
669
|
+
await applyToolCallsAsync(
|
|
670
|
+
aiResponse,
|
|
671
|
+
userMessageId,
|
|
672
|
+
vacantSlots,
|
|
673
|
+
);
|
|
674
|
+
|
|
675
|
+
lastAiMessage = aiMessage;
|
|
676
|
+
|
|
677
|
+
// Submit all tool results (success + failure) so the AI pipeline
|
|
678
|
+
// persists ToolMessages matching every tool_call in the conversation.
|
|
679
|
+
const failedResults = toolResults.filter(
|
|
680
|
+
(tr) => tr.success !== 'success',
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
aiLogger.log('tool submission for results', {
|
|
684
|
+
total: toolResults.length,
|
|
685
|
+
failed: failedResults.length,
|
|
686
|
+
failedResults,
|
|
687
|
+
retryCount,
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
const submitResult =
|
|
691
|
+
await client.submitToolResultsBuildAsync(
|
|
692
|
+
aiThreadStore.threadId!,
|
|
693
|
+
{
|
|
694
|
+
toolCalls: toolResults,
|
|
695
|
+
agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
|
|
696
|
+
connectionId,
|
|
697
|
+
retryCount,
|
|
698
|
+
userPlan,
|
|
699
|
+
},
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
if (failedResults.length === 0) {
|
|
703
|
+
settled = true;
|
|
704
|
+
clearTimeout(timeout);
|
|
705
|
+
finishBuild();
|
|
706
|
+
resolve({
|
|
707
|
+
text: aiMessage || 'Your website has been updated.',
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// Update buildId so the handler matches subsequent WS messages
|
|
714
|
+
if (submitResult.buildId) {
|
|
715
|
+
aiThreadStore.buildId = submitResult.buildId;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// eslint-disable-next-line no-plusplus
|
|
719
|
+
retryCount++;
|
|
720
|
+
} catch (err) {
|
|
721
|
+
aiLogger.log('server_response apply error', { err });
|
|
722
|
+
// Client-side apply failure (metadata builder, tool-call parser,
|
|
723
|
+
// missing field, etc.) — server thinks the build succeeded but we
|
|
724
|
+
// couldn't materialise the changes. Mark the chat bubble as an
|
|
725
|
+
// error so the user sees the real outcome, and report to Raygun
|
|
726
|
+
// so the failure is visible in monitoring (the server didn't see it).
|
|
727
|
+
raygunUtil.safeSendRaygunError(
|
|
728
|
+
err instanceof Error
|
|
729
|
+
? err
|
|
730
|
+
: new Error('AI apply failed: non-Error thrown'),
|
|
731
|
+
{
|
|
732
|
+
tags: [
|
|
733
|
+
'feature:ai-website-builder',
|
|
734
|
+
'failure:apply_error',
|
|
735
|
+
'stage:client_apply',
|
|
736
|
+
],
|
|
737
|
+
customData: {
|
|
738
|
+
thread_id: aiThreadStore.threadId ?? null,
|
|
739
|
+
build_id: aiThreadStore.buildId ?? null,
|
|
740
|
+
},
|
|
741
|
+
},
|
|
742
|
+
);
|
|
743
|
+
settled = true;
|
|
744
|
+
clearTimeout(timeout);
|
|
745
|
+
finishBuild();
|
|
746
|
+
resolve({
|
|
747
|
+
text: "I couldn't apply those changes to your website. Please try again.",
|
|
748
|
+
isError: true,
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
})();
|
|
752
|
+
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (msg.type === 'server_error' || msg.type === 'build_error') {
|
|
757
|
+
settled = true;
|
|
758
|
+
clearTimeout(timeout);
|
|
759
|
+
finishBuild();
|
|
760
|
+
|
|
761
|
+
// Backend already reports the underlying pipeline failure to
|
|
762
|
+
// Raygun via its structured handler (stage:run_build /
|
|
763
|
+
// stage:section_builder / etc. with full Python traceback).
|
|
764
|
+
// We report the frontend's RECEIPT of the error frame as a
|
|
765
|
+
// separate signal — distinct tag, distinct grouping. This
|
|
766
|
+
// gives triangulation: both sides have entry → normal crash;
|
|
767
|
+
// frontend only → backend Raygun outage; backend only → WS
|
|
768
|
+
// frame didn't reach client (different bug shape).
|
|
769
|
+
const wsMsg = msg as {
|
|
770
|
+
type: string;
|
|
771
|
+
code?: string;
|
|
772
|
+
message?: string;
|
|
773
|
+
error?: string;
|
|
774
|
+
buildId?: string;
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
raygunUtil.safeSendRaygunError(
|
|
778
|
+
new Error(
|
|
779
|
+
`AI build ${wsMsg.type}: ${wsMsg.code ?? wsMsg.error ?? 'unknown'}`,
|
|
780
|
+
),
|
|
781
|
+
{
|
|
782
|
+
tags: [
|
|
783
|
+
'feature:ai-website-builder',
|
|
784
|
+
'failure:websocket_error_received',
|
|
785
|
+
`frame_type:${wsMsg.type}`,
|
|
786
|
+
],
|
|
787
|
+
customData: {
|
|
788
|
+
code: wsMsg.code ?? null,
|
|
789
|
+
message: wsMsg.message ?? null,
|
|
790
|
+
error: wsMsg.error ?? null,
|
|
791
|
+
build_id: wsMsg.buildId ?? null,
|
|
792
|
+
thread_id: aiThreadStore.threadId ?? null,
|
|
793
|
+
},
|
|
794
|
+
},
|
|
795
|
+
);
|
|
796
|
+
|
|
797
|
+
resolve({ text: pickWsErrorMessage(msg), isError: true });
|
|
798
|
+
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
} catch (err) {
|
|
802
|
+
aiLogger.log('Unexpected error in WS message handler', {
|
|
803
|
+
err,
|
|
804
|
+
type: msg.type,
|
|
283
805
|
});
|
|
806
|
+
|
|
807
|
+
if (!settled) {
|
|
808
|
+
settled = true;
|
|
809
|
+
clearTimeout(timeout);
|
|
810
|
+
finishBuild();
|
|
811
|
+
resolve({ text: 'An error occurred during the build.' });
|
|
812
|
+
}
|
|
284
813
|
}
|
|
285
814
|
};
|
|
286
815
|
|
|
@@ -300,6 +829,8 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
300
829
|
|
|
301
830
|
if (!text?.trim() && (!images || images.length === 0)) return;
|
|
302
831
|
|
|
832
|
+
eventTracker.sendAiSendPromptEvent(AI_CTA_LOCATIONS.AI_BUILDER_TAB);
|
|
833
|
+
|
|
303
834
|
isStopped.value = false;
|
|
304
835
|
aiThreadStore.isProcessing = true;
|
|
305
836
|
|
|
@@ -322,6 +853,12 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
322
853
|
|
|
323
854
|
threadItems.value.push(userMessage);
|
|
324
855
|
|
|
856
|
+
// Checkpoint creation is now conditional and happens inside
|
|
857
|
+
// `applyToolCallsAsync` — it fires only when the response carries at
|
|
858
|
+
// least one change-producing tool call, and only once per user prompt
|
|
859
|
+
// (idempotent across the multi-turn retry flow). See the "Undo snapshot
|
|
860
|
+
// lifecycle (WL9)" block near the top of this composable.
|
|
861
|
+
|
|
325
862
|
const agentMessageId = (Date.now() + 1).toString();
|
|
326
863
|
|
|
327
864
|
currentTypingMessageId.value = agentMessageId;
|
|
@@ -340,7 +877,7 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
340
877
|
aiLogger.startGroup();
|
|
341
878
|
aiLogger.log('sendMessageAsync', { messageOptions });
|
|
342
879
|
|
|
343
|
-
const result = await callApiBuildAsync(text || '');
|
|
880
|
+
const result = await callApiBuildAsync(text || '', userMessage.id);
|
|
344
881
|
|
|
345
882
|
if (isStopped.value) return;
|
|
346
883
|
|
|
@@ -352,7 +889,13 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
352
889
|
if (result.text) {
|
|
353
890
|
threadItems.value[messageIndex].text = result.text;
|
|
354
891
|
threadItems.value[messageIndex].isTyping = false;
|
|
355
|
-
|
|
892
|
+
|
|
893
|
+
// Honour the isError flag set by error paths in callApiBuildAsync
|
|
894
|
+
// (apply-failure catch, server_error / build_error WS frames).
|
|
895
|
+
// Without this, error text would render as a normal AI reply.
|
|
896
|
+
if (result.isError) {
|
|
897
|
+
threadItems.value[messageIndex].isError = true;
|
|
898
|
+
}
|
|
356
899
|
} else {
|
|
357
900
|
threadItems.value[messageIndex].text =
|
|
358
901
|
'Failed to apply changes. Please try again.';
|
|
@@ -374,11 +917,22 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
374
917
|
if (messageIndex !== -1) {
|
|
375
918
|
threadItems.value[messageIndex].text = isConnectionError
|
|
376
919
|
? SERVICE_UNAVAILABLE_MESSAGE
|
|
377
|
-
: error
|
|
378
|
-
'An error occurred. Please try again.';
|
|
920
|
+
: pickErrorMessage(error);
|
|
379
921
|
threadItems.value[messageIndex].isTyping = false;
|
|
380
922
|
threadItems.value[messageIndex].isError = true;
|
|
381
923
|
}
|
|
924
|
+
|
|
925
|
+
// REL 1 item (4): report request-path failures to Raygun so we can
|
|
926
|
+
// see network / proxy / upstream errors separately from pipeline
|
|
927
|
+
// failures (which are reported server-side via build_error).
|
|
928
|
+
const report = buildSubmitErrorReport(error, { stage: 'submit' });
|
|
929
|
+
|
|
930
|
+
if (report) {
|
|
931
|
+
raygunUtil.safeSendRaygunError(report.error, {
|
|
932
|
+
tags: report.tags,
|
|
933
|
+
customData: report.customData,
|
|
934
|
+
});
|
|
935
|
+
}
|
|
382
936
|
} finally {
|
|
383
937
|
currentTypingMessageId.value = null;
|
|
384
938
|
isProcessing.value = false;
|
|
@@ -386,8 +940,6 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
386
940
|
// was destroyed (tab switch) and the writable computed setter is inert.
|
|
387
941
|
aiThreadStore.isProcessing = false;
|
|
388
942
|
}
|
|
389
|
-
|
|
390
|
-
console.log('aiLogger', aiLogger);
|
|
391
943
|
};
|
|
392
944
|
|
|
393
945
|
const deleteMessage = (messageId: string) => {
|
|
@@ -399,6 +951,8 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
399
951
|
};
|
|
400
952
|
|
|
401
953
|
const stopGenerationAsync = async () => {
|
|
954
|
+
eventTracker.sendAiStopGenerationEvent(AI_CTA_LOCATIONS.AI_BUILDER_TAB);
|
|
955
|
+
|
|
402
956
|
isStopped.value = true;
|
|
403
957
|
aiThreadStore.isBuildInProgress = false;
|
|
404
958
|
|
|
@@ -418,31 +972,34 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
|
|
|
418
972
|
|
|
419
973
|
aiThreadStore.stopGeneration = stopGenerationAsync;
|
|
420
974
|
|
|
421
|
-
onUnmounted(() => {
|
|
422
|
-
if (aiThreadStore.stopGeneration === stopGenerationAsync) {
|
|
423
|
-
aiThreadStore.stopGeneration = null;
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
|
|
427
975
|
const formattedMessages = computed(() => {
|
|
428
976
|
const initialMessageCount = hasStoredMessages
|
|
429
977
|
? aiThreadStore.messages.length
|
|
430
978
|
: 1;
|
|
431
979
|
|
|
432
|
-
return threadItems.value.map((msg, index) =>
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
980
|
+
return threadItems.value.map((msg, index) => {
|
|
981
|
+
return {
|
|
982
|
+
type: msg.type,
|
|
983
|
+
props: {
|
|
984
|
+
...(msg.type === 'text' && { text: msg.text }),
|
|
985
|
+
...(msg.type === 'image' && { images: msg.images, text: msg.text }),
|
|
986
|
+
isUser: msg.role === 'user',
|
|
987
|
+
isAi: msg.role === 'agent',
|
|
988
|
+
showIcon: msg.role === 'agent',
|
|
989
|
+
isTyping: msg.isTyping,
|
|
990
|
+
isResponse: index >= 1,
|
|
991
|
+
disableAnimation: index < initialMessageCount,
|
|
992
|
+
langChainUrl: msg.langChainUrl,
|
|
993
|
+
...(msg.type === 'text' &&
|
|
994
|
+
msg.isTyping && {
|
|
995
|
+
initialMessage: (msg as TextThreadMessage).initialMessage,
|
|
996
|
+
planItems: (msg as TextThreadMessage).planItems ?? [],
|
|
997
|
+
completedItemIds:
|
|
998
|
+
(msg as TextThreadMessage).completedItemIds ?? [],
|
|
999
|
+
}),
|
|
1000
|
+
} as Record<string, unknown>,
|
|
1001
|
+
};
|
|
1002
|
+
});
|
|
446
1003
|
});
|
|
447
1004
|
|
|
448
1005
|
return {
|