@designcrowd/library.brand-page 5.16.44 → 5.16.45

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 (47) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-TuSRObJQ.js → CreatedOnBrandCrowd-de-de-C7sXkIBb.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-BX3P38jt.js → CreatedOnBrandCrowd-en-us-DhtnVceC.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-CwpQMqO0.js → CreatedOnBrandCrowd-es-es-BptLAnJw.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-C4u38Ym4.js → CreatedOnBrandCrowd-fr-ca-Dhd8PVKw.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-C4u38Ym4.js → CreatedOnBrandCrowd-fr-fr-Dhd8PVKw.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-B4-ro8pc.js → CreatedOnBrandCrowd-pt-br-X5oQu67A.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-B4-ro8pc.js → CreatedOnBrandCrowd-pt-pt-X5oQu67A.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-Cl4pKvXX.js → CreatedOnDcom-de-de-D_2wZOXC.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us--Zj-GmSd.js → CreatedOnDcom-en-us-DYWjuLNy.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-D7Kogzlt.js → CreatedOnDcom-es-es-DNcNR11_.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-Br2Jrkb3.js → CreatedOnDcom-fr-ca-DqK95jxt.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-CrO42IpU.js → CreatedOnDcom-fr-fr-CAaFVidi.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-ClEMPT95.js → CreatedOnDcom-pt-br-80W59bFj.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-sYoiCgDC.js → CreatedOnDcom-pt-pt-BTELqfP0.js} +1 -1
  15. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +2 -0
  16. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +2 -0
  17. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +2 -0
  18. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +2 -0
  19. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +2 -0
  20. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +2 -0
  21. package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +2 -0
  22. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +2 -0
  23. package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +2 -0
  24. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +3 -0
  25. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.js +2 -0
  26. package/lib/src/brand-page-maker/stores/user/user-store.js +1 -0
  27. package/lib/src/clients/ai-assistant-client.d.ts +113 -0
  28. package/lib/src/clients/ai-assistant-client.js +117 -0
  29. package/lib/src/constants/ui.d.ts +1 -0
  30. package/lib/src/index.mjs +1 -1
  31. package/lib/src/{lib-pLG4c-nD.js → lib-xn7dUNvb.js} +109 -105
  32. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.stories.js +1 -0
  33. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +2 -0
  34. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +2 -0
  35. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +3 -0
  36. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +2 -0
  37. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +2 -0
  38. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +2 -0
  39. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +2 -0
  40. package/package.json +2 -2
  41. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +2 -0
  42. package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.ts +3 -0
  43. package/src/brand-page-maker/stores/user/user-store.ts +1 -0
  44. package/src/clients/ai-assistant-client.ts +268 -0
  45. package/src/constants/ui.ts +1 -0
  46. package/src/shared/content-settings/components/content-settings/ContentSettings.stories.ts +1 -0
  47. package/src/shared/content-settings/components/generate-ai-button/GenerateAiButton.vue +7 -1
@@ -26,6 +26,7 @@ export const Sample = () => {
26
26
  canUseGettyVideos: true,
27
27
  canUseGoogleAdsense: false,
28
28
  googleAdsenseEnabled: false,
29
+ canUseAITextGenerator: true,
29
30
  };
30
31
  const pinia = setupStoryPinia();
31
32
  const contentSettingsStore = useUiContentSettingsStore();
@@ -84,6 +84,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
84
84
  isSubsequentPublish: boolean;
85
85
  isContextualUpgradeModalVisible: boolean;
86
86
  contextualUpgradeType: string | number | symbol | undefined;
