@designcrowd/library.brand-page 6.0.25 → 6.0.26-json-prereq-2

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.
Files changed (176) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-CS3ct0wa.js → CreatedOnBrandCrowd-de-de-CKBqjDH3.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-CmBcnDu6.js → CreatedOnBrandCrowd-en-us-CRXiimvT.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-BdTuJtqV.js → CreatedOnBrandCrowd-es-es-DCYdY6sj.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-Cx_H9X6l.js → CreatedOnBrandCrowd-fr-ca-Dk7LOhxE.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-Cx_H9X6l.js → CreatedOnBrandCrowd-fr-fr-Dk7LOhxE.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-BZZ9hjUi.js → CreatedOnBrandCrowd-pt-br-Cp4ufHJP.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-BZZ9hjUi.js → CreatedOnBrandCrowd-pt-pt-Cp4ufHJP.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-SbONDQN1.js → CreatedOnDcom-de-de-ZSLkWMwM.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-Beno1_gw.js → CreatedOnDcom-en-us-BgdIGfT3.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-BBE_zdRu.js → CreatedOnDcom-es-es-D0KemkCZ.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-C6pgDB5x.js → CreatedOnDcom-fr-ca-BuwNjNbC.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-FD1eqtg3.js → CreatedOnDcom-fr-fr-DCQcsFPf.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-BiSx3OwM.js → CreatedOnDcom-pt-br-DD-oLAS1.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-Dn4DmKGA.js → CreatedOnDcom-pt-pt-D8qLq2wW.js} +1 -1
  15. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +5 -2
  16. package/lib/src/brand-page/models/index.d.ts +34 -26
  17. package/lib/src/brand-page/utils/migration/migration-20.d.ts +2 -0
  18. package/lib/src/brand-page/utils/migration/migration-20.js +66 -0
  19. package/lib/src/brand-page/utils/migration.util.js +11 -0
  20. package/lib/src/brand-page/utils/text.util.d.ts +1 -1
  21. package/lib/src/brand-page/utils/text.util.js +32 -0
  22. package/lib/src/brand-page/utils/vcard.util.js +4 -4
  23. package/lib/src/brand-page/utils/view-model.util.d.ts +1 -0
  24. package/lib/src/brand-page/utils/view-model.util.js +68 -20
  25. package/lib/src/brand-page/view-models.d.ts +3 -3
  26. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.d.ts +22 -0
  27. package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.js +22 -0
  28. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +1 -1
  29. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +1 -1
  30. package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.d.ts +0 -1
  31. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +21 -9
  32. package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.d.ts +2 -9
  33. package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.js +6 -10
  34. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.d.ts +14 -0
  35. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.js +39 -0
  36. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.d.ts +28 -0
  37. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.js +90 -0
  38. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +1 -1
  39. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +24 -103
  40. package/lib/src/brand-page-maker/stores/ai-thread/ai-website-builder-thread-store.d.ts +8 -2
  41. package/lib/src/brand-page-maker/stores/ai-thread/ai-website-builder-thread-store.js +20 -0
  42. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +7 -0
  43. package/lib/src/brand-page-maker/utils/index.d.ts +2 -0
  44. package/lib/src/brand-page-maker/utils/index.js +2 -0
  45. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +28 -5
  46. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +48 -5
  47. package/lib/src/brand-page-maker/utils/rte-render.util.d.ts +63 -0
  48. package/lib/src/brand-page-maker/utils/rte-render.util.js +95 -0
  49. package/lib/src/brand-page-maker/utils/rte-sanitize.util.d.ts +6 -0
  50. package/lib/src/brand-page-maker/utils/rte-sanitize.util.js +158 -0
  51. package/lib/src/brand-page-maker/validators/field.validator.js +10 -2
  52. package/lib/src/css/library.brand-page-brandCrowd.min.css +39 -39
  53. package/lib/src/css/library.brand-page-brandPage.min.css +39 -39
  54. package/lib/src/css/library.brand-page-designCom.min.css +39 -39
  55. package/lib/src/css/library.brand-page-preview.min.css +39 -39
  56. package/lib/src/index.d.ts +1 -0
  57. package/lib/src/index.mjs +143 -138
  58. package/lib/src/{lib-BVHFYSLL.js → lib-Db6j9fQZ.js} +56262 -52768
  59. package/lib/src/lib.js +2 -0
  60. package/lib/src/metadata-builder/ai-metadata-executor.d.ts +0 -1
  61. package/lib/src/metadata-builder/ai-metadata-executor.js +0 -19
  62. package/lib/src/metadata-builder/builders/section-builders/about-section-builder.js +2 -3
  63. package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.js +2 -3
  64. package/lib/src/metadata-builder/builders/section-builders/booking-section-builder.js +2 -3
  65. package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.js +3 -4
  66. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +2 -3
  67. package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.js +1 -2
  68. package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.js +1 -2
  69. package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.js +3 -4
  70. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +2 -3
  71. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +1 -2
  72. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +15 -11
  73. package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +11 -13
  74. package/lib/src/metadata-builder/helpers/rte.helpers.js +14 -73
  75. package/lib/src/metadata-builder/metadata-executor.js +0 -4
  76. package/lib/src/metadata-builder/models.d.ts +0 -2
  77. package/lib/src/metadata-builder/models.js +0 -2
  78. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +0 -19
  79. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +0 -4
  80. package/lib/src/metadata-builder/sanitizers/rte-schema-check.d.ts +7 -8
  81. package/lib/src/metadata-builder/sanitizers/rte-schema-check.js +21 -52
  82. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.d.ts +2 -2
  83. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.js +14 -1
  84. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +1 -1
  85. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +1 -1
  86. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +12 -3
  87. package/lib/src/shared/content-settings/components/email-field/EmailField.mixin.d.ts +2 -2
  88. package/lib/src/shared/content-settings/components/radio-field/RadioField.mixin.d.ts +1 -1
  89. package/lib/src/shared/content-settings/components/slider-field/SliderField.mixin.d.ts +1 -1
  90. package/lib/src/shared/content-settings/components/tel-field/TelField.mixin.d.ts +2 -2
  91. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.d.ts +3 -2
  92. package/lib/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.js +10 -3
  93. package/lib/src/shared/content-settings/components/text-field/TextField.mixin.d.ts +2 -2
  94. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.d.ts +2 -1
  95. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.js +4 -1
  96. package/lib/src/shared/content-settings/components/textarea-field/TextareaField.mixin.d.ts +2 -2
  97. package/lib/src/shared/content-settings/components/url-field/UrlField.mixin.d.ts +2 -2
  98. package/lib/src/shared/content-settings/components/youtube-video-field/YoutubeVideoField.mixin.d.ts +2 -2
  99. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +1 -1
  100. package/lib/src/utils/minify-brand-page-metadata.js +4 -1
  101. package/package.json +17 -15
  102. package/src/brand-page/components/contact-form/ContactForm.mixin.ts +5 -2
  103. package/src/brand-page/components/text-wrapper/TextWrapper.vue +17 -6
  104. package/src/brand-page/models/index.ts +35 -26
  105. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -3
  106. package/src/brand-page/utils/migration/migration-20.server.test.ts +125 -0
  107. package/src/brand-page/utils/migration/migration-20.test.ts +72 -0
  108. package/src/brand-page/utils/migration/migration-20.ts +87 -0
  109. package/src/brand-page/utils/migration.util.ts +12 -0
  110. package/src/brand-page/utils/style.util.test.ts +51 -0
  111. package/src/brand-page/utils/text.util.test.ts +47 -0
  112. package/src/brand-page/utils/text.util.ts +43 -4
  113. package/src/brand-page/utils/vcard.util.ts +6 -4
  114. package/src/brand-page/utils/view-model.pageid-rte.test.ts +104 -0
  115. package/src/brand-page/utils/view-model.util.ts +94 -26
  116. package/src/brand-page/view-models.ts +3 -2
  117. package/src/brand-page-maker/__fixtures__/complex-rte.fixture.ts +23 -0
  118. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +4 -3
  119. package/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.ts +0 -1
  120. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +28 -9
  121. package/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.ts +6 -10
  122. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +52 -0
  123. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.ts +42 -0
  124. package/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.ts +137 -0
  125. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +27 -111
  126. package/src/brand-page-maker/stores/ai-thread/ai-website-builder-thread-store.ts +23 -0
  127. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +8 -0
  128. package/src/brand-page-maker/utils/__snapshots__/rte-render-parity.test.ts.snap +202 -0
  129. package/src/brand-page-maker/utils/index.ts +2 -0
  130. package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +37 -3
  131. package/src/brand-page-maker/utils/rich-text-editor.util.ts +66 -11
  132. package/src/brand-page-maker/utils/rte-features.test.ts +351 -0
  133. package/src/brand-page-maker/utils/rte-render-parity.test.ts +157 -0
  134. package/src/brand-page-maker/utils/rte-render.util.ts +165 -0
  135. package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +158 -0
  136. package/src/brand-page-maker/utils/rte-sanitize.util.ts +195 -0
  137. package/src/brand-page-maker/validators/field.validator.test.ts +55 -0
  138. package/src/brand-page-maker/validators/field.validator.ts +10 -2
  139. package/src/lib.ts +9 -0
  140. package/src/metadata-builder/ai-metadata-executor.ts +0 -26
  141. package/src/metadata-builder/builders/section-builders/about-section-builder.ts +2 -3
  142. package/src/metadata-builder/builders/section-builders/banner-section-builder.ts +2 -3
  143. package/src/metadata-builder/builders/section-builders/booking-section-builder.ts +2 -5
  144. package/src/metadata-builder/builders/section-builders/contact-form-builder.ts +3 -4
  145. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +2 -3
  146. package/src/metadata-builder/builders/section-builders/mailing-list-builder.ts +1 -2
  147. package/src/metadata-builder/builders/section-builders/mailing-list-section-builder.ts +1 -2
  148. package/src/metadata-builder/builders/section-builders/profile-section-builder.ts +3 -6
  149. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +2 -3
  150. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +1 -2
  151. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +19 -13
  152. package/src/metadata-builder/helpers/rte.helpers.ts +15 -101
  153. package/src/metadata-builder/metadata-executor.ts +0 -5
  154. package/src/metadata-builder/models.ts +0 -3
  155. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +0 -27
  156. package/src/metadata-builder/sanitizers/rte-schema-check.ts +24 -53
  157. package/src/shared/content-settings/components/GenericTextInput.mixin.ts +17 -1
  158. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +16 -3
  159. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.mixin.ts +13 -4
  160. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.vue +6 -4
  161. package/src/shared/content-settings/components/text-image-field/TextImageField.vue +6 -3
  162. package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.ts +6 -1
  163. package/src/shared/content-settings/components/text-items-field/TextItemsField.vue +1 -1
  164. package/src/utils/minify-brand-page-metadata.ts +4 -1
  165. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.d.ts +0 -20
  166. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.js +0 -44
  167. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.d.ts +0 -27
  168. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.js +0 -44
  169. package/lib/src/metadata-builder/builders/update-content-text-builder.d.ts +0 -17
  170. package/lib/src/metadata-builder/builders/update-content-text-builder.js +0 -103
  171. package/lib/src/metadata-builder/builders/update-content-text-style-builder.d.ts +0 -16
  172. package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +0 -216
  173. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontSize.ts +0 -73
  174. package/src/brand-page-maker/composables/rich-text-editor/extensions/LineHeight.ts +0 -85
  175. package/src/metadata-builder/builders/update-content-text-builder.ts +0 -148
  176. package/src/metadata-builder/builders/update-content-text-style-builder.ts +0 -352
