@designcrowd/library.brand-page 6.2.11 → 6.2.12

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 (576) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-DDcraD5S.js → CreatedOnBrandCrowd-de-de-BtVKllNQ.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-DtOeaisf.js → CreatedOnBrandCrowd-en-us-CX5V0v8u.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-BZpmJPew.js → CreatedOnBrandCrowd-es-es-CbA02PCL.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-CqRwC4e7.js → CreatedOnBrandCrowd-fr-ca-BQAKruBF.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-CqRwC4e7.js → CreatedOnBrandCrowd-fr-fr-BQAKruBF.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-DId9Dfjh.js → CreatedOnBrandCrowd-pt-br-Yk_yzB3s.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-DId9Dfjh.js → CreatedOnBrandCrowd-pt-pt-Yk_yzB3s.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-BTDfmOcm.js → CreatedOnDcom-de-de-DJZQbPSD.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-CeAu9Ayx.js → CreatedOnDcom-en-us-BJvNAa4l.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-WCv0K41H.js → CreatedOnDcom-es-es-CUwB1FRY.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-BlfevQDY.js → CreatedOnDcom-fr-ca-CK54U4oh.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-Cvt7MHPZ.js → CreatedOnDcom-fr-fr-DuUNBGrM.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-DipZuylv.js → CreatedOnDcom-pt-br-BfODpFNV.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-BCw4lcKN.js → CreatedOnDcom-pt-pt-Cf6u2GCg.js} +1 -1
  15. package/lib/src/brand-page/__fixtures__/production-text-style-resolver.fixture.d.ts +16 -0
  16. package/lib/src/brand-page/__fixtures__/production-text-style-resolver.fixture.js +35 -0
  17. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.d.ts +3 -0
  18. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +19 -13
  19. package/lib/src/brand-page/components/mailing-list/MailingList.mixin.d.ts +3 -1
  20. package/lib/src/brand-page/components/mailing-list/MailingList.mixin.js +17 -2
  21. package/lib/src/brand-page/components/navigation/button-carousel/ButtonCarouselNavigation.mixin.d.ts +11 -3
  22. package/lib/src/brand-page/components/navigation/button-carousel/ButtonCarouselNavigation.mixin.js +35 -1
  23. package/lib/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.d.ts +8 -2
  24. package/lib/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.js +27 -3
  25. package/lib/src/brand-page/components/page/Page.mixin.d.ts +5 -2
  26. package/lib/src/brand-page/components/page/Page.mixin.js +33 -27
  27. package/lib/src/brand-page/components/page-footer/PageFooter.mixin.d.ts +7 -2
  28. package/lib/src/brand-page/components/page-footer/PageFooter.mixin.js +21 -1
  29. package/lib/src/brand-page/components/payment/Payment.mixin.d.ts +5 -2
  30. package/lib/src/brand-page/components/payment/Payment.mixin.js +22 -2
  31. package/lib/src/brand-page/components/payment/support-me/SupportMe.mixin.d.ts +6 -2
  32. package/lib/src/brand-page/components/payment/support-me/SupportMe.mixin.js +32 -1
  33. package/lib/src/brand-page/components/social-links/SocialLinks.stories.js +0 -3
  34. package/lib/src/brand-page/components/testimonial-section/__fixtures__/data.js +0 -3
  35. package/lib/src/brand-page/components/text/Text.stories.js +0 -17
  36. package/lib/src/brand-page/constants/font.constants.d.ts +62 -0
  37. package/lib/src/brand-page/constants/font.constants.js +35 -0
  38. package/lib/src/brand-page/models/index.d.ts +23 -24
  39. package/lib/src/brand-page/models/index.js +20 -7
  40. package/lib/src/brand-page/models/navigation-styles.model.d.ts +1 -1
  41. package/lib/src/brand-page/models/navigation-styles.model.js +17 -8
  42. package/lib/src/brand-page/models/payment.model.d.ts +11 -6
  43. package/lib/src/brand-page/models/styles.model.d.ts +53 -7
  44. package/lib/src/brand-page/models/styles.model.js +115 -27
  45. package/lib/src/brand-page/utils/color-palette.util.d.ts +43 -23
  46. package/lib/src/brand-page/utils/color-palette.util.js +65 -27
  47. package/lib/src/brand-page/utils/color.util.d.ts +13 -9
  48. package/lib/src/brand-page/utils/color.util.js +56 -15
  49. package/lib/src/brand-page/utils/container-text-color.util.d.ts +46 -13
  50. package/lib/src/brand-page/utils/container-text-color.util.js +321 -41
  51. package/lib/src/brand-page/utils/index.d.ts +1 -0
  52. package/lib/src/brand-page/utils/index.js +1 -0
  53. package/lib/src/brand-page/utils/migration/migration-20.js +6 -2
  54. package/lib/src/brand-page/utils/migration/migration-21.js +34 -6
  55. package/lib/src/brand-page/utils/migration/migration-22.d.ts +99 -0
  56. package/lib/src/brand-page/utils/migration/migration-22.js +909 -0
  57. package/lib/src/brand-page/utils/migration.util.js +17 -0
  58. package/lib/src/brand-page/utils/model.util.d.ts +4 -0
  59. package/lib/src/brand-page/utils/model.util.js +4 -0
  60. package/lib/src/brand-page/utils/personalisation.util.js +9 -1
  61. package/lib/src/brand-page/utils/rte-role-lift.util.d.ts +72 -0
  62. package/lib/src/brand-page/utils/rte-role-lift.util.js +275 -0
  63. package/lib/src/brand-page/utils/style.util.d.ts +5 -1
  64. package/lib/src/brand-page/utils/style.util.js +34 -11
  65. package/lib/src/brand-page/utils/text-role-css.util.d.ts +19 -0
  66. package/lib/src/brand-page/utils/text-role-css.util.js +186 -0
  67. package/lib/src/brand-page/utils/text-role-reconnect.util.d.ts +90 -0
  68. package/lib/src/brand-page/utils/text-role-reconnect.util.js +434 -0
  69. package/lib/src/brand-page/utils/text-style.util.d.ts +53 -23
  70. package/lib/src/brand-page/utils/text-style.util.js +122 -22
  71. package/lib/src/brand-page/utils/view-model.util.d.ts +9 -1
  72. package/lib/src/brand-page/utils/view-model.util.js +124 -111
  73. package/lib/src/brand-page/view-models.d.ts +1 -0
  74. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +4 -1
  75. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +13 -2
  76. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +4 -4
  77. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.mixin.d.ts +4 -1
  78. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +0 -10
  79. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/content-settings-items.fixture.js +0 -1
  80. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +23 -138
  81. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.js +0 -28
  82. package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.d.ts +4 -1
  83. package/lib/src/brand-page-maker/components/navigation/Navigation.mixin.d.ts +1 -1
  84. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +5 -2
  85. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +4 -1
  86. package/lib/src/brand-page-maker/components/social-share/SocialShare.mixin.d.ts +4 -1
  87. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +4 -1
  88. package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.d.ts +1 -0
  89. package/lib/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.js +1 -0
  90. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +1 -0
  91. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/RoleTypographyFill.d.ts +16 -0
  92. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/RoleTypographyFill.js +61 -0
  93. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/RteClipboard.d.ts +11 -0
  94. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/RteClipboard.js +236 -0
  95. package/lib/src/brand-page-maker/composables/rich-text-editor/font.d.ts +2 -1
  96. package/lib/src/brand-page-maker/composables/rich-text-editor/font.js +25 -6
  97. package/lib/src/brand-page-maker/composables/rich-text-editor/formatting.d.ts +17 -4
  98. package/lib/src/brand-page-maker/composables/rich-text-editor/formatting.js +204 -17
  99. package/lib/src/brand-page-maker/composables/rich-text-editor/hyperlink.d.ts +1 -1
  100. package/lib/src/brand-page-maker/composables/rich-text-editor/hyperlink.js +29 -31
  101. package/lib/src/brand-page-maker/composables/rich-text-editor/keepMarksOnEmpty.d.ts +20 -0
  102. package/lib/src/brand-page-maker/composables/rich-text-editor/keepMarksOnEmpty.js +60 -0
  103. package/lib/src/brand-page-maker/composables/rich-text-editor/spacing.d.ts +2 -1
  104. package/lib/src/brand-page-maker/composables/rich-text-editor/spacing.js +22 -6
  105. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +10 -4
  106. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +319 -275
  107. package/lib/src/brand-page-maker/constants/config-settings-constant.js +1 -5
  108. package/lib/src/brand-page-maker/convertors/text-css-style-options.convertor.js +6 -2
  109. package/lib/src/brand-page-maker/convertors/text-size-value.convertor.d.ts +1 -1
  110. package/lib/src/brand-page-maker/convertors/text-size-value.convertor.js +3 -1
  111. package/lib/src/brand-page-maker/default-getters/payment-text-and-border-color.default-getter.js +7 -14
  112. package/lib/src/brand-page-maker/default-getters/text-color.default-getter.js +6 -13
  113. package/lib/src/brand-page-maker/models/content-settings.d.ts +8 -0
  114. package/lib/src/brand-page-maker/models/content-settings.js +112 -81
  115. package/lib/src/brand-page-maker/stores/admin-logo-settings/admin-logo-settings-store-utils.d.ts +2 -2
  116. package/lib/src/brand-page-maker/stores/admin-logo-settings/admin-logo-settings-store-utils.js +5 -2
  117. package/lib/src/brand-page-maker/stores/admin-logo-settings/admin-logo-settings-store.js +4 -3
  118. package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +1257 -117
  119. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.d.ts +6 -1
  120. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +17 -9
  121. package/lib/src/brand-page-maker/stores/history/history-store.d.ts +59 -5
  122. package/lib/src/brand-page-maker/stores/history/history-store.js +22 -2
  123. package/lib/src/brand-page-maker/utils/apply-color-strategy.js +26 -8
  124. package/lib/src/brand-page-maker/utils/index.d.ts +2 -1
  125. package/lib/src/brand-page-maker/utils/index.js +2 -1
  126. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +117 -15
  127. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +435 -281
  128. package/lib/src/brand-page-maker/utils/rte-authoring.util.d.ts +4 -0
  129. package/lib/src/brand-page-maker/utils/rte-authoring.util.js +101 -0
  130. package/lib/src/brand-page-maker/utils/rte-clipboard.util.d.ts +126 -0
  131. package/lib/src/brand-page-maker/utils/rte-clipboard.util.js +307 -0
  132. package/lib/src/brand-page-maker/utils/rte-sanitize.util.js +13 -3
  133. package/lib/src/{bundled-translations-zfiA5Tsw.js → bundled-translations-yGrvPE5i.js} +17 -17
  134. package/lib/src/{bundled-translations.de-DE-ChsdSyAP.js → bundled-translations.de-DE-L4Znf8Hs.js} +17 -17
  135. package/lib/src/{bundled-translations.es-ES-DflsWQK0.js → bundled-translations.es-ES-D6iticrK.js} +15 -15
  136. package/lib/src/{bundled-translations.fr-CA-fpDRuZ52.js → bundled-translations.fr-CA-BgAVE-9k.js} +25 -25
  137. package/lib/src/{bundled-translations.fr-FR-Bub73Zt-.js → bundled-translations.fr-FR-B8GbyqxQ.js} +17 -17
  138. package/lib/src/{bundled-translations.pt-BR-BlnWE9TB.js → bundled-translations.pt-BR-DjPU3Db7.js} +25 -25
  139. package/lib/src/{bundled-translations.pt-PT-DmzXlnaS.js → bundled-translations.pt-PT-BaepaDpr.js} +23 -23
  140. package/lib/src/bundles/bundled-translations.de-DE.json +21 -2
  141. package/lib/src/bundles/bundled-translations.es-ES.json +21 -2
  142. package/lib/src/bundles/bundled-translations.fr-CA.json +21 -2
  143. package/lib/src/bundles/bundled-translations.fr-FR.json +21 -2
  144. package/lib/src/bundles/bundled-translations.json +21 -2
  145. package/lib/src/bundles/bundled-translations.pt-BR.json +21 -2
  146. package/lib/src/bundles/bundled-translations.pt-PT.json +21 -2
  147. package/lib/src/clients/ai-assistant-client.d.ts +1 -0
  148. package/lib/src/composables/useTranslate.d.ts +1 -1
  149. package/lib/src/composables/useTranslate.js +1 -1
  150. package/lib/src/css/library.brand-page-brandCrowd.min.css +303 -207
  151. package/lib/src/css/library.brand-page-brandPage.min.css +303 -207
  152. package/lib/src/css/library.brand-page-designCom.min.css +303 -207
  153. package/lib/src/css/library.brand-page-preview.min.css +303 -207
  154. package/lib/src/index.d.ts +2 -1
  155. package/lib/src/index.mjs +195 -191
  156. package/lib/src/{lib-1AqtEmvQ.js → lib-kGRlD3_P.js} +36855 -33893
  157. package/lib/src/lib.js +2 -1
  158. package/lib/src/metadata-builder/builders/footer-builder.js +4 -5
  159. package/lib/src/metadata-builder/builders/global-role-propagation.d.ts +58 -0
  160. package/lib/src/metadata-builder/builders/global-role-propagation.js +246 -0
  161. package/lib/src/metadata-builder/builders/global-styles-builder.d.ts +1 -0
  162. package/lib/src/metadata-builder/builders/global-styles-builder.js +116 -45
  163. package/lib/src/metadata-builder/builders/header-builder.js +13 -4
  164. package/lib/src/metadata-builder/builders/section-builders/about-section-builder.js +3 -3
  165. package/lib/src/metadata-builder/builders/section-builders/banner-section-builder.js +3 -3
  166. package/lib/src/metadata-builder/builders/section-builders/booking-section-builder.js +3 -3
  167. package/lib/src/metadata-builder/builders/section-builders/contact-form-builder.js +4 -4
  168. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +3 -3
  169. package/lib/src/metadata-builder/builders/section-builders/mailing-list-builder.js +2 -2
  170. package/lib/src/metadata-builder/builders/section-builders/mailing-list-section-builder.js +2 -2
  171. package/lib/src/metadata-builder/builders/section-builders/profile-section-builder.js +4 -4
  172. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +3 -3
  173. package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +8 -2
  174. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +2 -2
  175. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +60 -29
  176. package/lib/src/metadata-builder/builders/section-field-maps.js +1 -0
  177. package/lib/src/metadata-builder/builders/section-field-utils.d.ts +48 -19
  178. package/lib/src/metadata-builder/builders/section-field-utils.js +128 -52
  179. package/lib/src/metadata-builder/builders/section-rules/global-styles.rules.d.ts +4 -2
  180. package/lib/src/metadata-builder/builders/section-rules/global-styles.rules.js +28 -12
  181. package/lib/src/metadata-builder/builders/section-rules/social-links.rules.js +5 -1
  182. package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +1 -1
  183. package/lib/src/metadata-builder/helpers/rte.helpers.js +97 -40
  184. package/lib/src/metadata-builder/metadata-executor.js +23 -1
  185. package/lib/src/metadata-builder/models.d.ts +2 -0
  186. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +18 -19
  187. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +18 -1
  188. package/lib/src/metadata-builder/sanitizers/rte-schema-check.js +1 -1
  189. package/lib/src/metadata-builder/sanitizers/sanitizer-rules.d.ts +0 -1
  190. package/lib/src/metadata-builder/sanitizers/sanitizer-rules.js +5 -8
  191. package/lib/src/metadata-builder/sanitizers/section-args-validator.d.ts +7 -16
  192. package/lib/src/metadata-builder/sanitizers/section-args-validator.js +95 -22
  193. package/lib/src/shared/color-palette/section-color-matches.js +9 -25
  194. package/lib/src/shared/color-strategies/color-strategies.d.ts +15 -0
  195. package/lib/src/shared/color-strategies/color-strategies.js +31 -0
  196. package/lib/src/shared/color-strategies/rte-color.d.ts +8 -0
  197. package/lib/src/shared/color-strategies/rte-color.js +16 -0
  198. package/lib/src/shared/color-strategies/rte-field-pairs.d.ts +23 -0
  199. package/lib/src/shared/color-strategies/rte-field-pairs.js +28 -0
  200. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +9 -135
  201. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.js +1 -27
  202. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +12 -2
  203. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +7 -0
  204. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +75 -6
  205. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +18 -7
  206. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.d.ts +6 -1
  207. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.js +0 -7
  208. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +1 -0
  209. package/lib/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.js +8 -4
  210. package/lib/src/shared/content-settings/components/text-style-field/TextStyleField.mixin.d.ts +1255 -26
  211. package/lib/src/shared/content-settings/components/text-style-field/TextStyleField.mixin.js +203 -59
  212. package/lib/src/shared/content-settings/store/content-settings-store.d.ts +6 -0
  213. package/lib/src/shared/content-settings/store/content-settings-store.js +105 -8
  214. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +5 -1
  215. package/lib/src/shared/navigation-style-settings/store/index.d.ts +7 -3
  216. package/lib/src/shared/navigation-style-settings/store/index.js +40 -15
  217. package/lib/src/shared/page-settings/components/delete-page-modal/DeletePageModal.mixin.d.ts +1 -1
  218. package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.d.ts +1 -0
  219. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +17 -5
  220. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +70 -13
  221. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.js +10 -2
  222. package/lib/src/shared/page-settings/store/page-settings-store.d.ts +2 -1
  223. package/lib/src/shared/page-settings/store/page-settings-store.js +17 -0
  224. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +4 -1
  225. package/lib/src/shared/rte-extensions/AutoLink.d.ts +17 -0
  226. package/lib/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/AutoLink.js +67 -51
  227. package/lib/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/MaxLength.js +4 -1
  228. package/lib/src/shared/rte-extensions/TextEffects.d.ts +28 -0
  229. package/lib/src/shared/rte-extensions/TextEffects.js +96 -0
  230. package/lib/src/shared/rte-extensions/ViewDomParser.d.ts +2 -0
  231. package/lib/src/shared/rte-extensions/ViewDomParser.js +21 -0
  232. package/lib/src/shared/rte-extensions.util.d.ts +25 -0
  233. package/lib/src/{brand-page-maker/composables/rich-text-editor/getRteExtensions.js → shared/rte-extensions.util.js} +62 -7
  234. package/lib/src/shared/rte-link.util.d.ts +1 -0
  235. package/lib/src/shared/rte-link.util.js +1 -0
  236. package/lib/src/shared/rte-render.util.d.ts +112 -0
  237. package/lib/src/shared/rte-render.util.js +255 -0
  238. package/lib/src/shared/rte-shape.util.d.ts +6 -0
  239. package/lib/src/shared/rte-shape.util.js +18 -0
  240. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +2 -1
  241. package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +1 -0
  242. package/lib/src/shared/side-bar/store/side-bar-store.d.ts +2 -0
  243. package/lib/src/shared/side-bar/store/side-bar-store.js +9 -0
  244. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +155 -15
  245. package/lib/src/shared/style-settings/components/StyleSettings.mixin.js +7 -1
  246. package/lib/src/shared/style-settings/store/style-settings-store.d.ts +1 -9
  247. package/lib/src/shared/style-settings/store/style-settings-store.js +3 -33
  248. package/lib/src/shared/style-settings/text-style-size-order.d.ts +2 -0
  249. package/lib/src/shared/style-settings/text-style-size-order.js +13 -0
  250. package/lib/src/shared/text-role-typography.util.d.ts +64 -0
  251. package/lib/src/shared/text-role-typography.util.js +183 -0
  252. package/lib/src/utils/minify-brand-page-metadata.d.ts +16 -1
  253. package/lib/src/utils/minify-brand-page-metadata.js +31 -9
  254. package/package.json +1 -1
  255. package/src/brand-page/__fixtures__/migration-22-v21/edited-brandsite-sections.v21.json +982 -0
  256. package/src/brand-page/__fixtures__/migration-22-v21/new-brandcontact-every-section.v21.json +1231 -0
  257. package/src/brand-page/__fixtures__/migration-22-v21/new-brandpage-every-section.v21.json +1231 -0
  258. package/src/brand-page/__fixtures__/migration-22-v21/new-brandsite-every-section.v21.json +1243 -0
  259. package/src/brand-page/__fixtures__/migration-22-v21/real-brandcontact.v21.json +755 -0
  260. package/src/brand-page/__fixtures__/migration-22-v21/real-brandpage-general-link.v21.json +527 -0
  261. package/src/brand-page/__fixtures__/migration-22-v21/real-brandpage-links.v21.json +755 -0
  262. package/src/brand-page/__fixtures__/migration-22-v21/real-brandpage-social-links.v21.json +514 -0
  263. package/src/brand-page/__fixtures__/migration-22-v21/real-brandsite-about.v21.json +459 -0
  264. package/src/brand-page/__fixtures__/migration-22-v21/real-brandsite-footer.v21.json +469 -0
  265. package/src/brand-page/__fixtures__/migration-22-v21/real-brandsite-header-banner.v21.json +481 -0
  266. package/src/brand-page/__fixtures__/migration-22-v21/real-brandsite-sections.v21.json +800 -0
  267. package/src/brand-page/__fixtures__/production-text-style-resolver.fixture.ts +52 -0
  268. package/src/brand-page/components/about-section/AboutSection.vue.test.ts +0 -10
  269. package/src/brand-page/components/contact-form/ContactForm.mixin.test.ts +127 -0
  270. package/src/brand-page/components/contact-form/ContactForm.mixin.ts +31 -14
  271. package/src/brand-page/components/contact-form/ContactForm.vue +26 -12
  272. package/src/brand-page/components/contact-form/ContactForm.vue.test.ts +60 -0
  273. package/src/brand-page/components/mailing-list/MailingList.mixin.test.ts +92 -0
  274. package/src/brand-page/components/mailing-list/MailingList.mixin.ts +28 -3
  275. package/src/brand-page/components/mailing-list/MailingList.vue +9 -4
  276. package/src/brand-page/components/mailing-list/MailingList.vue.test.ts +63 -0
  277. package/src/brand-page/components/navigation/button-carousel/ButtonCarouselNavigation.mixin.ts +51 -1
  278. package/src/brand-page/components/navigation/button-carousel/ButtonCarouselNavigation.test.ts +114 -0
  279. package/src/brand-page/components/navigation/button-carousel/ButtonCarouselNavigation.vue +8 -2
  280. package/src/brand-page/components/navigation/navigation-carousel/NavigationCarousel.test.ts +182 -0
  281. package/src/brand-page/components/navigation/navigation-carousel/NavigationCarousel.vue +59 -12
  282. package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.ts +40 -3
  283. package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.vue +15 -7
  284. package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.vue.test.ts +133 -3
  285. package/src/brand-page/components/page/Page.mixin.ts +41 -34
  286. package/src/brand-page/components/page/Page.vue +17 -6
  287. package/src/brand-page/components/page/Page.vue.test.ts +91 -1
  288. package/src/brand-page/components/page-footer/PageFooter.mixin.ts +32 -2
  289. package/src/brand-page/components/page-footer/PageFooter.test.ts +79 -0
  290. package/src/brand-page/components/page-footer/PageFooter.vue +12 -4
  291. package/src/brand-page/components/payment/Payment.mixin.ts +34 -2
  292. package/src/brand-page/components/payment/Payment.test.ts +188 -0
  293. package/src/brand-page/components/payment/Payment.vue +12 -5
  294. package/src/brand-page/components/payment/RequestPaymentStripe.vue +1 -0
  295. package/src/brand-page/components/payment/support-me/SupportMe.mixin.ts +49 -2
  296. package/src/brand-page/components/payment/support-me/SupportMe.test.ts +100 -0
  297. package/src/brand-page/components/payment/support-me/SupportMe.vue +7 -1
  298. package/src/brand-page/components/payment/support-me/SupportMe.vue.test.ts +66 -0
  299. package/src/brand-page/components/save-contact/SaveContactWidget.test.ts +100 -0
  300. package/src/brand-page/components/save-contact/SaveContactWidget.vue +6 -3
  301. package/src/brand-page/components/shop/PaymentModal.vue +2 -6
  302. package/src/brand-page/components/shop/PaymentShippingAddress.vue +6 -0
  303. package/src/brand-page/components/shop/Shop.vue +15 -2
  304. package/src/brand-page/components/shop/Shop.vue.test.ts +74 -2
  305. package/src/brand-page/components/social-links/SocialLinks.stories.ts +0 -3
  306. package/src/brand-page/components/social-links/SocialLinks.vue.test.ts +0 -3
  307. package/src/brand-page/components/testimonial-section/__fixtures__/data.ts +0 -3
  308. package/src/brand-page/components/text/Text.stories.ts +0 -17
  309. package/src/brand-page/components/text-wrapper/TextWrapper.parity.test.ts +136 -0
  310. package/src/brand-page/components/text-wrapper/TextWrapper.test.ts +358 -0
  311. package/src/brand-page/components/text-wrapper/TextWrapper.vue +82 -22
  312. package/src/brand-page/constants/font.constants.ts +37 -0
  313. package/src/brand-page/models/footer-icon-color.test.ts +22 -0
  314. package/src/brand-page/models/index.ts +50 -28
  315. package/src/brand-page/models/navigation-styles.model.test.ts +77 -1
  316. package/src/brand-page/models/navigation-styles.model.ts +26 -9
  317. package/src/brand-page/models/payment.model.ts +15 -6
  318. package/src/brand-page/models/styles.model.test.ts +104 -0
  319. package/src/brand-page/models/styles.model.ts +181 -47
  320. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +619 -115
  321. package/src/brand-page/utils/__snapshots__/migration2.util.test.ts.snap +150 -9
  322. package/src/brand-page/utils/__snapshots__/model.util.test.ts.snap +300 -18
  323. package/src/brand-page/utils/color-palette.util.test.ts +126 -1
  324. package/src/brand-page/utils/color-palette.util.ts +92 -26
  325. package/src/brand-page/utils/color.util.test.ts +62 -29
  326. package/src/brand-page/utils/color.util.ts +77 -14
  327. package/src/brand-page/utils/container-text-color.util.test.ts +672 -26
  328. package/src/brand-page/utils/container-text-color.util.ts +472 -46
  329. package/src/brand-page/utils/index.ts +1 -0
  330. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/edited-brandsite-sections.v22.json +1191 -0
  331. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/new-brandcontact-every-section.v22.json +2026 -0
  332. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/new-brandpage-every-section.v22.json +2017 -0
  333. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/new-brandsite-every-section.v22.json +2031 -0
  334. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/real-brandcontact.v22.json +1145 -0
  335. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/real-brandpage-general-link.v22.json +645 -0
  336. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/real-brandpage-links.v22.json +1136 -0
  337. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/real-brandpage-social-links.v22.json +662 -0
  338. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/real-brandsite-about.v22.json +638 -0
  339. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/real-brandsite-footer.v22.json +610 -0
  340. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/real-brandsite-header-banner.v22.json +660 -0
  341. package/src/brand-page/utils/migration/__snapshots__/migration-22-output/real-brandsite-sections.v22.json +1132 -0
  342. package/src/brand-page/utils/migration/migration-01.ts +293 -237
  343. package/src/brand-page/utils/migration/migration-02.ts +8 -1
  344. package/src/brand-page/utils/migration/migration-20.ts +6 -3
  345. package/src/brand-page/utils/migration/migration-20_5.test.ts +5 -2
  346. package/src/brand-page/utils/migration/migration-21.test.ts +34 -0
  347. package/src/brand-page/utils/migration/migration-21.ts +46 -10
  348. package/src/brand-page/utils/migration/migration-22.fixed-input.test.ts +43 -0
  349. package/src/brand-page/utils/migration/migration-22.test.ts +4103 -0
  350. package/src/brand-page/utils/migration/migration-22.text-font-family.test.ts +72 -0
  351. package/src/brand-page/utils/migration/migration-22.ts +1336 -0
  352. package/src/brand-page/utils/migration.util.test.ts +146 -32
  353. package/src/brand-page/utils/migration.util.ts +18 -0
  354. package/src/brand-page/utils/model.util.ts +4 -0
  355. package/src/brand-page/utils/personalisation.util.test.ts +148 -3
  356. package/src/brand-page/utils/personalisation.util.ts +9 -1
  357. package/src/brand-page/utils/rte-role-lift.util.ts +409 -0
  358. package/src/brand-page/utils/style.util.test.ts +35 -2
  359. package/src/brand-page/utils/style.util.ts +45 -11
  360. package/src/brand-page/utils/text-role-css.util.test.ts +525 -0
  361. package/src/brand-page/utils/text-role-css.util.ts +321 -0
  362. package/src/brand-page/utils/text-role-reconnect.util.test.ts +604 -0
  363. package/src/brand-page/utils/text-role-reconnect.util.ts +727 -0
  364. package/src/brand-page/utils/text-style.util.test.ts +305 -0
  365. package/src/brand-page/utils/text-style.util.ts +198 -24
  366. package/src/brand-page/utils/view-model.page-data.test.ts +232 -0
  367. package/src/brand-page/utils/view-model.util.test.ts +954 -133
  368. package/src/brand-page/utils/view-model.util.ts +187 -139
  369. package/src/brand-page/view-models.ts +4 -0
  370. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.blank-site.test.ts +57 -0
  371. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +9 -5
  372. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +0 -10
  373. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/content-settings-items.fixture.ts +0 -1
  374. package/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.ts +0 -44
  375. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +253 -200
  376. package/src/brand-page-maker/components/shop/AddOrEditProductContainer.vue +1 -0
  377. package/src/brand-page-maker/components/template-preview/TemplatePreview.vue +1 -0
  378. package/src/brand-page-maker/composables/ai-chat/useAiMetadataBuilder.ts +2 -0
  379. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +1 -0
  380. package/src/brand-page-maker/composables/rich-text-editor/block-typography.test.ts +519 -0
  381. package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +1 -1
  382. package/src/brand-page-maker/composables/rich-text-editor/extensions/MaxLength.test.ts +26 -1
  383. package/src/brand-page-maker/composables/rich-text-editor/extensions/RoleTypographyFill.test.ts +110 -0
  384. package/src/brand-page-maker/composables/rich-text-editor/extensions/RoleTypographyFill.ts +99 -0
  385. package/src/brand-page-maker/composables/rich-text-editor/extensions/RteClipboard.ts +397 -0
  386. package/src/brand-page-maker/composables/rich-text-editor/extensions/ViewDomParser.test.ts +139 -0
  387. package/src/brand-page-maker/composables/rich-text-editor/font.ts +36 -5
  388. package/src/brand-page-maker/composables/rich-text-editor/formatting.test.ts +375 -1
  389. package/src/brand-page-maker/composables/rich-text-editor/formatting.ts +301 -17
  390. package/src/brand-page-maker/composables/rich-text-editor/hyperlink.test.ts +82 -1
  391. package/src/brand-page-maker/composables/rich-text-editor/hyperlink.ts +28 -34
  392. package/src/brand-page-maker/composables/rich-text-editor/keepMarksOnEmpty.test.ts +80 -0
  393. package/src/brand-page-maker/composables/rich-text-editor/keepMarksOnEmpty.ts +75 -0
  394. package/src/brand-page-maker/composables/rich-text-editor/spacing.ts +31 -6
  395. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.test.ts +2485 -0
  396. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +435 -337
  397. package/src/brand-page-maker/constants/config-settings-constant.ts +0 -6
  398. package/src/brand-page-maker/convertors/text-css-style-options.convertor.test.ts +43 -0
  399. package/src/brand-page-maker/convertors/text-css-style-options.convertor.ts +6 -2
  400. package/src/brand-page-maker/convertors/text-size-value.convertor.test.ts +24 -0
  401. package/src/brand-page-maker/convertors/text-size-value.convertor.ts +17 -13
  402. package/src/brand-page-maker/default-getters/payment-text-and-border-color.default-getter.ts +7 -22
  403. package/src/brand-page-maker/default-getters/text-color.default-getter.ts +5 -21
  404. package/src/brand-page-maker/models/content-settings.test.ts +708 -0
  405. package/src/brand-page-maker/models/content-settings.ts +133 -82
  406. package/src/brand-page-maker/stores/admin-logo-settings/admin-logo-settings-store-utils.test.ts +63 -1
  407. package/src/brand-page-maker/stores/admin-logo-settings/admin-logo-settings-store-utils.ts +16 -3
  408. package/src/brand-page-maker/stores/admin-logo-settings/admin-logo-settings-store.test.ts +34 -0
  409. package/src/brand-page-maker/stores/admin-logo-settings/admin-logo-settings-store.ts +4 -2
  410. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.test.ts +185 -1
  411. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +22 -34
  412. package/src/brand-page-maker/stores/history/history-store.ts +35 -2
  413. package/src/brand-page-maker/utils/__snapshots__/apply-color-strategy.golden.test.ts.snap +528 -0
  414. package/src/brand-page-maker/utils/__snapshots__/rte-render-parity.test.ts.snap +12 -0
  415. package/src/brand-page-maker/utils/apply-color-strategy.golden.test.ts +2 -0
  416. package/src/brand-page-maker/utils/apply-color-strategy.test.ts +181 -3
  417. package/src/brand-page-maker/utils/apply-color-strategy.ts +36 -4
  418. package/src/brand-page-maker/utils/index.ts +2 -1
  419. package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +447 -410
  420. package/src/brand-page-maker/utils/rich-text-editor.util.ts +610 -506
  421. package/src/brand-page-maker/utils/rte-authoring.util.test.ts +244 -0
  422. package/src/brand-page-maker/utils/rte-authoring.util.ts +175 -0
  423. package/src/brand-page-maker/utils/rte-clipboard.util.test.ts +558 -0
  424. package/src/brand-page-maker/utils/rte-clipboard.util.ts +473 -0
  425. package/src/brand-page-maker/utils/rte-features.test.ts +99 -3
  426. package/src/brand-page-maker/utils/rte-render-parity.test.ts +1 -1
  427. package/src/brand-page-maker/utils/rte-render.util.test.ts +230 -0
  428. package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +1 -1
  429. package/src/brand-page-maker/utils/rte-sanitize.util.ts +14 -3
  430. package/src/brand-page-maker/utils/rte-schema-version.test.ts +102 -0
  431. package/src/bundles/bundled-translations.de-DE.json +21 -2
  432. package/src/bundles/bundled-translations.es-ES.json +21 -2
  433. package/src/bundles/bundled-translations.fr-CA.json +21 -2
  434. package/src/bundles/bundled-translations.fr-FR.json +21 -2
  435. package/src/bundles/bundled-translations.json +21 -2
  436. package/src/bundles/bundled-translations.pt-BR.json +21 -2
  437. package/src/bundles/bundled-translations.pt-PT.json +21 -2
  438. package/src/clients/ai-assistant-client.ts +1 -0
  439. package/src/composables/useTranslate.ts +2 -1
  440. package/src/lib.ts +2 -2
  441. package/src/metadata-builder/builders/footer-builder.test.ts +70 -45
  442. package/src/metadata-builder/builders/footer-builder.ts +4 -8
  443. package/src/metadata-builder/builders/global-role-propagation.test.ts +907 -0
  444. package/src/metadata-builder/builders/global-role-propagation.ts +430 -0
  445. package/src/metadata-builder/builders/global-styles-builder.test.ts +1020 -0
  446. package/src/metadata-builder/builders/global-styles-builder.ts +194 -48
  447. package/src/metadata-builder/builders/header-builder.test.ts +112 -40
  448. package/src/metadata-builder/builders/header-builder.ts +18 -8
  449. package/src/metadata-builder/builders/section-builders/about-section-builder.ts +5 -3
  450. package/src/metadata-builder/builders/section-builders/banner-section-builder.ts +5 -3
  451. package/src/metadata-builder/builders/section-builders/booking-section-builder.ts +5 -3
  452. package/src/metadata-builder/builders/section-builders/contact-form-builder.ts +7 -4
  453. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +5 -3
  454. package/src/metadata-builder/builders/section-builders/mailing-list-builder.ts +3 -2
  455. package/src/metadata-builder/builders/section-builders/mailing-list-section-builder.ts +3 -2
  456. package/src/metadata-builder/builders/section-builders/profile-section-builder.ts +7 -7
  457. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +5 -3
  458. package/src/metadata-builder/builders/section-builders/social-links-builder.test.ts +2 -2
  459. package/src/metadata-builder/builders/section-builders/social-links-builder.ts +21 -10
  460. package/src/metadata-builder/builders/section-builders/support-me-builder.test.ts +10 -0
  461. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +3 -2
  462. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +111 -61
  463. package/src/metadata-builder/builders/section-field-maps.ts +1 -0
  464. package/src/metadata-builder/builders/section-field-utils.test.ts +545 -2
  465. package/src/metadata-builder/builders/section-field-utils.ts +202 -69
  466. package/src/metadata-builder/builders/section-rules/global-styles.rules.ts +38 -13
  467. package/src/metadata-builder/builders/section-rules/social-links.rules.ts +5 -0
  468. package/src/metadata-builder/helpers/rte.helpers.test.ts +101 -0
  469. package/src/metadata-builder/helpers/rte.helpers.ts +129 -43
  470. package/src/metadata-builder/metadata-executor.test.ts +167 -0
  471. package/src/metadata-builder/metadata-executor.ts +34 -1
  472. package/src/metadata-builder/models.ts +2 -0
  473. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +23 -7
  474. package/src/metadata-builder/parsers/section-tool-arguments.ts +21 -1
  475. package/src/metadata-builder/sanitizers/__tests__/section-args-validator.test.ts +215 -0
  476. package/src/metadata-builder/sanitizers/rte-schema-check.ts +1 -1
  477. package/src/metadata-builder/sanitizers/sanitizer-rules.ts +4 -8
  478. package/src/metadata-builder/sanitizers/section-args-validator.ts +136 -53
  479. package/src/shared/color-palette/section-color-matches.test.ts +15 -11
  480. package/src/shared/color-palette/section-color-matches.ts +9 -28
  481. package/src/shared/color-strategies/color-strategies.ts +35 -0
  482. package/src/shared/color-strategies/rte-color.test.ts +1 -1
  483. package/src/shared/color-strategies/rte-color.ts +19 -0
  484. package/src/shared/color-strategies/rte-field-pairs.test.ts +26 -5
  485. package/src/shared/color-strategies/rte-field-pairs.ts +33 -0
  486. package/src/shared/content-settings/components/color-matches-panel/ColorMatchesPanel.test.ts +43 -66
  487. package/src/shared/content-settings/components/color-matches-panel/ColorMatchesPanel.vue +7 -1
  488. package/src/shared/content-settings/components/content-settings/ContentSettings.mixin.ts +1 -48
  489. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +33 -7
  490. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.test.ts +128 -1
  491. package/src/shared/content-settings/components/draggable-item-list/DraggableItemList.vue +1 -0
  492. package/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.ts +0 -6
  493. package/src/shared/content-settings/components/logo-selection-field/LogoSelection.vue +3 -0
  494. package/src/shared/content-settings/components/logo-size-field/LogoSizeSlider.vue +3 -0
  495. package/src/shared/content-settings/components/logo-size-field/LogoSizeSliderField.vue +1 -0
  496. package/src/shared/content-settings/components/testimonial-items-field/TestimonialItemsField.vue +1 -0
  497. package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.test.ts +56 -3
  498. package/src/shared/content-settings/components/text-items-field/TextItemsField.mixin.ts +11 -4
  499. package/src/shared/content-settings/components/text-style-field/TextStyleField.mixin.test.ts +335 -25
  500. package/src/shared/content-settings/components/text-style-field/TextStyleField.mixin.ts +248 -70
  501. package/src/shared/content-settings/components/text-style-field/TextStyleField.vue +113 -25
  502. package/src/shared/content-settings/components/text-style-field/i18n/text-style-field.de-DE.json +17 -10
  503. package/src/shared/content-settings/components/text-style-field/i18n/text-style-field.es-ES.json +17 -10
  504. package/src/shared/content-settings/components/text-style-field/i18n/text-style-field.fr-CA.json +17 -10
  505. package/src/shared/content-settings/components/text-style-field/i18n/text-style-field.fr-FR.json +17 -10
  506. package/src/shared/content-settings/components/text-style-field/i18n/text-style-field.json +8 -1
  507. package/src/shared/content-settings/components/text-style-field/i18n/text-style-field.pt-BR.json +17 -10
  508. package/src/shared/content-settings/components/text-style-field/i18n/text-style-field.pt-PT.json +17 -10
  509. package/src/shared/content-settings/components/text-style-role-dropdown/TextStyleRoleDropdown.test.ts +141 -0
  510. package/src/shared/content-settings/components/text-style-role-dropdown/TextStyleRoleDropdown.vue +136 -0
  511. package/src/shared/content-settings/store/content-settings-store.test.ts +491 -6
  512. package/src/shared/content-settings/store/content-settings-store.ts +159 -17
  513. package/src/shared/logo-settings/components/EditLogoSettings.vue +1 -0
  514. package/src/shared/logo-settings/components/LogoVariations.vue +1 -0
  515. package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.vue +1 -0
  516. package/src/shared/navigation-style-settings/store/index.test.ts +295 -0
  517. package/src/shared/navigation-style-settings/store/index.ts +65 -15
  518. package/src/shared/page-settings/components/header-settings-panel/HeaderSettingsPanel.vue +2 -0
  519. package/src/shared/page-settings/components/page-settings/PageElement.vue +1 -0
  520. package/src/shared/page-settings/components/page-settings-section-styles/HeaderButtonSettings.test.ts +44 -4
  521. package/src/shared/page-settings/components/page-settings-section-styles/HeaderButtonSettings.vue +13 -4
  522. package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.navigation.test.ts +89 -0
  523. package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.text-style.test.ts +91 -0
  524. package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.ts +14 -2
  525. package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.vue +2 -0
  526. package/src/shared/page-settings/store/page-settings-store.text-style.test.ts +84 -0
  527. package/src/shared/page-settings/store/page-settings-store.ts +30 -1
  528. package/src/shared/rte-extensions/AutoLink.test.ts +98 -0
  529. package/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/AutoLink.ts +87 -58
  530. package/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/MaxLength.ts +4 -1
  531. package/src/shared/rte-extensions/TextEffects.ts +151 -0
  532. package/src/shared/rte-extensions/ViewDomParser.ts +27 -0
  533. package/src/{brand-page-maker/composables/rich-text-editor/getRteExtensions.ts → shared/rte-extensions.util.ts} +66 -9
  534. package/src/shared/rte-link.util.ts +2 -0
  535. package/src/shared/rte-render.util.ts +385 -0
  536. package/src/shared/rte-shape.util.ts +29 -0
  537. package/src/shared/side-bar/store/side-bar-store.test.ts +27 -1
  538. package/src/shared/side-bar/store/side-bar-store.ts +11 -0
  539. package/src/shared/style-settings/components/StyleSettings.mixin.ts +7 -3
  540. package/src/shared/style-settings/components/StyleSettings.test.ts +116 -0
  541. package/src/shared/style-settings/components/StyleSettings.vue +75 -25
  542. package/src/shared/style-settings/components/i18n/style-settings.de-DE.json +32 -20
  543. package/src/shared/style-settings/components/i18n/style-settings.es-ES.json +32 -20
  544. package/src/shared/style-settings/components/i18n/style-settings.fr-CA.json +32 -20
  545. package/src/shared/style-settings/components/i18n/style-settings.fr-FR.json +32 -20
  546. package/src/shared/style-settings/components/i18n/style-settings.json +13 -3
  547. package/src/shared/style-settings/components/i18n/style-settings.pt-BR.json +32 -20
  548. package/src/shared/style-settings/components/i18n/style-settings.pt-PT.json +32 -20
  549. package/src/shared/style-settings/components/text-style-options-settings-with-state/TextStyleOptionsSettingsWithState.vue +14 -34
  550. package/src/shared/style-settings/store/style-settings-store.ts +3 -49
  551. package/src/shared/style-settings/text-style-size-order.test.ts +76 -0
  552. package/src/shared/style-settings/text-style-size-order.ts +28 -0
  553. package/src/shared/template-settings/components/TemplatesSettings.vue +4 -0
  554. package/src/shared/text-role-typography.util.ts +277 -0
  555. package/src/utils/minify-brand-page-metadata.test.ts +102 -7
  556. package/src/utils/minify-brand-page-metadata.ts +62 -8
  557. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/AutoLink.d.ts +0 -2
  558. package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.d.ts +0 -28
  559. package/lib/src/brand-page-maker/utils/rte-render.util.d.ts +0 -63
  560. package/lib/src/brand-page-maker/utils/rte-render.util.js +0 -106
  561. package/lib/src/brand-page-maker/utils/rte-shape.d.ts +0 -18
  562. package/lib/src/brand-page-maker/utils/rte-shape.js +0 -21
  563. package/lib/src/metadata-builder/builders/global-font-propagation.d.ts +0 -41
  564. package/lib/src/metadata-builder/builders/global-font-propagation.js +0 -146
  565. package/src/brand-page-maker/utils/rte-render.util.ts +0 -179
  566. package/src/brand-page-maker/utils/rte-shape.ts +0 -29
  567. package/src/metadata-builder/builders/global-font-propagation.test.ts +0 -401
  568. package/src/metadata-builder/builders/global-font-propagation.ts +0 -238
  569. package/src/shared/style-settings/components/StyleSettingsSection.vue +0 -8
  570. /package/lib/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/FontFamily.d.ts +0 -0
  571. /package/lib/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/FontFamily.js +0 -0
  572. /package/lib/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/LetterSpacing.d.ts +0 -0
  573. /package/lib/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/LetterSpacing.js +0 -0
  574. /package/lib/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/MaxLength.d.ts +0 -0
  575. /package/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/FontFamily.ts +0 -0
  576. /package/src/{brand-page-maker/composables/rich-text-editor/extensions → shared/rte-extensions}/LetterSpacing.ts +0 -0
