@designcrowd/library.brand-page 6.1.0-websitev2-dev-2 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-D_qQ0sPW.js → CreatedOnBrandCrowd-de-de-CW5bD06L.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-CNMxavFU.js → CreatedOnBrandCrowd-en-us-CofG4e3V.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-Cyaiw1-R.js → CreatedOnBrandCrowd-es-es-XpL0-LWx.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-DbniOtuE.js → CreatedOnBrandCrowd-fr-ca-dlVN3zaX.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-DbniOtuE.js → CreatedOnBrandCrowd-fr-fr-dlVN3zaX.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-DI3JfwR8.js → CreatedOnBrandCrowd-pt-br-98gqdDVF.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-DI3JfwR8.js → CreatedOnBrandCrowd-pt-pt-98gqdDVF.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-DqBzJ_O9.js → CreatedOnDcom-de-de-Dbmlc0i1.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-B8uZjYMi.js → CreatedOnDcom-en-us-Dy9z7h1o.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-BOLxcx3H.js → CreatedOnDcom-es-es-B3ksvNj9.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-CbGneDvu.js → CreatedOnDcom-fr-ca-CC9ieRzX.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-B-UwXpW3.js → CreatedOnDcom-fr-fr-BtnGJjF2.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-CRPrLx5_.js → CreatedOnDcom-pt-br-DT4doFYO.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-4p2rdAnc.js → CreatedOnDcom-pt-pt-CloXyOe6.js} +1 -1
  15. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +3 -5
  16. package/lib/src/brand-page/components/mailing-list/MailingList.mixin.js +4 -6
  17. package/lib/src/brand-page/components/page-footer/PageFooter.mixin.d.ts +0 -1
  18. package/lib/src/brand-page/components/page-footer/PageFooter.mixin.js +1 -4
  19. package/lib/src/brand-page/components/payment/Payment.mixin.js +3 -4
  20. package/lib/src/brand-page/components/payment/__fixtures__/request-payment.d.ts +0 -1
  21. package/lib/src/brand-page/components/payment/__fixtures__/support-me.d.ts +0 -1
  22. package/lib/src/brand-page/components/payment/request-payment/RequestPayment.mixin.d.ts +0 -1
  23. package/lib/src/brand-page/components/payment/request-payment/RequestPayment.mixin.js +1 -4
  24. package/lib/src/brand-page/composables/consent.composable.d.ts +0 -2
  25. package/lib/src/brand-page/composables/consent.composable.js +3 -9
  26. package/lib/src/brand-page/models/brand-page-context.model.d.ts +0 -7
  27. package/lib/src/brand-page/utils/context.util.d.ts +0 -8
  28. package/lib/src/brand-page/utils/context.util.js +0 -8
  29. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +3 -7
  30. package/lib/src/clients/ai-assistant-client.d.ts +0 -1
  31. package/lib/src/clients/ai-assistant-client.js +0 -1
  32. package/lib/src/css/library.brand-page-brandCrowd.min.css +60 -60
  33. package/lib/src/css/library.brand-page-brandPage.min.css +60 -60
  34. package/lib/src/css/library.brand-page-designCom.min.css +60 -60
  35. package/lib/src/css/library.brand-page-preview.min.css +60 -60
  36. package/lib/src/index.d.ts +0 -1
  37. package/lib/src/index.mjs +217 -220
  38. package/lib/src/{lib-DhEJMZFn.js → lib-D7ubPlpu.js} +18168 -18341
  39. package/lib/src/lib.js +0 -1
  40. package/lib/src/metadata-builder/builders/global-styles-builder.js +0 -16
  41. package/lib/src/metadata-builder/builders/section-builders/index.d.ts +0 -1
  42. package/lib/src/metadata-builder/builders/section-builders/index.js +0 -1
  43. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +2 -5
  44. package/lib/src/metadata-builder/builders/section-field-maps.js +0 -15
  45. package/lib/src/metadata-builder/builders/section-field-utils.d.ts +1 -6
  46. package/lib/src/metadata-builder/builders/section-field-utils.js +12 -48
  47. package/lib/src/metadata-builder/metadata-executor.js +1 -3
  48. package/lib/src/metadata-builder/models.d.ts +0 -2
  49. package/lib/src/metadata-builder/models.js +0 -2
  50. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +0 -1
  51. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +0 -4
  52. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +2 -14
  53. package/lib/src/utils/minify-brand-page-metadata.js +0 -7
  54. package/package.json +1 -1
  55. package/src/brand-page/components/contact-form/ContactForm.mixin.ts +2 -7
  56. package/src/brand-page/components/cookie-consent/CookieConsent.vue +0 -1
  57. package/src/brand-page/components/mailing-list/MailingList.mixin.ts +3 -8
  58. package/src/brand-page/components/navigation/navigation-carousel/NavigationCarousel.vue +1 -5
  59. package/src/brand-page/components/page-footer/PageFooter.mixin.ts +1 -4
  60. package/src/brand-page/components/page-footer/PageFooter.vue +1 -1
  61. package/src/brand-page/components/payment/Payment.mixin.ts +3 -4
  62. package/src/brand-page/components/payment/request-payment/RequestPayment.mixin.ts +1 -8
  63. package/src/brand-page/components/payment/request-payment/RequestPayment.vue +1 -1
  64. package/src/brand-page/components/save-contact/SaveContactWidget.vue +1 -2
  65. package/src/brand-page/components/shop/Shop.vue +3 -6
  66. package/src/brand-page/components/social-links/SocialLinks.vue +2 -5
  67. package/src/brand-page/composables/consent.composable.ts +4 -18
  68. package/src/brand-page/models/brand-page-context.model.ts +0 -7
  69. package/src/brand-page/utils/context.util.ts +0 -10
  70. package/src/brand-page-maker/components/ai-website-builder/AiWebsiteBuilder.vue +1 -1
  71. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +3 -7
  72. package/src/clients/ai-assistant-client.ts +0 -1
  73. package/src/lib.ts +0 -1
  74. package/src/metadata-builder/builders/global-styles-builder.ts +0 -21
  75. package/src/metadata-builder/builders/section-builders/index.ts +0 -1
  76. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +2 -5
  77. package/src/metadata-builder/builders/section-field-maps.ts +0 -18
  78. package/src/metadata-builder/builders/section-field-utils.ts +18 -69
  79. package/src/metadata-builder/metadata-executor.ts +0 -3
  80. package/src/metadata-builder/models.ts +0 -2
  81. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +0 -4
  82. package/src/metadata-builder/parsers/section-tool-arguments.ts +1 -14
  83. package/src/utils/minify-brand-page-metadata.ts +0 -8
  84. package/lib/src/metadata-builder/builders/global-font-propagation.d.ts +0 -28
  85. package/lib/src/metadata-builder/builders/global-font-propagation.js +0 -125
  86. package/lib/src/metadata-builder/builders/section-builders/support-me-builder.d.ts +0 -8
  87. package/lib/src/metadata-builder/builders/section-builders/support-me-builder.js +0 -25
  88. package/lib/src/metadata-builder/builders/section-rules/support-me.rules.d.ts +0 -2
  89. package/lib/src/metadata-builder/builders/section-rules/support-me.rules.js +0 -17
  90. package/src/brand-page/utils/context.util.test.ts +0 -43
  91. package/src/metadata-builder/builders/global-font-propagation.test.ts +0 -269
  92. package/src/metadata-builder/builders/global-font-propagation.ts +0 -205
  93. package/src/metadata-builder/builders/section-builders/support-me-builder.test.ts +0 -177
  94. package/src/metadata-builder/builders/section-builders/support-me-builder.ts +0 -43
  95. package/src/metadata-builder/builders/section-field-utils.test.ts +0 -83
  96. package/src/metadata-builder/builders/section-rules/support-me.rules.ts +0 -39
  97. package/src/utils/minify-brand-page-metadata.test.ts +0 -61