@@ -3,6 +3,7 @@ import type { PropType } from 'vue';
3
3
  import type {
4
4
  Content,
5
5
  LogoTheme,
6
+ RichTextValue,
6
7
  TextItem,
7
8
  TextStyleDeclaration,
8
9
  } from '@brand-page/models';
@@ -19,7 +20,7 @@ import type { UpdateItemValuePayload } from '../../models';
19
20
  import cloneDeep from 'lodash.clonedeep';
20
21
  import { decodeHtmlEntities, escapeContentText } from '@brand-page/utils';
21
22
  import { useSnapshotStore } from '@brand-page-maker/stores';
22
- import { getPlainTextFromRTE } from '@brand-page-maker/utils';
23
+ import { getPlainTextFromRTE, getRteTextLength } from '@brand-page-maker/utils';
23
24
  import { isDefaultContentText } from '../../utils';
24
25
 
25
26
  const DEBOUNCE_IN_MS = 250;
@@ -234,6 +235,10 @@ export default defineComponent({
234
235
  this.emitChange();
235
236
  },
236
237
 
238
+ rteTextLength(value: RichTextValue): number {
239
+ return getRteTextLength(value);
240
+ },
241
+
237
242
  getPlainTextValue(index: number): string {
238
243
  const text = this.options[index]?.text || '';
239
244
  const plainText = getPlainTextFromRTE(text);
@@ -32,7 +32,7 @@
32
32
  size="sm"
33
33
  :error="
34
34
  isEmpty[option.index] ||
35
- options[option.index].text.length >= maxTextLength
35
+ rteTextLength(options[option.index].text) >= maxTextLength
36
36
  "
37
37
  :resize="false"
38
38
  class="tw-w-full"
@@ -10,7 +10,7 @@ import {
10
10
  } from '../brand-page/models';
11
11
  import type { Content, ContentType } from '../brand-page/models/content.model';
12
12
  import type { TextStyleDeclaration } from '../brand-page/models/styles.model';
13
- import { hasRTEContent } from '../brand-page-maker/utils';
13
+ import { hasRTEContent, isTiptapDocument } from '../brand-page-maker/utils';
14
14
  import { useExternalRichTextEditor } from '../brand-page-maker/composables/rich-text-editor/useRichTextEditor';
15
15
  import type { Editor, JSONContent } from '@tiptap/vue-3';
16
16
  import {
@@ -106,6 +106,9 @@ const convertText = (
106
106
  value: unknown,
107
107
  editor: Editor,
108
108
  ): RichTextValue | undefined => {
109
+ // Already Tiptap JSON (BP-5787 storage) — keep as-is.
110
+ if (isTiptapDocument(value)) return value;
111
+
109
112
  if (typeof value !== 'string' || !value) return undefined;
110
113
 
111
114
  if (hasRTEContent(value)) {
@@ -1,20 +0,0 @@
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>;
@@ -1,44 +0,0 @@
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
- });
@@ -1,27 +0,0 @@
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>;
@@ -1,44 +0,0 @@
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
- });
@@ -1,17 +0,0 @@
1
- import { type BrandPageModel } from '../../brand-page/models';
2
- import type { Logger } from '../logger';
3
- import { BaseMetadataBuilder, type BuilderResult, type MetadataBuilderRequest } from '../models';
4
- export interface UpdateTextPayload {
5
- contentId: string;
6
- fieldName: string;
7
- newText: string;
8
- maxCharacters?: number;
9
- }
10
- export declare const buildUpdateTextRequest: (id: string, payload: UpdateTextPayload) => MetadataBuilderRequest<UpdateTextPayload>;
11
- export declare class UpdateContentTextBuilder extends BaseMetadataBuilder<UpdateTextPayload> {
12
- constructor(request: MetadataBuilderRequest<UpdateTextPayload>, logger?: Logger);
13
- validateAsync(): Promise<{
14
- errors: string[];
15
- }>;
16
- buildAsync(model: BrandPageModel): Promise<BuilderResult>;
17
- }
@@ -1,103 +0,0 @@
1
- import {} from '../../brand-page/models';
2
- import { BaseMetadataBuilder, BUILDER_TYPES, } from '../models';
3
- import { hasRTEContent, replaceTextInRTE } from '../../brand-page-maker/utils';
4
- export const buildUpdateTextRequest = (id, payload) => {
5
- return {
6
- id,
7
- type: BUILDER_TYPES.UPDATE_CONTENT_TEXT,
8
- payload,
9
- };
10
- };
11
- const updateTextField = (target, fieldName, newText) => {
12
- // Check if this is an array item path like "textItems.0.text"
13
- const match = fieldName.match(/^(\w+)\.(\d+)\.(\w+)$/);
14
- if (match) {
15
- const [, arrayName, index, property] = match;
16
- const array = target[arrayName];
17
- if (Array.isArray(array) && array[+index]) {
18
- const existingText = array[+index][property];
19
- const updatedValue = existingText &&
20
- typeof existingText === 'string' &&
21
- hasRTEContent(existingText)
22
- ? replaceTextInRTE(existingText, newText)
23
- : newText;
24
- // Create a new array with the updated item to trigger reactivity
25
- const newArray = [...array];
26
- newArray[+index] = {
27
- ...newArray[+index],
28
- [property]: updatedValue,
29
- };
30
- target[arrayName] = newArray;
31
- }
32
- }
33
- else {
34
- // Simple field
35
- const existingText = target[fieldName];
36
- if (existingText &&
37
- typeof existingText === 'string' &&
38
- hasRTEContent(existingText)) {
39
- target[fieldName] = replaceTextInRTE(existingText, newText);
40
- }
41
- else {
42
- target[fieldName] = newText;
43
- }
44
- }
45
- };
46
- export class UpdateContentTextBuilder extends BaseMetadataBuilder {
47
- constructor(request, logger) {
48
- super(request, logger);
49
- }
50
- validateAsync() {
51
- const newText = this.request.payload?.newText ?? '';
52
- const errors = [];
53
- const maxChars = this.request.payload?.maxCharacters ?? 2000;
54
- if (newText.length > maxChars) {
55
- errors.push(`Text exceeds maximum allowed length of ${maxChars}`);
56
- }
57
- this.log({
58
- message: `Validation called`,
59
- errors,
60
- });
61
- return Promise.resolve({ errors: errors });
62
- }
63
- async buildAsync(model) {
64
- this.log(`Build called`);
65
- const { contentId, fieldName, newText } = this.request.payload;
66
- const validation = await this.validateAsync();
67
- if (validation.errors.length) {
68
- return {
69
- request: this.request,
70
- model,
71
- errors: validation.errors,
72
- };
73
- }
74
- let updated = false;
75
- for (const page of model.pages) {
76
- for (const content of page.contents) {
77
- // Use includes to handle potential ID format differences
78
- if (content.id.includes(contentId) || contentId.includes(content.id)) {
79
- this.log({
80
- message: `Content found`,
81
- model,
82
- });
83
- updateTextField(content, fieldName, newText);
84
- this.log({
85
- message: `Successfully updated text content.`,
86
- model,
87
- });
88
- updated = true;
89
- break;
90
- }
91
- }
92
- }
93
- return {
94
- request: this.request,
95
- model,
96
- errors: updated
97
- ? undefined
98
- : [
99
- `Could not update the text because content with ID "${contentId}" not found.`,
100
- ],
101
- };
102
- }
103
- }
@@ -1,16 +0,0 @@
1
- import { type BrandPageModel, type TextStyleDeclaration } from '../../brand-page/models';
2
- import { BaseMetadataBuilder, type BuilderResult, type MetadataBuilderRequest } from '../models';
3
- import type { Logger } from '../logger';
4
- export interface UpdateTextStylePayload {
5
- contentId: string;
6
- fieldName: string;
7
- newStyles: TextStyleDeclaration;
8
- }
9
- export declare const buildUpdateTextStyleRequest: (id: string, payload: UpdateTextStylePayload) => MetadataBuilderRequest<UpdateTextStylePayload>;
10
- export declare class UpdateContentTextStyleBuilder extends BaseMetadataBuilder<UpdateTextStylePayload> {
11
- constructor(request: MetadataBuilderRequest<UpdateTextStylePayload>, logger?: Logger);
12
- validateAsync(): Promise<{
13
- errors: string[];
14
- }>;
15
- buildAsync(model: BrandPageModel): Promise<BuilderResult>;
16
- }
@@ -1,216 +0,0 @@
1
- import {} from '../../brand-page/models';
2
- import { BaseMetadataBuilder, BUILDER_TYPES, } from '../models';
3
- import { hasRTEContent } from '../../brand-page-maker/utils';
4
- import { useExternalRichTextEditor } from '../../brand-page-maker/composables/rich-text-editor/useRichTextEditor';
5
- import { applyStyleToRTEText } from '../helpers/rte.helpers';
6
- import { letterSpacingValueConvertor } from '../../brand-page-maker/convertors/letter-spacing-value.convertor';
7
- export const buildUpdateTextStyleRequest = (id, payload) => {
8
- return {
9
- id,
10
- type: BUILDER_TYPES.UPDATE_CONTENT_TEXT_STYLE,
11
- payload,
12
- };
13
- };
14
- const mergeStyles = (currentStyle, stylesToUpdate, textStyleOptions) => {
15
- let merged = { ...currentStyle };
16
- // If content uses a textStyleType preset (e.g. "titles"), resolve the preset
17
- // styles into the style object before clearing the preset reference.
18
- // This ensures properties like fontSize, fontFamily etc. are preserved
19
- // when the AI only changes a single property like color.
20
- if (merged.textStyleType && textStyleOptions?.[merged.textStyleType]) {
21
- const presetStyle = textStyleOptions[merged.textStyleType].style;
22
- merged = { ...merged, ...presetStyle };
23
- merged.textStyleType = undefined;
24
- }
25
- Object.entries(stylesToUpdate).forEach(([key, value]) => {
26
- if (value === null) {
27
- // backgroundColor: null means "remove background color"
28
- if (key === 'backgroundColor') {
29
- merged.backgroundColor = '';
30
- }
31
- // All other null values mean "not changed" — skip to preserve existing values
32
- return;
33
- }
34
- merged[key] = value;
35
- });
36
- return merged;
37
- };
38
- const updateRTEText = (text, style) => {
39
- if (!text || typeof text !== 'string' || !hasRTEContent(text)) {
40
- return text;
41
- }
42
- const { externalEditor } = useExternalRichTextEditor();
43
- const updatedText = applyStyleToRTEText(externalEditor, text, style);
44
- externalEditor.destroy();
45
- return updatedText;
46
- };
47
- const findStyleKey = (target, property) => {
48
- // Try exact match first (e.g., 'testimonialsStyle')
49
- const exactKey = `${property}Style`;
50
- if (exactKey in target)
51
- return exactKey;
52
- // Try singular form (e.g., 'testimonialStyle' for 'testimonials')
53
- if (property.endsWith('s')) {
54
- const singularKey = `${property.slice(0, -1)}Style`;
55
- if (singularKey in target)
56
- return singularKey;
57
- }
58
- // Try plural form (edge case)
59
- const pluralKey = `${property}sStyle`;
60
- if (pluralKey in target)
61
- return pluralKey;
62
- return exactKey; // fallback to exact match
63
- };
64
- const updateSharedArrayStyle = (content, property, stylesToUpdate, textStyleOptions) => {
65
- const styleKey = findStyleKey(content, property);
66
- if (!styleKey)
67
- return;
68
- const currentStyle = content[styleKey] || {};
69
- const mergedStyle = mergeStyles(currentStyle, stylesToUpdate, textStyleOptions);
70
- content[styleKey] = mergedStyle;
71
- };
72
- const updateArrayItemStyle = (content, fieldName, index, property, stylesToUpdate, textStyleOptions) => {
73
- const currentContentItems = content[fieldName];
74
- if (!Array.isArray(currentContentItems) || !currentContentItems[+index])
75
- return;
76
- const contentItem = currentContentItems[+index];
77
- const styleKey = `${property}Style`;
78
- const currentStyle = contentItem[styleKey] || {};
79
- const mergedStyle = mergeStyles(currentStyle, stylesToUpdate, textStyleOptions);
80
- const newContentItems = [...currentContentItems];
81
- newContentItems[+index] = {
82
- ...newContentItems[+index],
83
- [styleKey]: mergedStyle,
84
- [property]: updateRTEText(contentItem[property], mergedStyle),
85
- };
86
- content[fieldName] = newContentItems;
87
- };
88
- const updateTextStyle = (content, fieldName, stylesToUpdate, textStyleOptions) => {
89
- // Check if fieldName is in the format of "fieldName.index.fieldItem"
90
- const matchFieldItems = fieldName.match(/^(\w+)\.(\d+)\.(.+)$/);
91
- if (matchFieldItems) {
92
- const [, fieldName, index, fieldItem] = matchFieldItems;
93
- const array = content[fieldName];
94
- if (!Array.isArray(array) || !array[+index])
95
- return;
96
- const item = array[+index];
97
- const itemStyleKey = `${fieldItem}Style`;
98
- const hasItemStyle = itemStyleKey in item;
99
- if (hasItemStyle) {
100
- updateArrayItemStyle(content, fieldName, +index, fieldItem, stylesToUpdate, textStyleOptions);
101
- }
102
- else {
103
- updateSharedArrayStyle(content, fieldItem, stylesToUpdate, textStyleOptions);
104
- }
105
- }
106
- else {
107
- const styleKey = `${fieldName}Style`;
108
- const currentStyle = content[styleKey] || {};
109
- const mergedStyle = mergeStyles(currentStyle, stylesToUpdate, textStyleOptions);
110
- content[styleKey] = mergedStyle;
111
- content[fieldName] = updateRTEText(content[fieldName], mergedStyle);
112
- }
113
- };
114
- export class UpdateContentTextStyleBuilder extends BaseMetadataBuilder {
115
- constructor(request, logger) {
116
- super(request, logger);
117
- }
118
- validateAsync() {
119
- const { newStyles } = this.request.payload;
120
- const errors = [];
121
- // Validate lineHeight: must be between 0 and 2.5 inclusive, snap to 0.1 step
122
- if (newStyles.lineHeight !== undefined && newStyles.lineHeight !== null) {
123
- const lineHeightValue = parseFloat(String(newStyles.lineHeight));
124
- if (isNaN(lineHeightValue) ||
125
- lineHeightValue < 0 ||
126
- lineHeightValue > 2.5) {
127
- errors.push('Line height must be a value between 0 and 2.5 inclusive.');
128
- }
129
- else {
130
- // Snap to 0.1 increments to match the UI slider step
131
- newStyles.lineHeight = String(Math.floor(lineHeightValue * 10) / 10);
132
- }
133
- }
134
- // Validate and normalize letterSpacing: AI sends unitless 0-100, convert to em
135
- if (newStyles.letterSpacing !== undefined &&
136
- newStyles.letterSpacing !== null) {
137
- const letterSpacingStr = String(newStyles.letterSpacing);
138
- if (/[a-zA-Z]/.test(letterSpacingStr)) {
139
- errors.push('Letter spacing must be a unitless value between 0 and 100.');
140
- }
141
- else {
142
- const letterSpacingValue = parseFloat(letterSpacingStr);
143
- if (isNaN(letterSpacingValue) ||
144
- letterSpacingValue < 0 ||
145
- letterSpacingValue > 100) {
146
- errors.push('Letter spacing must be between 0 and 100 inclusive.');
147
- }
148
- else {
149
- newStyles.letterSpacing = letterSpacingValueConvertor.convertBack(String(letterSpacingValue)).letterSpacing;
150
- }
151
- }
152
- }
153
- // Validate fontSize: must be in 'em' unit and between 0.75em and 8em inclusive
154
- if (newStyles?.fontSize != null) {
155
- const fontSizeStr = String(newStyles.fontSize);
156
- const fontSizeMatch = fontSizeStr.match(/^(-?\d*\.?\d+)em$/);
157
- if (!fontSizeMatch) {
158
- errors.push('Font size must be in "em" unit (e.g., "1.5em").');
159
- }
160
- else {
161
- const fontSizeValue = parseFloat(fontSizeMatch[1]);
162
- if (isNaN(fontSizeValue) || fontSizeValue < 0.75 || fontSizeValue > 8) {
163
- errors.push('Font size must be between 0.75em and 8em inclusive. If unitless from user always treat as px and convert to em using 16px = 1em ratio.');
164
- }
165
- }
166
- }
167
- return Promise.resolve({ errors });
168
- }
169
- async buildAsync(model) {
170
- this.log(`Build called`);
171
- const validation = await this.validateAsync();
172
- if (validation.errors.length) {
173
- return {
174
- request: this.request,
175
- model,
176
- errors: validation.errors,
177
- };
178
- }
179
- const { contentId, fieldName, newStyles } = this.request.payload;
180
- // Filter out undefined values
181
- const stylesToUpdate = Object.entries(newStyles).reduce((acc, [key, value]) => {
182
- if (value !== undefined) {
183
- acc[key] = value;
184
- }
185
- return acc;
186
- }, {});
187
- let updated = false;
188
- for (const page of model.pages) {
189
- for (const content of page.contents) {
190
- // Use includes to handle potential ID format differences
191
- if (content.id.includes(contentId) || contentId.includes(content.id)) {
192
- this.log({
193
- message: `Content found`,
194
- model,
195
- });
196
- updateTextStyle(content, fieldName, stylesToUpdate, model.styles?.textStyles?.textStyleOptions);
197
- this.log({
198
- message: `Successfully updated text style content.`,
199
- model,
200
- });
201
- updated = true;
202
- break;
203
- }
204
- }
205
- }
206
- return {
207
- request: this.request,
208
- model,
209
- errors: updated
210
- ? undefined
211
- : [
212
- `Could not update the text style because content with ID "${contentId}" not found.`,
213
- ],
214
- };
215
- }
216
- }
@@ -1,73 +0,0 @@
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
- });