@@ -1,41 +1,325 @@
1
1
  import type { Editor } from '@tiptap/vue-3';
2
- import { skipStyleDetection } from '@brand-page-maker/utils';
2
+ import type { Mark } from 'prosemirror-model';
3
+ import {
4
+ skipStyleDetection,
5
+ roleVarOrLiteral,
6
+ isRoleVarRef,
7
+ restoreLinkUnderlines,
8
+ } from '@brand-page-maker/utils';
9
+ import {
10
+ composeTextDecoration,
11
+ roleVarRef,
12
+ textDecorationClearsRole,
13
+ textDecorationFlags,
14
+ } from '@shared/text-role-typography.util';
15
+ import type { TextStyleDeclaration } from '@brand-page';
16
+
17
+ // text-decoration is a single, combinable property (underline + line-through). Compose/decompose the
18
+ // two flags in a stable order so a role-equality compare (roleVarOrLiteral) is deterministic.
19
+ const DECORATION_FLAGS = ['underline', 'line-through', 'overline'] as const;
20
+
21
+ const isBoldWeight = (value?: string): boolean =>
22
+ value === 'bold' || (value != null && Number.parseInt(value, 10) >= 600);
23
+
24
+ const decorationSet = (value?: string): Set<string> =>
25
+ new Set((value ?? '').split(/\s+/).filter((flag) => flag && flag !== 'none'));
26
+
27
+ const composeDecoration = (flags: Set<string>): string =>
28
+ DECORATION_FLAGS.filter((flag) => flags.has(flag)).join(' ') || 'none';
29
+
30
+ /**
31
+ * BP-5969 (Phase A) — bold / italic / underline / strike are written as `textStyle` mark ATTRIBUTES
32
+ * (font-weight / font-style / text-decoration), not Tiptap semantic marks, so they sit in the same
33
+ * var-or-literal system as family/size: a pick equal to the role stores the role var (follows role
34
+ * edits), else a literal override. Each toggle ALSO unsets the legacy semantic mark, so a legacy
35
+ * `<strong>`/`<u>` run becomes attr-driven the moment the user toggles it (migrate-on-edit). Reading
36
+ * still honours both forms (see the active-state resolution in `selectionUpdate`), and the semantic
37
+ * extensions remain registered for rendering un-migrated docs + paste (Phase B removes them).
38
+ */
39
+ export const createFormattingActions = (
40
+ editor: Editor,
41
+ // The linked role's style (canonical-linked block), else undefined (legacy/unlinked → literals).
42
+ getRoleStyle: () => TextStyleDeclaration | undefined = () => undefined,
43
+ // Render fill gives an absent run this whole-block style before the role, so toolbar state and
44
+ // mutations must use the same source instead of treating the run as role-only.
45
+ getBlockStyle: () => TextStyleDeclaration | undefined = () => undefined,
46
+ ) => {
47
+ // An absent attr receives the block fill, but an explicit role reference keeps following the role.
48
+ const effectiveValue = (attr: string, mark: unknown): string | undefined => {
49
+ if (typeof mark === 'string' && mark !== '') {
50
+ if (!isRoleVarRef(mark)) return mark;
51
+
52
+ return (getRoleStyle() as Record<string, string> | undefined)?.[attr];
53
+ }
54
+
55
+ return (
56
+ (getBlockStyle() as Record<string, string> | undefined)?.[attr] ||
57
+ (getRoleStyle() as Record<string, string> | undefined)?.[attr]
58
+ );
59
+ };
60
+
61
+ const effective = (attr: string): string | undefined =>
62
+ effectiveValue(attr, editor.getAttributes('textStyle')?.[attr]);
63
+
64
+ // A run is bold/italic/underlined via EITHER the new attr OR a legacy semantic mark — check both.
65
+ const boldActive = () =>
66
+ editor.isActive('bold') || isBoldWeight(effective('fontWeight'));
67
+ const italicActive = () =>
68
+ editor.isActive('italic') || effective('fontStyle') === 'italic';
69
+
70
+ // The text runs a decoration toggle acts on: the cursor's marks when nothing is selected (stored
71
+ // marks win, as typing applies them), else each text node clipped to the selection.
72
+ const selectedRuns = (): {
73
+ from: number;
74
+ to: number;
75
+ marks: readonly Mark[];
76
+ }[] => {
77
+ const { selection, storedMarks, doc } = editor.state;
78
+
79
+ if (selection.empty) {
80
+ return [
81
+ {
82
+ from: selection.from,
83
+ to: selection.to,
84
+ marks: storedMarks ?? selection.$from.marks(),
85
+ },
86
+ ];
87
+ }
88
+
89
+ const runs: { from: number; to: number; marks: readonly Mark[] }[] = [];
90
+
91
+ selection.ranges.forEach(({ $from, $to }) => {
92
+ doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
93
+ if (!node.isText) return;
94
+
95
+ runs.push({
96
+ from: Math.max(pos, $from.pos),
97
+ to: Math.min(pos + node.nodeSize, $to.pos),
98
+ marks: node.marks,
99
+ });
100
+ });
101
+ });
102
+
103
+ return runs;
104
+ };
105
+
106
+ const runDecoration = (marks: readonly Mark[]): Set<string> => {
107
+ const attrs = marks.find((mark) => mark.type.name === 'textStyle')?.attrs;
108
+ const markValue = attrs?.textDecoration as string | undefined;
109
+ const localValue =
110
+ markValue && !isRoleVarRef(markValue)
111
+ ? markValue
112
+ : getBlockStyle()?.textDecoration;
113
+ const flags = decorationSet(
114
+ composeTextDecoration(
115
+ getRoleStyle()?.textDecoration,
116
+ localValue,
117
+ textDecorationClearsRole(
118
+ markValue,
119
+ attrs?.textDecorationReset === true,
120
+ ),
121
+ ),
122
+ );
123
+
124
+ if (marks.some((mark) => mark.type.name === 'underline')) {
125
+ flags.add('underline');
126
+ }
127
+
128
+ if (marks.some((mark) => mark.type.name === 'strike')) {
129
+ flags.add('line-through');
130
+ }
131
+
132
+ return flags;
133
+ };
134
+
135
+ // Active only when every selected run has the flag, so toggling a mixed selection turns it on.
136
+ const decorationActive = (flag: string): boolean => {
137
+ const runs = selectedRuns();
138
+
139
+ return (
140
+ runs.length > 0 &&
141
+ runs.every(({ marks }) => runDecoration(marks).has(flag))
142
+ );
143
+ };
144
+
145
+ // text-decoration holds both flags in one value, so each run gets its own composed value: one
146
+ // uniform write would drop the other flag from runs that carry it.
147
+ const toggleDecoration = (flag: string): void => {
148
+ const turnOn = !decorationActive(flag);
149
+ const runs = selectedRuns();
150
+ const decorationFor = (
151
+ marks: readonly Mark[],
152
+ ): { textDecoration: string; textDecorationReset: true | null } => {
153
+ const current = runDecoration(marks);
154
+
155
+ if (turnOn) current.add(flag);
156
+ else current.delete(flag);
157
+
158
+ const fallback = decorationSet(
159
+ composeTextDecoration(
160
+ getRoleStyle()?.textDecoration,
161
+ getBlockStyle()?.textDecoration,
162
+ ),
163
+ );
164
+ const roleFlags = textDecorationFlags(getRoleStyle()?.textDecoration);
165
+
166
+ // The role ref lets render-fill continue applying a whole-block override. Keep it only when
167
+ // the toggle returns to that exact fallback; every other literal must describe the complete
168
+ // desired result because it deliberately bypasses the block fill.
169
+ if (
170
+ current.size === fallback.size &&
171
+ [...current].every((currentFlag) => fallback.has(currentFlag))
172
+ ) {
173
+ return getRoleStyle()
174
+ ? {
175
+ textDecoration: roleVarRef('textDecoration')!,
176
+ textDecorationReset: null,
177
+ }
178
+ : {
179
+ textDecoration: composeDecoration(current),
180
+ textDecorationReset: null,
181
+ };
182
+ }
183
+
184
+ if ([...roleFlags].some((roleFlag) => !current.has(roleFlag))) {
185
+ return {
186
+ textDecoration: composeDecoration(current),
187
+ textDecorationReset: true,
188
+ };
189
+ }
190
+
191
+ return {
192
+ textDecoration: composeDecoration(current),
193
+ textDecorationReset: null,
194
+ };
195
+ };
196
+
197
+ // Unset both legacy decoration marks so the composed attr is the single source of truth.
198
+ if (editor.state.selection.empty) {
199
+ editor
200
+ .chain()
201
+ .focus()
202
+ .unsetUnderline()
203
+ .unsetStrike()
204
+ .setMark('textStyle', decorationFor(runs[0].marks))
205
+ .run();
206
+
207
+ return;
208
+ }
209
+
210
+ editor
211
+ .chain()
212
+ .focus()
213
+ .command(({ tr }) => {
214
+ const { textStyle, underline, strike } = tr.doc.type.schema.marks;
215
+
216
+ runs.forEach(({ from, to, marks }) => {
217
+ tr.removeMark(from, to, underline)
218
+ .removeMark(from, to, strike)
219
+ .addMark(
220
+ from,
221
+ to,
222
+ textStyle.create({
223
+ ...marks.find((mark) => mark.type === textStyle)?.attrs,
224
+ ...decorationFor(marks),
225
+ }),
226
+ );
227
+ });
228
+
229
+ return true;
230
+ })
231
+ .run();
232
+ };
3
233
 