87
+ requestVerificationToken?: string | undefined;
87
88
  } & import("pinia").PiniaCustomStateProperties<import("../../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
88
89
  isUserDraft: (state: {
89
90
  availableTaxes: any[];
@@ -145,6 +146,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
145
146
  isSubsequentPublish: boolean;
146
147
  isContextualUpgradeModalVisible: boolean;
147
148
  contextualUpgradeType: string | number | symbol | undefined;
149
+ requestVerificationToken?: string | undefined;
148
150
  } & import("pinia").PiniaCustomStateProperties<import("../../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
149
151
  }, {
150
152
  initialise(payload: import("../../../../brand-page-maker/stores/brandpage-container").InitialisePayload): void;
@@ -364,6 +364,7 @@ declare const _default: import("vue").DefineComponent<{}, {
364
364
  isSubsequentPublish: boolean;
365
365
  isContextualUpgradeModalVisible: boolean;
366
366
  contextualUpgradeType: string | number | symbol | undefined;
367
+ requestVerificationToken?: string | undefined;
367
368
  } & import("pinia").PiniaCustomStateProperties<import("../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
368
369
  isUserDraft: (state: {
369
370
  availableTaxes: any[];
@@ -425,6 +426,7 @@ declare const _default: import("vue").DefineComponent<{}, {
425
426
  isSubsequentPublish: boolean;
426
427
  isContextualUpgradeModalVisible: boolean;
427
428
  contextualUpgradeType: string | number | symbol | undefined;
429
+ requestVerificationToken?: string | undefined;
428
430
  } & import("pinia").PiniaCustomStateProperties<import("../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
429
431
  }, {
430
432
  initialise(payload: import("../../../brand-page-maker/stores/brandpage-container").InitialisePayload): void;
@@ -3103,6 +3103,7 @@ declare const _default: import("vue").DefineComponent<{}, {
3103
3103
  maxNumOfPages: number;
3104
3104
  canUseRichTextEditor: boolean;
3105
3105
  canUseGettyVideos: boolean;
3106
+ canUseAITextGenerator: boolean;
3106
3107
  canUseGoogleAdsense: boolean;
3107
3108
  googleAdsenseEnabled: boolean;
3108
3109
  }>;
@@ -4528,6 +4529,7 @@ declare const _default: import("vue").DefineComponent<{}, {
4528
4529
  isSubsequentPublish: boolean;
4529
4530
  isContextualUpgradeModalVisible: boolean;
4530
4531
  contextualUpgradeType: string | number | symbol | undefined;
4532
+ requestVerificationToken?: string | undefined;
4531
4533
  } & import("pinia").PiniaCustomStateProperties<import("../../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
4532
4534
  isUserDraft: (state: {
4533
4535
  availableTaxes: any[];
@@ -4589,6 +4591,7 @@ declare const _default: import("vue").DefineComponent<{}, {
4589
4591
  isSubsequentPublish: boolean;
4590
4592
  isContextualUpgradeModalVisible: boolean;
4591
4593
  contextualUpgradeType: string | number | symbol | undefined;
4594
+ requestVerificationToken?: string | undefined;
4592
4595
  } & import("pinia").PiniaCustomStateProperties<import("../../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
4593
4596
  }, {
4594
4597
  initialise(payload: import("../../../../brand-page-maker/stores/brandpage-container").InitialisePayload): void;
@@ -3940,6 +3940,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
3940
3940
  isSubsequentPublish: boolean;
3941
3941
  isContextualUpgradeModalVisible: boolean;
3942
3942
  contextualUpgradeType: string | number | symbol | undefined;
3943
+ requestVerificationToken?: string | undefined;
3943
3944
  } & import("pinia").PiniaCustomStateProperties<import("../../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
3944
3945
  isUserDraft: (state: {
3945
3946
  availableTaxes: any[];
@@ -4001,6 +4002,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
4001
4002
  isSubsequentPublish: boolean;
4002
4003
  isContextualUpgradeModalVisible: boolean;
4003
4004
  contextualUpgradeType: string | number | symbol | undefined;
4005
+ requestVerificationToken?: string | undefined;
4004
4006
  } & import("pinia").PiniaCustomStateProperties<import("../../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
4005
4007
  }, {
4006
4008
  initialise(payload: import("../../../../brand-page-maker/stores/brandpage-container").InitialisePayload): void;
@@ -261,6 +261,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
261
261
  isSubsequentPublish: boolean;
262
262
  isContextualUpgradeModalVisible: boolean;
263
263
  contextualUpgradeType: string | number | symbol | undefined;
264
+ requestVerificationToken?: string | undefined;
264
265
  } & import("pinia").PiniaCustomStateProperties<import("../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
265
266
  isUserDraft: (state: {
266
267
  availableTaxes: any[];
@@ -322,6 +323,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
322
323
  isSubsequentPublish: boolean;
323
324
  isContextualUpgradeModalVisible: boolean;
324
325
  contextualUpgradeType: string | number | symbol | undefined;
326
+ requestVerificationToken?: string | undefined;
325
327
  } & import("pinia").PiniaCustomStateProperties<import("../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
326
328
  }, {
327
329
  initialise(payload: import("../../brand-page-maker/stores/brandpage-container").InitialisePayload): void;
@@ -77,6 +77,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
77
77
  isSubsequentPublish: boolean;
78
78
  isContextualUpgradeModalVisible: boolean;
79
79
  contextualUpgradeType: string | number | symbol | undefined;
80
+ requestVerificationToken?: string | undefined;
80
81
  } & import("pinia").PiniaCustomStateProperties<import("../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
81
82
  isUserDraft: (state: {
82
83
  availableTaxes: any[];
@@ -138,6 +139,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
138
139
  isSubsequentPublish: boolean;
139
140
  isContextualUpgradeModalVisible: boolean;
140
141
  contextualUpgradeType: string | number | symbol | undefined;
142
+ requestVerificationToken?: string | undefined;
141
143
  } & import("pinia").PiniaCustomStateProperties<import("../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
142
144
  }, {
143
145
  initialise(payload: import("../../../brand-page-maker/stores/brandpage-container").InitialisePayload): void;
@@ -668,6 +668,7 @@ declare const _default: import("vue").DefineComponent<{}, {
668
668
  isSubsequentPublish: boolean;
669
669
  isContextualUpgradeModalVisible: boolean;
670
670
  contextualUpgradeType: string | number | symbol | undefined;
671
+ requestVerificationToken?: string | undefined;
671
672
  } & import("pinia").PiniaCustomStateProperties<import("../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
672
673
  isUserDraft: (state: {
673
674
  availableTaxes: any[];
@@ -729,6 +730,7 @@ declare const _default: import("vue").DefineComponent<{}, {
729
730
  isSubsequentPublish: boolean;
730
731
  isContextualUpgradeModalVisible: boolean;
731
732
  contextualUpgradeType: string | number | symbol | undefined;
733
+ requestVerificationToken?: string | undefined;
732
734
  } & import("pinia").PiniaCustomStateProperties<import("../../../brand-page-maker/stores").BrandPageContainerState>) => boolean;
733
735
  }, {
734
736
  initialise(payload: import("../../../brand-page-maker/stores/brandpage-container").InitialisePayload): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@designcrowd/library.brand-page",
3
3
  "description": "A collection of Maker/Designer applications",
4
- "version": "5.16.44",
4
+ "version": "5.16.45",
5
5
  "author": "Design.com Eng",
6
6
  "license": "UNLICENSED",
7
7
  "type": "module",
@@ -52,7 +52,7 @@
52
52
  "cy:run:ci": "cypress run --browser chrome --headless --config \"baseUrl=https://maker.designcrowd.ninja\"",
53
53
  "cy:report": "npx junit-merge -o ./cypress/results/report.xml ./cypress/results/*.xml",
54
54
  "cy:open": "cypress open",
55
- "pre-push": "NODE_ENV=production lint-staged"
55
+ "pre-push": "npm run test"
56
56
  },
57
57
  "dependencies": {
58
58
  "@ckpack/vue-color": "1.4.1",
@@ -90,6 +90,7 @@ export const userFeatures: UserFeatures = {
90
90
  canUseGettyVideos: false,
91
91
  canUseGoogleAdsense: false,
92
92
  googleAdsenseEnabled: false,
93
+ canUseAITextGenerator: true,
93
94
  };
94
95
 
95
96
  export const BRAND_PAGE: BrandPageData = {
@@ -2125,6 +2126,7 @@ export const USER_FEATURES: UIUserFeatures = Object.freeze({
2125
2126
  canUseGettyVideos: true,
2126
2127
  canUseGoogleAdsense: false,
2127
2128
  googleAdsenseEnabled: true,
2129
+ canUseAITextGenerator: true,
2128
2130
  } as UIUserFeatures);
2129
2131
 
2130
2132
  export const INITIALISE_PAYLOAD: InitialisePayload = Object.freeze({
@@ -89,6 +89,7 @@ export interface BrandPageContainerState {
89
89
  isSubsequentPublish: boolean;
90
90
  isContextualUpgradeModalVisible: boolean;
91
91
  contextualUpgradeType: WebsiteContextUpgradeType | undefined;
92
+ requestVerificationToken?: string;
92
93
  }
93
94
 
94
95
  export type GettyVideoEditorViewModelResponse = {
@@ -170,6 +171,7 @@ export const useBrandPageContainerStore = defineStore('brandPageContainer', {
170
171
  isSubsequentPublish: false,
171
172
  isContextualUpgradeModalVisible: false,
172
173
  contextualUpgradeType: undefined,
174
+ requestVerificationToken: undefined,
173
175
  };
174
176
  },
175
177
  actions: {
@@ -237,6 +239,7 @@ export const useBrandPageContainerStore = defineStore('brandPageContainer', {
237
239
  this.showPublishOptionModal = payload.showPublishOptionModal;
238
240
  this.abTests = getAbTestResults(payload.abTests || {});
239
241
  this.isSubsequentPublish = payload.isSubsequentPublish;
242
+ this.requestVerificationToken = payload.requestVerificationToken;
240
243
 
241
244
  const logoStore = useLogoStore();
242
245
 
@@ -31,6 +31,7 @@ export const useBrandPageMakerUserStore = defineStore('brandPageMakerUser', {
31
31
  canUsePremiumFontFamily: false,
32
32
  maxNumOfPages: DEFAULT_MAX_NUM_OF_PAGES,
33
33
  canUseGettyVideos: false,
34
+ canUseAITextGenerator: false,
34
35
  canUseGoogleAdsense: false,
35
36
  googleAdsenseEnabled: false,
36
37
  },
@@ -0,0 +1,268 @@
1
+ import axios, { type AxiosError } from 'axios';
2
+ import { useBrandPageContainerStore } from '../brand-page-maker/stores/brandpage-container/brand-page-container-store';
3
+
4
+ // Request types
5
+ export interface AiAssistantMessage {
6
+ type: 'text' | 'image';
7
+ text?: string;
8
+ image_url?: {
9
+ url: string;
10
+ };
11
+ }
12
+
13
+ export interface AiAssistantToolCall {
14
+ toolId: string;
15
+ toolName: string;
16
+ input: Record<string, unknown>;
17
+ }
18
+
19
+ export interface AiAssistantToolCallOutput {
20
+ toolId: string;
21
+ success: boolean;
22
+ }
23
+
24
+ export interface CreateThreadRequest {
25
+ message: string;
26
+ agentType?: string;
27
+ elementIds?: string[];
28
+ designSnapshot?: string;
29
+ objectSnapshot?: string;
30
+ designMetadata?: Record<string, unknown>;
31
+ logoTemplateToken?: string;
32
+ logoDraftToken?: string;
33
+ }
34
+
35
+ export interface SendMessageRequest {
36
+ message: string;
37
+ agentType?: string;
38
+ elementIds?: string[];
39
+ designSnapshot?: string;
40
+ objectSnapshot?: string;
41
+ designMetadata?: Record<string, unknown>;
42
+ logoTemplateToken?: string;
43
+ logoDraftToken?: string;
44
+ }
45
+
46
+ export interface ToolSubmissionRequest {
47
+ runId: string;
48
+ toolCalls: AiAssistantToolCallOutput[];
49
+ message?: string;
50
+ agentType?: string;
51
+ elementIds?: string[];
52
+ designSnapshot?: string;
53
+ objectSnapshot?: string;
54
+ designMetadata?: Record<string, unknown>;
55
+ }
56
+
57
+ // Response types
58
+ export type AiAssistantResponseType = 'message' | 'tool_calls';
59
+
60
+ export interface AiAssistantResponse {
61
+ threadId: string;
62
+ runId?: string;
63
+ type: AiAssistantResponseType;
64
+ content: string | null;
65
+ toolCalls: AiAssistantToolCall[] | null;
66
+ totalPaidToolCalls: number;
67
+ timestamp: number;
68
+ }
69
+
70
+ export interface AiAssistantErrorResponse {
71
+ error: string;
72
+ errorCode?: string;
73
+ errorParams?: Record<string, unknown>;
74
+ }
75
+
76
+ export interface AiAssistantCreditInfo {
77
+ remaining: number;
78
+ usage: number;
79
+ }
80
+
81
+ // Endpoints - using relative URLs (same as design-template-client pattern)
82
+ export const getCreateThreadEndpoint = (): string => `/api/assistant/thread`;
83
+
84
+ export const getSendMessageEndpoint = (threadId: string): string =>
85
+ `/api/assistant/thread/${threadId}`;
86
+
87
+ export const getToolSubmissionEndpoint = (threadId: string): string =>
88
+ `/api/assistant/thread/${threadId}/tool-submission`;
89
+
90
+ export class AiAssistantClient {
91
+ private getRequestVerificationToken(): string {
92
+ const brandPageContainerStore = useBrandPageContainerStore();
93
+
94
+ return brandPageContainerStore.requestVerificationToken || '';
95
+ }
96
+
97
+ private getRequestHeaders(): Record<string, string> {
98
+ const token = this.getRequestVerificationToken();
99
+
100
+ return {
101
+ 'Content-Type': 'application/json',
102
+ ...(token && { RequestVerificationToken: token }),
103
+ };
104
+ }
105
+
106
+ private extractCreditInfo(
107
+ headers: Record<string, string>,
108
+ ): AiAssistantCreditInfo {
109
+ return {
110
+ remaining: parseInt(headers['x-ai-credit-remaining'] || '0', 10),
111
+ usage: parseInt(headers['x-ai-credit-usage'] || '0', 10),
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Create a new AI Assistant thread
117
+ */
118
+ async createThread(request: CreateThreadRequest): Promise<{
119
+ response: AiAssistantResponse;
120
+ creditInfo: AiAssistantCreditInfo;
121
+ }> {
122
+ try {
123
+ const response = await axios.post<AiAssistantResponse>(
124
+ getCreateThreadEndpoint(),
125
+ request,
126
+ {
127
+ withCredentials: true,
128
+ headers: this.getRequestHeaders(),
129
+ },
130
+ );
131
+
132
+ return {
133
+ response: response.data,
134
+ creditInfo: this.extractCreditInfo(
135
+ response.headers as Record<string, string>,
136
+ ),
137
+ };
138
+ } catch (error) {
139
+ this.handleError(error as AxiosError<AiAssistantErrorResponse>);
140
+ throw error;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Send a message to an existing thread
146
+ */
147
+ async sendMessage(
148
+ threadId: string,
149
+ request: SendMessageRequest,
150
+ ): Promise<{
151
+ response: AiAssistantResponse;
152
+ creditInfo: AiAssistantCreditInfo;
153
+ }> {
154
+ if (!threadId) {
155
+ throw new Error('threadId is required');
156
+ }
157
+
158
+ try {
159
+ const response = await axios.post<AiAssistantResponse>(
160
+ getSendMessageEndpoint(threadId),
161
+ request,
162
+ {
163
+ withCredentials: true,
164
+ headers: this.getRequestHeaders(),
165
+ },
166
+ );
167
+
168
+ return {
169
+ response: response.data,
170
+ creditInfo: this.extractCreditInfo(
171
+ response.headers as Record<string, string>,
172
+ ),
173
+ };
174
+ } catch (error) {
175
+ this.handleError(error as AxiosError<AiAssistantErrorResponse>);
176
+ throw error;
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Submit tool call outputs to a thread
182
+ */
183
+ async submitToolCallOutput(
184
+ threadId: string,
185
+ request: ToolSubmissionRequest,
186
+ ): Promise<{
187
+ response: AiAssistantResponse;
188
+ creditInfo: AiAssistantCreditInfo;
189
+ }> {
190
+ if (!threadId) {
191
+ throw new Error('threadId is required');
192
+ }
193
+
194
+ try {
195
+ const response = await axios.post<AiAssistantResponse>(
196
+ getToolSubmissionEndpoint(threadId),
197
+ request,
198
+ {
199
+ withCredentials: true,
200
+ headers: this.getRequestHeaders(),
201
+ },
202
+ );
203
+
204
+ return {
205
+ response: response.data,
206
+ creditInfo: this.extractCreditInfo(
207
+ response.headers as Record<string, string>,
208
+ ),
209
+ };
210
+ } catch (error) {
211
+ this.handleError(error as AxiosError<AiAssistantErrorResponse>);
212
+ throw error;
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Convenience method: Create thread or continue existing thread
218
+ * Useful for managing thread lifecycle in components
219
+ */
220
+ async sendPrompt(
221
+ message: string,
222
+ options: {
223
+ threadId?: string;
224
+ agentType?: string;
225
+ elementIds?: string[];
226
+ designSnapshot?: string;
227
+ objectSnapshot?: string;
228
+ designMetadata?: Record<string, unknown>;
229
+ logoTemplateToken?: string;
230
+ logoDraftToken?: string;
231
+ } = {},
232
+ ): Promise<{
233
+ response: AiAssistantResponse;
234
+ creditInfo: AiAssistantCreditInfo;
235
+ }> {
236
+ const { threadId, ...requestOptions } = options;
237
+ const request = { message, ...requestOptions };
238
+
239
+ if (threadId) {
240
+ return this.sendMessage(threadId, request);
241
+ }
242
+
243
+ return this.createThread(request);
244
+ }
245
+
246
+ private handleError(error: AxiosError<AiAssistantErrorResponse>): void {
247
+ if (process.env.NODE_ENV !== 'production') {
248
+ const errorData = error.response?.data;
249
+ const statusCode = error.response?.status;
250
+
251
+ if (statusCode === 429) {
252
+ console.error(
253
+ 'AI Assistant: Insufficient credits',
254
+ errorData?.errorCode,
255
+ );
256
+ } else if (statusCode === 400) {
257
+ console.error('AI Assistant: Bad request', errorData?.error);
258
+ } else {
259
+ console.error('AI Assistant error:', errorData?.error || error.message);
260
+ }
261
+ }
262
+ }
263
+ }
264
+
265
+ let _instance: AiAssistantClient | undefined;
266
+
267
+ export const getInstance = (): AiAssistantClient =>
268
+ _instance ? _instance : (_instance = new AiAssistantClient());
@@ -12,6 +12,7 @@ export interface UIUserFeatures {
12
12
  maxNumOfPages: number;
13
13
  canUseRichTextEditor: boolean;
14
14
  canUseGettyVideos: boolean;
15
+ canUseAITextGenerator: boolean;
15
16
  canUseGoogleAdsense: boolean;
16
17
  googleAdsenseEnabled: boolean;
17
18
  }
@@ -32,6 +32,7 @@ export const Sample = (): any => {
32
32
  canUseGettyVideos: true,
33
33
  canUseGoogleAdsense: false,
34
34
  googleAdsenseEnabled: false,
35
+ canUseAITextGenerator: true,
35
36
  };
36
37
 
37
38
  const pinia = setupStoryPinia();
@@ -31,6 +31,7 @@ import GenerateAiModal from '../generate-ai-modal/GenerateAiModal.vue';
31
31
  import { commonTr } from '../../../../composables/useTranslate';
32
32
  import {
33
33
  useBrandPageContainerStore,
34
+ useBrandPageMakerUserStore,
34
35
  useSnapshotStore,
35
36
  } from '../../../../brand-page-maker/stores';
36
37
  import type { AbTestResult } from '../../../../brand-page/models/brand-page-context.model';
@@ -61,15 +62,20 @@ const emit = defineEmits<{
61
62
  const brandPageContainerStore = useBrandPageContainerStore();
62
63
  const eventTracker = useEventTracker();
63
64
  const snapshotStore = useSnapshotStore();
65
+ const userStore = useBrandPageMakerUserStore();
64
66
 
65
67
  const abTests = computed(() => brandPageContainerStore.abTests);
66
68
 
69
+ const canUseAITextGenerator = computed(
70
+ () => userStore.features.canUseAITextGenerator,
71
+ );
72
+
67
73
  const showButton = computed(() => {
68
74
  const abTest = abTests.value
69
75
  .bp_4572_ai_text_generation_post_purchase as unknown as
70
76
  | AbTestResult
71
77
  | undefined;
72
- return abTest?.isVariation ?? false;
78
+ return canUseAITextGenerator.value && abTest?.isVariation;
73
79
  });
74
80
 
75
81
  const generateTextModalVisible = ref(false);