@designcrowd/library.brand-page 6.1.1-json-prereq-3 → 6.1.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-hj_ievMc.js → CreatedOnBrandCrowd-de-de-B5B5ThJ6.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-jAOSpfCw.js → CreatedOnBrandCrowd-en-us-a48CaORy.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-DZMVc-y3.js → CreatedOnBrandCrowd-es-es-xpuZ317-.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-DvB54H38.js → CreatedOnBrandCrowd-fr-ca-V-iUMArW.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-DvB54H38.js → CreatedOnBrandCrowd-fr-fr-V-iUMArW.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-bf-x1xhi.js → CreatedOnBrandCrowd-pt-br-DZ0NKSAy.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-bf-x1xhi.js → CreatedOnBrandCrowd-pt-pt-DZ0NKSAy.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-B-NY1B44.js → CreatedOnDcom-de-de-_950cgGp.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-Cd4C6KQf.js → CreatedOnDcom-en-us-DogKzH-g.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-DONlsQsm.js → CreatedOnDcom-es-es-CmGM3IPX.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-Dsv3FRpZ.js → CreatedOnDcom-fr-ca-BonEDkWZ.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-DOgyvY5C.js → CreatedOnDcom-fr-fr-DQHtKrd_.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-DX66MhJS.js → CreatedOnDcom-pt-br-CtYJSBjs.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-COe2G0bU.js → CreatedOnDcom-pt-pt-DoO9r4lA.js} +1 -1
- package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +7 -8
- package/lib/src/brand-page/components/mailing-list/MailingList.mixin.js +6 -4
- package/lib/src/brand-page/components/page-footer/PageFooter.mixin.d.ts +1 -0
- package/lib/src/brand-page/components/page-footer/PageFooter.mixin.js +4 -1
- package/lib/src/brand-page/components/payment/Payment.mixin.js +4 -3
- package/lib/src/brand-page/components/payment/__fixtures__/request-payment.d.ts +1 -0
- package/lib/src/brand-page/components/payment/__fixtures__/support-me.d.ts +1 -0
- package/lib/src/brand-page/components/payment/request-payment/RequestPayment.mixin.d.ts +1 -0
- package/lib/src/brand-page/components/payment/request-payment/RequestPayment.mixin.js +4 -1
- package/lib/src/brand-page/composables/consent.composable.d.ts +2 -0
- package/lib/src/brand-page/composables/consent.composable.js +9 -3
- package/lib/src/brand-page/models/brand-page-context.model.d.ts +7 -0
- package/lib/src/brand-page/models/index.d.ts +26 -34
- package/lib/src/brand-page/utils/context.util.d.ts +8 -0
- package/lib/src/brand-page/utils/context.util.js +8 -0
- package/lib/src/brand-page/utils/migration.util.js +0 -11
- package/lib/src/brand-page/utils/text.util.d.ts +1 -13
- package/lib/src/brand-page/utils/text.util.js +0 -64
- 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/components/brand-page-maker/BrandPageMaker.mixin.d.ts +1 -1
- package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.js +51 -18
- package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.d.ts +1 -0
- 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/stores/brandpage-container/brand-page-container-store.js +8 -0
- package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +9 -3
- package/lib/src/brand-page-maker/stores/ui/ui-store.d.ts +1 -1
- 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/models.d.ts +2 -0
- package/lib/src/css/library.brand-page-brandCrowd.min.css +99 -102
- package/lib/src/css/library.brand-page-brandPage.min.css +99 -102
- package/lib/src/css/library.brand-page-designCom.min.css +99 -102
- package/lib/src/css/library.brand-page-preview.min.css +99 -102
- package/lib/src/index.d.ts +1 -1
- package/lib/src/index.mjs +219 -221
- package/lib/src/{lib-scDtk-Pi.js → lib-DtalJKI3.js} +72445 -75920
- package/lib/src/lib.js +1 -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/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/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/testimonial-section-builder.js +2 -1
- package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +11 -15
- 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 +4 -0
- package/lib/src/metadata-builder/models.d.ts +2 -0
- package/lib/src/metadata-builder/models.js +2 -0
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +19 -0
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +4 -0
- 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.d.ts +1 -1
- package/lib/src/shared/content-settings/components/GenericTextInput.mixin.js +10 -35
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +1 -1
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +3 -12
- package/lib/src/shared/content-settings/components/email-field/EmailField.mixin.d.ts +1 -1
- package/lib/src/shared/content-settings/components/tel-field/TelField.mixin.d.ts +1 -1
- 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-field/TextField.mixin.d.ts +1 -1
- 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/shared/content-settings/components/textarea-field/TextareaField.mixin.d.ts +1 -1
- package/lib/src/shared/content-settings/components/url-field/UrlField.mixin.d.ts +1 -1
- package/lib/src/shared/content-settings/components/youtube-video-field/YoutubeVideoField.mixin.d.ts +1 -1
- package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +1 -1
- package/lib/src/shared/logo-versions/LogoVersions.mixin.d.ts +1 -1
- package/lib/src/shared/my-designs/MyDesigns.mixin.d.ts +1 -1
- package/lib/src/shared/my-uploads/MyMedia.mixin.d.ts +1 -1
- package/lib/src/shared/my-uploads/MyUploads.mixin.d.ts +1 -1
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +1 -1
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +1 -1
- package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +1 -1
- package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +1 -1
- package/lib/src/shared/template-settings/store/template-settings-store.d.ts +2 -0
- package/lib/src/shared/template-settings/store/template-settings-store.js +72 -24
- package/lib/src/shared/unsplash-library/UnsplashLibrary.mixin.d.ts +1 -1
- package/lib/src/utils/minify-brand-page-metadata.js +1 -4
- package/package.json +15 -17
- package/src/brand-page/components/contact-form/ContactForm.mixin.ts +9 -7
- package/src/brand-page/components/cookie-consent/CookieConsent.vue +1 -0
- package/src/brand-page/components/mailing-list/MailingList.mixin.ts +8 -3
- package/src/brand-page/components/navigation/navigation-carousel/NavigationCarousel.vue +5 -1
- package/src/brand-page/components/page-footer/PageFooter.mixin.ts +4 -1
- package/src/brand-page/components/page-footer/PageFooter.vue +1 -1
- package/src/brand-page/components/payment/Payment.mixin.ts +4 -3
- package/src/brand-page/components/payment/request-payment/RequestPayment.mixin.ts +8 -1
- package/src/brand-page/components/payment/request-payment/RequestPayment.vue +1 -1
- package/src/brand-page/components/save-contact/SaveContactWidget.vue +2 -1
- package/src/brand-page/components/shop/Shop.vue +6 -3
- package/src/brand-page/components/social-links/SocialLinks.vue +5 -2
- package/src/brand-page/components/text-wrapper/TextWrapper.vue +6 -17
- package/src/brand-page/composables/consent.composable.ts +18 -4
- package/src/brand-page/models/brand-page-context.model.ts +7 -0
- 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/context.util.test.ts +43 -0
- package/src/brand-page/utils/context.util.ts +10 -0
- 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 -83
- 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/rich-text-editor/RichTextEditor.vue +4 -5
- package/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.test.ts +135 -0
- package/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.ts +62 -21
- package/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.ts +1 -0
- 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/stores/brandpage-container/brand-page-container-store.ts +9 -0
- package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +11 -3
- package/src/brand-page-maker/stores/ui/ui-store.ts +1 -1
- 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/models.ts +3 -0
- package/src/lib.ts +1 -9
- package/src/metadata-builder/ai-metadata-executor.ts +26 -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/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/testimonial-section-builder.ts +2 -1
- package/src/metadata-builder/builders/section-builders/text-section-builder.ts +13 -19
- 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 +5 -0
- package/src/metadata-builder/models.ts +3 -0
- package/src/metadata-builder/parsers/ai-toolcall-parser.ts +27 -0
- package/src/metadata-builder/sanitizers/rte-schema-check.ts +53 -24
- package/src/shared/content-settings/components/GenericTextInput.mixin.ts +11 -42
- 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/shared/template-settings/store/template-settings-store.ts +93 -27
- package/src/utils/minify-brand-page-metadata.ts +1 -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 -117
- 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 -156
- 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 -106
- 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 -162
- 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 -378
- package/src/brand-page-maker/utils/rte-render-parity.test.ts +0 -164
- package/src/brand-page-maker/utils/rte-render.util.ts +0 -165
- package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +0 -172
- package/src/brand-page-maker/utils/rte-sanitize.util.ts +0 -192
- package/src/brand-page-maker/validators/field.validator.test.ts +0 -55
|
@@ -1,79 +1,15 @@
|
|
|
1
1
|
import { ESCAPED_NEWLINE_CHARACTER } from '../../constants/ui';
|
|
2
|
-
/**
|
|
3
|
-
* BP-5787 — a Tiptap JSON document is an object with `type: 'doc'`.
|
|
4
|
-
* Inlined here (rather than importing from brand-page-maker) to avoid a
|
|
5
|
-
* circular import: rich-text-editor.util imports getRenderText from this file.
|
|
6
|
-
*/
|
|
7
|
-
const isTiptapDoc = (value) => typeof value === 'object' &&
|
|
8
|
-
value !== null &&
|
|
9
|
-
value.type === 'doc';
|
|
10
|
-
const jsonDocPlainText = (doc) => {
|
|
11
|
-
const parts = [];
|
|
12
|
-
const walk = (node) => {
|
|
13
|
-
if (typeof node?.text === 'string')
|
|
14
|
-
parts.push(node.text);
|
|
15
|
-
node?.content?.forEach(walk);
|
|
16
|
-
};
|
|
17
|
-
walk(doc);
|
|
18
|
-
return parts.join(' ').replace(/\s+/g, ' ').trim();
|
|
19
|
-
};
|
|
20
2
|
export const getRenderText = (text) => {
|
|
21
|
-
// BP-5787: RTE fields may now be Tiptap JSON objects. Non-strings pass
|
|
22
|
-
// through untouched so the renderer (TextWrapper) handles them via its
|
|
23
|
-
// JSON-aware path, instead of throwing on `.replace`.
|
|
24
|
-
if (typeof text !== 'string') {
|
|
25
|
-
return text;
|
|
26
|
-
}
|
|
27
3
|
return text.replace(new RegExp(`(${ESCAPED_NEWLINE_CHARACTER}|\\n)`, 'g'), '<br/>');
|
|
28
4
|
};
|
|
29
|
-
// Accepts a legacy inline-HTML string or a Tiptap JSON document (BP-5787):
|
|
30
|
-
// for JSON it returns the concatenated visible text; for HTML it strips tags.
|
|
31
5
|
export const escapeContentText = (text) => {
|
|
32
6
|
if (!text) {
|
|
33
7
|
return text;
|
|
34
8
|
}
|
|
35
|
-
if (isTiptapDoc(text)) {
|
|
36
|
-
return jsonDocPlainText(text);
|
|
37
|
-
}
|
|
38
|
-
if (typeof text !== 'string') {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
9
|
return text
|
|
42
10
|
.replace(/(<([^>]+)>)/gi, '')
|
|
43
11
|
.replace(/(\r\n|\r|\n)/g, ESCAPED_NEWLINE_CHARACTER);
|
|
44
12
|
};
|
|
45
|
-
/**
|
|
46
|
-
* Character length of RTE content as the *editor* counts it — ProseMirror
|
|
47
|
-
* `doc.textContent`: text nodes concatenated with NO separator. This is the
|
|
48
|
-
* metric the MaxLength extension gates on, so length indicators must use it too.
|
|
49
|
-
*
|
|
50
|
-
* Do NOT use escapeContentText/jsonDocPlainText for length limiting: those
|
|
51
|
-
* join text nodes with a space for readable plain-text extraction, so they
|
|
52
|
-
* over-count by one character per inline mark boundary. Converting a word to a
|
|
53
|
-
* hyperlink (or bolding it, etc.) splits its text node, making the counter
|
|
54
|
-
* report length+1 for content the editor considers exactly at the cap (BP-5787).
|
|
55
|
-
*/
|
|
56
|
-
export const getContentTextLength = (value) => {
|
|
57
|
-
if (!value) {
|
|
58
|
-
return 0;
|
|
59
|
-
}
|
|
60
|
-
if (isTiptapDoc(value)) {
|
|
61
|
-
let length = 0;
|
|
62
|
-
const walk = (node) => {
|
|
63
|
-
if (typeof node?.text === 'string') {
|
|
64
|
-
length += node.text.length;
|
|
65
|
-
}
|
|
66
|
-
node?.content?.forEach(walk);
|
|
67
|
-
};
|
|
68
|
-
walk(value);
|
|
69
|
-
return length;
|
|
70
|
-
}
|
|
71
|
-
if (typeof value !== 'string') {
|
|
72
|
-
return 0;
|
|
73
|
-
}
|
|
74
|
-
// Legacy inline-HTML string: count visible text with tags stripped.
|
|
75
|
-
return value.replace(/(<([^>]+)>)/gi, '').length;
|
|
76
|
-
};
|
|
77
13
|
export const decodeHtmlEntities = (text) => {
|
|
78
14
|
return text
|
|
79
15
|
.replace(/</g, '<')
|
|
@@ -150,9 +150,9 @@ const processSocialLinks = (socialLinksContent, vcardLines, addedEntries) => {
|
|
|
150
150
|
});
|
|
151
151
|
};
|
|
152
152
|
const addProfileInfo = (profile, vcardLines) => {
|
|
153
|
-
const name = sanitizeForVCard(
|
|
154
|
-
const title = sanitizeForVCard(
|
|
155
|
-
const description = sanitizeForVCard(
|
|
153
|
+
const name = sanitizeForVCard(profile.name || '');
|
|
154
|
+
const title = sanitizeForVCard(profile.title || '');
|
|
155
|
+
const description = sanitizeForVCard(profile.description || '');
|
|
156
156
|
if (name) {
|
|
157
157
|
const nameParts = name.trim().split(/\s+/);
|
|
158
158
|
const lastName = nameParts.length > 1 ? nameParts.pop() || '' : '';
|
|
@@ -205,6 +205,6 @@ const downloadVCard = (vcard, filename) => {
|
|
|
205
205
|
export const saveContact = (brandPageData) => {
|
|
206
206
|
const vcard = generateVCard(brandPageData);
|
|
207
207
|
const profile = findProfileContent(brandPageData);
|
|
208
|
-
const filename = generateVCardFilename(
|
|
208
|
+
const filename = generateVCardFilename(profile?.name);
|
|
209
209
|
downloadVCard(vcard, filename);
|
|
210
210
|
};
|
|
@@ -28,7 +28,6 @@ interface ViewModelMappingOptions {
|
|
|
28
28
|
setVideoUrlsWithToken?: (token: string, videoUrl: string, previewImageUrl: string, assetId: string) => void;
|
|
29
29
|
}
|
|
30
30
|
export declare const getBrandPageViewModelFromBrandPageModel: (model: BrandPageModel, brandPageToken: string | undefined, showBrandCrowdLogo: boolean | undefined, options: ViewModelMappingOptions) => BrandPageViewModel;
|
|
31
|
-
export declare const resolvePageIdsInRteJson: (doc: unknown, nav: NavViewModel, options: ViewModelMappingOptions) => unknown;
|
|
32
31
|
export declare const isLogoViewModel: (viewModel: ContentViewModel) => viewModel is LogoViewModel;
|
|
33
32
|
/** for service.brand-page website preview by logo draft token */
|
|
34
33
|
export declare const applyLogoImageToBrandPageViewModel: (viewModel: BrandPageViewModel, logoImage: LogoImageModel) => BrandPageViewModel;
|
|
@@ -10,7 +10,7 @@ import { isValidSvgUrl } from './image.util';
|
|
|
10
10
|
import { isLogoContent } from './model.util';
|
|
11
11
|
import { getBoxShadowValue } from './button.util';
|
|
12
12
|
import { findNavItemByPageId, getRouteNameByPageIndex, getRouteParamsByPage, } from './route.util';
|
|
13
|
-
import { hasRTEContent
|
|
13
|
+
import { hasRTEContent } from '../../brand-page-maker/utils';
|
|
14
14
|
import { getPageIdFromHref, isHrefPageId, } from '../../brand-page-maker/composables/rich-text-editor/hyperlink';
|
|
15
15
|
const BUTTON_BORDER_RADIUS = '9999px';
|
|
16
16
|
const CONTENT_BORDER_RADIUS = '48px';
|
|
@@ -864,75 +864,13 @@ const mapPageIdReferencesToRelativePaths = (pages, nav, options) => {
|
|
|
864
864
|
});
|
|
865
865
|
});
|
|
866
866
|
};
|
|
867
|
-
// Resolve a `pageid:<id>` internal-link href to the published relative path.
|
|
868
|
-
// Returns the resolved href, or null when `href` is not an internal pageid link
|
|
869
|
-
// (external/absolute links are left untouched). Falls back to the site root when
|
|
870
|
-
// the pageid can't be matched to a nav route.
|
|
871
|
-
const resolvePageIdHref = (href, nav, options) => {
|
|
872
|
-
// Only process internal links (no protocol, doesn't start with /)
|
|
873
|
-
if (!href || href.match(/^(https?:\/\/|\/)/) || !isHrefPageId(href)) {
|
|
874
|
-
return null;
|
|
875
|
-
}
|
|
876
|
-
const pageId = getPageIdFromHref(href);
|
|
877
|
-
const route = findNavItemByPageId(nav.navItems, pageId);
|
|
878
|
-
const brandPageSlugRoute = options.brandPageSlug
|
|
879
|
-
? `/${options.brandPageSlug}`
|
|
880
|
-
: '/';
|
|
881
|
-
if (route) {
|
|
882
|
-
if (route.isRoot) {
|
|
883
|
-
return brandPageSlugRoute;
|
|
884
|
-
}
|
|
885
|
-
return `${brandPageSlugRoute}${brandPageSlugRoute === '/' ? '' : '/'}${route.relativePath}`;
|
|
886
|
-
}
|
|
887
|
-
// fallback to root if pageid cannot be resolved.
|
|
888
|
-
return brandPageSlugRoute;
|
|
889
|
-
};
|
|
890
|
-
// BP-5847: walk a Tiptap JSON doc and rewrite every link mark whose href is a
|
|
891
|
-
// `pageid:` reference into its published relative path. Returns a NEW doc when
|
|
892
|
-
// something changed (so the shared model doc is never mutated), else the same
|
|
893
|
-
// reference.
|
|
894
|
-
export const resolvePageIdsInRteJson = (doc, nav, options) => {
|
|
895
|
-
let changed = false;
|
|
896
|
-
const walk = (node) => {
|
|
897
|
-
if (!node || typeof node !== 'object') {
|
|
898
|
-
return node;
|
|
899
|
-
}
|
|
900
|
-
let next = node;
|
|
901
|
-
if (Array.isArray(node.marks)) {
|
|
902
|
-
const marks = node.marks.map((mark) => {
|
|
903
|
-
if (mark?.type === 'link' && typeof mark.attrs?.href === 'string') {
|
|
904
|
-
const resolved = resolvePageIdHref(mark.attrs.href, nav, options);
|
|
905
|
-
if (resolved !== null) {
|
|
906
|
-
changed = true;
|
|
907
|
-
return { ...mark, attrs: { ...mark.attrs, href: resolved } };
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
return mark;
|
|
911
|
-
});
|
|
912
|
-
next = { ...next, marks };
|
|
913
|
-
}
|
|
914
|
-
if (Array.isArray(node.content)) {
|
|
915
|
-
next = { ...next, content: node.content.map(walk) };
|
|
916
|
-
}
|
|
917
|
-
return next;
|
|
918
|
-
};
|
|
919
|
-
const result = walk(doc);
|
|
920
|
-
return changed ? result : doc;
|
|
921
|
-
};
|
|
922
867
|
const dynamicallyMapPageIdInHyperlinksToRelativePaths = (content, nav, options) => {
|
|
923
868
|
Object.keys(content).forEach((key) => {
|
|
924
869
|
const value = content[key];
|
|
925
870
|
if (value === undefined || value === null) {
|
|
926
871
|
return;
|
|
927
872
|
}
|
|
928
|
-
if (
|
|
929
|
-
// BP-5847: RTE fields may now be Tiptap JSON. Resolve pageid link marks
|
|
930
|
-
// here too, or internal links render as raw `pageid:` and don't navigate.
|
|
931
|
-
content[key] =
|
|
932
|
-
resolvePageIdsInRteJson(value, nav, options);
|
|
933
|
-
}
|
|
934
|
-
else if (typeof value === 'object' &&
|
|
935
|
-
(key === 'items' || key === 'textItems')) {
|
|
873
|
+
if (typeof value === 'object' && (key === 'items' || key === 'textItems')) {
|
|
936
874
|
if (Array.isArray(value)) {
|
|
937
875
|
value.forEach((item) => {
|
|
938
876
|
dynamicallyMapPageIdInHyperlinksToRelativePaths(item, nav, options);
|
|
@@ -944,10 +882,24 @@ const dynamicallyMapPageIdInHyperlinksToRelativePaths = (content, nav, options)
|
|
|
944
882
|
}
|
|
945
883
|
else if (typeof value === 'string' && hasRTEContent(value)) {
|
|
946
884
|
content[key] = value.replace(/<a\s+([^>]*?)href="([^"]*?)"([^>]*?)>/gi, (match, existingAttrsStart, href, existingAttrsEnd) => {
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
885
|
+
// Only process internal links (no protocol, doesn't start with /)
|
|
886
|
+
if (href && !href.match(/^(https?:\/\/|\/)/) && isHrefPageId(href)) {
|
|
887
|
+
const pageId = getPageIdFromHref(href);
|
|
888
|
+
const route = findNavItemByPageId(nav.navItems, pageId);
|
|
889
|
+
const brandPageSlugRoute = options.brandPageSlug
|
|
890
|
+
? `/${options.brandPageSlug}`
|
|
891
|
+
: '/';
|
|
892
|
+
if (route) {
|
|
893
|
+
let href = `${brandPageSlugRoute}`;
|
|
894
|
+
if (!route.isRoot) {
|
|
895
|
+
href = `${brandPageSlugRoute}${brandPageSlugRoute === '/' ? '' : '/'}${route.relativePath}`;
|
|
896
|
+
}
|
|
897
|
+
return `<a ${existingAttrsStart}href="${href}"${existingAttrsEnd}>`;
|
|
898
|
+
}
|
|
899
|
+
// fallback to root if pageid cannot be resolved.
|
|
900
|
+
return `<a ${existingAttrsStart}href="${brandPageSlugRoute}"${existingAttrsEnd}>`;
|
|
901
|
+
}
|
|
902
|
+
return match;
|
|
951
903
|
});
|
|
952
904
|
}
|
|
953
905
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ButtonWithModalContent, ProfileContent, TextContent, VideoContent, ImageContent, ContentType, ButtonStyles, LogoStyles, MapContent, ImageGalleryContent, GalleryStyles, MailingListContent, RequestPaymentContent, ShopContent, SupportMeContent, ContactFormContent, BannerSectionContent, BrandPageType, SocialLinksContent, TestimonialSectionContent, AboutSectionContent, DividerSectionContent, ShapesSectionContent, ButtonLinkType, PageHeaderMobile, PageHeaderDesktop, PageFooterMobile, PageFooterDesktop, TextMediaItem, TextShapeItem, TextStyleDeclaration, AspectRatio, LogoImageModel, LogoTemplateImageModel, CookieOptions, BookingSectionContent, MediaTypes, TextStyles, BackgroundStyle, BackgroundShape, PersonalisationRole, SaveContactOptions
|
|
1
|
+
import type { ButtonWithModalContent, ProfileContent, TextContent, VideoContent, ImageContent, ContentType, ButtonStyles, LogoStyles, MapContent, ImageGalleryContent, GalleryStyles, MailingListContent, RequestPaymentContent, ShopContent, SupportMeContent, ContactFormContent, BannerSectionContent, BrandPageType, SocialLinksContent, TestimonialSectionContent, AboutSectionContent, DividerSectionContent, ShapesSectionContent, ButtonLinkType, PageHeaderMobile, PageHeaderDesktop, PageFooterMobile, PageFooterDesktop, TextMediaItem, TextShapeItem, TextStyleDeclaration, AspectRatio, LogoImageModel, LogoTemplateImageModel, CookieOptions, BookingSectionContent, MediaTypes, TextStyles, BackgroundStyle, BackgroundShape, PersonalisationRole, SaveContactOptions } from './models';
|
|
2
2
|
import type { ImagesSectionContentDisplayStyle } from './constants';
|
|
3
3
|
export type ViewModelType = 'button' | 'image' | 'image-gallery' | 'logo' | 'map' | 'mailing-list' | 'profile' | 'support-me' | 'request-payment' | 'text' | 'video' | 'contact-form' | 'banner-section' | 'images-section' | 'about-section' | 'divider-section' | 'testimonial-section' | 'social-links' | 'shapes-section' | 'shop-section' | 'booking-section';
|
|
4
4
|
export interface ContentViewModel {
|
|
@@ -94,8 +94,8 @@ export interface TextImageSectionViewModel {
|
|
|
94
94
|
id: string;
|
|
95
95
|
type: 'images-section';
|
|
96
96
|
role: ContentType;
|
|
97
|
-
title:
|
|
98
|
-
description:
|
|
97
|
+
title: string;
|
|
98
|
+
description: string;
|
|
99
99
|
displayStyle: ImagesSectionContentDisplayStyle;
|
|
100
100
|
aspectRatio: AspectRatio;
|
|
101
101
|
areImagesFullWidth: boolean;
|
|
@@ -952,7 +952,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
952
952
|
mode: import("@shared/models").UiScreenSizeMode;
|
|
953
953
|
}): void;
|
|
954
954
|
setUserPersonalisationData({ personalisationData, }: {
|
|
955
|
-
personalisationData: UserPersonalisationContent[];
|
|
955
|
+
personalisationData: UserPersonalisationContent[] | undefined;
|
|
956
956
|
}): void;
|
|
957
957
|
setUserPromptContentKey({ userPromptContentKey, }: {
|
|
958
958
|
userPromptContentKey: string | undefined;
|
|
@@ -8,6 +8,20 @@ import { useBrandPageContainerStore, useBrandPageMakerStore, useBrandPageMakerUs
|
|
|
8
8
|
import { useUiTemplateSettingsStore } from '@shared/template-settings/store';
|
|
9
9
|
import { applyLogoImageBase64UrlToBrandPageTemplateModel, applyLogoImageToBrandPageTemplateModel, applyLogoTemplateImageToBrandPageTemplateModel, } from '@brand-page/utils/logo.util';
|
|
10
10
|
import { useVideoStore } from '@src/lib';
|
|
11
|
+
// Content params the backend may bake into a preview URL. When the editor's key wins,
|
|
12
|
+
// none of these may survive — a stale one alongside the new key is the bp-5941 mismatch.
|
|
13
|
+
// contentKey MUST stay listed: preview-renderer prefers contentKey+userToken over
|
|
14
|
+
// userPromptContentKey, so a surviving contentKey would silently win. userToken alone
|
|
15
|
+
// can't trigger that branch, so it's left as the backend sent it.
|
|
16
|
+
const BAKED_CONTENT_PARAMS = ['contentKey', 'userPromptContentKey'];
|
|
17
|
+
const toUrl = (value) => {
|
|
18
|
+
try {
|
|
19
|
+
return new URL(value);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
11
25
|
const DEBOUNCE_IN_MS = 250;
|
|
12
26
|
export default defineComponent({
|
|
13
27
|
emits: ['template-selected'],
|
|
@@ -169,25 +183,44 @@ export default defineComponent({
|
|
|
169
183
|
if (!previewImageUrl) {
|
|
170
184
|
return '';
|
|
171
185
|
}
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
186
|
+
const hasAuthoritativeUserPromptKey = this.isPersonalisable &&
|
|
187
|
+
this.template.usesUserPromptContentKey &&
|
|
188
|
+
!!this.template.contentKey;
|
|
189
|
+
if (hasAuthoritativeUserPromptKey) {
|
|
190
|
+
// The editor resolves post-purchase content from the current industry, so its
|
|
191
|
+
// userPrompt key is authoritative. On empty search the backend bakes a per-template
|
|
192
|
+
// logo-draft contentKey (DY-1305) that diverges from the editor (bp-5941); strip any
|
|
193
|
+
// baked content params and resolve from the userPrompt S3 bucket via the editor's key.
|
|
194
|
+
const url = toUrl(previewImageUrl);
|
|
195
|
+
// Not a parseable absolute URL — nothing to personalise, leave it alone.
|
|
196
|
+
if (!url) {
|
|
197
|
+
return previewImageUrl;
|
|
185
198
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
199
|
+
BAKED_CONTENT_PARAMS.forEach((param) => url.searchParams.delete(param));
|
|
200
|
+
url.searchParams.set('userPromptContentKey', this.template.contentKey);
|
|
201
|
+
previewImageUrl = url.toString();
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
const isAlreadyPersonalised = previewImageUrl.includes('contentKey=') ||
|
|
205
|
+
previewImageUrl.includes('userPromptContentKey=');
|
|
206
|
+
if (this.isPersonalisable &&
|
|
207
|
+
this.template.contentKey &&
|
|
208
|
+
!isAlreadyPersonalised) {
|
|
209
|
+
if (this.userToken) {
|
|
210
|
+
previewImageUrl += `&contentKey=${this.template.contentKey}`;
|
|
211
|
+
// The backend already bakes userToken into brand-site preview URLs. Only add
|
|
212
|
+
// it when missing - two userToken params parse as an array and the preview
|
|
213
|
+
// renderer rejects it ("userToken must be a string type") (bp-5765).
|
|
214
|
+
if (!previewImageUrl.includes('userToken=')) {
|
|
215
|
+
previewImageUrl += `&userToken=${this.userToken}`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
// userPromptContentKey resolves from the userPrompt S3 bucket (real AI images).
|
|
220
|
+
// Post-purchase designs with a userPrompt key route here too, NOT through the
|
|
221
|
+
// userToken/regular-bucket branch above which holds placeholders (bp-5454).
|
|
222
|
+
previewImageUrl += `&userPromptContentKey=${this.template.contentKey}`;
|
|
223
|
+
}
|
|
191
224
|
}
|
|
192
225
|
}
|
|
193
226
|
if (this.logoImage?.logoVariation !== undefined) {
|
|
@@ -21,6 +21,7 @@ export interface AiMetadataBuilderResult {
|
|
|
21
21
|
}
|
|
22
22
|
export declare function useAiMetadataBuilder(logger: Logger): {
|
|
23
23
|
applyAiToolCallsAsync: (model: BrandPageModel, aiAssistantToolCall: AiAssistantToolCall[], options?: {
|
|
24
|
+
maxCharacters?: number;
|
|
24
25
|
/**
|
|
25
26
|
* Invoked by the UndoBuilder to resolve the pre-prompt snapshot.
|
|
26
27
|
* Required when the response includes an `undo` tool call.
|
|
@@ -5,8 +5,15 @@ export declare const useBubbleMenu: (editor: Editor, isHyperlinkPopupOpen?: Ref<
|
|
|
5
5
|
editor: any;
|
|
6
6
|
state: any;
|
|
7
7
|
}) => any;
|
|
8
|
-
|
|
8
|
+
tippyOptions: {
|
|
9
|
+
duration: number;
|
|
9
10
|
placement: "bottom";
|
|
10
|
-
|
|
11
|
+
interactive: boolean;
|
|
12
|
+
appendTo: "parent";
|
|
13
|
+
hideOnClick: boolean;
|
|
14
|
+
arrow: boolean;
|
|
15
|
+
offset: [number, number];
|
|
16
|
+
trigger: "manual";
|
|
17
|
+
zIndex: number;
|
|
11
18
|
};
|
|
12
19
|
};
|
|
@@ -13,15 +13,19 @@ export const useBubbleMenu = (editor, isHyperlinkPopupOpen) => {
|
|
|
13
13
|
}
|
|
14
14
|
return false;
|
|
15
15
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// zIndex) is either the v3 default or handled in CSS.
|
|
19
|
-
const bubbleMenuOptions = {
|
|
16
|
+
const tippyOptions = {
|
|
17
|
+
duration: 100,
|
|
20
18
|
placement: 'bottom',
|
|
21
|
-
|
|
19
|
+
interactive: true,
|
|
20
|
+
appendTo: 'parent',
|
|
21
|
+
hideOnClick: false,
|
|
22
|
+
arrow: false,
|
|
23
|
+
offset: [0, 10],
|
|
24
|
+
trigger: 'manual',
|
|
25
|
+
zIndex: 10,
|
|
22
26
|
};
|
|
23
27
|
return {
|
|
24
28
|
shouldShow,
|
|
25
|
-
|
|
29
|
+
tippyOptions,
|
|
26
30
|
};
|
|
27
31
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
import '@tiptap/extension-text-style';
|
|
3
|
+
export type FontSizeOptions = {
|
|
4
|
+
types: string[];
|
|
5
|
+
};
|
|
6
|
+
declare module '@tiptap/core' {
|
|
7
|
+
interface Commands<ReturnType> {
|
|
8
|
+
fontSize: {
|
|
9
|
+
/**
|
|
10
|
+
* Set the font size
|
|
11
|
+
*/
|
|
12
|
+
setFontSize: (fontSize: string) => ReturnType;
|
|
13
|
+
/**
|
|
14
|
+
* Unset the font size
|
|
15
|
+
*/
|
|
16
|
+
unsetFontSize: () => ReturnType;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export declare const FontSize: Extension<FontSizeOptions, any>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
import '@tiptap/extension-text-style';
|
|
3
|
+
export const FontSize = Extension.create({
|
|
4
|
+
name: 'fontSize',
|
|
5
|
+
addOptions() {
|
|
6
|
+
return {
|
|
7
|
+
types: ['textStyle'],
|
|
8
|
+
};
|
|
9
|
+
},
|
|
10
|
+
addGlobalAttributes() {
|
|
11
|
+
return [
|
|
12
|
+
{
|
|
13
|
+
types: this.options.types,
|
|
14
|
+
attributes: {
|
|
15
|
+
fontSize: {
|
|
16
|
+
default: null,
|
|
17
|
+
parseHTML: (element) => element.style.fontSize.replace(/['"]+/g, ''),
|
|
18
|
+
renderHTML: (attributes) => {
|
|
19
|
+
if (!attributes.fontSize) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
style: `font-size: ${attributes.fontSize}`,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
},
|
|
31
|
+
addCommands() {
|
|
32
|
+
return {
|
|
33
|
+
setFontSize: (fontSize) => ({ chain }) => {
|
|
34
|
+
return chain().setMark('textStyle', { fontSize }).run();
|
|
35
|
+
},
|
|
36
|
+
unsetFontSize: () => ({ chain }) => {
|
|
37
|
+
return chain()
|
|
38
|
+
.setMark('textStyle', { fontSize: null })
|
|
39
|
+
.removeEmptyTextStyle()
|
|
40
|
+
.run();
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
export type LineHeightOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* Node / mark types that should carry the `lineHeight` attribute.
|
|
5
|
+
* Defaults to `['textStyle']` to piggy‑back on TipTap’s built‑in mark.
|
|
6
|
+
*/
|
|
7
|
+
types: string[];
|
|
8
|
+
};
|
|
9
|
+
declare module '@tiptap/core' {
|
|
10
|
+
interface Commands<ReturnType> {
|
|
11
|
+
lineHeight: {
|
|
12
|
+
/**
|
|
13
|
+
* Apply a line‑height to the current selection (or stored marks).
|
|
14
|
+
*/
|
|
15
|
+
setLineHeight: (heightValue: string) => ReturnType;
|
|
16
|
+
/**
|
|
17
|
+
* Remove any line‑height attribute.
|
|
18
|
+
*/
|
|
19
|
+
unsetLineHeight: () => ReturnType;
|
|
20
|
+
/**
|
|
21
|
+
* Read the current line‑height from the selection / stored marks.
|
|
22
|
+
*/
|
|
23
|
+
getLineHeight: () => ReturnType;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export declare const LineHeight: Extension<LineHeightOptions, any>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
export const LineHeight = Extension.create({
|
|
3
|
+
name: 'lineHeight',
|
|
4
|
+
addOptions() {
|
|
5
|
+
return {
|
|
6
|
+
types: ['textStyle'],
|
|
7
|
+
};
|
|
8
|
+
},
|
|
9
|
+
addGlobalAttributes() {
|
|
10
|
+
return [
|
|
11
|
+
{
|
|
12
|
+
types: this.options.types,
|
|
13
|
+
attributes: {
|
|
14
|
+
lineHeight: {
|
|
15
|
+
default: null,
|
|
16
|
+
parseHTML: (element) => (element.style.lineHeight || '').replace(/['"]+/g, ''),
|
|
17
|
+
renderHTML: (attributes) => {
|
|
18
|
+
if (!attributes.lineHeight) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
return { style: `line-height: ${attributes.lineHeight}` };
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
},
|
|
28
|
+
addCommands() {
|
|
29
|
+
return {
|
|
30
|
+
setLineHeight: (lineHeight) => ({ chain }) => {
|
|
31
|
+
return chain().setMark('textStyle', { lineHeight }).run();
|
|
32
|
+
},
|
|
33
|
+
unsetLineHeight: () => ({ chain }) => {
|
|
34
|
+
return chain()
|
|
35
|
+
.setMark('textStyle', { lineHeight: null })
|
|
36
|
+
.removeEmptyTextStyle()
|
|
37
|
+
.run();
|
|
38
|
+
},
|
|
39
|
+
getLineHeight: () => ({ editor }) => {
|
|
40
|
+
return editor.getAttributes('textStyle').lineHeight;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
});
|