@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.
- package/lib/src/{CreatedOnBrandCrowd-de-de-D_qQ0sPW.js → CreatedOnBrandCrowd-de-de-CW5bD06L.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-CNMxavFU.js → CreatedOnBrandCrowd-en-us-CofG4e3V.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-Cyaiw1-R.js → CreatedOnBrandCrowd-es-es-XpL0-LWx.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-DbniOtuE.js → CreatedOnBrandCrowd-fr-ca-dlVN3zaX.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-DbniOtuE.js → CreatedOnBrandCrowd-fr-fr-dlVN3zaX.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-DI3JfwR8.js → CreatedOnBrandCrowd-pt-br-98gqdDVF.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-DI3JfwR8.js → CreatedOnBrandCrowd-pt-pt-98gqdDVF.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-DqBzJ_O9.js → CreatedOnDcom-de-de-Dbmlc0i1.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-B8uZjYMi.js → CreatedOnDcom-en-us-Dy9z7h1o.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-BOLxcx3H.js → CreatedOnDcom-es-es-B3ksvNj9.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-CbGneDvu.js → CreatedOnDcom-fr-ca-CC9ieRzX.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-B-UwXpW3.js → CreatedOnDcom-fr-fr-BtnGJjF2.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-CRPrLx5_.js → CreatedOnDcom-pt-br-DT4doFYO.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-4p2rdAnc.js → CreatedOnDcom-pt-pt-CloXyOe6.js} +1 -1
- package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +3 -5
- package/lib/src/brand-page/components/mailing-list/MailingList.mixin.js +4 -6
- package/lib/src/brand-page/components/page-footer/PageFooter.mixin.d.ts +0 -1
- package/lib/src/brand-page/components/page-footer/PageFooter.mixin.js +1 -4
- package/lib/src/brand-page/components/payment/Payment.mixin.js +3 -4
- package/lib/src/brand-page/components/payment/__fixtures__/request-payment.d.ts +0 -1
- package/lib/src/brand-page/components/payment/__fixtures__/support-me.d.ts +0 -1
- package/lib/src/brand-page/components/payment/request-payment/RequestPayment.mixin.d.ts +0 -1
- package/lib/src/brand-page/components/payment/request-payment/RequestPayment.mixin.js +1 -4
- package/lib/src/brand-page/composables/consent.composable.d.ts +0 -2
- package/lib/src/brand-page/composables/consent.composable.js +3 -9
- package/lib/src/brand-page/models/brand-page-context.model.d.ts +0 -7
- package/lib/src/brand-page/utils/context.util.d.ts +0 -8
- package/lib/src/brand-page/utils/context.util.js +0 -8
- package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +3 -7
- package/lib/src/clients/ai-assistant-client.d.ts +0 -1
- package/lib/src/clients/ai-assistant-client.js +0 -1
- package/lib/src/css/library.brand-page-brandCrowd.min.css +60 -60
- package/lib/src/css/library.brand-page-brandPage.min.css +60 -60
- package/lib/src/css/library.brand-page-designCom.min.css +60 -60
- package/lib/src/css/library.brand-page-preview.min.css +60 -60
- package/lib/src/index.d.ts +0 -1
- package/lib/src/index.mjs +217 -220
- package/lib/src/{lib-DhEJMZFn.js → lib-D7ubPlpu.js} +18168 -18341
- package/lib/src/lib.js +0 -1
- package/lib/src/metadata-builder/builders/global-styles-builder.js +0 -16
- package/lib/src/metadata-builder/builders/section-builders/index.d.ts +0 -1
- package/lib/src/metadata-builder/builders/section-builders/index.js +0 -1
- package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +2 -5
- package/lib/src/metadata-builder/builders/section-field-maps.js +0 -15
- package/lib/src/metadata-builder/builders/section-field-utils.d.ts +1 -6
- package/lib/src/metadata-builder/builders/section-field-utils.js +12 -48
- package/lib/src/metadata-builder/metadata-executor.js +1 -3
- package/lib/src/metadata-builder/models.d.ts +0 -2
- package/lib/src/metadata-builder/models.js +0 -2
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +0 -1
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +0 -4
- package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +2 -14
- package/lib/src/utils/minify-brand-page-metadata.js +0 -7
- package/package.json +1 -1
- package/src/brand-page/components/contact-form/ContactForm.mixin.ts +2 -7
- package/src/brand-page/components/cookie-consent/CookieConsent.vue +0 -1
- package/src/brand-page/components/mailing-list/MailingList.mixin.ts +3 -8
- package/src/brand-page/components/navigation/navigation-carousel/NavigationCarousel.vue +1 -5
- package/src/brand-page/components/page-footer/PageFooter.mixin.ts +1 -4
- package/src/brand-page/components/page-footer/PageFooter.vue +1 -1
- package/src/brand-page/components/payment/Payment.mixin.ts +3 -4
- package/src/brand-page/components/payment/request-payment/RequestPayment.mixin.ts +1 -8
- package/src/brand-page/components/payment/request-payment/RequestPayment.vue +1 -1
- package/src/brand-page/components/save-contact/SaveContactWidget.vue +1 -2
- package/src/brand-page/components/shop/Shop.vue +3 -6
- package/src/brand-page/components/social-links/SocialLinks.vue +2 -5
- package/src/brand-page/composables/consent.composable.ts +4 -18
- package/src/brand-page/models/brand-page-context.model.ts +0 -7
- package/src/brand-page/utils/context.util.ts +0 -10
- package/src/brand-page-maker/components/ai-website-builder/AiWebsiteBuilder.vue +1 -1
- package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +3 -7
- package/src/clients/ai-assistant-client.ts +0 -1
- package/src/lib.ts +0 -1
- package/src/metadata-builder/builders/global-styles-builder.ts +0 -21
- package/src/metadata-builder/builders/section-builders/index.ts +0 -1
- package/src/metadata-builder/builders/section-builders/text-section-builder.ts +2 -5
- package/src/metadata-builder/builders/section-field-maps.ts +0 -18
- package/src/metadata-builder/builders/section-field-utils.ts +18 -69
- package/src/metadata-builder/metadata-executor.ts +0 -3
- package/src/metadata-builder/models.ts +0 -2
- package/src/metadata-builder/parsers/ai-toolcall-parser.ts +0 -4
- package/src/metadata-builder/parsers/section-tool-arguments.ts +1 -14
- package/src/utils/minify-brand-page-metadata.ts +0 -8
- package/lib/src/metadata-builder/builders/global-font-propagation.d.ts +0 -28
- package/lib/src/metadata-builder/builders/global-font-propagation.js +0 -125
- package/lib/src/metadata-builder/builders/section-builders/support-me-builder.d.ts +0 -8
- package/lib/src/metadata-builder/builders/section-builders/support-me-builder.js +0 -25
- package/lib/src/metadata-builder/builders/section-rules/support-me.rules.d.ts +0 -2
- package/lib/src/metadata-builder/builders/section-rules/support-me.rules.js +0 -17
- package/src/brand-page/utils/context.util.test.ts +0 -43
- package/src/metadata-builder/builders/global-font-propagation.test.ts +0 -269
- package/src/metadata-builder/builders/global-font-propagation.ts +0 -205
- package/src/metadata-builder/builders/section-builders/support-me-builder.test.ts +0 -177
- package/src/metadata-builder/builders/section-builders/support-me-builder.ts +0 -43
- package/src/metadata-builder/builders/section-field-utils.test.ts +0 -83
- package/src/metadata-builder/builders/section-rules/support-me.rules.ts +0 -39
- 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 =
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
734
|
+
agentType: AI_ASSISTANT_AGENT_TYPES.WEBSITE_BUILDER,
|
|
739
735
|
connectionId,
|
|
740
736
|
retryCount,
|
|
741
737
|
userPlan,
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
270
|
-
|
|
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
|
-
}
|