4
- export const createFormattingActions = (editor: Editor) => {
5
234
  return {
6
- toggleBold: () => editor.chain().focus().toggleBold().run(),
7
- toggleItalic: () => editor.chain().focus().toggleItalic().run(),
8
- toggleUnderline: () => editor.chain().focus().toggleUnderline().run(),
9
- toggleStrike: () => editor.chain().focus().toggleStrike().run(),
10
- // Colour is an element-first override that must NOT detach the element from its
11
- // global text style (unlike typography edits, which do). `skipStyleDetection`
12
- // marks the transaction so the detach handler leaves `textStyleType` intact —
13
- // same reason `toggleTextColorAll` below skips it.
235
+ toggleBold: () =>
236
+ editor
237
+ .chain()
238
+ .focus()
239
+ .unsetBold()
240
+ .setMark('textStyle', {
241
+ fontWeight: roleVarOrLiteral(
242
+ 'fontWeight',
243
+ boldActive() ? 'normal' : 'bold',
244
+ getRoleStyle(),
245
+ ),
246
+ })
247
+ .run(),
248
+ toggleItalic: () =>
249
+ editor
250
+ .chain()
251
+ .focus()
252
+ .unsetItalic()
253
+ .setMark('textStyle', {
254
+ fontStyle: roleVarOrLiteral(
255
+ 'fontStyle',
256
+ italicActive() ? 'normal' : 'italic',
257
+ getRoleStyle(),
258
+ ),
259
+ })
260
+ .run(),
261
+ toggleUnderline: () => toggleDecoration('underline'),
262
+ toggleStrike: () => toggleDecoration('line-through'),
263
+ // Colour is an element-first override that must NOT detach the element from its global text style
264
+ // (unlike typography edits, which do). `skipStyleDetection` marks the transaction so the detach
265
+ // handler leaves `textStyleType` intact — same reason `toggleTextColorAll` below skips it.
14
266
  toggleTextColor: (color: string) =>
15
267
  skipStyleDetection(
16
268
  editor.chain().focus().setMark('textStyle', { color }),
17
269
  ).run(),
18
270
  toggleBackgroundColor: (color: string) =>
19
271
  editor.chain().focus().setHighlight({ color }).run(),
272
+ // *All (role application): write the role var ref to every run so it follows the container class,
273
+ // clearing any legacy semantic mark that would otherwise pin it.
20
274
  toggleBoldAll: () => {
21
- skipStyleDetection(editor.chain().selectAll().toggleBold()).run();
275
+ skipStyleDetection(
276
+ editor
277
+ .chain()
278
+ .selectAll()
279
+ .unsetBold()
280
+ .setMark('textStyle', { fontWeight: roleVarRef('fontWeight') }),
281
+ ).run();
22
282
  },
23
283
  toggleItalicAll: () => {
24
- skipStyleDetection(editor.chain().selectAll().toggleItalic()).run();
284
+ skipStyleDetection(
285
+ editor
286
+ .chain()
287
+ .selectAll()
288
+ .unsetItalic()
289
+ .setMark('textStyle', { fontStyle: roleVarRef('fontStyle') }),
290
+ ).run();
25
291
  },
26
292
  toggleUnderlineAll: () => {
27
- skipStyleDetection(editor.chain().selectAll().toggleUnderline()).run();
293
+ skipStyleDetection(
294
+ editor
295
+ .chain()
296
+ .selectAll()
297
+ .unsetUnderline()
298
+ .unsetStrike()
299
+ .setTextDecoration(roleVarRef('textDecoration')!)
300
+ .command(restoreLinkUnderlines(editor)),
301
+ ).run();
28
302
  },
29
303
  toggleStrikeAll: () => {
30
- skipStyleDetection(editor.chain().selectAll().toggleStrike()).run();
304
+ skipStyleDetection(
305
+ editor
306
+ .chain()
307
+ .selectAll()
308
+ .unsetUnderline()
309
+ .unsetStrike()
310
+ .setTextDecoration(roleVarRef('textDecoration')!)
311
+ .command(restoreLinkUnderlines(editor)),
312
+ ).run();
31
313
  },
32
314
  toggleTextColorAll: (color: string) => {
33
315
  skipStyleDetection(
34
316
  editor.chain().selectAll().setMark('textStyle', { color }),
35
317
  ).run();
36
318
  },
37
- unsetAllMarks: () => {
38
- skipStyleDetection(editor.chain().selectAll().unsetAllMarks()).run();
39
- },
319
+ // Effective active-state (attr OR legacy mark) for the toolbar buttons.
320
+ boldActive,
321
+ italicActive,
322
+ underlineActive: () => decorationActive('underline'),
323
+ strikeActive: () => decorationActive('line-through'),
40
324
  };
41
325
  };
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, afterEach } from 'vitest';
2
2
  import { Editor } from '@tiptap/vue-3';
