@designcrowd/library.brand-page 6.0.26-json-prereq-2 → 6.0.26
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-DU6YEVcW.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-CRXiimvT.js → CreatedOnBrandCrowd-en-us-DNWWK6Tj.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-DCYdY6sj.js → CreatedOnBrandCrowd-es-es-DVFvV9Qp.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-Dk7LOhxE.js → CreatedOnBrandCrowd-fr-ca-B4ZkjNdw.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-Dk7LOhxE.js → CreatedOnBrandCrowd-fr-fr-B4ZkjNdw.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-Cp4ufHJP.js → CreatedOnBrandCrowd-pt-br-DP7AQGgW.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-Cp4ufHJP.js → CreatedOnBrandCrowd-pt-pt-DP7AQGgW.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-ZSLkWMwM.js → CreatedOnDcom-de-de-CaC3GtG9.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-BgdIGfT3.js → CreatedOnDcom-en-us-D16yT0-P.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-D0KemkCZ.js → CreatedOnDcom-es-es-DA5sHgD_.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-BuwNjNbC.js → CreatedOnDcom-fr-ca-CDv2SG1-.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-DCQcsFPf.js → CreatedOnDcom-fr-fr-Cygc7c5d.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-DD-oLAS1.js → CreatedOnDcom-pt-br-B_3RIzgF.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-D8qLq2wW.js → CreatedOnDcom-pt-pt-Bz5w-rrD.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/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/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-6_P6kXPu.js} +51709 -55194
- 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/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.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 +1 -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/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/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/lib.ts +0 -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 +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.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 -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
|
@@ -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
|
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
BrandPageModel,
|
|
4
4
|
TextStyleDeclaration,
|
|
5
5
|
} from '../../../brand-page/models';
|
|
6
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
6
7
|
import type { BuilderResult } from '../../models';
|
|
7
8
|
import {
|
|
8
9
|
isTiptapDocument,
|
|
@@ -53,7 +54,7 @@ export class AboutSectionBuilder extends BaseSectionBuilder<AboutSectionContent>
|
|
|
53
54
|
|
|
54
55
|
if (aboutArgs.title !== undefined) {
|
|
55
56
|
if (isTiptapDocument(aboutArgs.title)) {
|
|
56
|
-
resolved.content.title = aboutArgs.title;
|
|
57
|
+
resolved.content.title = tiptapJsonToHtml(aboutArgs.title);
|
|
57
58
|
resolved.content.titleStyle = resolveGlobalPresetIntoStyle(
|
|
58
59
|
resolved.content.titleStyle,
|
|
59
60
|
textStyleOptions,
|
|
@@ -65,7 +66,7 @@ export class AboutSectionBuilder extends BaseSectionBuilder<AboutSectionContent>
|
|
|
65
66
|
|
|
66
67
|
if (aboutArgs.paragraph !== undefined) {
|
|
67
68
|
if (isTiptapDocument(aboutArgs.paragraph)) {
|
|
68
|
-
resolved.content.paragraph = aboutArgs.paragraph;
|
|
69
|
+
resolved.content.paragraph = tiptapJsonToHtml(aboutArgs.paragraph);
|
|
69
70
|
resolved.content.paragraphStyle = resolveGlobalPresetIntoStyle(
|
|
70
71
|
resolved.content.paragraphStyle,
|
|
71
72
|
textStyleOptions,
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
BrandPageModel,
|
|
4
4
|
TextStyleDeclaration,
|
|
5
5
|
} from '../../../brand-page/models';
|
|
6
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
6
7
|
import type { BuilderResult } from '../../models';
|
|
7
8
|
import {
|
|
8
9
|
isTiptapDocument,
|
|
@@ -90,7 +91,7 @@ export class BannerSectionBuilder extends BaseSectionBuilder<BannerSectionConten
|
|
|
90
91
|
|
|
91
92
|
if (bannerArgs.title !== undefined) {
|
|
92
93
|
if (isTiptapDocument(bannerArgs.title)) {
|
|
93
|
-
resolved.content.title = bannerArgs.title;
|
|
94
|
+
resolved.content.title = tiptapJsonToHtml(bannerArgs.title);
|
|
94
95
|
resolved.content.titleStyle = resolveGlobalPresetIntoStyle(
|
|
95
96
|
resolved.content.titleStyle,
|
|
96
97
|
textStyleOptions,
|
|
@@ -102,7 +103,7 @@ export class BannerSectionBuilder extends BaseSectionBuilder<BannerSectionConten
|
|
|
102
103
|
|
|
103
104
|
if (bannerArgs.description !== undefined) {
|
|
104
105
|
if (isTiptapDocument(bannerArgs.description)) {
|
|
105
|
-
resolved.content.description = bannerArgs.description;
|
|
106
|
+
resolved.content.description = tiptapJsonToHtml(bannerArgs.description);
|
|
106
107
|
resolved.content.descriptionStyle = resolveGlobalPresetIntoStyle(
|
|
107
108
|
resolved.content.descriptionStyle,
|
|
108
109
|
textStyleOptions,
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
BrandPageModel,
|
|
4
4
|
TextStyleDeclaration,
|
|
5
5
|
} from '../../../brand-page/models';
|
|
6
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
6
7
|
import type { BuilderResult } from '../../models';
|
|
7
8
|
import {
|
|
8
9
|
isTiptapDocument,
|
|
@@ -50,7 +51,7 @@ export class BookingSectionBuilder extends BaseSectionBuilder<BookingSectionCont
|
|
|
50
51
|
|
|
51
52
|
if (bookingArgs.title !== undefined) {
|
|
52
53
|
if (isTiptapDocument(bookingArgs.title)) {
|
|
53
|
-
resolved.content.title = bookingArgs.title;
|
|
54
|
+
resolved.content.title = tiptapJsonToHtml(bookingArgs.title);
|
|
54
55
|
resolved.content.titleStyle = resolveGlobalPresetIntoStyle(
|
|
55
56
|
resolved.content.titleStyle,
|
|
56
57
|
textStyleOptions,
|
|
@@ -62,7 +63,9 @@ export class BookingSectionBuilder extends BaseSectionBuilder<BookingSectionCont
|
|
|
62
63
|
|
|
63
64
|
if (bookingArgs.description !== undefined) {
|
|
64
65
|
if (isTiptapDocument(bookingArgs.description)) {
|
|
65
|
-
resolved.content.description =
|
|
66
|
+
resolved.content.description = tiptapJsonToHtml(
|
|
67
|
+
bookingArgs.description,
|
|
68
|
+
);
|
|
66
69
|
resolved.content.descriptionStyle = resolveGlobalPresetIntoStyle(
|
|
67
70
|
resolved.content.descriptionStyle,
|
|
68
71
|
textStyleOptions,
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
BrandPageModel,
|
|
4
4
|
TextStyleDeclaration,
|
|
5
5
|
} from '../../../brand-page/models';
|
|
6
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
6
7
|
import {
|
|
7
8
|
isTiptapDocument,
|
|
8
9
|
type ContactFormSectionArguments,
|
|
@@ -49,7 +50,7 @@ export class ContactFormBuilder extends BaseSectionBuilder<ContactFormContent> {
|
|
|
49
50
|
|
|
50
51
|
if (args.title !== undefined) {
|
|
51
52
|
if (isTiptapDocument(args.title)) {
|
|
52
|
-
content.title = args.title;
|
|
53
|
+
content.title = tiptapJsonToHtml(args.title);
|
|
53
54
|
content.titleStyle = resolveGlobalPresetIntoStyle(
|
|
54
55
|
content.titleStyle,
|
|
55
56
|
textStyleOptions,
|
|
@@ -61,7 +62,7 @@ export class ContactFormBuilder extends BaseSectionBuilder<ContactFormContent> {
|
|
|
61
62
|
|
|
62
63
|
if (args.description !== undefined) {
|
|
63
64
|
if (isTiptapDocument(args.description)) {
|
|
64
|
-
content.description = args.description;
|
|
65
|
+
content.description = tiptapJsonToHtml(args.description);
|
|
65
66
|
content.descriptionStyle = resolveGlobalPresetIntoStyle(
|
|
66
67
|
content.descriptionStyle,
|
|
67
68
|
textStyleOptions,
|
|
@@ -73,7 +74,7 @@ export class ContactFormBuilder extends BaseSectionBuilder<ContactFormContent> {
|
|
|
73
74
|
|
|
74
75
|
if (args.success_message !== undefined) {
|
|
75
76
|
if (isTiptapDocument(args.success_message)) {
|
|
76
|
-
content.successMessage = args.success_message;
|
|
77
|
+
content.successMessage = tiptapJsonToHtml(args.success_message);
|
|
77
78
|
content.successMessageStyle = resolveGlobalPresetIntoStyle(
|
|
78
79
|
content.successMessageStyle,
|
|
79
80
|
textStyleOptions,
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
BrandPageModel,
|
|
4
4
|
TextStyleDeclaration,
|
|
5
5
|
} from '../../../brand-page/models';
|
|
6
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
6
7
|
import type { BuilderResult } from '../../models';
|
|
7
8
|
import {
|
|
8
9
|
isTiptapDocument,
|
|
@@ -141,7 +142,7 @@ export class ImagesSectionBuilder extends BaseSectionBuilder<ImagesSectionConten
|
|
|
141
142
|
|
|
142
143
|
if (args.title !== undefined) {
|
|
143
144
|
if (isTiptapDocument(args.title)) {
|
|
144
|
-
content.title = args.title;
|
|
145
|
+
content.title = tiptapJsonToHtml(args.title);
|
|
145
146
|
content.titleStyle = resolveGlobalPresetIntoStyle(
|
|
146
147
|
content.titleStyle,
|
|
147
148
|
textStyleOptions,
|
|
@@ -153,7 +154,7 @@ export class ImagesSectionBuilder extends BaseSectionBuilder<ImagesSectionConten
|
|
|
153
154
|
|
|
154
155
|
if (args.description !== undefined) {
|
|
155
156
|
if (isTiptapDocument(args.description)) {
|
|
156
|
-
content.description = args.description;
|
|
157
|
+
content.description = tiptapJsonToHtml(args.description);
|
|
157
158
|
content.descriptionStyle = resolveGlobalPresetIntoStyle(
|
|
158
159
|
content.descriptionStyle,
|
|
159
160
|
textStyleOptions,
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
MailingListContent,
|
|
5
5
|
TextStyleDeclaration,
|
|
6
6
|
} from '../../../brand-page/models';
|
|
7
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
7
8
|
import {
|
|
8
9
|
isTiptapDocument,
|
|
9
10
|
type MailingListSectionArguments,
|
|
@@ -49,7 +50,7 @@ export class MailingListBuilder extends BaseSectionBuilder<MailingListContent> {
|
|
|
49
50
|
|
|
50
51
|
if (mailingListArgs.title !== undefined) {
|
|
51
52
|
if (isTiptapDocument(mailingListArgs.title)) {
|
|
52
|
-
content.title = mailingListArgs.title;
|
|
53
|
+
content.title = tiptapJsonToHtml(mailingListArgs.title);
|
|
53
54
|
content.titleStyle = resolveGlobalPresetIntoStyle(
|
|
54
55
|
content.titleStyle,
|
|
55
56
|
textStyleOptions,
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
BrandPageModel,
|
|
4
4
|
TextStyleDeclaration,
|
|
5
5
|
} from '../../../brand-page/models';
|
|
6
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
6
7
|
import type { BuilderResult } from '../../models';
|
|
7
8
|
import {
|
|
8
9
|
isTiptapDocument,
|
|
@@ -47,7 +48,7 @@ export class MailingListSectionBuilder extends BaseSectionBuilder<MailingListCon
|
|
|
47
48
|
|
|
48
49
|
if (mailingListArgs.title !== undefined) {
|
|
49
50
|
if (isTiptapDocument(mailingListArgs.title)) {
|
|
50
|
-
resolved.content.title = mailingListArgs.title;
|
|
51
|
+
resolved.content.title = tiptapJsonToHtml(mailingListArgs.title);
|
|
51
52
|
resolved.content.titleStyle = resolveGlobalPresetIntoStyle(
|
|
52
53
|
resolved.content.titleStyle,
|
|
53
54
|
textStyleOptions,
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
BrandPageModel,
|
|
4
4
|
TextStyleDeclaration,
|
|
5
5
|
} from '../../../brand-page/models';
|
|
6
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
6
7
|
import type { BuilderResult } from '../../models';
|
|
7
8
|
import {
|
|
8
9
|
isTiptapDocument,
|
|
@@ -40,7 +41,7 @@ export class ProfileSectionBuilder extends BaseSectionBuilder<ProfileContent> {
|
|
|
40
41
|
|
|
41
42
|
if (profileArgs.name !== undefined) {
|
|
42
43
|
if (isTiptapDocument(profileArgs.name)) {
|
|
43
|
-
resolved.content.name = profileArgs.name;
|
|
44
|
+
resolved.content.name = tiptapJsonToHtml(profileArgs.name);
|
|
44
45
|
resolved.content.nameStyle = resolveGlobalPresetIntoStyle(
|
|
45
46
|
resolved.content.nameStyle,
|
|
46
47
|
textStyleOptions,
|
|
@@ -52,7 +53,7 @@ export class ProfileSectionBuilder extends BaseSectionBuilder<ProfileContent> {
|
|
|
52
53
|
|
|
53
54
|
if (profileArgs.title !== undefined) {
|
|
54
55
|
if (isTiptapDocument(profileArgs.title)) {
|
|
55
|
-
resolved.content.title = profileArgs.title;
|
|
56
|
+
resolved.content.title = tiptapJsonToHtml(profileArgs.title);
|
|
56
57
|
resolved.content.titleStyle = resolveGlobalPresetIntoStyle(
|
|
57
58
|
resolved.content.titleStyle,
|
|
58
59
|
textStyleOptions,
|
|
@@ -64,7 +65,9 @@ export class ProfileSectionBuilder extends BaseSectionBuilder<ProfileContent> {
|
|
|
64
65
|
|
|
65
66
|
if (profileArgs.description !== undefined) {
|
|
66
67
|
if (isTiptapDocument(profileArgs.description)) {
|
|
67
|
-
resolved.content.description =
|
|
68
|
+
resolved.content.description = tiptapJsonToHtml(
|
|
69
|
+
profileArgs.description,
|
|
70
|
+
);
|
|
68
71
|
resolved.content.descriptionStyle = resolveGlobalPresetIntoStyle(
|
|
69
72
|
resolved.content.descriptionStyle,
|
|
70
73
|
textStyleOptions,
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
TextShapeItem,
|
|
6
6
|
TextStyleDeclaration,
|
|
7
7
|
} from '../../../brand-page/models';
|
|
8
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
8
9
|
import type { BuilderResult } from '../../models';
|
|
9
10
|
import {
|
|
10
11
|
isTiptapDocument,
|
|
@@ -194,7 +195,7 @@ export class ShapesSectionBuilder extends BaseSectionBuilder<ShapesSectionConten
|
|
|
194
195
|
|
|
195
196
|
if (args.title !== undefined) {
|
|
196
197
|
if (isTiptapDocument(args.title)) {
|
|
197
|
-
content.title = args.title;
|
|
198
|
+
content.title = tiptapJsonToHtml(args.title);
|
|
198
199
|
content.titleStyle = resolveGlobalPresetIntoStyle(
|
|
199
200
|
content.titleStyle,
|
|
200
201
|
textStyleOptions,
|
|
@@ -206,7 +207,7 @@ export class ShapesSectionBuilder extends BaseSectionBuilder<ShapesSectionConten
|
|
|
206
207
|
|
|
207
208
|
if (args.description !== undefined) {
|
|
208
209
|
if (isTiptapDocument(args.description)) {
|
|
209
|
-
content.description = args.description;
|
|
210
|
+
content.description = tiptapJsonToHtml(args.description);
|
|
210
211
|
content.descriptionStyle = resolveGlobalPresetIntoStyle(
|
|
211
212
|
content.descriptionStyle,
|
|
212
213
|
textStyleOptions,
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
BrandPageModel,
|
|
5
5
|
TextStyleDeclaration,
|
|
6
6
|
} from '../../../brand-page/models';
|
|
7
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
7
8
|
import {
|
|
8
9
|
isTiptapDocument,
|
|
9
10
|
type TestimonialItem,
|
|
@@ -172,7 +173,7 @@ export class TestimonialSectionBuilder extends BaseSectionBuilder<TestimonialSec
|
|
|
172
173
|
|
|
173
174
|
if (args.title !== undefined) {
|
|
174
175
|
if (isTiptapDocument(args.title)) {
|
|
175
|
-
content.title = args.title;
|
|
176
|
+
content.title = tiptapJsonToHtml(args.title);
|
|
176
177
|
content.titleStyle = resolveGlobalPresetIntoStyle(
|
|
177
178
|
content.titleStyle,
|
|
178
179
|
textStyleOptions,
|
|
@@ -4,9 +4,9 @@ import type {
|
|
|
4
4
|
TextItem,
|
|
5
5
|
TextStyleDeclaration,
|
|
6
6
|
BrandPageModel,
|
|
7
|
-
RichTextValue,
|
|
8
7
|
} from '../../../brand-page/models';
|
|
9
8
|
import type { TextStyleOptions } from '../../../brand-page/models/styles.model';
|
|
9
|
+
import { tiptapJsonToHtml } from '../../helpers/rte.helpers';
|
|
10
10
|
import {
|
|
11
11
|
isTiptapDocument,
|
|
12
12
|
type TextSectionArguments,
|
|
@@ -26,18 +26,12 @@ import {
|
|
|
26
26
|
repositionItemByAnchor,
|
|
27
27
|
} from '../item-id-utils';
|
|
28
28
|
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* text-style preset resolution for rich content.
|
|
34
|
-
*/
|
|
35
|
-
const resolveText = (
|
|
36
|
-
value: unknown,
|
|
37
|
-
): { value: RichTextValue; wasTiptap: boolean } => {
|
|
38
|
-
if (isTiptapDocument(value)) return { value, wasTiptap: true };
|
|
29
|
+
/** Resolve a RichTextField (string or TiptapDocument) to a plain HTML string. */
|
|
30
|
+
const resolveText = (value: unknown): { html: string; wasTiptap: boolean } => {
|
|
31
|
+
if (isTiptapDocument(value))
|
|
32
|
+
return { html: tiptapJsonToHtml(value), wasTiptap: true };
|
|
39
33
|
|
|
40
|
-
return {
|
|
34
|
+
return { html: typeof value === 'string' ? value : '', wasTiptap: false };
|
|
41
35
|
};
|
|
42
36
|
|
|
43
37
|
const clearTextStyleTypeIfTiptap = (
|
|
@@ -61,11 +55,11 @@ const createTextItem = (
|
|
|
61
55
|
item.text_style ? { ...baseStyle, ...item.text_style } : { ...baseStyle }
|
|
62
56
|
) as TextStyleDeclaration;
|
|
63
57
|
|
|
64
|
-
const {
|
|
58
|
+
const { html, wasTiptap } = resolveText(item.text);
|
|
65
59
|
|
|
66
60
|
return {
|
|
67
61
|
id: item.item_id || generateItemId(),
|
|
68
|
-
text:
|
|
62
|
+
text: html,
|
|
69
63
|
textStyle: clearTextStyleTypeIfTiptap(
|
|
70
64
|
textStyle,
|
|
71
65
|
wasTiptap,
|
|
@@ -117,9 +111,9 @@ const mergeTextItems = (
|
|
|
117
111
|
if (!existing) continue;
|
|
118
112
|
|
|
119
113
|
if (item.text !== undefined) {
|
|
120
|
-
const {
|
|
114
|
+
const { html, wasTiptap } = resolveText(item.text);
|
|
121
115
|
|
|
122
|
-
existing.text =
|
|
116
|
+
existing.text = html;
|
|
123
117
|
existing.textStyle = clearTextStyleTypeIfTiptap(
|
|
124
118
|
existing.textStyle || {},
|
|
125
119
|
wasTiptap,
|
|
@@ -166,12 +160,12 @@ const applySingleTextField = (
|
|
|
166
160
|
textStyleOptions: TextStyleOptions | undefined,
|
|
167
161
|
): void => {
|
|
168
162
|
if (args.text !== undefined) {
|
|
169
|
-
const {
|
|
163
|
+
const { html, wasTiptap } = resolveText(args.text);
|
|
170
164
|
|
|
171
165
|
if (Array.isArray(content.textItems) && content.textItems.length > 0) {
|
|
172
166
|
content.textItems[0] = {
|
|
173
167
|
...content.textItems[0],
|
|
174
|
-
text:
|
|
168
|
+
text: html,
|
|
175
169
|
textStyle: clearTextStyleTypeIfTiptap(
|
|
176
170
|
content.textItems[0].textStyle || {},
|
|
177
171
|
wasTiptap,
|
|
@@ -182,7 +176,7 @@ const applySingleTextField = (
|
|
|
182
176
|
content.textItems = [
|
|
183
177
|
{
|
|
184
178
|
id: generateItemId(),
|
|
185
|
-
text:
|
|
179
|
+
text: html,
|
|
186
180
|
textStyle: clearTextStyleTypeIfTiptap(
|
|
187
181
|
{ ...(content.textStyle || {}) },
|
|
188
182
|
wasTiptap,
|