@designcrowd/library.brand-page 6.0.26-json-prereq-2 → 6.0.27-websitev2-dev-1
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-CKBqjDH3.js → CreatedOnBrandCrowd-de-de-BpFKOKIo.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-CRXiimvT.js → CreatedOnBrandCrowd-en-us-USGAnC_n.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-DCYdY6sj.js → CreatedOnBrandCrowd-es-es-CvVx3h4H.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-Dk7LOhxE.js → CreatedOnBrandCrowd-fr-ca-BCwamW5a.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-Dk7LOhxE.js → CreatedOnBrandCrowd-fr-fr-BCwamW5a.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-Cp4ufHJP.js → CreatedOnBrandCrowd-pt-br-Bt72hMrQ.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-Cp4ufHJP.js → CreatedOnBrandCrowd-pt-pt-Bt72hMrQ.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-ZSLkWMwM.js → CreatedOnDcom-de-de-BxUDrgG9.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-BgdIGfT3.js → CreatedOnDcom-en-us-wjVp5eOs.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-D0KemkCZ.js → CreatedOnDcom-es-es-CwFL2q5h.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-BuwNjNbC.js → CreatedOnDcom-fr-ca-BPZ2_PNq.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-DCQcsFPf.js → CreatedOnDcom-fr-fr-D0vLOikZ.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-DD-oLAS1.js → CreatedOnDcom-pt-br-CTwbxm9H.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-D8qLq2wW.js → CreatedOnDcom-pt-pt-Cfqf8LaM.js} +1 -1
- package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +2 -5
- package/lib/src/brand-page/models/index.d.ts +26 -34
- package/lib/src/brand-page/utils/migration.util.js +0 -11
- package/lib/src/brand-page/utils/text.util.d.ts +1 -1
- package/lib/src/brand-page/utils/text.util.js +0 -32
- package/lib/src/brand-page/utils/vcard.util.js +4 -4
- package/lib/src/brand-page/utils/view-model.util.d.ts +0 -1
- package/lib/src/brand-page/utils/view-model.util.js +20 -68
- package/lib/src/brand-page/view-models.d.ts +3 -3
- package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.d.ts +1 -0
- package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +7 -3
- package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.d.ts +9 -2
- package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.js +10 -6
- package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.d.ts +20 -0
- package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.js +44 -0
- package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.d.ts +27 -0
- package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.js +44 -0
- package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +1 -1
- package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +103 -24
- package/lib/src/brand-page-maker/utils/index.d.ts +0 -2
- package/lib/src/brand-page-maker/utils/index.js +0 -2
- package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +5 -28
- package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +5 -48
- package/lib/src/brand-page-maker/validators/field.validator.js +2 -10
- package/lib/src/clients/ai-assistant-client.d.ts +1 -0
- package/lib/src/clients/ai-assistant-client.js +1 -0
- package/lib/src/css/library.brand-page-brandCrowd.min.css +39 -39
- package/lib/src/css/library.brand-page-brandPage.min.css +39 -39
- package/lib/src/css/library.brand-page-designCom.min.css +39 -39
- package/lib/src/css/library.brand-page-preview.min.css +39 -39
- package/lib/src/index.d.ts +0 -1
- package/lib/src/index.mjs +138 -143
- package/lib/src/{lib-Db6j9fQZ.js → lib-Cg4BExY4.js} +72135 -75458
- package/lib/src/lib.js +0 -2
- package/lib/src/metadata-builder/ai-metadata-executor.d.ts +1 -0
- package/lib/src/metadata-builder/ai-metadata-executor.js +19 -0
- package/lib/src/metadata-builder/builders/global-font-propagation.d.ts +28 -0
- package/lib/src/metadata-builder/builders/global-font-propagation.js +125 -0
- package/lib/src/metadata-builder/builders/global-styles-builder.js +16 -0
- package/lib/src/metadata-builder/builders/section-builders/about-section-builder.js +3 -2
- package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.js +3 -2
- package/lib/src/metadata-builder/builders/section-builders/booking-section-builder.js +3 -2
- package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.js +4 -3
- package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +3 -2
- package/lib/src/metadata-builder/builders/section-builders/index.d.ts +1 -0
- package/lib/src/metadata-builder/builders/section-builders/index.js +1 -0
- package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.js +2 -1
- package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.js +2 -1
- package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.js +4 -3
- package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +3 -2
- package/lib/src/metadata-builder/builders/section-builders/support-me-builder.d.ts +8 -0
- package/lib/src/metadata-builder/builders/section-builders/support-me-builder.js +25 -0
- package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +2 -1
- package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +16 -17
- package/lib/src/metadata-builder/builders/section-field-maps.js +15 -0
- package/lib/src/metadata-builder/builders/section-field-utils.d.ts +6 -1
- package/lib/src/metadata-builder/builders/section-field-utils.js +48 -12
- package/lib/src/metadata-builder/builders/section-rules/support-me.rules.d.ts +2 -0
- package/lib/src/metadata-builder/builders/section-rules/support-me.rules.js +17 -0
- package/lib/src/metadata-builder/builders/update-content-text-builder.d.ts +17 -0
- package/lib/src/metadata-builder/builders/update-content-text-builder.js +103 -0
- package/lib/src/metadata-builder/builders/update-content-text-style-builder.d.ts +16 -0
- package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +216 -0
- package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +13 -11
- package/lib/src/metadata-builder/helpers/rte.helpers.js +73 -14
- package/lib/src/metadata-builder/metadata-executor.js +7 -1
- package/lib/src/metadata-builder/models.d.ts +4 -0
- package/lib/src/metadata-builder/models.js +4 -0
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +20 -0
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +8 -0
- package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +14 -2
- package/lib/src/metadata-builder/sanitizers/rte-schema-check.d.ts +8 -7
- package/lib/src/metadata-builder/sanitizers/rte-schema-check.js +52 -21
- package/lib/src/shared/content-settings/components/GenericTextInput.mixin.js +1 -14
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +3 -12
- package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.d.ts +2 -3
- package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.js +3 -10
- package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.d.ts +1 -2
- package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.js +1 -4
- package/lib/src/utils/minify-brand-page-metadata.js +8 -4
- package/package.json +15 -17
- package/src/brand-page/components/contact-form/ContactForm.mixin.ts +2 -5
- package/src/brand-page/components/text-wrapper/TextWrapper.vue +6 -17
- package/src/brand-page/models/index.ts +26 -35
- package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -3
- package/src/brand-page/utils/migration.util.ts +0 -12
- package/src/brand-page/utils/style.util.test.ts +0 -51
- package/src/brand-page/utils/text.util.ts +4 -43
- package/src/brand-page/utils/vcard.util.ts +4 -6
- package/src/brand-page/utils/view-model.util.ts +26 -94
- package/src/brand-page/view-models.ts +2 -3
- package/src/brand-page-maker/components/ai-website-builder/AiWebsiteBuilder.vue +1 -1
- package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +3 -4
- package/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.ts +1 -0
- package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +7 -3
- package/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.ts +10 -6
- package/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.ts +73 -0
- package/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.ts +85 -0
- package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +111 -27
- package/src/brand-page-maker/utils/index.ts +0 -2
- package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +3 -37
- package/src/brand-page-maker/utils/rich-text-editor.util.ts +11 -66
- package/src/brand-page-maker/validators/field.validator.ts +2 -10
- package/src/clients/ai-assistant-client.ts +1 -0
- package/src/lib.ts +0 -9
- package/src/metadata-builder/ai-metadata-executor.ts +26 -0
- package/src/metadata-builder/builders/global-font-propagation.test.ts +269 -0
- package/src/metadata-builder/builders/global-font-propagation.ts +205 -0
- package/src/metadata-builder/builders/global-styles-builder.ts +21 -0
- package/src/metadata-builder/builders/section-builders/about-section-builder.ts +3 -2
- package/src/metadata-builder/builders/section-builders/banner-section-builder.ts +3 -2
- package/src/metadata-builder/builders/section-builders/booking-section-builder.ts +5 -2
- package/src/metadata-builder/builders/section-builders/contact-form-builder.ts +4 -3
- package/src/metadata-builder/builders/section-builders/images-section-builder.ts +3 -2
- package/src/metadata-builder/builders/section-builders/index.ts +1 -0
- package/src/metadata-builder/builders/section-builders/mailing-list-builder.ts +2 -1
- package/src/metadata-builder/builders/section-builders/mailing-list-section-builder.ts +2 -1
- package/src/metadata-builder/builders/section-builders/profile-section-builder.ts +6 -3
- package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +3 -2
- package/src/metadata-builder/builders/section-builders/support-me-builder.test.ts +177 -0
- package/src/metadata-builder/builders/section-builders/support-me-builder.ts +43 -0
- package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +2 -1
- package/src/metadata-builder/builders/section-builders/text-section-builder.ts +18 -21
- package/src/metadata-builder/builders/section-field-maps.ts +18 -0
- package/src/metadata-builder/builders/section-field-utils.test.ts +83 -0
- package/src/metadata-builder/builders/section-field-utils.ts +69 -18
- package/src/metadata-builder/builders/section-rules/support-me.rules.ts +39 -0
- package/src/metadata-builder/builders/update-content-text-builder.ts +148 -0
- package/src/metadata-builder/builders/update-content-text-style-builder.ts +352 -0
- package/src/metadata-builder/helpers/rte.helpers.ts +101 -15
- package/src/metadata-builder/metadata-executor.ts +8 -0
- package/src/metadata-builder/models.ts +5 -0
- package/src/metadata-builder/parsers/ai-toolcall-parser.ts +31 -0
- package/src/metadata-builder/parsers/section-tool-arguments.ts +14 -1
- package/src/metadata-builder/sanitizers/rte-schema-check.ts +53 -24
- package/src/shared/content-settings/components/GenericTextInput.mixin.ts +1 -17
- package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +3 -16
- package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.ts +4 -13
- package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.vue +4 -6
- package/src/shared/content-settings/components/text-image-field/TextImageField.vue +3 -6
- package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.ts +1 -6
- package/src/shared/content-settings/components/text-items-field/TextItemsField.vue +1 -1
- package/src/utils/minify-brand-page-metadata.test.ts +61 -0
- package/src/utils/minify-brand-page-metadata.ts +9 -4
- package/lib/src/brand-page/utils/migration/migration-20.d.ts +0 -2
- package/lib/src/brand-page/utils/migration/migration-20.js +0 -66
- package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.d.ts +0 -22
- package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.js +0 -22
- package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.d.ts +0 -14
- package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.js +0 -39
- package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.d.ts +0 -28
- package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.js +0 -90
- package/lib/src/brand-page-maker/utils/rte-render.util.d.ts +0 -63
- package/lib/src/brand-page-maker/utils/rte-render.util.js +0 -95
- package/lib/src/brand-page-maker/utils/rte-sanitize.util.d.ts +0 -6
- package/lib/src/brand-page-maker/utils/rte-sanitize.util.js +0 -158
- package/src/brand-page/utils/migration/migration-20.server.test.ts +0 -125
- package/src/brand-page/utils/migration/migration-20.test.ts +0 -72
- package/src/brand-page/utils/migration/migration-20.ts +0 -87
- package/src/brand-page/utils/text.util.test.ts +0 -47
- package/src/brand-page/utils/view-model.pageid-rte.test.ts +0 -104
- package/src/brand-page-maker/__fixtures__/complex-rte.fixture.ts +0 -23
- package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +0 -52
- package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.ts +0 -42
- package/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.ts +0 -137
- package/src/brand-page-maker/utils/__snapshots__/rte-render-parity.test.ts.snap +0 -202
- package/src/brand-page-maker/utils/rte-features.test.ts +0 -351
- package/src/brand-page-maker/utils/rte-render-parity.test.ts +0 -157
- package/src/brand-page-maker/utils/rte-render.util.ts +0 -165
- package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +0 -158
- package/src/brand-page-maker/utils/rte-sanitize.util.ts +0 -195
- package/src/brand-page-maker/validators/field.validator.test.ts +0 -55
package/lib/src/lib.js
CHANGED
|
@@ -30,8 +30,6 @@ export { minifyBrandPageMetadata, } from './utils/minify-brand-page-metadata';
|
|
|
30
30
|
export * as brandPageUtils from './brand-page/utils';
|
|
31
31
|
export { getOptimalLogoSize, calculateOptimizedLogoDimensions, applyLogoImageBase64UrlToBrandPageTemplateModel, recalculateLogoContentDimensions, recalculateHeaderLogoSizes, } from './brand-page/utils/logo.util';
|
|
32
32
|
export { sanitizeRTEHtml } from './brand-page-maker/utils';
|
|
33
|
-
// BP-5787: RTE JSON storage boundary helpers (render/normalise/sanitise/extract).
|
|
34
|
-
export { renderRteToHtml, toRteJson, isTiptapDocument, sanitizeRteJson, getRteJsonPlainText, } from './brand-page-maker/utils';
|
|
35
33
|
export { getPageSlugFromPageTitle } from './brand-page-maker/utils/slug.util';
|
|
36
34
|
// AI-pipeline preview (longclaw per-turn snapshots): web-renderer's
|
|
37
35
|
// /preview/ai-pipeline render page + manifest server route apply the agent's
|
|
@@ -6,6 +6,7 @@ import { type MetadataExecutorResult } from './models';
|
|
|
6
6
|
import { type ParsedToolCall } from './parsers/ai-toolcall-parser';
|
|
7
7
|
export declare const executeToolCallsAsync: (metadata: BrandPageModel, toolcalls: ParsedToolCall[], options: {
|
|
8
8
|
logger?: Logger;
|
|
9
|
+
maxCharacters?: number;
|
|
9
10
|
/**
|
|
10
11
|
* Callback invoked by the UndoBuilder at execution time to resolve the
|
|
11
12
|
* pre-prompt snapshot to restore to. The library is agnostic about where
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {} from '../brand-page/models';
|
|
2
|
+
import { buildUpdateTextRequest } from './builders/update-content-text-builder';
|
|
3
|
+
import { buildUpdateTextStyleRequest } from './builders/update-content-text-style-builder';
|
|
2
4
|
import { MetadataExecutor } from './metadata-executor';
|
|
3
5
|
import { BUILDER_TYPES, } from './models';
|
|
4
6
|
import { AI_ASSISTANT_TOOL_NAMES, SECTION_TOOL_MAP,
|
|
@@ -9,6 +11,23 @@ export const executeToolCallsAsync = async (metadata, toolcalls, options) => {
|
|
|
9
11
|
const requests = toolcalls
|
|
10
12
|
.map((toolcall) => {
|
|
11
13
|
switch (toolcall.function) {
|
|
14
|
+
case AI_ASSISTANT_TOOL_NAMES.UPDATE_TEXT: {
|
|
15
|
+
const arg = toolcall.arguments;
|
|
16
|
+
return buildUpdateTextRequest(toolcall.toolId, {
|
|
17
|
+
contentId: arg.contentId,
|
|
18
|
+
fieldName: arg.fieldName,
|
|
19
|
+
newText: arg.metadata.text,
|
|
20
|
+
maxCharacters: options.maxCharacters,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
case AI_ASSISTANT_TOOL_NAMES.UPDATE_STYLE: {
|
|
24
|
+
const arg = toolcall.arguments;
|
|
25
|
+
return buildUpdateTextStyleRequest(toolcall.toolId, {
|
|
26
|
+
contentId: arg.contentId,
|
|
27
|
+
fieldName: arg.fieldName,
|
|
28
|
+
newStyles: arg.metadata.style,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
12
31
|
case AI_ASSISTANT_TOOL_NAMES.DELETE_SECTION: {
|
|
13
32
|
const arg = toolcall.arguments;
|
|
14
33
|
return {
|
|
@@ -0,0 +1,28 @@
|
|
|
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;
|
|
@@ -0,0 +1,125 @@
|
|
|
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,6 +1,7 @@
|
|
|
1
1
|
import {} from '../../brand-page/models';
|
|
2
2
|
import { BaseMetadataBuilder, } from '../models';
|
|
3
3
|
import { GLOBAL_STYLE_CHECKS, TEXT_STYLE_OPTION_KEYS, } from './section-rules/global-styles.rules';
|
|
4
|
+
import { collectChangedFontFamilies, propagateFontFamiliesToContents, } from './global-font-propagation';
|
|
4
5
|
export class GlobalStylesBuilder extends BaseMetadataBuilder {
|
|
5
6
|
constructor(request, logger) {
|
|
6
7
|
super(request, logger);
|
|
@@ -30,6 +31,21 @@ export class GlobalStylesBuilder extends BaseMetadataBuilder {
|
|
|
30
31
|
}
|
|
31
32
|
if (args.textStyles) {
|
|
32
33
|
this.applyTextStyles(model, args.textStyles);
|
|
34
|
+
// A category font change only reaches preset-bound content by itself;
|
|
35
|
+
// detached section styles and inline RTE font marks would still render
|
|
36
|
+
// the old family. Propagate so the change is actually visible (BP-5780).
|
|
37
|
+
const changedFamilies = collectChangedFontFamilies(args.textStyles);
|
|
38
|
+
if (Object.keys(changedFamilies).length) {
|
|
39
|
+
try {
|
|
40
|
+
propagateFontFamiliesToContents(model, changedFamilies);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
this.log({
|
|
44
|
+
message: 'Global font propagation to existing contents failed — presets still updated',
|
|
45
|
+
error,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
33
49
|
}
|
|
34
50
|
if (args.buttonStyles) {
|
|
35
51
|
this.applyButtonStyles(model, args.buttonStyles);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyScalarFields, applyStyleFields, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { applyColorThemeToReplacements, resolveShapeColorReplacementsAsync, } from '../shape-color-utils';
|
|
@@ -23,7 +24,7 @@ export class AboutSectionBuilder extends BaseSectionBuilder {
|
|
|
23
24
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
24
25
|
if (aboutArgs.title !== undefined) {
|
|
25
26
|
if (isTiptapDocument(aboutArgs.title)) {
|
|
26
|
-
resolved.content.title = aboutArgs.title;
|
|
27
|
+
resolved.content.title = tiptapJsonToHtml(aboutArgs.title);
|
|
27
28
|
resolved.content.titleStyle = resolveGlobalPresetIntoStyle(resolved.content.titleStyle, textStyleOptions);
|
|
28
29
|
}
|
|
29
30
|
else {
|
|
@@ -32,7 +33,7 @@ export class AboutSectionBuilder extends BaseSectionBuilder {
|
|
|
32
33
|
}
|
|
33
34
|
if (aboutArgs.paragraph !== undefined) {
|
|
34
35
|
if (isTiptapDocument(aboutArgs.paragraph)) {
|
|
35
|
-
resolved.content.paragraph = aboutArgs.paragraph;
|
|
36
|
+
resolved.content.paragraph = tiptapJsonToHtml(aboutArgs.paragraph);
|
|
36
37
|
resolved.content.paragraphStyle = resolveGlobalPresetIntoStyle(resolved.content.paragraphStyle, textStyleOptions);
|
|
37
38
|
}
|
|
38
39
|
else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyButtonLink, applyScalarFields, applyStyleFields, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { applyColorThemeToReplacements, resolveShapeColorReplacementsAsync, } from '../shape-color-utils';
|
|
@@ -43,7 +44,7 @@ export class BannerSectionBuilder extends BaseSectionBuilder {
|
|
|
43
44
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
44
45
|
if (bannerArgs.title !== undefined) {
|
|
45
46
|
if (isTiptapDocument(bannerArgs.title)) {
|
|
46
|
-
resolved.content.title = bannerArgs.title;
|
|
47
|
+
resolved.content.title = tiptapJsonToHtml(bannerArgs.title);
|
|
47
48
|
resolved.content.titleStyle = resolveGlobalPresetIntoStyle(resolved.content.titleStyle, textStyleOptions);
|
|
48
49
|
}
|
|
49
50
|
else {
|
|
@@ -52,7 +53,7 @@ export class BannerSectionBuilder extends BaseSectionBuilder {
|
|
|
52
53
|
}
|
|
53
54
|
if (bannerArgs.description !== undefined) {
|
|
54
55
|
if (isTiptapDocument(bannerArgs.description)) {
|
|
55
|
-
resolved.content.description = bannerArgs.description;
|
|
56
|
+
resolved.content.description = tiptapJsonToHtml(bannerArgs.description);
|
|
56
57
|
resolved.content.descriptionStyle = resolveGlobalPresetIntoStyle(resolved.content.descriptionStyle, textStyleOptions);
|
|
57
58
|
}
|
|
58
59
|
else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyButtonLink, applyScalarFields, applyStyleFields, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { BaseSectionBuilder } from './base-section-builder';
|
|
@@ -22,7 +23,7 @@ export class BookingSectionBuilder extends BaseSectionBuilder {
|
|
|
22
23
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
23
24
|
if (bookingArgs.title !== undefined) {
|
|
24
25
|
if (isTiptapDocument(bookingArgs.title)) {
|
|
25
|
-
resolved.content.title = bookingArgs.title;
|
|
26
|
+
resolved.content.title = tiptapJsonToHtml(bookingArgs.title);
|
|
26
27
|
resolved.content.titleStyle = resolveGlobalPresetIntoStyle(resolved.content.titleStyle, textStyleOptions);
|
|
27
28
|
}
|
|
28
29
|
else {
|
|
@@ -31,7 +32,7 @@ export class BookingSectionBuilder extends BaseSectionBuilder {
|
|
|
31
32
|
}
|
|
32
33
|
if (bookingArgs.description !== undefined) {
|
|
33
34
|
if (isTiptapDocument(bookingArgs.description)) {
|
|
34
|
-
resolved.content.description = bookingArgs.description;
|
|
35
|
+
resolved.content.description = tiptapJsonToHtml(bookingArgs.description);
|
|
35
36
|
resolved.content.descriptionStyle = resolveGlobalPresetIntoStyle(resolved.content.descriptionStyle, textStyleOptions);
|
|
36
37
|
}
|
|
37
38
|
else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyScalarFields, applyStyleFields, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { BaseSectionBuilder } from './base-section-builder';
|
|
@@ -22,7 +23,7 @@ export class ContactFormBuilder extends BaseSectionBuilder {
|
|
|
22
23
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
23
24
|
if (args.title !== undefined) {
|
|
24
25
|
if (isTiptapDocument(args.title)) {
|
|
25
|
-
content.title = args.title;
|
|
26
|
+
content.title = tiptapJsonToHtml(args.title);
|
|
26
27
|
content.titleStyle = resolveGlobalPresetIntoStyle(content.titleStyle, textStyleOptions);
|
|
27
28
|
}
|
|
28
29
|
else {
|
|
@@ -31,7 +32,7 @@ export class ContactFormBuilder extends BaseSectionBuilder {
|
|
|
31
32
|
}
|
|
32
33
|
if (args.description !== undefined) {
|
|
33
34
|
if (isTiptapDocument(args.description)) {
|
|
34
|
-
content.description = args.description;
|
|
35
|
+
content.description = tiptapJsonToHtml(args.description);
|
|
35
36
|
content.descriptionStyle = resolveGlobalPresetIntoStyle(content.descriptionStyle, textStyleOptions);
|
|
36
37
|
}
|
|
37
38
|
else {
|
|
@@ -40,7 +41,7 @@ export class ContactFormBuilder extends BaseSectionBuilder {
|
|
|
40
41
|
}
|
|
41
42
|
if (args.success_message !== undefined) {
|
|
42
43
|
if (isTiptapDocument(args.success_message)) {
|
|
43
|
-
content.successMessage = args.success_message;
|
|
44
|
+
content.successMessage = tiptapJsonToHtml(args.success_message);
|
|
44
45
|
content.successMessageStyle = resolveGlobalPresetIntoStyle(content.successMessageStyle, textStyleOptions);
|
|
45
46
|
}
|
|
46
47
|
else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyScalarFields, applyStyleFields, isValidUrl, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { BaseSectionBuilder } from './base-section-builder';
|
|
@@ -95,7 +96,7 @@ export class ImagesSectionBuilder extends BaseSectionBuilder {
|
|
|
95
96
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
96
97
|
if (args.title !== undefined) {
|
|
97
98
|
if (isTiptapDocument(args.title)) {
|
|
98
|
-
content.title = args.title;
|
|
99
|
+
content.title = tiptapJsonToHtml(args.title);
|
|
99
100
|
content.titleStyle = resolveGlobalPresetIntoStyle(content.titleStyle, textStyleOptions);
|
|
100
101
|
}
|
|
101
102
|
else {
|
|
@@ -104,7 +105,7 @@ export class ImagesSectionBuilder extends BaseSectionBuilder {
|
|
|
104
105
|
}
|
|
105
106
|
if (args.description !== undefined) {
|
|
106
107
|
if (isTiptapDocument(args.description)) {
|
|
107
|
-
content.description = args.description;
|
|
108
|
+
content.description = tiptapJsonToHtml(args.description);
|
|
108
109
|
content.descriptionStyle = resolveGlobalPresetIntoStyle(content.descriptionStyle, textStyleOptions);
|
|
109
110
|
}
|
|
110
111
|
else {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { BaseSectionBuilder, type SectionBuilderPayload, } from './base-section-builder';
|
|
2
2
|
export { BannerSectionBuilder } from './banner-section-builder';
|
|
3
3
|
export { BookingSectionBuilder } from './booking-section-builder';
|
|
4
|
+
export { SupportMeBuilder } from './support-me-builder';
|
|
4
5
|
export { AboutSectionBuilder } from './about-section-builder';
|
|
5
6
|
export { ContactFormBuilder } from './contact-form-builder';
|
|
6
7
|
export { DividerSectionBuilder } from './divider-section-builder';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { BaseSectionBuilder, } from './base-section-builder';
|
|
2
2
|
export { BannerSectionBuilder } from './banner-section-builder';
|
|
3
3
|
export { BookingSectionBuilder } from './booking-section-builder';
|
|
4
|
+
export { SupportMeBuilder } from './support-me-builder';
|
|
4
5
|
export { AboutSectionBuilder } from './about-section-builder';
|
|
5
6
|
export { ContactFormBuilder } from './contact-form-builder';
|
|
6
7
|
export { DividerSectionBuilder } from './divider-section-builder';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyScalarFields, applyStyleFields, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { BaseSectionBuilder } from './base-section-builder';
|
|
@@ -22,7 +23,7 @@ export class MailingListBuilder extends BaseSectionBuilder {
|
|
|
22
23
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
23
24
|
if (mailingListArgs.title !== undefined) {
|
|
24
25
|
if (isTiptapDocument(mailingListArgs.title)) {
|
|
25
|
-
content.title = mailingListArgs.title;
|
|
26
|
+
content.title = tiptapJsonToHtml(mailingListArgs.title);
|
|
26
27
|
content.titleStyle = resolveGlobalPresetIntoStyle(content.titleStyle, textStyleOptions);
|
|
27
28
|
}
|
|
28
29
|
else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyScalarFields, applyStyleFields, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { BaseSectionBuilder } from './base-section-builder';
|
|
@@ -21,7 +22,7 @@ export class MailingListSectionBuilder extends BaseSectionBuilder {
|
|
|
21
22
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
22
23
|
if (mailingListArgs.title !== undefined) {
|
|
23
24
|
if (isTiptapDocument(mailingListArgs.title)) {
|
|
24
|
-
resolved.content.title = mailingListArgs.title;
|
|
25
|
+
resolved.content.title = tiptapJsonToHtml(mailingListArgs.title);
|
|
25
26
|
resolved.content.titleStyle = resolveGlobalPresetIntoStyle(resolved.content.titleStyle, textStyleOptions);
|
|
26
27
|
}
|
|
27
28
|
else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyScalarFields, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { BaseSectionBuilder } from './base-section-builder';
|
|
@@ -20,7 +21,7 @@ export class ProfileSectionBuilder extends BaseSectionBuilder {
|
|
|
20
21
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
21
22
|
if (profileArgs.name !== undefined) {
|
|
22
23
|
if (isTiptapDocument(profileArgs.name)) {
|
|
23
|
-
resolved.content.name = profileArgs.name;
|
|
24
|
+
resolved.content.name = tiptapJsonToHtml(profileArgs.name);
|
|
24
25
|
resolved.content.nameStyle = resolveGlobalPresetIntoStyle(resolved.content.nameStyle, textStyleOptions);
|
|
25
26
|
}
|
|
26
27
|
else {
|
|
@@ -29,7 +30,7 @@ export class ProfileSectionBuilder extends BaseSectionBuilder {
|
|
|
29
30
|
}
|
|
30
31
|
if (profileArgs.title !== undefined) {
|
|
31
32
|
if (isTiptapDocument(profileArgs.title)) {
|
|
32
|
-
resolved.content.title = profileArgs.title;
|
|
33
|
+
resolved.content.title = tiptapJsonToHtml(profileArgs.title);
|
|
33
34
|
resolved.content.titleStyle = resolveGlobalPresetIntoStyle(resolved.content.titleStyle, textStyleOptions);
|
|
34
35
|
}
|
|
35
36
|
else {
|
|
@@ -38,7 +39,7 @@ export class ProfileSectionBuilder extends BaseSectionBuilder {
|
|
|
38
39
|
}
|
|
39
40
|
if (profileArgs.description !== undefined) {
|
|
40
41
|
if (isTiptapDocument(profileArgs.description)) {
|
|
41
|
-
resolved.content.description = profileArgs.description;
|
|
42
|
+
resolved.content.description = tiptapJsonToHtml(profileArgs.description);
|
|
42
43
|
resolved.content.descriptionStyle = resolveGlobalPresetIntoStyle(resolved.content.descriptionStyle, textStyleOptions);
|
|
43
44
|
}
|
|
44
45
|
else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyScalarFields, applyStyleFields, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { applyColorThemeToReplacements, resolveShapeColorReplacementsAsync, } from '../shape-color-utils';
|
|
@@ -116,7 +117,7 @@ export class ShapesSectionBuilder extends BaseSectionBuilder {
|
|
|
116
117
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
117
118
|
if (args.title !== undefined) {
|
|
118
119
|
if (isTiptapDocument(args.title)) {
|
|
119
|
-
content.title = args.title;
|
|
120
|
+
content.title = tiptapJsonToHtml(args.title);
|
|
120
121
|
content.titleStyle = resolveGlobalPresetIntoStyle(content.titleStyle, textStyleOptions);
|
|
121
122
|
}
|
|
122
123
|
else {
|
|
@@ -125,7 +126,7 @@ export class ShapesSectionBuilder extends BaseSectionBuilder {
|
|
|
125
126
|
}
|
|
126
127
|
if (args.description !== undefined) {
|
|
127
128
|
if (isTiptapDocument(args.description)) {
|
|
128
|
-
content.description = args.description;
|
|
129
|
+
content.description = tiptapJsonToHtml(args.description);
|
|
129
130
|
content.descriptionStyle = resolveGlobalPresetIntoStyle(content.descriptionStyle, textStyleOptions);
|
|
130
131
|
}
|
|
131
132
|
else {
|
|
@@ -0,0 +1,8 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { applyScalarFields, applyStyleFields } from '../section-field-utils';
|
|
2
|
+
import { BaseSectionBuilder } from './base-section-builder';
|
|
3
|
+
import { validateSupportMe } from '../section-rules/support-me.rules';
|
|
4
|
+
export class SupportMeBuilder extends BaseSectionBuilder {
|
|
5
|
+
get sectionValidate() {
|
|
6
|
+
return validateSupportMe;
|
|
7
|
+
}
|
|
8
|
+
async buildAsync(model) {
|
|
9
|
+
this.log('Build called');
|
|
10
|
+
const validation = await this.validateAsync();
|
|
11
|
+
if (validation.errors.length) {
|
|
12
|
+
return { request: this.request, model, errors: validation.errors };
|
|
13
|
+
}
|
|
14
|
+
const resolved = this.resolveAndRepositionContent(model);
|
|
15
|
+
if ('error' in resolved)
|
|
16
|
+
return resolved.error;
|
|
17
|
+
const content = resolved.content;
|
|
18
|
+
// title/description/success_message are plain-text fields (no RTE support),
|
|
19
|
+
// so scalar application covers all content fields. supportMeConfig (payment
|
|
20
|
+
// options) is intentionally not AI-editable — it comes from the factory.
|
|
21
|
+
applyScalarFields(content, this.args, this.sectionType);
|
|
22
|
+
applyStyleFields(content, this.args, this.sectionType, this.getGlobalTextStyles(model));
|
|
23
|
+
return { request: this.request, model };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyScalarFields, applyStyleFields, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { applyColorThemeToReplacements, resolveShapeColorReplacementsAsync, } from '../shape-color-utils';
|
|
@@ -102,7 +103,7 @@ export class TestimonialSectionBuilder extends BaseSectionBuilder {
|
|
|
102
103
|
const textStyleOptions = model.styles?.textStyles?.textStyleOptions;
|
|
103
104
|
if (args.title !== undefined) {
|
|
104
105
|
if (isTiptapDocument(args.title)) {
|
|
105
|
-
content.title = args.title;
|
|
106
|
+
content.title = tiptapJsonToHtml(args.title);
|
|
106
107
|
content.titleStyle = resolveGlobalPresetIntoStyle(content.titleStyle, textStyleOptions);
|
|
107
108
|
}
|
|
108
109
|
else {
|
|
@@ -1,28 +1,24 @@
|
|
|
1
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
1
2
|
import { isTiptapDocument, } from '../../parsers/section-tool-arguments';
|
|
2
3
|
import { applyScalarFields, applyStyleFields, resolveGlobalPresetIntoStyle, } from '../section-field-utils';
|
|
3
4
|
import { BaseSectionBuilder } from './base-section-builder';
|
|
4
5
|
import { validateTextSection } from '../section-rules/text-section.rules';
|
|
5
6
|
import { ensureItemIds, generateItemId, removeItemsByIds, repositionItemByAnchor, } from '../item-id-utils';
|
|
6
|
-
/**
|
|
7
|
-
* Resolve a RichTextField to its stored RTE value. BP-5787: Tiptap JSON is
|
|
8
|
-
* stored as-is (no JSON→HTML round-trip — migration-20 is now a one-time
|
|
9
|
-
* legacy conversion), plain strings pass through. `wasTiptap` drives the
|
|
10
|
-
* text-style preset resolution for rich content.
|
|
11
|
-
*/
|
|
7
|
+
/** Resolve a RichTextField (string or TiptapDocument) to a plain HTML string. */
|
|
12
8
|
const resolveText = (value) => {
|
|
13
9
|
if (isTiptapDocument(value))
|
|
14
|
-
return { value, wasTiptap: true };
|
|
15
|
-
return {
|
|
10
|
+
return { html: tiptapJsonToHtml(value), wasTiptap: true };
|
|
11
|
+
return { html: typeof value === 'string' ? value : '', wasTiptap: false };
|
|
16
12
|
};
|
|
17
13
|
const clearTextStyleTypeIfTiptap = (style, wasTiptap, textStyleOptions) => wasTiptap
|
|
18
14
|
? resolveGlobalPresetIntoStyle(style, textStyleOptions)
|
|
19
15
|
: style;
|
|
20
16
|
const createTextItem = (item, baseStyle, textStyleOptions) => {
|
|
21
17
|
const textStyle = (item.text_style ? { ...baseStyle, ...item.text_style } : { ...baseStyle });
|
|
22
|
-
const {
|
|
18
|
+
const { html, wasTiptap } = resolveText(item.text);
|
|
23
19
|
return {
|
|
24
20
|
id: item.item_id || generateItemId(),
|
|
25
|
-
text:
|
|
21
|
+
text: html,
|
|
26
22
|
textStyle: clearTextStyleTypeIfTiptap(textStyle, wasTiptap, textStyleOptions),
|
|
27
23
|
};
|
|
28
24
|
};
|
|
@@ -55,8 +51,8 @@ const mergeTextItems = (content, items, removeIds, textStyleOptions) => {
|
|
|
55
51
|
if (!existing)
|
|
56
52
|
continue;
|
|
57
53
|
if (item.text !== undefined) {
|
|
58
|
-
const {
|
|
59
|
-
existing.text =
|
|
54
|
+
const { html, wasTiptap } = resolveText(item.text);
|
|
55
|
+
existing.text = html;
|
|
60
56
|
existing.textStyle = clearTextStyleTypeIfTiptap(existing.textStyle || {}, wasTiptap, textStyleOptions);
|
|
61
57
|
}
|
|
62
58
|
if (item.text_style && typeof item.text_style === 'object') {
|
|
@@ -80,11 +76,11 @@ const mergeTextItems = (content, items, removeIds, textStyleOptions) => {
|
|
|
80
76
|
*/
|
|
81
77
|
const applySingleTextField = (content, args, textStyleOptions) => {
|
|
82
78
|
if (args.text !== undefined) {
|
|
83
|
-
const {
|
|
79
|
+
const { html, wasTiptap } = resolveText(args.text);
|
|
84
80
|
if (Array.isArray(content.textItems) && content.textItems.length > 0) {
|
|
85
81
|
content.textItems[0] = {
|
|
86
82
|
...content.textItems[0],
|
|
87
|
-
text:
|
|
83
|
+
text: html,
|
|
88
84
|
textStyle: clearTextStyleTypeIfTiptap(content.textItems[0].textStyle || {}, wasTiptap, textStyleOptions),
|
|
89
85
|
};
|
|
90
86
|
}
|
|
@@ -92,7 +88,7 @@ const applySingleTextField = (content, args, textStyleOptions) => {
|
|
|
92
88
|
content.textItems = [
|
|
93
89
|
{
|
|
94
90
|
id: generateItemId(),
|
|
95
|
-
text:
|
|
91
|
+
text: html,
|
|
96
92
|
textStyle: clearTextStyleTypeIfTiptap({ ...(content.textStyle || {}) }, wasTiptap, textStyleOptions),
|
|
97
93
|
},
|
|
98
94
|
];
|
|
@@ -135,14 +131,17 @@ export class TextSectionBuilder extends BaseSectionBuilder {
|
|
|
135
131
|
applyTextItems(content, args.items, textStyleOptions);
|
|
136
132
|
}
|
|
137
133
|
else if (this.mode === 'create') {
|
|
138
|
-
|
|
134
|
+
// An EMPTY items array must not shadow the single text field — the AI emits
|
|
135
|
+
// `items: []` alongside `text` and the section rendered blank (QA 2026-07-05,
|
|
136
|
+
// T06: items=[] set textItems=[] and `text` was silently discarded).
|
|
137
|
+
if (args.items && Array.isArray(args.items) && args.items.length > 0) {
|
|
139
138
|
applyTextItems(content, args.items, textStyleOptions);
|
|
140
139
|
}
|
|
141
140
|
else {
|
|
142
141
|
applySingleTextField(content, args, textStyleOptions);
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
|
-
else if ((args.items && Array.isArray(args.items)) ||
|
|
144
|
+
else if ((args.items && Array.isArray(args.items) && args.items.length > 0) ||
|
|
146
145
|
args._remove_item_ids?.length) {
|
|
147
146
|
// Update: id-based add / edit / move / remove in a single pass.
|
|
148
147
|
mergeTextItems(content, args.items ?? [], args._remove_item_ids, textStyleOptions);
|
|
@@ -131,9 +131,16 @@ const socialButtonFields = defineFieldMap({
|
|
|
131
131
|
title: 'title',
|
|
132
132
|
url: 'url',
|
|
133
133
|
});
|
|
134
|
+
const supportMeFields = defineFieldMap({
|
|
135
|
+
title: 'title',
|
|
136
|
+
description: 'description',
|
|
137
|
+
success_message: 'successMessage',
|
|
138
|
+
display_style: 'displayStyle',
|
|
139
|
+
});
|
|
134
140
|
export const SECTION_FIELD_MAPS = {
|
|
135
141
|
'banner-section': bannerFields,
|
|
136
142
|
'booking-section': bookingFields,
|
|
143
|
+
'support-me': supportMeFields,
|
|
137
144
|
'about-section': aboutFields,
|
|
138
145
|
'testimonial-section': testimonialFields,
|
|
139
146
|
'images-section': imagesSectionFields,
|
|
@@ -208,6 +215,13 @@ const mailingListStyleFields = defineFieldMap({
|
|
|
208
215
|
title_style: 'titleStyle',
|
|
209
216
|
button_text_style: 'buttonTextStyle',
|
|
210
217
|
});
|
|
218
|
+
const supportMeStyleFields = defineFieldMap({
|
|
219
|
+
title_style: 'titleStyle',
|
|
220
|
+
heading_style: 'headingStyle',
|
|
221
|
+
description_style: 'descriptionStyle',
|
|
222
|
+
item_text_style: 'itemTextStyle',
|
|
223
|
+
button_text_style: 'buttonTextStyle',
|
|
224
|
+
});
|
|
211
225
|
const socialLinksStyleFields = defineFieldMap({
|
|
212
226
|
icon_fill_style: 'iconFillStyles',
|
|
213
227
|
icon_background_style: 'iconBackgroundStyles',
|
|
@@ -215,6 +229,7 @@ const socialLinksStyleFields = defineFieldMap({
|
|
|
215
229
|
export const STYLE_FIELD_MAPS = {
|
|
216
230
|
'banner-section': bannerStyleFields,
|
|
217
231
|
'booking-section': bookingStyleFields,
|
|
232
|
+
'support-me': supportMeStyleFields,
|
|
218
233
|
'about-section': aboutStyleFields,
|
|
219
234
|
text: textStyleFields,
|
|
220
235
|
'testimonial-section': testimonialStyleFields,
|