3
- import { getRteExtensions } from './getRteExtensions';
3
+ import { getRteExtensions } from '@shared/rte-extensions.util';
4
4
  import { useHyperlink } from './hyperlink';
5
5
  import { DEFAULT_LINK_COLOR } from '@src/constants/rich-text-editor.constants';
6
6
 
@@ -79,3 +79,84 @@ describe('useHyperlink — applyHyperlinkToEditor retains styling, overrides col
79
79
  expect(markTypes(run)).toContain('link');
80
80
  });
81
81
  });
82
+
83
+ // Each test loads a stored link into a fresh editor, as after a reload: nothing from the session
84
+ // that made the link is left to restore from.
85
+ describe('useHyperlink — removeHyperlink after a reload', () => {
86
+ let editors: Editor[] = [];
87
+
88
+ afterEach(() => {
89
+ editors.forEach((e) => e.destroy());
90
+ editors = [];
91
+ });
92
+
93
+ const linkRun = (color: string) => ({
94
+ type: 'text',
95
+ text: 'guide',
96
+ marks: [
97
+ { type: 'link', attrs: { href: 'https://example.com' } },
98
+ { type: 'underline' },
99
+ { type: 'textStyle', attrs: { color } },
100
+ ],
101
+ });
102
+ const textRun = (text: string, color?: string) => ({
103
+ type: 'text',
104
+ text,
105
+ ...(color ? { marks: [{ type: 'textStyle', attrs: { color } }] } : {}),
106
+ });
107
+
108
+ const unlink = (runs: object[]) => {
109
+ const editor = new Editor({
110
+ extensions: getRteExtensions({ restrictStarterKit: true }),
111
+ content: { type: 'doc', content: [{ type: 'paragraph', content: runs }] },
112
+ });
113
+
114
+ editors.push(editor);
115
+
116
+ const text = editor.state.doc.textContent;
117
+ const from = text.indexOf('guide') + 1;
118
+
119
+ editor.commands.setTextSelection({ from, to: from + 'guide'.length });
120
+ useHyperlink(editor).removeHyperlink();
121
+
122
+ let marks: { type: string; color?: unknown }[] = [];
123
+
124
+ editor.state.doc.descendants((node, pos) => {
125
+ if (node.isText && pos <= from && from < pos + node.nodeSize)
126
+ marks = node.marks.map((mark) => ({
127
+ type: mark.type.name,
128
+ color: mark.attrs.color,
129
+ }));
130
+ });
131
+
132
+ return marks;
133
+ };
134
+
135
+ it('gives link-blue text the colour of the text before it', () => {
136
+ expect(
137
+ unlink([
138
+ textRun('Read our ', '#111111'),
139
+ linkRun(DEFAULT_LINK_COLOR),
140
+ textRun(' today', '#222222'),
141
+ ]),
142
+ ).toEqual([{ type: 'textStyle', color: '#111111' }]);
143
+ });
144
+
145
+ it('uses the text after the link when nothing comes before it', () => {
146
+ expect(
147
+ unlink([linkRun(DEFAULT_LINK_COLOR), textRun(' today', '#222222')]),
148
+ ).toEqual([{ type: 'textStyle', color: '#222222' }]);
149
+ });
150
+
151
+ it('leaves no colour when the text around the link has none', () => {
152
+ expect(unlink([textRun('Read our '), linkRun(DEFAULT_LINK_COLOR)])).toEqual(
153
+ [],
154
+ );
155
+ });
156
+
157
+ it('keeps a colour the user picked for the link', () => {
158
+ expect(
159
+ unlink([textRun('Read our ', '#111111'), linkRun('#ff0000')]),
160
+ ).toEqual([{ type: 'textStyle', color: '#ff0000' }]);
161
+ });
162
+ });
@@ -2,14 +2,7 @@ import { ref, computed } from 'vue';
2
2
  import type { Editor } from '@tiptap/vue-3';