@@ -38,34 +38,26 @@ type Store = {
38
38
  updateConsent: (c: ConsentType) => void;
39
39
  featureEnabled: boolean;
40
40
  showConsentModal: boolean;
41
- /** When false, consent is not read from / written to localStorage — used in
42
- * preview so the modal re-shows on every refresh. */
43
- persist: boolean;
44
41
  };
45
42
 
46
43
  const store = reactive<Store>({
47
44
  consent: undefined,
48
45
  featureEnabled: false,
49
46
  showConsentModal: false,
50
- persist: true,
51
47
  updateConsent(c) {
52
48
  this.consent = c;
53
49
  },
54
50
  });
55
51
 
56
52
  export const useConsent = () => {
57
- const initialise = (options: {
58
- featureEnabled: boolean;
59
- persist?: boolean;
60
- }): ConsentType => {
53
+ const initialise = (options: { featureEnabled: boolean }): ConsentType => {
61
54
  if (!options.featureEnabled) {
62
55
  return undefined;
63
56
  }
64
57
 
65
58
  store.featureEnabled = options.featureEnabled;
66
- store.persist = options.persist ?? true;
67
59
 
68
- store.consent = store.persist ? getStoredConsent() : undefined;
60
+ store.consent = getStoredConsent();
69
61
 
70
62
  store.showConsentModal =
71
63
  options.featureEnabled && !accepted.value && !declined.value;
@@ -82,20 +74,14 @@ export const useConsent = () => {
82
74
  );
83
75
 
84
76
  const grant = (cb?: () => unknown) => {
85
- if (store.persist) {
86
- storeConsent('granted');
87
- }
88
-
77
+ storeConsent('granted');
89
78
  store.updateConsent('granted');
90
79
  store.showConsentModal = store.featureEnabled && !accepted.value;
91
80
  cb && cb();
92
81
  };
93
82
 
94
83
  const deny = (cb?: () => unknown) => {
95
- if (store.persist) {
96
- storeConsent('denied');
97
- }
98
-
84
+ storeConsent('denied');
99
85
  store.updateConsent('denied');
100
86
  store.showConsentModal = false;
101
87
  cb && cb();
@@ -67,13 +67,6 @@ export type MediaSizes = keyof typeof MEDIA_SIZES;
67
67
  export interface BrandPageContext {
68
68
  isTemplate: boolean;
69
69
  isPreview: boolean;
70
- /**
71
- * Rendered as a customer-facing live preview (safe/inert actions, no
72
- * watermark, ephemeral consent). Distinct from the editor `isPreview`, which
73
- * also drives editor/thumbnail-only behaviour. Use `isPreviewMode()` to gate
74
- * inert-action behaviour that applies to both.
75
- */
76
- isLivePreview?: boolean;
77
70
  /** @deprecated should be used on maker only */
78
71
  isMobile?: boolean;
79
72
  isDesignCom: boolean;
@@ -7,13 +7,3 @@ export const getSlugFromContext = (context: BrandPageContext) => {
7
7
 
8
8
  return context.brandPageSlug;
9
9
  };
10
-
11
- /**
12
- * True when interactive actions must be inert and payment/shop blocks always
13
- * shown — i.e. the render is a preview, not a live site. Covers both the
14
- * editor preview (`isPreview`) and the customer live-preview
15
- * (`isLivePreview`). Editor-only artifacts (cursor, nav interception,
16
- * placeholders) must keep reading `context.isPreview` directly.
17
- */
18
- export const isPreviewMode = (context: BrandPageContext): boolean =>
19
- !!(context.isPreview || context.isLivePreview);
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div data-component="ai-website-builder" class="ai-container">
2
+ <div class="ai-container">
3
3
  <div class="tw-px-5">
4
4
  <SideBarSettingsHeader
5
5
  title="AI Website Builder"
@@ -513,7 +513,7 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
513
513
  message,
514
514
  websiteContext,
515
515
  websiteSnapshot: snapshotStore.snapshotBase64 || undefined,
516
- agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER_V2,
516
+ agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
517
517
  userPlan,
518
518
  connectionId,
519
519
  });
@@ -524,7 +524,7 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
524
524
  message,
525
525
  websiteContext,
526
526
  websiteSnapshot: snapshotStore.snapshotBase64 || undefined,
527
- agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER_V2,
527
+ agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
528
528
  userPlan,
529
529
  connectionId,
530
530
  },
@@ -727,15 +727,11 @@ export function useAiWebsiteBuilderChat(): UseAiChatResult {
727
727
  retryCount,
728
728
  });
729
729
 
730
- // Tool submissions must go to the SAME agent that ran the thread
731
- // (AI QA 2026-07-05 T19: a mismatched agentType routes tool results
732
- // to a service with no state for the thread and fails the build).
733
- // brandSite AI is V2-only; every call site sends WEBSITE_BUILDER_V2.
734
730
  const submitResult = await client.submitToolResultsBuildAsync(
735
731
  aiThreadStore.threadId!,
736
732
  {
737
733
  toolCalls: toolResults,
738
- agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER_V2,
734
+ agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
739
735
  connectionId,
740
736
  retryCount,
741
737
  userPlan,
@@ -30,7 +30,6 @@ export interface WebsiteContext {
30
30
  // Agent types
31
31
  export const AI_ASSISTANT_AGENT_TYPES = {
32
32
  WEBSITE_BUILDER: 'website_builder',
33
- WEBSITE_BUILDER_V2: 'website_builder_v2',
34
33
  } as const;
35
34
 
36
35
  export type AiAssistantAgentType =
package/src/lib.ts CHANGED
@@ -62,7 +62,6 @@ export {
62
62
  import LogoRender from './brand-page/components/logo/LogoRender.vue';
63
63
  export { LogoRender };
64
64
  export { default as Page } from './brand-page/components/page/Page.vue';
65
- export { Loader } from '@designcrowd/fe-shared-lib';
66
65
 
67
66
  export { default as CookieConsent } from './brand-page/components/cookie-consent/CookieConsent.vue';
68
67
  export { default as SensitiveContentModal } from './brand-page/components/sensitive-content/SensitiveContentModal.vue';
@@ -14,10 +14,6 @@ import {
14
14
  GLOBAL_STYLE_CHECKS,
15
15
  TEXT_STYLE_OPTION_KEYS,
16
16
  } from './section-rules/global-styles.rules';
17
- import {
18
- collectChangedFontFamilies,
19
- propagateFontFamiliesToContents,
20
- } from './global-font-propagation';
21
17
 
22
18
  export class GlobalStylesBuilder extends BaseMetadataBuilder<UpdateGlobalStylesArguments> {
23
19
  constructor(
@@ -58,23 +54,6 @@ export class GlobalStylesBuilder extends BaseMetadataBuilder<UpdateGlobalStylesA
58
54
 
59
55
  if (args.textStyles) {
60
56
  this.applyTextStyles(model, args.textStyles);
61
-
62
- // A category font change only reaches preset-bound content by itself;
63
- // detached section styles and inline RTE font marks would still render
64
- // the old family. Propagate so the change is actually visible (BP-5780).
65
- const changedFamilies = collectChangedFontFamilies(args.textStyles);
66
-
67
- if (Object.keys(changedFamilies).length) {
68
- try {
69
- propagateFontFamiliesToContents(model, changedFamilies);
70
- } catch (error) {
71
- this.log({
72
- message:
73
- 'Global font propagation to existing contents failed — presets still updated',
74
- error,
75
- });
76
- }
77
- }
78
57
  }
79
58
 
80
59
  if (args.buttonStyles) {
@@ -5,7 +5,6 @@ export {
5
5
 
6
6
  export { BannerSectionBuilder } from './banner-section-builder';
7
7
  export { BookingSectionBuilder } from './booking-section-builder';
8
- export { SupportMeBuilder } from './support-me-builder';
9
8
  export { AboutSectionBuilder } from './about-section-builder';
10
9
  export { ContactFormBuilder } from './contact-form-builder';
11
10
  export { DividerSectionBuilder } from './divider-section-builder';
@@ -240,16 +240,13 @@ export class TextSectionBuilder extends BaseSectionBuilder<TextContent> {
240
240
  // Full replacement: discard existing items and rebuild from the payload.
241
241
  applyTextItems(content, args.items, textStyleOptions);
242
242
  } else if (this.mode === 'create') {
243
- // An EMPTY items array must not shadow the single text field — the AI emits
244
- // `items: []` alongside `text` and the section rendered blank (QA 2026-07-05,
245
- // T06: items=[] set textItems=[] and `text` was silently discarded).
246
- if (args.items && Array.isArray(args.items) && args.items.length > 0) {
243
+ if (args.items && Array.isArray(args.items)) {
247
244
  applyTextItems(content, args.items, textStyleOptions);
248
245
  } else {
249
246
  applySingleTextField(content, args, textStyleOptions);
250
247
  }
251
248
  } else if (
252
- (args.items && Array.isArray(args.items) && args.items.length > 0) ||
249
+ (args.items && Array.isArray(args.items)) ||
253
250
  args._remove_item_ids?.length
254
251
  ) {
255
252
  // Update: id-based add / edit / move / remove in a single pass.
@@ -23,7 +23,6 @@ import type {
23
23
  ImageContent,
24
24
  ButtonContent,
25
25
  ImageGalleryContent,
26
- SupportMeContent,
27
26
  } from '../../brand-page/models';
28
27
 
29
28
  export type SectionFieldMap = Record<string, string>;
@@ -176,17 +175,9 @@ const socialButtonFields = defineFieldMap<ButtonContent>({
176
175
  url: 'url',
177
176
  });
178
177
 
179
- const supportMeFields = defineFieldMap<SupportMeContent>({
180
- title: 'title',
181
- description: 'description',
182
- success_message: 'successMessage',
183
- display_style: 'displayStyle',
184
- });
185
-
186
178
  export const SECTION_FIELD_MAPS: Record<string, SectionFieldMap> = {
187
179
  'banner-section': bannerFields,
188
180
  'booking-section': bookingFields,
189
- 'support-me': supportMeFields,
190
181
  'about-section': aboutFields,
191
182
  'testimonial-section': testimonialFields,
192
183
  'images-section': imagesSectionFields,
@@ -274,14 +265,6 @@ const mailingListStyleFields = defineFieldMap<MailingListContent>({
274
265
  button_text_style: 'buttonTextStyle',
275
266
  });
276
267
 
277
- const supportMeStyleFields = defineFieldMap<SupportMeContent>({
278
- title_style: 'titleStyle',
279
- heading_style: 'headingStyle',
280
- description_style: 'descriptionStyle',
281
- item_text_style: 'itemTextStyle',
282
- button_text_style: 'buttonTextStyle',
283
- });
284
-
285
268
  const socialLinksStyleFields = defineFieldMap<SocialLinksContent>({
286
269
  icon_fill_style: 'iconFillStyles',
287
270
  icon_background_style: 'iconBackgroundStyles',
@@ -290,7 +273,6 @@ const socialLinksStyleFields = defineFieldMap<SocialLinksContent>({
290
273
  export const STYLE_FIELD_MAPS: Record<string, SectionFieldMap> = {
291
274
  'banner-section': bannerStyleFields,
292
275
  'booking-section': bookingStyleFields,
293
- 'support-me': supportMeStyleFields,
294
276
  'about-section': aboutStyleFields,
295
277
  text: textStyleFields,
296
278
  'testimonial-section': testimonialStyleFields,
@@ -9,10 +9,6 @@ import {
9
9
  isTiptapDocument,
10
10
  type SectionToolArguments,
11
11
  } from '../parsers/section-tool-arguments';
12
- import { hasRTEContent } from '../../brand-page-maker/utils';
13
- import { useExternalRichTextEditor } from '../../brand-page-maker/composables/rich-text-editor/useRichTextEditor';
14
- import { applyStyleToRTEText } from '../helpers/rte.helpers';
15
- import type { TextStyleDeclaration } from '../../brand-page/models';
16
12
  import { getSectionFieldMap, getStyleFieldMap } from './section-field-maps';
17
13
 
18
14
  /**
@@ -190,83 +186,36 @@ export const applyDetachedStyleOverride = <T extends object>(
190
186
  ...updates,
191
187
  }) as T;
192
188
 
193
- /**
194
- * Rewrites the stored RTE HTML's inline colour marks. Injectable for tests;
195
- * the default uses the external Tiptap editor (font-propagation precedent).
196
- */
197
- export type ApplyColorToRTE = (html: string, color: string) => string;
198
-
199
189
  export const applyStyleFields = (
200
190
  content: Content,
201
191
  args: SectionToolArguments,
202
192
  sectionType: string,
203
193
  textStyleOptions?: TextStyleOptions,
204
- applyColor?: ApplyColorToRTE,
205
194
  ): void => {
206
195
  const styleMap = getStyleFieldMap(sectionType);
207
196
 
208
197
  if (!styleMap) return;
209
198
 
210
199
  const record = content as IndexableContent;
211
- let editor:
212
- | ReturnType<typeof useExternalRichTextEditor>['externalEditor']
213
- | undefined;
214
- const applyColorToHtml: ApplyColorToRTE =
215
- applyColor ??
216
- ((html, color) => {
217
- editor = editor ?? useExternalRichTextEditor().externalEditor;
218
-
219
- return applyStyleToRTEText(editor, html, {
220
- color,
221
- } as TextStyleDeclaration);
222
- });
223
200
 
224
- try {
225
- for (const [aiKey, modelKey] of Object.entries(styleMap)) {
226
- if (!(aiKey in args) || !args[aiKey] || typeof args[aiKey] !== 'object')
227
- continue;
228
-
229
- const aiStyle = args[aiKey] as Record<string, unknown>;
230
- const filtered: Record<string, unknown> = {};
231
-
232
- for (const [k, v] of Object.entries(aiStyle)) {
233
- const normalized = normalizeStyleValue(k, v);
234
-
235
- if (normalized !== undefined) filtered[k] = normalized;
236
- }
237
-
238
- const resolved = resolveGlobalPresetIntoStyle(
239
- content[modelKey] as object,
240
- textStyleOptions,
241
- );
242
-
243
- record[modelKey] = { ...resolved, ...filtered, textStyleType: undefined };
244
-
245
- // A colour patch without new text leaves the field's EXISTING inline
246
- // textStyle marks in place — and inline marks override every style layer
247
- // at render, so the old colour survives on a recoloured band (invisible
248
- // text, AI QA 2026-07-05 rerun). Propagate the new colour into the
249
- // stored HTML, mirroring global-font-propagation for font families.
250
- const textKey = modelKey.endsWith('Style')
251
- ? modelKey.slice(0, -'Style'.length)
252
- : undefined;
253
- const textAiKey = aiKey.endsWith('_style')
254
- ? aiKey.slice(0, -'_style'.length)
255
- : undefined;
256
- const newColor = filtered.color;
257
-
258
- if (
259
- textKey &&
260
- typeof newColor === 'string' &&
261
- newColor &&
262
- !(textAiKey && textAiKey in args) &&
263
- typeof record[textKey] === 'string' &&
264
- hasRTEContent(record[textKey] as string)
265
- ) {
266
- record[textKey] = applyColorToHtml(record[textKey] as string, newColor);
267
- }
201
+ for (const [aiKey, modelKey] of Object.entries(styleMap)) {
202
+ if (!(aiKey in args) || !args[aiKey] || typeof args[aiKey] !== 'object')
203
+ continue;
204
+
205
+ const aiStyle = args[aiKey] as Record<string, unknown>;
206
+ const filtered: Record<string, unknown> = {};
207
+
208
+ for (const [k, v] of Object.entries(aiStyle)) {
209
+ const normalized = normalizeStyleValue(k, v);
210
+
211
+ if (normalized !== undefined) filtered[k] = normalized;
268
212
  }
269
- } finally {
270
- editor?.destroy();
213
+
214
+ const resolved = resolveGlobalPresetIntoStyle(
215
+ content[modelKey] as object,
216
+ textStyleOptions,
217
+ );
218
+
219
+ record[modelKey] = { ...resolved, ...filtered, textStyleType: undefined };
271
220
  }
272
221
  };
@@ -13,7 +13,6 @@ import { UpdateContentTextStyleBuilder } from './builders/update-content-text-st
13
13
  import {
14
14
  BannerSectionBuilder,
15
15
  BookingSectionBuilder,
16
- SupportMeBuilder,
17
16
  AboutSectionBuilder,
18
17
  ContactFormBuilder,
19
18
  DividerSectionBuilder,
@@ -55,7 +54,6 @@ const getBuilderRegistry = (): Record<BuilderType, BuilderConstructor> => ({
55
54
  // Section builders (create)
56
55
  [BUILDER_TYPES.CREATE_BANNER_SECTION]: BannerSectionBuilder,
57
56
  [BUILDER_TYPES.CREATE_BOOKING_SECTION]: BookingSectionBuilder,
58
- [BUILDER_TYPES.CREATE_SUPPORT_ME]: SupportMeBuilder,
59
57
  [BUILDER_TYPES.CREATE_ABOUT_SECTION]: AboutSectionBuilder,
60
58
  [BUILDER_TYPES.CREATE_CONTACT_FORM]: ContactFormBuilder,
61
59
  [BUILDER_TYPES.CREATE_DIVIDER_SECTION]: DividerSectionBuilder,
@@ -83,7 +81,6 @@ const getBuilderRegistry = (): Record<BuilderType, BuilderConstructor> => ({
83
81
  // Section builders (update)
84
82
  [BUILDER_TYPES.UPDATE_BANNER_SECTION]: BannerSectionBuilder,
85
83
  [BUILDER_TYPES.UPDATE_BOOKING_SECTION]: BookingSectionBuilder,
86
- [BUILDER_TYPES.UPDATE_SUPPORT_ME]: SupportMeBuilder,
87
84
  [BUILDER_TYPES.UPDATE_ABOUT_SECTION]: AboutSectionBuilder,
88
85
  [BUILDER_TYPES.UPDATE_CONTACT_FORM]: ContactFormBuilder,
89
86
  [BUILDER_TYPES.UPDATE_DIVIDER_SECTION]: DividerSectionBuilder,
@@ -32,7 +32,6 @@ export const BUILDER_TYPES = {
32
32
  CREATE_TWITCH: 'create-twitch',
33
33
  CREATE_TIKTOK: 'create-tiktok',
34
34
  CREATE_BOOKING_SECTION: 'create-booking-section',
35
- CREATE_SUPPORT_ME: 'create-support-me',
36
35
 
37
36
  // Per-section builder types (update)
38
37
  UPDATE_BANNER_SECTION: 'update-banner-section',
@@ -60,7 +59,6 @@ export const BUILDER_TYPES = {
60
59
  UPDATE_TWITCH: 'update-twitch',
61
60
  UPDATE_TIKTOK: 'update-tiktok',
62
61
  UPDATE_BOOKING_SECTION: 'update-booking-section',
63
- UPDATE_SUPPORT_ME: 'update-support-me',
64
62
 
65
63
  // Section-level operations
66
64
  DELETE_SECTION: 'delete-section',
@@ -43,7 +43,6 @@ export const AI_ASSISTANT_TOOL_NAMES = {
43
43
  BUILD_TESTIMONIAL_SECTION: 'build_testimonial-section_section',
44
44
  BUILD_DIVIDER_SECTION: 'build_divider-section_section',
45
45
  BUILD_BOOKING_SECTION: 'build_booking-section_section',
46
- BUILD_SUPPORT_ME_SECTION: 'build_support-me_section',
47
46
 
48
47
  // Section-level operation tools
49
48
  DELETE_SECTION: 'delete_section',
@@ -164,9 +163,6 @@ export const SECTION_TOOL_MAP: Record<string, { sectionType: SectionType }> = {
164
163
  [AI_ASSISTANT_TOOL_NAMES.BUILD_BOOKING_SECTION]: {
165
164
  sectionType: 'booking-section',
166
165
  },
167
- [AI_ASSISTANT_TOOL_NAMES.BUILD_SUPPORT_ME_SECTION]: {
168
- sectionType: 'support-me',
169
- },
170
166
  };
171
167
 
172
168
  export interface DeleteSectionArguments {
@@ -17,6 +17,7 @@ type UnsupportedContentType =
17
17
  | 'video'
18
18
  | 'map'
19
19
  | 'request-payment'
20
+ | 'support-me'
20
21
  | 'shop-section';
21
22
 
22
23
  export type SectionType = Exclude<ContentType, UnsupportedContentType>;
@@ -536,19 +537,6 @@ export interface BookingSectionArguments {
536
537
  padding_bottom?: number;
537
538
  }
538
539
 
539
- export interface SupportMeArguments {
540
- title?: string;
541
- description?: string;
542
- success_message?: string;
543
- /** Matches DISPLAY_STYLE_PREVIEW / DISPLAY_STYLE_COLLAPSED. */
544
- display_style?: 'preview' | 'collapsed';
545
- title_style?: TextStyleArguments;
546
- heading_style?: TextStyleArguments;
547
- description_style?: TextStyleArguments;
548
- item_text_style?: TextStyleArguments;
549
- button_text_style?: ButtonTextStyleArguments;
550
- }
551
-
552
540
  export interface DividerSectionArguments {
553
541
  divider_style?: 'none' | 'thin' | 'thick';
554
542
  divider_color?: string | null;
@@ -586,7 +574,6 @@ export type SectionToolArguments =
586
574
  | TestimonialSectionArguments
587
575
  | ShapesSectionArguments
588
576
  | BookingSectionArguments
589
- | SupportMeArguments
590
577
  | DividerSectionArguments;
591
578
 
592
579
  // ---------------------------------------------------------------------------
@@ -507,19 +507,11 @@ const minifyContent = (
507
507
  return base;
508
508
 
509
509
  case 'request-payment':
510
- return {
511
- ...base,
512
- ...st('title', c.title, editor),
513
- ...st('description', c.description, editor),
514
- };
515
-
516
510
  case 'support-me':
517
511
  return {
518
512
  ...base,
519
513
  ...st('title', c.title, editor),
520
514
  ...st('description', c.description, editor),
521
- ...st('successMessage', c.successMessage, editor),
522
- ...(includeString(c.displayStyle) && { displayStyle: c.displayStyle }),
523
515
  };
524
516
 
525
517
  default:
@@ -1,28 +0,0 @@
1
- import type { BrandPageModel } from '../../brand-page/models';
2
- import type { UpdateGlobalStylesArguments } from '../parsers/ai-toolcall-parser';
3
- /**
4
- * Propagation of AI global font-family changes into existing section content.
5
- *
6
- * A `tool_update_global_styles` call merges new per-category font families into
7
- * `styles.textStyles.textStyleOptions` — but only content whose style is still
8
- * bound to a preset (`textStyleType`) follows that change. Template-authored
9
- * content carries detached `*Style` objects AND inline `font-family` marks in
10
- * its RTE HTML, both of which override the preset at render time, so a global
11
- * font change was invisible on most templates (BP-5780 ISSUE-01).
12
- *
13
- * This module walks every page's contents and, for each text field whose
14
- * resolved category received a new family, aligns the detached style object and
15
- * rewrites the RTE HTML's inline font-family marks. Preset-bound styles are NOT
16
- * detached — they already follow the preset; only their RTE inline marks are
17
- * rewritten (the marks would otherwise still win over the preset).
18
- */
19
- export type GlobalFontCategory = 'titles' | 'headings' | 'paragraphs' | 'buttons';
20
- /** Extract the per-category families this AI call actually changes. */
21
- export declare const collectChangedFontFamilies: (textStyles: UpdateGlobalStylesArguments["textStyles"]) => Partial<Record<GlobalFontCategory, string>>;
22
- export type ApplyFontToRTE = (text: string, fontFamily: string) => string;
23
- /**
24
- * Walk every page's contents and propagate the changed families.
25
- * `applyFont` is injectable for tests; the default rewrites inline marks via
26
- * the external Tiptap editor.
27
- */
28
- export declare const propagateFontFamiliesToContents: (model: BrandPageModel, changed: Partial<Record<GlobalFontCategory, string>>, applyFont?: ApplyFontToRTE) => void;
@@ -1,125 +0,0 @@
1
- import { hasRTEContent } from '../../brand-page-maker/utils';
2
- import { useExternalRichTextEditor } from '../../brand-page-maker/composables/rich-text-editor/useRichTextEditor';
3
- import { applyStyleToRTEText } from '../helpers/rte.helpers';
4
- /**
5
- * Default category per text field when its style is detached (no `textStyleType`).
6
- * Mirrors the AI service's slot→category stamping (titles←h1, headings←h2-h4,
7
- * paragraphs←body, buttons←button). `title` upgrades to `titles` on banner
8
- * sections (the hero headline) via `resolveFieldCategory`.
9
- */
10
- const FIELD_DEFAULT_CATEGORY = {
11
- title: 'headings',
12
- heading: 'headings',
13
- name: 'headings',
14
- description: 'paragraphs',
15
- paragraph: 'paragraphs',
16
- text: 'paragraphs',
17
- testimonial: 'paragraphs',
18
- position: 'paragraphs',
19
- question: 'headings',
20
- answer: 'paragraphs',
21
- buttonText: 'buttons',
22
- successMessage: 'paragraphs',
23
- };
24
- const CATEGORIES = [
25
- 'titles',
26
- 'headings',
27
- 'paragraphs',
28
- 'buttons',
29
- ];
30
- const isPlainObject = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
31
- /** Extract the per-category families this AI call actually changes. */
32
- export const collectChangedFontFamilies = (textStyles) => {
33
- const changed = {};
34
- if (!textStyles)
35
- return changed;
36
- for (const category of CATEGORIES) {
37
- const family = textStyles.textStyleOptions?.[category]?.style?.fontFamily;
38
- if (typeof family === 'string' && family.trim()) {
39
- changed[category] = family;
40
- }
41
- }
42
- // Base text family covers paragraphs when no explicit paragraphs entry came in.
43
- if (!changed.paragraphs &&
44
- typeof textStyles.textFontFamily === 'string' &&
45
- textStyles.textFontFamily.trim()) {
46
- changed.paragraphs = textStyles.textFontFamily;
47
- }
48
- return changed;
49
- };
50
- const resolveFieldCategory = (fieldName, style, content) => {
51
- const preset = isPlainObject(style)
52
- ? style.textStyleType
53
- : undefined;
54
- if (preset && CATEGORIES.includes(preset))
55
- return preset;
56
- if (fieldName === 'title' && content.type === 'banner-section') {
57
- return 'titles';
58
- }
59
- return FIELD_DEFAULT_CATEGORY[fieldName];
60
- };
61
- /** `testimonialsStyle` → `testimonial`, `titleStyle` → `title`. */
62
- const styleKeyToFieldName = (styleKey) => {
63
- const base = styleKey.slice(0, -'Style'.length);
64
- return base.endsWith('s') && !(base in FIELD_DEFAULT_CATEGORY)
65
- ? base.slice(0, -1)
66
- : base;
67
- };
68
- const walkRecord = (record, content, changed, applyFont) => {
69
- for (const [key, value] of Object.entries(record)) {
70
- if (key.endsWith('Style') && isPlainObject(value)) {
71
- // Detached style objects carry their own fontFamily override — align it.
72
- // Preset-bound styles keep following the preset (never detach here).
73
- const category = resolveFieldCategory(styleKeyToFieldName(key), value, content);
74
- const family = category ? changed[category] : undefined;
75
- if (family && !value.textStyleType) {
76
- value.fontFamily =
77
- family;
78
- }
79
- }
80
- else if (key in FIELD_DEFAULT_CATEGORY &&
81
- typeof value === 'string' &&
82
- hasRTEContent(value)) {
83
- // Inline RTE marks override every style layer at render — rewrite them.
84
- const category = resolveFieldCategory(key, record[`${key}Style`], content);
85
- const family = category ? changed[category] : undefined;
86
- if (family) {
87
- record[key] = applyFont(value, family);
88
- }
89
- }
90
- else if (Array.isArray(value)) {
91
- for (const item of value) {
92
- if (isPlainObject(item)) {
93
- walkRecord(item, content, changed, applyFont);
94
- }
95
- }
96
- }
97
- }
98
- };
99
- /**
100
- * Walk every page's contents and propagate the changed families.
101
- * `applyFont` is injectable for tests; the default rewrites inline marks via
102
- * the external Tiptap editor.
103
- */
104
- export const propagateFontFamiliesToContents = (model, changed, applyFont) => {
105
- if (!Object.keys(changed).length)
106
- return;
107
- let editor;
108
- const apply = applyFont ??
109
- ((text, fontFamily) => {
110
- editor = editor ?? useExternalRichTextEditor().externalEditor;
111
- return applyStyleToRTEText(editor, text, {
112
- fontFamily,
113
- });
114
- });
115
- try {
116
- for (const page of model.pages) {
117
- for (const content of page.contents) {
118
- walkRecord(content, content, changed, apply);
119
- }
120
- }
121
- }
122
- finally {
123
- editor?.destroy();
124
- }
125
- };
@@ -1,8 +0,0 @@
1
- import type { BrandPageModel, SupportMeContent } from '../../../brand-page/models';
2
- import type { BuilderResult } from '../../models';
3
- import { BaseSectionBuilder } from './base-section-builder';
4
- import { validateSupportMe } from '../section-rules/support-me.rules';
5
- export declare class SupportMeBuilder extends BaseSectionBuilder<SupportMeContent> {
6
- protected get sectionValidate(): typeof validateSupportMe;
7
- buildAsync(model: BrandPageModel): Promise<BuilderResult>;
8
- }