@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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
import '@tiptap/extension-text-style';
|
|
3
|
+
|
|
4
|
+
export type FontSizeOptions = {
|
|
5
|
+
types: string[];
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
declare module '@tiptap/core' {
|
|
9
|
+
interface Commands<ReturnType> {
|
|
10
|
+
fontSize: {
|
|
11
|
+
/**
|
|
12
|
+
* Set the font size
|
|
13
|
+
*/
|
|
14
|
+
setFontSize: (fontSize: string) => ReturnType;
|
|
15
|
+
/**
|
|
16
|
+
* Unset the font size
|
|
17
|
+
*/
|
|
18
|
+
unsetFontSize: () => ReturnType;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const FontSize = Extension.create<FontSizeOptions>({
|
|
24
|
+
name: 'fontSize',
|
|
25
|
+
|
|
26
|
+
addOptions() {
|
|
27
|
+
return {
|
|
28
|
+
types: ['textStyle'],
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
addGlobalAttributes() {
|
|
33
|
+
return [
|
|
34
|
+
{
|
|
35
|
+
types: this.options.types,
|
|
36
|
+
attributes: {
|
|
37
|
+
fontSize: {
|
|
38
|
+
default: null,
|
|
39
|
+
parseHTML: (element) =>
|
|
40
|
+
element.style.fontSize.replace(/['"]+/g, ''),
|
|
41
|
+
renderHTML: (attributes) => {
|
|
42
|
+
if (!attributes.fontSize) {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
style: `font-size: ${attributes.fontSize}`,
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
addCommands() {
|
|
57
|
+
return {
|
|
58
|
+
setFontSize:
|
|
59
|
+
(fontSize) =>
|
|
60
|
+
({ chain }) => {
|
|
61
|
+
return chain().setMark('textStyle', { fontSize }).run();
|
|
62
|
+
},
|
|
63
|
+
unsetFontSize:
|
|
64
|
+
() =>
|
|
65
|
+
({ chain }) => {
|
|
66
|
+
return chain()
|
|
67
|
+
.setMark('textStyle', { fontSize: null })
|
|
68
|
+
.removeEmptyTextStyle()
|
|
69
|
+
.run();
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
|
|
3
|
+
export type LineHeightOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Node / mark types that should carry the `lineHeight` attribute.
|
|
6
|
+
* Defaults to `['textStyle']` to piggy‑back on TipTap’s built‑in mark.
|
|
7
|
+
*/
|
|
8
|
+
types: string[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
declare module '@tiptap/core' {
|
|
12
|
+
interface Commands<ReturnType> {
|
|
13
|
+
lineHeight: {
|
|
14
|
+
/**
|
|
15
|
+
* Apply a line‑height to the current selection (or stored marks).
|
|
16
|
+
*/
|
|
17
|
+
setLineHeight: (heightValue: string) => ReturnType;
|
|
18
|
+
/**
|
|
19
|
+
* Remove any line‑height attribute.
|
|
20
|
+
*/
|
|
21
|
+
unsetLineHeight: () => ReturnType;
|
|
22
|
+
/**
|
|
23
|
+
* Read the current line‑height from the selection / stored marks.
|
|
24
|
+
*/
|
|
25
|
+
getLineHeight: () => ReturnType;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const LineHeight = Extension.create<LineHeightOptions>({
|
|
31
|
+
name: 'lineHeight',
|
|
32
|
+
|
|
33
|
+
addOptions() {
|
|
34
|
+
return {
|
|
35
|
+
types: ['textStyle'],
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
addGlobalAttributes() {
|
|
40
|
+
return [
|
|
41
|
+
{
|
|
42
|
+
types: this.options.types,
|
|
43
|
+
attributes: {
|
|
44
|
+
lineHeight: {
|
|
45
|
+
default: null,
|
|
46
|
+
parseHTML: (element) =>
|
|
47
|
+
(element.style.lineHeight || '').replace(/['"]+/g, ''),
|
|
48
|
+
renderHTML: (attributes) => {
|
|
49
|
+
if (!attributes.lineHeight) {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { style: `line-height: ${attributes.lineHeight}` };
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
addCommands() {
|
|
62
|
+
return {
|
|
63
|
+
setLineHeight:
|
|
64
|
+
(lineHeight) =>
|
|
65
|
+
({ chain }) => {
|
|
66
|
+
return chain().setMark('textStyle', { lineHeight }).run();
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
unsetLineHeight:
|
|
70
|
+
() =>
|
|
71
|
+
({ chain }) => {
|
|
72
|
+
return chain()
|
|
73
|
+
.setMark('textStyle', { lineHeight: null })
|
|
74
|
+
.removeEmptyTextStyle()
|
|
75
|
+
.run();
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
getLineHeight:
|
|
79
|
+
() =>
|
|
80
|
+
({ editor }) => {
|
|
81
|
+
return editor.getAttributes('textStyle').lineHeight;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
});
|
|
@@ -8,7 +8,18 @@ import {
|
|
|
8
8
|
watch,
|
|
9
9
|
} from 'vue';
|
|
10
10
|
import { Editor } from '@tiptap/vue-3';
|
|
11
|
-
import
|
|
11
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
12
|
+
import Underline from '@tiptap/extension-underline';
|
|
13
|
+
import TextStyle from '@tiptap/extension-text-style';
|
|
14
|
+
import TextAlign from '@tiptap/extension-text-align';
|
|
15
|
+
import FontFamily from '@tiptap/extension-font-family';
|
|
16
|
+
import ColorExtension from '@tiptap/extension-color';
|
|
17
|
+
import { FontSize } from './extensions/FontSize';
|
|
18
|
+
import { LetterSpacing } from './extensions/LetterSpacing';
|
|
19
|
+
import { LineHeight } from './extensions/LineHeight';
|
|
20
|
+
import Highlight from '@tiptap/extension-highlight';
|
|
21
|
+
import Placeholder from '@tiptap/extension-placeholder';
|
|
22
|
+
import Link from '@tiptap/extension-link';
|
|
12
23
|
import type { StyleTextFontFamilyOption } from '@shared/style-settings/models';
|
|
13
24
|
import { createFormattingActions } from './formatting';
|
|
14
25
|
import { useTextAlign } from './textAlign';
|
|
@@ -20,7 +31,7 @@ import {
|
|
|
20
31
|
emToPx,
|
|
21
32
|
isStyleChanged,
|
|
22
33
|
pxToEm,
|
|
23
|
-
|
|
34
|
+
sanitiseClientSideRTE,
|
|
24
35
|
skipStyleDetection,
|
|
25
36
|
} from '@brand-page-maker/utils';
|
|
26
37
|
import type { HyperlinkType, TextStyles } from '@brand-page';
|
|
@@ -38,7 +49,9 @@ import {
|
|
|
38
49
|
import { useUiContentSettingsStore } from '@shared/content-settings/store';
|
|
39
50
|
import { useHistoryStore } from '@brand-page-maker/stores/history/history-store';
|
|
40
51
|
import { AllSelection } from 'prosemirror-state';
|
|
41
|
-
import {
|
|
52
|
+
import { MaxLength } from './extensions/MaxLength';
|
|
53
|
+
import { AutoLink } from './extensions/AutoLink';
|
|
54
|
+
import { isHrefPageId, useHyperlink } from './hyperlink';
|
|
42
55
|
import { useBubbleMenu } from './bubbleMenu';
|
|
43
56
|
import {
|
|
44
57
|
hasLogoColorVar,
|
|
@@ -79,7 +92,7 @@ interface UseRichTextEditorReturn {
|
|
|
79
92
|
hyperlinkValue: ComputedRef<{ id: string; name: string; value: string }>;
|
|
80
93
|
setHyperlinkUrlValue: (url: string) => void;
|
|
81
94
|
shouldShow: ({ editor, state }: { editor: any; state: any }) => boolean;
|
|
82
|
-
|
|
95
|
+
tippyOptions: Record<string, any>;
|
|
83
96
|
handleAdminColorChange: (
|
|
84
97
|
color: {
|
|
85
98
|
id: string;
|
|
@@ -116,7 +129,44 @@ export const useExternalRichTextEditor = (): {
|
|
|
116
129
|
} => {
|
|
117
130
|
const externalEditor = new Editor({
|
|
118
131
|
editable: false,
|
|
119
|
-
extensions:
|
|
132
|
+
extensions: [
|
|
133
|
+
StarterKit,
|
|
134
|
+
Underline,
|
|
135
|
+
TextStyle.configure({ mergeNestedSpanStyles: true }),
|
|
136
|
+
TextAlign.configure({
|
|
137
|
+
types: ['heading', 'paragraph'],
|
|
138
|
+
}),
|
|
139
|
+
FontFamily.configure({
|
|
140
|
+
types: ['textStyle'],
|
|
141
|
+
}),
|
|
142
|
+
FontSize,
|
|
143
|
+
LetterSpacing,
|
|
144
|
+
LineHeight,
|
|
145
|
+
ColorExtension.configure({
|
|
146
|
+
types: ['textStyle'],
|
|
147
|
+
}),
|
|
148
|
+
Highlight.configure({
|
|
149
|
+
multicolor: true,
|
|
150
|
+
}),
|
|
151
|
+
Link.configure({
|
|
152
|
+
openOnClick: false,
|
|
153
|
+
defaultProtocol: 'https',
|
|
154
|
+
autolink: true,
|
|
155
|
+
HTMLAttributes: (attrs) => {
|
|
156
|
+
const href = attrs.href;
|
|
157
|
+
const isExternal =
|
|
158
|
+
href && (href.startsWith('http://') || href.startsWith('https://'));
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
rel: 'noopener noreferrer',
|
|
162
|
+
target: isExternal ? '_blank' : null,
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
isAllowedUri: (url, ctx) =>
|
|
166
|
+
ctx.defaultValidate(url) || isHrefPageId(url),
|
|
167
|
+
}),
|
|
168
|
+
AutoLink,
|
|
169
|
+
],
|
|
120
170
|
});
|
|
121
171
|
|
|
122
172
|
return {
|
|
@@ -164,13 +214,54 @@ export const useRichTextEditor = (
|
|
|
164
214
|
const editor = new Editor({
|
|
165
215
|
content: model.value ?? '',
|
|
166
216
|
editable: !props.readOnly,
|
|
167
|
-
extensions:
|
|
168
|
-
|
|
169
|
-
|
|
217
|
+
extensions: [
|
|
218
|
+
StarterKit.configure({
|
|
219
|
+
bulletList: false,
|
|
220
|
+
orderedList: false,
|
|
221
|
+
listItem: false,
|
|
222
|
+
horizontalRule: false,
|
|
223
|
+
codeBlock: false,
|
|
224
|
+
code: false,
|
|
225
|
+
}),
|
|
226
|
+
Underline,
|
|
227
|
+
TextStyle.configure({ mergeNestedSpanStyles: true }),
|
|
228
|
+
TextAlign.configure({
|
|
229
|
+
types: ['heading', 'paragraph'],
|
|
230
|
+
}),
|
|
231
|
+
FontFamily.configure({
|
|
232
|
+
types: ['textStyle'],
|
|
233
|
+
}),
|
|
234
|
+
FontSize,
|
|
235
|
+
LetterSpacing,
|
|
236
|
+
LineHeight,
|
|
237
|
+
ColorExtension.configure({
|
|
238
|
+
types: ['textStyle'],
|
|
239
|
+
}),
|
|
240
|
+
Highlight.configure({
|
|
241
|
+
multicolor: true,
|
|
242
|
+
}),
|
|
243
|
+
Placeholder.configure({
|
|
170
244
|
placeholder: props.placeholder,
|
|
171
|
-
|
|
172
|
-
},
|
|
173
|
-
|
|
245
|
+
}),
|
|
246
|
+
MaxLength.configure({ maxLength: props.maxLength }),
|
|
247
|
+
Link.configure({
|
|
248
|
+
openOnClick: false,
|
|
249
|
+
defaultProtocol: 'https',
|
|
250
|
+
autolink: true,
|
|
251
|
+
HTMLAttributes: (attrs) => {
|
|
252
|
+
const href = attrs.href;
|
|
253
|
+
const isExternal =
|
|
254
|
+
href && (href.startsWith('http://') || href.startsWith('https://'));
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
target: isExternal ? '_blank' : null,
|
|
258
|
+
};
|
|
259
|
+
},
|
|
260
|
+
isAllowedUri: (url, ctx) =>
|
|
261
|
+
ctx.defaultValidate(url) || isHrefPageId(url),
|
|
262
|
+
}),
|
|
263
|
+
AutoLink,
|
|
264
|
+
],
|
|
174
265
|
editorProps: {
|
|
175
266
|
handleDOMEvents: {
|
|
176
267
|
drop(view, event: DragEvent) {
|
|
@@ -349,7 +440,7 @@ export const useRichTextEditor = (
|
|
|
349
440
|
applyHyperlinkToEditor,
|
|
350
441
|
} = useHyperlink(editor);
|
|
351
442
|
|
|
352
|
-
const { shouldShow,
|
|
443
|
+
const { shouldShow, tippyOptions } = useBubbleMenu(
|
|
353
444
|
editor,
|
|
354
445
|
isHyperlinkPopupOpen,
|
|
355
446
|
);
|
|
@@ -381,14 +472,8 @@ export const useRichTextEditor = (
|
|
|
381
472
|
|
|
382
473
|
const isExternalMutation = updateRichTextEditor.value || appliedSinceLast;
|
|
383
474
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
// setContent and reset the user's just-applied styles).
|
|
387
|
-
const hasChanged =
|
|
388
|
-
JSON.stringify(editor.getJSON()) !== JSON.stringify(newValue);
|
|
389
|
-
|
|
390
|
-
if (isExternalMutation && hasChanged) {
|
|
391
|
-
editor.commands.setContent(newValue, { emitUpdate: false });
|
|
475
|
+
if (isExternalMutation && editor.getHTML() !== newValue) {
|
|
476
|
+
editor.commands.setContent(newValue, false);
|
|
392
477
|
updateRichTextEditor.value = false;
|
|
393
478
|
//text style type is not switching when reordering
|
|
394
479
|
textStyleTypeOptions.forEach((item) => {
|
|
@@ -418,13 +503,12 @@ export const useRichTextEditor = (
|
|
|
418
503
|
}
|
|
419
504
|
});
|
|
420
505
|
|
|
421
|
-
// Update model when editor content changes
|
|
422
|
-
// JSON (BP-5787). getJSON() is already in hand (the editor holds the doc) and
|
|
423
|
-
// is sanitised natively — no getHTML(), no DOMPurify, no extra editor — so
|
|
424
|
-
// the per-keystroke path allocates nothing heavy.
|
|
506
|
+
// Update model when editor content changes
|
|
425
507
|
editor.on('update', () => {
|
|
508
|
+
const sanitizedData = sanitiseClientSideRTE(editor.getHTML());
|
|
509
|
+
|
|
426
510
|
// An update fired while the editor is unfocused is TipTap rewriting the
|
|
427
|
-
// model back into its canonical
|
|
511
|
+
// model back into its canonical HTML during mount rehydration or a
|
|
428
512
|
// programmatic setContent — not a user edit. Tag the emit so the write
|
|
429
513
|
// folds into the current undo head instead of pushing a phantom entry
|
|
430
514
|
// (BP-5810). The flag travels WITH the value through the debounced
|
|
@@ -438,7 +522,7 @@ export const useRichTextEditor = (
|
|
|
438
522
|
// the RTE only mounts inside an already-open settings drawer and the user
|
|
439
523
|
// focuses it before formatting, so this doesn't occur — revisit if a
|
|
440
524
|
// consumer ever drives toolbar actions on an unfocused editor.
|
|
441
|
-
emit('update:modelValue',
|
|
525
|
+
emit('update:modelValue', sanitizedData.sanitizedHtml, {
|
|
442
526
|
isHydrationWrite: !editor.isFocused,
|
|
443
527
|
});
|
|
444
528
|
});
|
|
@@ -965,7 +1049,7 @@ export const useRichTextEditor = (
|
|
|
965
1049
|
hyperlinkValue,
|
|
966
1050
|
setHyperlinkUrlValue,
|
|
967
1051
|
shouldShow,
|
|
968
|
-
|
|
1052
|
+
tippyOptions,
|
|
969
1053
|
handleAdminColorChange,
|
|
970
1054
|
};
|
|
971
1055
|
};
|
|
@@ -7,8 +7,6 @@ export * from './url.util';
|
|
|
7
7
|
export * from './slug.util';
|
|
8
8
|
export * from './text.util';
|
|
9
9
|
export * from './rich-text-editor.util';
|
|
10
|
-
export * from './rte-render.util';
|
|
11
|
-
export * from './rte-sanitize.util';
|
|
12
10
|
|
|
13
11
|
export const isColorMatched = (color: string | null | undefined): boolean => {
|
|
14
12
|
if (!color) return false;
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
getUpdatedHTMLFromEditor,
|
|
9
9
|
sanitiseClientSideRTE,
|
|
10
10
|
getRTEColor,
|
|
11
|
-
getRteTextLength,
|
|
12
11
|
} from './rich-text-editor.util';
|
|
13
12
|
import type { TextStyleDeclaration } from '@brand-page';
|
|
14
13
|
|
|
@@ -76,40 +75,8 @@ describe('hasHTML', () => {
|
|
|
76
75
|
});
|
|
77
76
|
});
|
|
78
77
|
|
|
79
|
-
describe('getRteTextLength', () => {
|
|
80
|
-
it('returns the raw length for a plain string (unchanged legacy behaviour)', () => {
|
|
81
|
-
expect(getRteTextLength('hello')).toBe(5);
|
|
82
|
-
expect(getRteTextLength('<p>hello</p>')).toBe('<p>hello</p>'.length);
|
|
83
|
-
expect(getRteTextLength('')).toBe(0);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('returns visible-text length for a Tiptap JSON doc (not undefined/throw)', () => {
|
|
87
|
-
const doc = {
|
|
88
|
-
type: 'doc',
|
|
89
|
-
content: [
|
|
90
|
-
{
|
|
91
|
-
type: 'paragraph',
|
|
92
|
-
content: [
|
|
93
|
-
{ type: 'text', text: 'hello ' },
|
|
94
|
-
{ type: 'text', marks: [{ type: 'bold' }], text: 'world' },
|
|
95
|
-
],
|
|
96
|
-
},
|
|
97
|
-
],
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// "hello world" — measured from the text nodes, not the JSON wrapper.
|
|
101
|
-
expect(getRteTextLength(doc)).toBe(11);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('returns 0 for null/undefined and an empty doc', () => {
|
|
105
|
-
expect(getRteTextLength(null)).toBe(0);
|
|
106
|
-
expect(getRteTextLength(undefined)).toBe(0);
|
|
107
|
-
expect(getRteTextLength({ type: 'doc', content: [] })).toBe(0);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
78
|
describe('getUpdatedHTMLFromEditor', () => {
|
|
112
|
-
it('applies style commands and returns updated
|
|
79
|
+
it('applies style commands and returns updated HTML', () => {
|
|
113
80
|
// Mock editor
|
|
114
81
|
const runMock = vi.fn();
|
|
115
82
|
const chain = {
|
|
@@ -152,7 +119,7 @@ describe('getUpdatedHTMLFromEditor', () => {
|
|
|
152
119
|
},
|
|
153
120
|
},
|
|
154
121
|
chain: () => chain as any,
|
|
155
|
-
|
|
122
|
+
getHTML: () => '<div>styled</div>',
|
|
156
123
|
} as unknown as Editor;
|
|
157
124
|
|
|
158
125
|
const newStyle = {
|
|
@@ -170,8 +137,7 @@ describe('getUpdatedHTMLFromEditor', () => {
|
|
|
170
137
|
|
|
171
138
|
expect(externalEditor.commands.setContent).toHaveBeenCalledWith('foo');
|
|
172
139
|
expect(runMock).toHaveBeenCalled();
|
|
173
|
-
|
|
174
|
-
expect(result).toEqual({ type: 'doc', content: [{ type: 'paragraph' }] });
|
|
140
|
+
expect(result).toBe('<div>styled</div>');
|
|
175
141
|
});
|
|
176
142
|
});
|
|
177
143
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChainedCommands, Editor
|
|
1
|
+
import type { ChainedCommands, Editor } from '@tiptap/vue-3';
|
|
2
2
|
import type { EditorState, Transaction, Selection } from 'prosemirror-state';
|
|
3
3
|
import {
|
|
4
4
|
STYLE_THEME_KEYS,
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
type RequestPaymentContent,
|
|
20
20
|
type SupportMeContent,
|
|
21
21
|
type ShopContent,
|
|
22
|
-
type RichTextValue,
|
|
23
22
|
} from '@brand-page';
|
|
24
23
|
import type { PageSettingsState } from '@shared/page-settings/store';
|
|
25
24
|
import type { StyleSettingsState } from '@shared/style-settings/store';
|
|
@@ -244,38 +243,8 @@ export const skipStyleDetection = (chain: ChainedCommands, marks?: Mark[]) =>
|
|
|
244
243
|
return true;
|
|
245
244
|
});
|
|
246
245
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
* document is an object with `type: 'doc'`.
|
|
250
|
-
*/
|
|
251
|
-
export const isTiptapDocument = (value: unknown): value is JSONContent =>
|
|
252
|
-
typeof value === 'object' &&
|
|
253
|
-
value !== null &&
|
|
254
|
-
(value as JSONContent).type === 'doc';
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Extract visible plain text from a Tiptap JSON document (concatenated text
|
|
258
|
-
* nodes). Dependency-free — no editor instantiation.
|
|
259
|
-
*/
|
|
260
|
-
export const getRteJsonPlainText = (doc: JSONContent): string => {
|
|
261
|
-
const parts: string[] = [];
|
|
262
|
-
|
|
263
|
-
const walk = (node: JSONContent): void => {
|
|
264
|
-
if (typeof node.text === 'string') parts.push(node.text);
|
|
265
|
-
|
|
266
|
-
node.content?.forEach(walk);
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
walk(doc);
|
|
270
|
-
|
|
271
|
-
return parts.join(' ').replace(/\s+/g, ' ').trim();
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
// Accepts a legacy inline-HTML string or a Tiptap JSON document (BP-5787).
|
|
275
|
-
export const hasRTEContent = (content: unknown): boolean => {
|
|
276
|
-
if (isTiptapDocument(content)) return true;
|
|
277
|
-
|
|
278
|
-
if (typeof content !== 'string' || content.length === 0) return false;
|
|
246
|
+
export const hasRTEContent = (content: string): boolean => {
|
|
247
|
+
if (!content || content.length === 0) return false;
|
|
279
248
|
|
|
280
249
|
const trimmed = content.trim();
|
|
281
250
|
const tagWrapRegex = /^<([A-Za-z][A-Za-z0-9-]*)\b[^>]*>[\s\S]*<\/\1>$/;
|
|
@@ -283,11 +252,7 @@ export const hasRTEContent = (content: unknown): boolean => {
|
|
|
283
252
|
return tagWrapRegex.test(trimmed);
|
|
284
253
|
};
|
|
285
254
|
|
|
286
|
-
export const getPlainTextFromRTE = (content:
|
|
287
|
-
if (isTiptapDocument(content)) {
|
|
288
|
-
return getRteJsonPlainText(content);
|
|
289
|
-
}
|
|
290
|
-
|
|
255
|
+
export const getPlainTextFromRTE = (content: string): string => {
|
|
291
256
|
if (typeof content !== 'string') {
|
|
292
257
|
return '';
|
|
293
258
|
}
|
|
@@ -309,22 +274,6 @@ export const getPlainTextFromRTE = (content: unknown): string => {
|
|
|
309
274
|
return plainText.trim();
|
|
310
275
|
};
|
|
311
276
|
|
|
312
|
-
/**
|
|
313
|
-
* BP-5787 — visible-text length of an RTE value, for the maker item-field
|
|
314
|
-
* max-length / emptiness checks. A plain string keeps its raw `.length`, so the
|
|
315
|
-
* pre-JSON behaviour is byte-for-byte identical; a Tiptap JSON doc measures its
|
|
316
|
-
* concatenated visible text. Without this, `doc.length` is `undefined` (silently
|
|
317
|
-
* disabling the check) and `doc.trim()` throws — the class of bug that survived
|
|
318
|
-
* the `RichTextValue` widening because Vue Options-API `this`/item access is `any`.
|
|
319
|
-
*/
|
|
320
|
-
export const getRteTextLength = (
|
|
321
|
-
value: RichTextValue | null | undefined,
|
|
322
|
-
): number => {
|
|
323
|
-
if (typeof value === 'string') return value.length;
|
|
324
|
-
|
|
325
|
-
return value ? getRteJsonPlainText(value).length : 0;
|
|
326
|
-
};
|
|
327
|
-
|
|
328
277
|
export const replaceTextInRTE = (
|
|
329
278
|
existingHtml: string,
|
|
330
279
|
newText: string,
|
|
@@ -772,20 +721,16 @@ export const switchAllContentToRTE = (
|
|
|
772
721
|
|
|
773
722
|
export const getUpdatedHTMLFromEditor = (
|
|
774
723
|
externalEditor: Editor,
|
|
775
|
-
contentText:
|
|
724
|
+
contentText: string,
|
|
776
725
|
newContentStyle: TextStyleDeclaration,
|
|
777
726
|
) => {
|
|
778
727
|
if (!contentText) {
|
|
779
728
|
return contentText;
|
|
780
729
|
}
|
|
781
730
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
typeof contentText === 'string' &&
|
|
786
|
-
contentText.includes(ESCAPED_NEWLINE_CHARACTER)
|
|
787
|
-
? getRenderText(contentText)
|
|
788
|
-
: contentText;
|
|
731
|
+
const content = contentText.includes(ESCAPED_NEWLINE_CHARACTER)
|
|
732
|
+
? getRenderText(contentText)
|
|
733
|
+
: contentText;
|
|
789
734
|
|
|
790
735
|
externalEditor.commands.setContent(content);
|
|
791
736
|
|
|
@@ -843,9 +788,9 @@ export const getUpdatedHTMLFromEditor = (
|
|
|
843
788
|
chain = newContentStyle?.fontStyle ? chain.setItalic() : chain.unsetItalic();
|
|
844
789
|
|
|
845
790
|
chain.run();
|
|
791
|
+
const html = externalEditor.getHTML();
|
|
846
792
|
|
|
847
|
-
|
|
848
|
-
return externalEditor.getJSON();
|
|
793
|
+
return html;
|
|
849
794
|
};
|
|
850
795
|
|
|
851
796
|
// Helper to build stored marks from a style declaration
|
|
@@ -969,7 +914,7 @@ export const sanitiseClientSideRTE = (html: string): SanitizationResult => {
|
|
|
969
914
|
/**
|
|
970
915
|
* Validates if an href value is safe and returns a sanitized version
|
|
971
916
|
*/
|
|
972
|
-
|
|
917
|
+
const validateAndSanitizeHref = (href: string): string | null => {
|
|
973
918
|
if (!href || typeof href !== 'string') return null;
|
|
974
919
|
|
|
975
920
|
// Trim whitespace and decode entities
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isURL, isEmail, isTel } from '@utils/validator.util';
|
|
2
2
|
import type { ItemFieldType, Validator } from '@brand-page-maker/models';
|
|
3
|
-
import { YOUTUBE_URL_RE
|
|
3
|
+
import { YOUTUBE_URL_RE } from '@brand-page-maker/utils';
|
|
4
4
|
import { validatorTr } from '@src/composables/useTranslate';
|
|
5
5
|
|
|
6
6
|
const addressValueValidator: Validator = {
|
|
@@ -76,15 +76,7 @@ export const dummyValidator: Validator = {
|
|
|
76
76
|
|
|
77
77
|
const nonEmptyStringValidator: Validator = {
|
|
78
78
|
validate: (name, value: any) => {
|
|
79
|
-
|
|
80
|
-
// An RTE field's stored value used to be inline HTML — an empty editor was
|
|
81
|
-
// "<p></p>", a NON-empty string, so the required check always passed for RTE
|
|
82
|
-
// fields (it only ever failed for genuinely empty PLAIN inputs). Now RTE
|
|
83
|
-
// fields are Tiptap JSON docs; treat any doc as "present" so behaviour
|
|
84
|
-
// stays identical. A plain string is still required to be non-empty.
|
|
85
|
-
const isValid = isTiptapDocument(value)
|
|
86
|
-
? true
|
|
87
|
-
: typeof value === 'string' && value.length > 0;
|
|
79
|
+
const isValid = typeof value === 'string' && value.length > 0;
|
|
88
80
|
|
|
89
81
|
return {
|
|
90
82
|
isValid,
|
package/src/lib.ts
CHANGED
|
@@ -85,15 +85,6 @@ export {
|
|
|
85
85
|
recalculateHeaderLogoSizes,
|
|
86
86
|
} from './brand-page/utils/logo.util';
|
|
87
87
|
export { sanitizeRTEHtml } from './brand-page-maker/utils';
|
|
88
|
-
// BP-5787: RTE JSON storage boundary helpers (render/normalise/sanitise/extract).
|
|
89
|
-
export {
|
|
90
|
-
renderRteToHtml,
|
|
91
|
-
toRteJson,
|
|
92
|
-
isTiptapDocument,
|
|
93
|
-
sanitizeRteJson,
|
|
94
|
-
getRteJsonPlainText,
|
|
95
|
-
type RichTextValue,
|
|
96
|
-
} from './brand-page-maker/utils';
|
|
97
88
|
export { getPageSlugFromPageTitle } from './brand-page-maker/utils/slug.util';
|
|
98
89
|
|
|
99
90
|
// AI-pipeline preview (longclaw per-turn snapshots): web-renderer's
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type BrandPageModel } from '../brand-page/models';
|
|
2
2
|
import type { EventTracker } from '../brand-page-maker/composables/useEventTracker';
|
|
3
|
+
import { buildUpdateTextRequest } from './builders/update-content-text-builder';
|
|
4
|
+
import { buildUpdateTextStyleRequest } from './builders/update-content-text-style-builder';
|
|
3
5
|
import type { UndoHandler } from './builders/undo-builder';
|
|
4
6
|
import type { Logger } from './logger';
|
|
5
7
|
import { MetadataExecutor } from './metadata-executor';
|
|
@@ -13,6 +15,8 @@ import {
|
|
|
13
15
|
AI_ASSISTANT_TOOL_NAMES,
|
|
14
16
|
SECTION_TOOL_MAP,
|
|
15
17
|
type ParsedToolCall,
|
|
18
|
+
type UpdateStyleToolArguments,
|
|
19
|
+
type UpdateTextToolArguments,
|
|
16
20
|
type DeleteSectionArguments,
|
|
17
21
|
type MoveSectionArguments,
|
|
18
22
|
type AddPageArguments,
|
|
@@ -33,6 +37,7 @@ export const executeToolCallsAsync = async (
|
|
|
33
37
|
toolcalls: ParsedToolCall[],
|
|
34
38
|
options: {
|
|
35
39
|
logger?: Logger;
|
|
40
|
+
maxCharacters?: number;
|
|
36
41
|
/**
|
|
37
42
|
* Callback invoked by the UndoBuilder at execution time to resolve the
|
|
38
43
|
* pre-prompt snapshot to restore to. The library is agnostic about where
|
|
@@ -57,6 +62,27 @@ export const executeToolCallsAsync = async (
|
|
|
57
62
|
const requests: MetadataBuilderRequest[] = toolcalls
|
|
58
63
|
.map((toolcall): MetadataBuilderRequest | null => {
|
|
59
64
|
switch (toolcall.function) {
|
|
65
|
+
case AI_ASSISTANT_TOOL_NAMES.UPDATE_TEXT: {
|
|
66
|
+
const arg = toolcall.arguments as unknown as UpdateTextToolArguments;
|
|
67
|
+
|
|
68
|
+
return buildUpdateTextRequest(toolcall.toolId, {
|
|
69
|
+
contentId: arg.contentId,
|
|
70
|
+
fieldName: arg.fieldName,
|
|
71
|
+
newText: arg.metadata.text,
|
|
72
|
+
maxCharacters: options.maxCharacters,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
case AI_ASSISTANT_TOOL_NAMES.UPDATE_STYLE: {
|
|
77
|
+
const arg = toolcall.arguments as unknown as UpdateStyleToolArguments;
|
|
78
|
+
|
|
79
|
+
return buildUpdateTextStyleRequest(toolcall.toolId, {
|
|
80
|
+
contentId: arg.contentId,
|
|
81
|
+
fieldName: arg.fieldName,
|
|
82
|
+
newStyles: arg.metadata.style,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
60
86
|
case AI_ASSISTANT_TOOL_NAMES.DELETE_SECTION: {
|
|
61
87
|
const arg = toolcall.arguments as unknown as DeleteSectionArguments;
|
|
62
88
|
|