3
3
  import { DEFAULT_LINK_COLOR } from '@src/constants/rich-text-editor.constants';
4
4
 
5
- // Store original colors before applying hyperlinks
6
- const originalColors = new WeakMap<any, string | null>();
7
-
8
- export const isHrefPageId = (href: string) => {
9
- if (!href) return false;
10
-
11
- return href.startsWith('pageid:');
12
- };
5
+ export { isHrefPageId } from '@shared/rte-link.util';
13
6
 
14
7
  export const getPageIdFromHref = (href: string) => {
15
8
  if (!href) return href;
@@ -26,29 +19,25 @@ export const getHrefForPage = (pageId: string) => {
26
19
  export const useHyperlink = (editor: Editor) => {
27
20
  const hyperlinkUrl = ref('');
28
21
 
29
- const storeOriginalColor = () => {
30
- const currentTextStyle = editor.getAttributes('textStyle');
31
- const originalColor = currentTextStyle.color || null;
22
+ // Nothing stores the colour text had before it became a link, so on unlink the nearest
23
+ // unlinked text stands in: the last run before the link, else the first after it.
24
+ const surroundingColor = (from: number, to: number): string | null => {
25
+ let before: string | null | undefined;
26
+ let after: string | null | undefined;
32
27
 
33
- // Store the original color using the editor instance as key
34
- originalColors.set(editor, originalColor);
28
+ editor.state.doc.descendants((node, pos) => {
29
+ if (!node.isText || node.marks.some((mark) => mark.type.name === 'link'))
30
+ return;
35
31
 
36
- return originalColor;
37
- };
38
-
39
- const restoreOriginalColor = () => {
40
- const originalColor = originalColors.get(editor);
32
+ const color =
33
+ node.marks.find((mark) => mark.type.name === 'textStyle')?.attrs
34
+ .color || null;
41
35
 
42
- if (originalColor) {
43
- // Restore the original color
44
- editor.chain().focus().setColor(originalColor).run();
45
- } else {
46
- // No original color, remove color completely
47
- editor.chain().focus().unsetColor().run();
48
- }
36
+ if (pos + node.nodeSize <= from) before = color;
37
+ else if (pos >= to && after === undefined) after = color;
38
+ });
49
39
 
50
- // Clean up stored color
51
- originalColors.delete(editor);
40
+ return before !== undefined ? before : (after ?? null);
52
41
  };
53
42
 
54
43
  const setHyperlinkUrl = (url: string) => {
@@ -56,8 +45,6 @@ export const useHyperlink = (editor: Editor) => {
56
45
 
57
46
  hyperlinkUrl.value = safeUrl;
58
47
 
59
- storeOriginalColor();
60
-
61
48
  // Set the link, apply blue color, and add underline by default
62
49
  editor
63
50
  .chain()
@@ -88,8 +75,6 @@ export const useHyperlink = (editor: Editor) => {
88
75
  text?: string,
89
76
  selection?: { from: number; to: number },
90
77
  ) => {
91
- storeOriginalColor();
92
-
93
78
  const from = selection?.from ?? editor.state.selection.from;
94
79
  const to = selection?.to ?? editor.state.selection.to;
95
80
  const currentText = editor.state.doc.textBetween(from, to);
@@ -134,11 +119,20 @@ export const useHyperlink = (editor: Editor) => {
134
119
  .run();
135
120
  };
136
121
 
122
+ // Unlinking drops the link's own look: its underline, and the link blue for the colour of the
123
+ // text around it. A colour the user picked for the link stays.
137
124
  const removeHyperlink = (clearUrlValue = false) => {
138
- editor.chain().focus().unsetLink().unsetUnderline().run();
125
+ const { from, to } = editor.state.selection;
126
+ const chain = editor.chain().focus().unsetLink().unsetUnderline();
127
+
128
+ if (editor.getAttributes('textStyle').color === DEFAULT_LINK_COLOR) {
129
+ const color = surroundingColor(from, to);
130
+
131
+ if (color) chain.setColor(color);
132
+ else chain.unsetColor();
133
+ }
139
134
 
140
- // Restore original color (or remove color if there was none)
141
- restoreOriginalColor();
135
+ chain.run();
142
136
 
143
137
  if (clearUrlValue) {
144
138
  setHyperlinkUrlValue('');
@@ -0,0 +1,80 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { Editor } from '@tiptap/vue-3';
3
+ import { getRteExtensions } from '@shared/rte-extensions.util';
4
+ import {
5
+ KeepMarksOnEmpty,
6
+ keepMarksOnEmptyPluginKey,
7
+ } from './keepMarksOnEmpty';
8
+
9
+ // When a field is fully cleared, ProseMirror drops every mark, so a clear-and-retype
10
+ // would lose the run's colour/typography. KeepMarksOnEmpty captures the cleared run's
11
+ // marks into its plugin state; the composable's first-type seed reads that state and
12
+ // re-applies them (that seed lives in useRichTextEditor and is covered by the live smoke).
13
+ // These tests pin the capture contract the seed depends on.
14
+ describe('KeepMarksOnEmpty', () => {
15
+ let editors: Editor[] = [];
16
+
17
+ const mk = (content = '') => {
18
+ const e = new Editor({
19
+ extensions: [
20
+ ...getRteExtensions({ restrictStarterKit: true }),
21
+ KeepMarksOnEmpty,
22
+ ],
23
+ content,
24
+ });
25
+
26
+ editors.push(e);
27
+
28
+ return e;
29
+ };
30
+
31
+ afterEach(() => {
32
+ editors.forEach((e) => e.destroy());
33
+ editors = [];
34
+ });
35
+
36
+ const clearAll = (e: Editor) => {
37
+ e.commands.selectAll();
38
+ e.commands.deleteSelection();
39
+ };
40
+
41
+ const captured = (e: Editor) => keepMarksOnEmptyPluginKey.getState(e.state);
42
+
43
+ const capturedColour = (e: Editor) =>
44
+ captured(e)?.find((m) => m.type.name === 'textStyle')?.attrs.color ?? null;
45
+
46
+ it("captures the cleared run's colour so the seed can restore it", () => {
47
+ const e = mk('<p><span style="color: rgb(255, 0, 0)">Hello</span></p>');
48
+
49
+ clearAll(e);
50
+
51
+ expect(capturedColour(e)).toBe('rgb(255, 0, 0)');
52
+ });
53
+
54
+ it('captures nothing for a genuinely fresh (never-populated) field', () => {
55
+ const e = mk('');
56
+
57
+ clearAll(e);
58
+
59
+ // Nothing to preserve → the first-type seed (not this plugin) owns the fresh case.
60
+ expect(captured(e)).toBeNull();
61
+ });
62
+
63
+ it('captures nothing when the cleared run had no marks', () => {
64
+ const e = mk('<p>plain</p>');
65
+
66
+ clearAll(e);
67
+
68
+ expect(captured(e)).toBeNull();
69
+ });
70
+
71
+ it('drops the capture once the field has content again', () => {
72
+ const e = mk('<p><span style="color: rgb(0, 128, 0)">Hi</span></p>');
73
+
74
+ clearAll(e);
75
+ expect(capturedColour(e)).toBe('rgb(0, 128, 0)');
76
+
77
+ e.commands.insertContent('X');
78
+ expect(captured(e)).toBeNull();
79
+ });
80
+ });
@@ -0,0 +1,75 @@
1
+ import { Extension } from '@tiptap/core';
2
+ import { Plugin, PluginKey, type EditorState } from 'prosemirror-state';
3
+ import type { Mark } from 'prosemirror-model';
4
+
5
+ /**
6
+ * Clearing all text in an RTE field drops every mark: ProseMirror empties
7
+ * `storedMarks` and the emptied block has no run left to inherit from, so the next
8
+ * character is typed bare. For an *edit* that is wrong — the user cleared text, not
9
+ * formatting — and post-ADR-0020 (the first-type seed no longer re-applies the role
10
+ * colour) it silently strips the run's colour on every clear-and-retype.
11
+ *
12
+ * This plugin remembers the marks of the run that was just cleared, in its own plugin
13
+ * *state* — deliberately not `storedMarks`, which a later plugin's appendTransaction can
14
+ * reset before the next keystroke (observed on the real Backspace path). The composable's
15
+ * first-type seed reads it via `keepMarksOnEmptyPluginKey.getState(state)` and re-applies
16
+ * those marks instead of the role/element defaults, so the previous style survives the
17
+ * retype. A genuinely fresh field never captures anything (state stays null), so the seed
18
+ * keeps owning that case.
19
+ */
20
+ export const keepMarksOnEmptyPluginKey = new PluginKey<readonly Mark[] | null>(
21
+ 'keepMarksOnEmpty',
22
+ );
23
+
24
+ // Marks of the first inline run — the leading style the retype should continue in
25
+ // (mixed-style runs collapse to the first, where the cursor lands after a clear).
26
+ const firstRunMarks = (state: EditorState): readonly Mark[] | null => {
27
+ let marks: readonly Mark[] | null = null;
28
+
29
+ state.doc.descendants((node) => {
30
+ if (marks) return false;
31
+
32
+ if (node.isText) {
33
+ marks = node.marks;
34
+
35
+ return false;
36
+ }
37
+
38
+ return true;
39
+ });
40
+
41
+ return marks;
42
+ };
43
+
44
+ export const KeepMarksOnEmpty = Extension.create({
45
+ name: 'keepMarksOnEmpty',
46
+
47
+ addProseMirrorPlugins() {
48
+ return [
49
+ new Plugin<readonly Mark[] | null>({
50
+ key: keepMarksOnEmptyPluginKey,
51
+ state: {
52
+ init: () => null,
53
+ apply(tr, value, oldState, newState) {
54
+ // Capture the leading run's marks at the moment the field is emptied.
55
+ if (
56
+ tr.docChanged &&
57
+ oldState.doc.textContent !== '' &&
58
+ newState.doc.textContent === ''
59
+ ) {
60
+ const marks = firstRunMarks(oldState);
61
+
62
+ return marks && marks.length > 0 ? marks : null;
63
+ }
64
+
65
+ // Drop the capture once the field has content again (the retype landed).
66
+ if (newState.doc.textContent !== '') return null;
67
+
68
+ // Field still empty, awaiting the retype — hold the capture.
69
+ return value;
70
+ },
71
+ },
72
+ }),
73
+ ];
74
+ },
75
+ });