@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
@@ -8,7 +8,13 @@ import {
8
8
  watch,
9
9
  } from 'vue';
10
10
  import { Editor } from '@tiptap/vue-3';
11
- import { getRteExtensions } from './getRteExtensions';
11
+ import { getRteExtensions } from '@shared/rte-extensions.util';
12
+ import {
13
+ KeepMarksOnEmpty,
14
+ keepMarksOnEmptyPluginKey,
15
+ } from './keepMarksOnEmpty';
16
+ import { RoleTypographyFill } from './extensions/RoleTypographyFill';
17
+ import { RteClipboard } from './extensions/RteClipboard';
12
18
  import type { StyleTextFontFamilyOption } from '@shared/style-settings/models';
13
19
  import { createFormattingActions } from './formatting';
14
20
  import { useTextAlign } from './textAlign';
@@ -18,8 +24,6 @@ import {
18
24
  buildInitialMarks,
19
25
  emToPx,
20
26
  isSameRteContent,
21
- isStyleChanged,
22
- pxToEm,
23
27
  sanitizeRteJson,
24
28
  skipStyleDetection,
25
29
  } from '@brand-page-maker/utils';
@@ -32,16 +36,28 @@ import {
32
36
  import type { ButtonGroupOption } from '@brand-page-maker/models/button-group.model';
33
37
  import { letterSpacingValueConvertor } from '@brand-page-maker/convertors';
34
38
  import { useUiContentSettingsStore } from '@shared/content-settings/store';
39
+ import { useUiStyleSettingsStore } from '@shared/style-settings/store';
35
40
  import { useHistoryStore } from '@brand-page-maker/stores/history/history-store';
36
41
  import { AllSelection } from 'prosemirror-state';
42
+ import type { EditorState } from 'prosemirror-state';
43
+ import type { Mark } from 'prosemirror-model';
44
+ import { useUiColorPaletteStore } from '@shared/color-palette/store';
37
45
  import { useHyperlink } from './hyperlink';
38
46
  import { useBubbleMenu } from './bubbleMenu';
39
47
  import {
40
48
  hasLogoColorVar,
41
- getRTEColor,
42
49
  getToolbarColorFromMark,
43
50
  resolveInitialToolbarTextColor,
51
+ hasRunTypographyOverride,
52
+ isCanonicalLinked,
53
+ isRoleVarRef,
54
+ mergePickedMarks,
55
+ normTypographyValue,
56
+ readBlockTypography,
57
+ relinkRoleEqualRunsChain,
58
+ relinkTypographyChain,
44
59
  } from '@brand-page-maker/utils/rich-text-editor.util';
60
+ import { composeTextDecoration } from '@shared/text-role-typography.util';
45
61
  import { useEventTracker, EVENTS } from '../useEventTracker';
46
62
 
47
63
  interface UseRichTextEditorReturn {
@@ -63,12 +79,14 @@ interface UseRichTextEditorReturn {
63
79
  }>;
64
80
  toggleFont: (font: string) => void;
65
81
  toggleFontSize: (size: string) => void;
66
- letterSpacingValue: ComputedRef<string>;
67
- lineHeightValue: ComputedRef<string>;
82
+ letterSpacingValue: WritableComputedRef<string>;
83
+ lineHeightValue: WritableComputedRef<string>;
68
84
  selectedFont: ComputedRef<string>;
69
85
  setInitialStyleSettings: (selectedTextStyle?: BrandPageCssStyle) => void;
70
- copyGlobalStyleSettings: (selectedTextStyle?: BrandPageCssStyle) => void;
71
- unsetAllMarks: () => void;
86
+ hasTypographyOverride: () => boolean;
87
+ canApplyTypographyToRole: () => boolean;
88
+ resetTypographyToRole: () => void;
89
+ applyTypographyToRole: () => void;
72
90
  handleHyperlink: (
73
91
  action: 'toggle' | 'apply' | 'remove',
74
92
  data?: { href?: string; text?: string },
@@ -100,6 +118,9 @@ interface EditorOptionType {
100
118
  value: string | StyleTextFontFamilyOption;
101
119
  }
102
120
 
121
+ // Outlasts the slowest debounced toolbar command (spacing, 300 ms). See undo-redo.md.
122
+ const USER_COMMAND_WINDOW_MS = 1000;
123
+
103
124
  export const useExternalRichTextEditor = (): {
104
125
  externalEditor: Editor;
105
126
  } => {
@@ -135,12 +156,18 @@ export const useRichTextEditor = (
135
156
  selectedFontSizePx: number;
136
157
  selectedTextColor: string;
137
158
  selectedBackgroundColor: string;
159
+ // BP-5969: bold/italic/underline/strike toolbar active-state, resolved from the effective
160
+ // textStyle attr OR a legacy semantic mark (set in selectionUpdate).
161
+ isBold: boolean;
162
+ isItalic: boolean;
163
+ isUnderline: boolean;
164
+ isStrike: boolean;
138
165
  },
139
166
  fontStyleDropdownRef: Ref<
140
167
  DropdownInstance | undefined,
141
168
  DropdownInstance | undefined
142
169
  >,
143
- // One entry of `textStyleOptions`; `undefined` when the selection is "none".
170
+ // The selected role's entry of `textStyleOptions`.
144
171
  selectedGlobalTextStyles: WritableComputedRef<
145
172
  TextStyleOption | undefined,
146
173
  string
@@ -153,17 +180,88 @@ export const useRichTextEditor = (
153
180
  selectedLinkTypeValue: Ref<HyperlinkType>,
154
181
  ): UseRichTextEditorReturn => {
155
182
  const historyStore = useHistoryStore();
183
+ const colorPaletteStore = useUiColorPaletteStore();
184
+
185
+ // A toolbar click moves focus off the editor before its command runs (Tiptap's `.focus()` restores
186
+ // it a frame later), so `isFocused` alone would file the user's edit as a mount write-back.
187
+ let userCommandUntil = 0;
188
+
189
+ const markUserCommand = (): void => {
190
+ userCommandUntil = Date.now() + USER_COMMAND_WINDOW_MS;
191
+ };
192
+
193
+ // The linked role's style for the var-or-literal write rule (BP-5969): a canonical-linked
194
+ // block resolves typography from its role, so a toolbar pick equal to the role is stored as
195
+ // the role var, not a literal. Undefined for legacy/unlinked blocks (they bake literals).
196
+ // Declared above the editor so RteClipboard's getContext (below) can close over it too.
197
+ const getRoleStyle = () =>
198
+ isCanonicalLinked(props.styles)
199
+ ? selectedGlobalTextStyles.value?.style
200
+ : undefined;
201
+
202
+ // The first-type seed a genuinely fresh field takes, or the previous run's marks when the
203
+ // field was just cleared (KeepMarksOnEmpty's capture) — shared by `beforeinput` below and
204
+ // RteClipboard's text-only paste into an empty field, so a paste matches typing.
205
+ const getFirstInputMarks = (state: EditorState): Mark[] => {
206
+ const clearedMarks = keepMarksOnEmptyPluginKey.getState(state);
207
+ const pickedMarks = state.storedMarks;
208
+
209
+ if (clearedMarks && clearedMarks.length > 0) {
210
+ return mergePickedMarks(clearedMarks, pickedMarks);
211
+ }
212
+
213
+ const globalStyle = selectedGlobalTextStyles.value?.style;
214
+ // Element-first colour: see the matching comment in `beforeinput` below.
215
+ const styleSource = globalStyle
216
+ ? { ...globalStyle, color: props.styles?.color }
217
+ : props.styles;
218
+
219
+ const marks = buildInitialMarks(
220
+ state,
221
+ styleSource,
222
+ !isCanonicalLinked(props.styles),
223
+ );
224
+
225
+ return mergePickedMarks(marks, pickedMarks);
226
+ };
156
227
 
157
228
  const editor = new Editor({
158
229
  content: model.value ?? '',
159
230
  editable: !props.readOnly,
160
- extensions: getRteExtensions({
161
- restrictStarterKit: true,
162
- editing: {
163
- placeholder: props.placeholder,
164
- maxLength: props.maxLength,
165
- },
166
- }),
231
+ extensions: [
232
+ ...getRteExtensions({
233
+ restrictStarterKit: true,
234
+ editing: {
235
+ placeholder: props.placeholder,
236
+ maxLength: props.maxLength,
237
+ },
238
+ }),
239
+ // Captures a cleared run's marks so the first-type seed below can restore the
240
+ // style on retype (rather than reseeding from the role).
241
+ KeepMarksOnEmpty,
242
+ RoleTypographyFill.configure({
243
+ getFillSource: () => {
244
+ const role = isCanonicalLinked(props.styles)
245
+ ? selectedGlobalTextStyles.value?.style
246
+ : undefined;
247
+
248
+ return role
249
+ ? {
250
+ role: role as unknown as Record<string, string>,
251
+ block: props.styles as unknown as Record<string, string>,
252
+ }
253
+ : undefined;
254
+ },
255
+ }),
256
+ RteClipboard.configure({
257
+ getContext: () => ({
258
+ role: getRoleStyle(),
259
+ block: props.styles,
260
+ colors: colorPaletteStore.colors,
261
+ }),
262
+ getFirstInputMarks,
263
+ }),
264
+ ],
167
265
  editorProps: {
168
266
  handleDOMEvents: {
169
267
  drop(view, event: DragEvent) {
@@ -173,109 +271,25 @@ export const useRichTextEditor = (
173
271
  },
174
272
  beforeinput(view, event) {
175
273
  const { state } = view;
176
- // On first insert into an empty doc, set storedMarks to global styles so all typed chars inherit them
177
274
 
178
275
  if (
179
- event.inputType === 'insertText' &&
180
- state.doc.textContent === ''
276
+ event.inputType !== 'insertText' ||
277
+ state.doc.textContent !== ''
181
278
  ) {
182
- const globalStyle = selectedGlobalTextStyles.value?.style;
183
- // Element-first colour: once the doc is emptied, freshly-typed text must
184
- // inherit a linked element's colour override (a creation-time contrast bake,
185
- // or a colour picked while linked), not the linked global colour. Typography
186
- // still comes from the global style (the element is linked), mirroring
187
- // resolveTextColor's colour-only cascade.
188
- const styleSource = globalStyle
189
- ? {
190
- ...globalStyle,
191
- color: props.styles?.color || globalStyle.color,
192
- }
193
- : props.styles;
194
-
195
- const marks = buildInitialMarks(state, styleSource);
196
-
197
- const chain = editor.chain();
198
-
199
- if (props.styles.textAlign)
200
- chain.setTextAlign(props.styles.textAlign);
201
-
202
- skipStyleDetection(chain, marks).run();
203
- }
204
-
205
- return false;
206
- },
207
- copy(view, event: ClipboardEvent) {
208
- const selection = view.state.selection;
209
-
210
- if (selection.empty) return;
211
-
212
- const data = event.clipboardData;
213
-
214
- const styleData = JSON.stringify(props.styles);
215
-
216
- const slice = selection.content(),
217
- { dom, text } = view.serializeForClipboard(slice);
218
-
219
- if (data) {
220
- event.preventDefault();
221
- data.clearData();
222
- data.setData('text/html', dom.innerHTML);
223
- data.setData('text/plain', text);
224
-
225
- if (styleData) data.setData('application/x-rte-styles', styleData);
279
+ return false;
226
280
  }
227
281
 
228
- return true;
229
- },
230
-
231
- paste(view, event) {
232
- // Unified paste handler: strip styles if no styleData, enforce maxLength, handle selection
233
- const styleData = event.clipboardData?.getData(
234
- 'application/x-rte-styles',
235
- );
236
- const pastedText = event.clipboardData?.getData('text/plain') || '';
237
- const { state, dispatch } = view;
238
- const { from, to } = state.selection;
239
-
240
- let tr = state.tr;
241
-
242
- if (styleData) {
243
- try {
244
- const pastedStyles = JSON.parse(styleData);
245
- const fontStyleChanges: TextStyleDeclaration = {
246
- color: pastedStyles.color,
247
- fontFamily: pastedStyles.fontFamily,
248
- fontSize: pastedStyles.fontSize,
249
- fontWeight: pastedStyles.fontWeight,
250
- fontStyle: pastedStyles.fontStyle,
251
- textDecoration: pastedStyles.textDecoration,
252
- lineHeight: pastedStyles.lineHeight,
253
- letterSpacing: pastedStyles.letterSpacing,
254
- textAlign: pastedStyles.textAlign,
255
- backgroundColor: pastedStyles.backgroundColor,
256
- textStyleType: undefined,
257
- };
258
-
259
- syncContentSettings(fontStyleChanges);
260
-
261
- return false;
262
- } catch {
263
- // ignore malformed styleData
264
- }
265
- } else {
266
- event.preventDefault();
267
- tr.delete(from, to);
268
-
269
- const createdMarks = buildInitialMarks(state, props.styles);
270
-
271
- tr = tr.setStoredMarks(createdMarks).insertText(pastedText, from);
282
+ const chain = editor.chain();
272
283
 
273
- onSelectTextStyleType({ label: 'None' } as ButtonGroupOption);
274
- }
284
+ // textAlign is a block-node attr, not a mark, and a full clear resets it — so
285
+ // re-apply the element's alignment regardless of which seed `getFirstInputMarks`
286
+ // takes below (it's the one thing that survives a full clear).
287
+ if (props.styles.textAlign)
288
+ chain.setTextAlign(props.styles.textAlign);
275
289
 
276
- dispatch(tr);
290
+ skipStyleDetection(chain, getFirstInputMarks(state)).run();
277
291
 
278
- return true;
292
+ return false;
279
293
  },
280
294
  click(view, event) {
281
295
  if (isHyperlinkPopupOpen?.value) return false;
@@ -303,6 +317,7 @@ export const useRichTextEditor = (
303
317
  });
304
318
 
305
319
  const contentSettingStore = useUiContentSettingsStore();
320
+ const styleSettingsStore = useUiStyleSettingsStore();
306
321
  const eventTracker = useEventTracker();
307
322
 
308
323
  const {
@@ -312,13 +327,11 @@ export const useRichTextEditor = (
312
327
  toggleStrike,
313
328
  toggleTextColor,
314
329
  toggleBackgroundColor,
315
- toggleBoldAll,
316
- toggleItalicAll,
317
- toggleUnderlineAll,
318
- toggleStrikeAll,
319
- toggleTextColorAll,
320
- unsetAllMarks,
321
- } = createFormattingActions(editor);
330
+ boldActive,
331
+ italicActive,
332
+ underlineActive,
333
+ strikeActive,
334
+ } = createFormattingActions(editor, getRoleStyle, () => props.styles);
322
335
 
323
336
  const {
324
337
  currentAlignment,
@@ -327,22 +340,37 @@ export const useRichTextEditor = (
327
340
  keys: alignmentKeys,
328
341
  } = useTextAlign(editor);
329
342
 
330
- const {
331
- selectedFont,
332
- toggleFont,
333
- toggleFontSize,
334
- toggleFontAll,
335
- toggleFontSizeAll,
336
- } = useFont(editor, fontSettings, fontStyleDropdownRef);
343
+ const { selectedFont, toggleFont, toggleFontSize } = useFont(
344
+ editor,
345
+ fontSettings,
346
+ fontStyleDropdownRef,
347
+ getRoleStyle,
348
+ );
337
349
 
338
350
  const {
339
- letterSpacingValue,
340
- lineHeightValue,
351
+ letterSpacingValue: letterSpacingSlider,
352
+ lineHeightValue: lineHeightSlider,
341
353
  letterSpacing,
342
354
  lineHeight,
343
355
  setLetterSpaceAll,
344
356
  setLineHeightAll,
345
- } = useSpacing(editor);
357
+ } = useSpacing(editor, getRoleStyle);
358
+
359
+ const letterSpacingValue = computed<string>({
360
+ get: () => letterSpacingSlider.value,
361
+ set: (value) => {
362
+ markUserCommand();
363
+ letterSpacingSlider.value = value;
364
+ },
365
+ });
366
+
367
+ const lineHeightValue = computed<string>({
368
+ get: () => lineHeightSlider.value,
369
+ set: (value) => {
370
+ markUserCommand();
371
+ lineHeightSlider.value = value;
372
+ },
373
+ });
346
374
 
347
375
  const {
348
376
  hyperlinkValue,
@@ -410,121 +438,123 @@ export const useRichTextEditor = (
410
438
  },
411
439
  );
412
440
 
413
- editor.on('transaction', ({ transaction }) => {
414
- // Style detection only makes sense for user styling — an unfocused
415
- // editor's transactions are mount rehydration / programmatic setContent
416
- // churn, and clearing textStyleType from those would push a phantom
417
- // style write into the model (BP-5810 family).
418
- if (!editor.isFocused) {
419
- return;
420
- }
441
+ // BP-5969 (ADR-0019): the auto-detach that cleared `textStyleType` whenever the doc's
442
+ // style changed is gone — a style edit is now a per-run mark and the block keeps its
443
+ // role. (Was: `editor.on('transaction')` → `onSelectTextStyleType('None')`.)
444
+
445
+ // Refresh the B/I/U/S button state from the effective formatting (textStyle attr, resolved
446
+ // against the role, OR a legacy semantic mark). It runs on every transaction: a format picked with
447
+ // nothing selected only sets a stored mark, which fires neither `update` nor `selectionUpdate`.
448
+ const syncFormattingActiveState = () => {
449
+ fontSettings.isBold = boldActive();
450
+ fontSettings.isItalic = italicActive();
451
+ fontSettings.isUnderline = underlineActive();
452
+ fontSettings.isStrike = strikeActive();
453
+ };
421
454
 
422
- if (isStyleChanged(transaction) && props.styles.textStyleType) {
423
- onSelectTextStyleType({
424
- label: 'None',
425
- } as ButtonGroupOption);
426
- }
455
+ // Skipped while a history snapshot applies, as `selectionUpdate` is (BP-5506).
456
+ editor.on('transaction', () => {
457
+ if (historyStore.isApplyingSnapshot) return;
458
+
459
+ syncFormattingActiveState();
427
460
  });
428
461
 
462
+ // A role edit (Style tab, AI) reaches the editor without a transaction. An empty one repaints
463
+ // the B/I/U/S state and the box's role fill (`RoleTypographyFill`) without touching the doc.
464
+ watch(
465
+ () => selectedGlobalTextStyles.value?.style,
466
+ () => {
467
+ if (editor.isDestroyed) return;
468
+
469
+ editor.view.dispatch(editor.state.tr.setMeta('addToHistory', false));
470
+ },
471
+ { deep: true },
472
+ );
473
+
429
474
  // Update model when editor content changes. RTE content is stored as Tiptap
430
475
  // JSON (BP-5787). getJSON() is already in hand (the editor holds the doc) and
431
476
  // is sanitised natively — no getHTML(), no DOMPurify, no extra editor — so
432
477
  // the per-keystroke path allocates nothing heavy.
433
478
  editor.on('update', () => {
434
- // An update fired while the editor is unfocused is TipTap rewriting the
435
- // model back into its canonical form during mount rehydration or a
436
- // programmatic setContent — not a user edit. Tag the emit so the write
437
- // folds into the current undo head instead of pushing a phantom entry
438
- // (BP-5810). The flag travels WITH the value through the debounced
439
- // field → store chain and arms the history merge at the moment of the
440
- // store write — arming here at emit time would lapse before the
441
- // debounce fires. Genuine edits happen while focused and commit
442
- // normally.
443
- //
444
- // Known limitation: a programmatic toolbar command run on an editor the
445
- // user hasn't focused yet would also merge (losing that undo step). Today
446
- // the RTE only mounts inside an already-open settings drawer and the user
447
- // focuses it before formatting, so this doesn't occur — revisit if a
448
- // consumer ever drives toolbar actions on an unfocused editor.
479
+ // An update fired while the editor is unfocused and no toolbar command is in flight is
480
+ // TipTap rewriting the model into its canonical form during mount rehydration or a
481
+ // programmatic setContent — not a user edit. Tag the emit so the write folds into the
482
+ // current undo head instead of pushing a phantom entry (BP-5810). The flag travels WITH
483
+ // the value through the debounced field → store chain and arms the history merge at the
484
+ // moment of the store write — arming here at emit time would lapse before the debounce
485
+ // fires. Typing (focused) and toolbar commands (`markUserCommand`) commit normally.
449
486
  emit('update:modelValue', sanitizeRteJson(editor.getJSON()), {
450
- isHydrationWrite: !editor.isFocused,
487
+ isHydrationWrite: !editor.isFocused && Date.now() > userCommandUntil,
451
488
  });
452
489
  });
453
490
 
454
- editor.on('selectionUpdate', () => {
455
- // Skip while a history snapshot is being applied — `setContent` fires
456
- // selectionUpdate before the editor has fully rehydrated, which causes
457
- // toolbar buttons (alignment, letter-spacing) to flash through bogus
458
- // active states before settling. The editor emits a final selectionUpdate
459
- // after apply completes, which lands the correct values (BP-5506).
460
- if (historyStore.isApplyingSnapshot) return;
461
-
491
+ // The toolbar's read of the editor at the cursor: a mark holding a role var (or no mark) shows the
492
+ // linked role's value, a literal shows itself. Colours fall back to the given values when the run
493
+ // has none, so the mount-time seed can pass the element colour it resolved.
494
+ const syncToolbarFromEditor = ({
495
+ textColorFallback = '#000000',
496
+ backgroundColorFallback = 'transparent',
497
+ }: {
498
+ textColorFallback?: string;
499
+ backgroundColorFallback?: string;
500
+ } = {}): void => {
462
501
  const textStyleAttributes = editor.getAttributes('textStyle');
463
502
  const highlightAttributes = editor.getAttributes('highlight');
464
503
  const focusedAlignment = editor.getAttributes(
465
504
  editor.state.selection.$anchor.node().type.name,
466
505
  );
467
506
 
468
- const selection = editor.state.selection;
507
+ const roleStyle = getRoleStyle();
469
508
 
470
- if (selection instanceof AllSelection) {
471
- // to prevent selecting editor state
472
- // when user presses ctrl + a the beforeinput event doesn't get triggered
473
- editor.commands.setTextSelection({
474
- from: selection.from + 1,
475
- to: selection.to - 1,
476
- });
477
- }
509
+ const markFontFamily = textStyleAttributes?.fontFamily;
510
+ const displayFont =
511
+ markFontFamily && !isRoleVarRef(markFontFamily)
512
+ ? markFontFamily.replace(/^['"]|['"]$/g, '')
513
+ : (roleStyle?.fontFamily ?? 'Lato');
478
514
 
479
- // TODO: resolve the issue of missing style on text between empty lines
480
- // retainStyleOnEmptyLine(editor, selection);
515
+ selectedFont.value = displayFont;
516
+ fontSettings.selectedFont = displayFont;
481
517
 
482
- if (textStyleAttributes && textStyleAttributes.fontFamily) {
483
- const font = textStyleAttributes.fontFamily.replace(/^['"]|['"]$/g, '');
518
+ const markFontSize = textStyleAttributes?.fontSize;
519
+ const sizeSource =
520
+ markFontSize && !isRoleVarRef(markFontSize)
521
+ ? markFontSize
522
+ : (roleStyle?.fontSize ?? fontSettings.selectedFontSize);
484
523
 
485
- selectedFont.value = font;
486
- fontSettings.selectedFont = font;
487
- } else {
488
- selectedFont.value = 'Lato';
489
- fontSettings.selectedFont = 'Lato';
490
- }
524
+ fontSettings.selectedFontSizePx = Math.ceil(parseFloat(emToPx(sizeSource)));
491
525
 
492
- if (textStyleAttributes && textStyleAttributes.fontSize) {
493
- fontSettings.selectedFontSizePx = Math.ceil(
494
- parseFloat(emToPx(textStyleAttributes.fontSize)),
495
- );
496
- } else {
497
- fontSettings.selectedFontSizePx = Math.ceil(
498
- parseFloat(emToPx(fontSettings.selectedFontSize)),
499
- );
500
- }
501
-
502
- // Reflect the active mark colour on the toolbar. A palette reference is kept
503
- // verbatim (so ColorField can highlight the swatch); a literal is hex-normalised —
504
- // Color().hex() throws on a var(), so the split lives in getToolbarColorFromMark.
526
+ // A palette reference is kept verbatim (so ColorField can highlight the swatch); a literal is
527
+ // hex-normalised — Color().hex() throws on a var(), so the split lives in getToolbarColorFromMark.
505
528
  fontSettings.selectedTextColor = getToolbarColorFromMark(
506
529
  textStyleAttributes?.color,
507
- '#000000',
530
+ textColorFallback,
508
531
  );
509
532
 
510
533
  fontSettings.selectedBackgroundColor = getToolbarColorFromMark(
511
534
  highlightAttributes?.color,
512
- 'transparent',
535
+ backgroundColorFallback,
513
536
  );
514
537
 
515
- if (textStyleAttributes.lineHeight) {
516
- lineHeight.value = textStyleAttributes.lineHeight;
517
- } else {
518
- lineHeight.value = '1';
519
- }
538
+ syncFormattingActiveState();
520
539
 
521
- if (textStyleAttributes && textStyleAttributes.letterSpacing) {
522
- letterSpacing.value = letterSpacingValueConvertor.convert({
523
- letterSpacing: textStyleAttributes.letterSpacing,
524
- });
525
- } else {
526
- letterSpacing.value = '0';
527
- }
540
+ const markLineHeight = textStyleAttributes.lineHeight;
541
+
542
+ lineHeight.value =
543
+ markLineHeight && !isRoleVarRef(markLineHeight)
544
+ ? markLineHeight
545
+ : (roleStyle?.lineHeight ?? '1');
546
+
547
+ const markLetterSpacing = textStyleAttributes?.letterSpacing;
548
+ const letterSpacingSource =
549
+ markLetterSpacing && !isRoleVarRef(markLetterSpacing)
550
+ ? markLetterSpacing
551
+ : roleStyle?.letterSpacing;
552
+
553
+ letterSpacing.value = letterSpacingSource
554
+ ? letterSpacingValueConvertor.convert({
555
+ letterSpacing: letterSpacingSource,
556
+ })
557
+ : '0';
528
558
 
529
559
  if (focusedAlignment && focusedAlignment.textAlign) {
530
560
  const idx = alignmentKeys.indexOf(focusedAlignment.textAlign);
@@ -535,6 +565,31 @@ export const useRichTextEditor = (
535
565
  } else {
536
566
  currentAlignmentIndex.value = 0;
537
567
  }
568
+ };
569
+
570
+ editor.on('selectionUpdate', () => {
571
+ // Skip while a history snapshot is being applied — `setContent` fires
572
+ // selectionUpdate before the editor has fully rehydrated, which causes
573
+ // toolbar buttons (alignment, letter-spacing) to flash through bogus
574
+ // active states before settling. The editor emits a final selectionUpdate
575
+ // after apply completes, which lands the correct values (BP-5506).
576
+ if (historyStore.isApplyingSnapshot) return;
577
+
578
+ const selection = editor.state.selection;
579
+
580
+ if (selection instanceof AllSelection) {
581
+ // to prevent selecting editor state
582
+ // when user presses ctrl + a the beforeinput event doesn't get triggered
583
+ editor.commands.setTextSelection({
584
+ from: selection.from + 1,
585
+ to: selection.to - 1,
586
+ });
587
+ }
588
+
589
+ // TODO: resolve the issue of missing style on text between empty lines
590
+ // retainStyleOnEmptyLine(editor, selection);
591
+
592
+ syncToolbarFromEditor();
538
593
  });
539
594
 
540
595
  const setInitialStyleSettings = (selectedTextStyle?: BrandPageCssStyle) => {
@@ -546,6 +601,14 @@ export const useRichTextEditor = (
546
601
  );
547
602
 
548
603
  if (globalStyles) {
604
+ // A canonical-linked block inherits typography from the role CSS class and stores
605
+ // only its per-property OVERRIDES (BP-5969). Re-deriving the role's typography onto
606
+ // the element on every RTE mount clobbered those overrides — selecting a block
607
+ // reverted its title to the role font/size — so bake typography here only for
608
+ // legacy/unlinked blocks (which still carry the resolved style inline). Mirrors the
609
+ // isCanonicalLinked gate in buildInitialMarks / getUpdatedHTMLFromEditor.
610
+ const bakeTypography = !isCanonicalLinked(props.styles);
611
+
549
612
  // sync content settings style — a mount-time re-derivation, not a user
550
613
  // edit. Flag it so the store folds it into the current undo head: this
551
614
  // write lands ~100ms BEFORE the editor's canonical text write-back, and
@@ -555,38 +618,46 @@ export const useRichTextEditor = (
555
618
  id: props.rawId,
556
619
  name: props.name,
557
620
  value: {
558
- fontSize: globalStyles.fontSize,
559
- fontFamily: globalStyles.fontFamily,
560
- fontWeight: globalStyles.fontWeight,
561
- fontStyle: globalStyles.fontStyle,
562
- textDecoration: globalStyles.textDecoration,
563
- // Keep an element colour override (a creation-time contrast bake, or a
564
- // colour picked while linked) — only fall back to the global style's colour
565
- // when the element has none, mirroring `resolveTextColor`'s element-first
566
- // cascade. Blindly taking `globalStyles.color` here re-derived the linked
567
- // colour on every RTE mount and clobbered the override.
568
- color: props.styles?.color || globalStyles.color,
569
- lineHeight: globalStyles.lineHeight,
570
- letterSpacing: globalStyles.letterSpacing,
621
+ ...(bakeTypography
622
+ ? {
623
+ fontSize: globalStyles.fontSize,
624
+ fontFamily: globalStyles.fontFamily,
625
+ fontWeight: globalStyles.fontWeight,
626
+ fontStyle: globalStyles.fontStyle,
627
+ textDecoration: globalStyles.textDecoration,
628
+ lineHeight: globalStyles.lineHeight,
629
+ letterSpacing: globalStyles.letterSpacing,
630
+ }
631
+ : {}),
632
+ // The element's own colour override (a creation-time contrast bake, or a colour
633
+ // picked while linked). ADR-0020: the role carries no colour, so there is no
634
+ // global fallback — an uncoloured element renders the page base coat.
635
+ color: props.styles?.color,
571
636
  },
572
637
  isHydrationWrite: !editor.isFocused,
573
638
  });
574
639
 
575
- // sync toolbar editor styles
576
- fontSettings.selectedFont = globalStyles?.fontFamily ?? 'Lato';
577
- fontSettings.selectedFontSize = globalStyles?.fontSize ?? '2.25em';
640
+ // sync toolbar editor styles — show the block's EFFECTIVE typography (its override,
641
+ // else the linked role) so the toolbar matches the render, not the role blindly.
642
+ const effectiveFontSize =
643
+ props.styles?.fontSize ?? globalStyles?.fontSize ?? '2.25em';
644
+
645
+ fontSettings.selectedFont =
646
+ props.styles?.fontFamily ?? globalStyles?.fontFamily ?? 'Lato';
647
+ fontSettings.selectedFontSize = effectiveFontSize;
578
648
  fontSettings.selectedFontSizePx = Math.ceil(
579
- parseFloat(emToPx(globalStyles?.fontSize ?? '2.25em')),
649
+ parseFloat(emToPx(effectiveFontSize)),
580
650
  );
581
651
  // For an RTE field the mark is the source of truth — the render and the focus-time
582
652
  // `selectionUpdate` both paint from it — so seed the swatch from the mark's first
583
653
  // coloured run, falling back to the element→global cascade. Otherwise the unfocused
584
654
  // swatch shows the linked global while the text renders its baked frame/override
585
655
  // colour, and only snaps to the right colour on focus (BP-5886).
656
+ // BP-5969 (ADR-0020): the role contributes no colour — seed the swatch from the RTE
657
+ // mark, else the element's own colour.
586
658
  fontSettings.selectedTextColor = resolveInitialToolbarTextColor(
587
659
  model.value,
588
660
  props.styles?.color,
589
- globalStyles.color,
590
661
  );
591
662
  // A global text style has no backgroundColor, so this resolves to undefined there.
592
663
  const backgroundColour =
@@ -612,83 +683,31 @@ export const useRichTextEditor = (
612
683
  ) {
613
684
  toggleTextAlign(props.styles.textAlign);
614
685
  }
615
- } else if (props.styles) {
616
- // copy the content settings styles to the editor
617
- fontSettings.selectedFont = props.styles?.fontFamily ?? 'Lato';
618
- fontSettings.selectedFontSize = props.styles?.fontSize ?? '2.25em';
619
- fontSettings.selectedFontSizePx = Math.ceil(
620
- parseFloat(emToPx(props.styles?.fontSize ?? '2.25em')),
621
- );
622
- // No linked global (props.styles carries no textStyleType) — still resolve the RTE
623
- // mark first, so a field whose colour lives only in its mark (e.g. the tipping/payment
624
- // success message, which has no style object) shows it instead of collapsing to black.
625
- fontSettings.selectedTextColor = resolveInitialToolbarTextColor(
626
- model.value,
627
- props.styles?.color,
628
- );
629
- fontSettings.selectedBackgroundColor = hasLogoColorVar(
630
- props.styles?.backgroundColor ?? '',
631
- )
632
- ? 'transparent'
633
- : (props.styles?.backgroundColor ?? 'transparent');
634
- lineHeight.value = props.styles?.lineHeight?.includes('em')
635
- ? emToPx(props.styles?.lineHeight ?? '1em')
636
- : (props.styles?.lineHeight ?? '1');
637
- letterSpacing.value = letterSpacingValueConvertor.convert(
638
- props.styles ?? {},
639
- );
640
-
641
- if (
642
- props.styles?.textAlign &&
643
- currentAlignment?.textAlign !== props.styles.textAlign
644
- ) {
645
- toggleTextAlign(props.styles.textAlign);
646
- }
647
- }
648
- };
649
-
650
- const copyGlobalStyleSettings = (selectedTextStyle?: BrandPageCssStyle) => {
651
- const globalStyles =
652
- selectedTextStyle ?? selectedGlobalTextStyles.value?.style;
653
-
654
- if (globalStyles) {
655
- globalStyles.fontWeight === 'bold' && toggleBoldAll();
656
- globalStyles.fontStyle && toggleItalicAll();
657
- globalStyles.textDecoration?.includes('underline') &&
658
- toggleUnderlineAll();
659
- globalStyles.textDecoration?.includes('line-through') &&
660
- toggleStrikeAll();
661
-
662
- if (globalStyles.color) {
663
- if (!hasLogoColorVar(globalStyles.color)) {
664
- toggleTextColorAll(globalStyles.color);
665
- fontSettings.selectedTextColor = globalStyles.color;
666
- } else {
667
- // Apply logo color using CSS variable
668
- toggleTextColorAll(getRTEColor(globalStyles.color, 'text'));
669
- fontSettings.selectedTextColor = '#000000';
670
- }
671
- }
672
686
 
673
- globalStyles.fontFamily && toggleFontAll(globalStyles.fontFamily);
674
- globalStyles.fontSize && toggleFontSizeAll(globalStyles.fontSize);
675
- globalStyles.lineHeight && setLineHeightAll(globalStyles.lineHeight);
676
- globalStyles.letterSpacing &&
677
- setLetterSpaceAll(globalStyles.letterSpacing);
678
- props.styles.textAlign &&
679
- toggleTextAlign(props.styles.textAlign ?? 'none');
687
+ // Show the runs' own values (the role, or a per-run override) before the first click.
688
+ syncToolbarFromEditor({
689
+ textColorFallback: fontSettings.selectedTextColor,
690
+ backgroundColorFallback: fontSettings.selectedBackgroundColor,
691
+ });
680
692
  }
681
693
  };
682
694
 
695
+ // BP-5969 (ADR-0019): a toolbar edit applies a TipTap mark ONLY. The old sync wrote
696
+ // the flattened style back onto the block object AND cleared its role link
697
+ // (`textStyleType: undefined`) — that dual source of truth is the "a per-run colour
698
+ // edit repaints the whole block / drops the role" bug class. Now per-run formatting
699
+ // lives on marks; the block stays linked; whole-box size/family are authored from the
700
+ // sidebar. Alignment is the one block-level toolbar action, so it still persists — but
701
+ // without detaching the role.
683
702
  const triggerEditorStyleChange = (editorOption: EditorOptionType) => {
684
- const fontStyleChanges = { ...props.styles, textStyleType: undefined };
703
+ markUserCommand();
685
704
 
686
705
  switch (editorOption.type) {
687
706
  case 'fontFamily':
688
707
  toggleFont(editorOption.value as string);
689
- fontStyleChanges.fontFamily = editorOption.value as string;
690
708
  break;
691
- case 'fontSize':
709
+
710
+ case 'fontSize': {
692
711
  const size = editorOption.value as string;
693
712
 
694
713
  fontSettings.selectedFontSizePx = size.includes('em')
@@ -696,93 +715,168 @@ export const useRichTextEditor = (
696
715
  : parseInt(size);
697
716
 
698
717
  toggleFontSize(size);
699
- fontStyleChanges.fontSize = size.includes('em') ? size : pxToEm(size);
700
718
  break;
719
+ }
720
+
701
721
  case 'fontWeight':
702
722
  toggleBold();
703
- editor.isActive('bold')
704
- ? (fontStyleChanges.fontWeight = 'bold')
705
- : (fontStyleChanges.fontWeight = undefined);
706
723
  break;
707
724
  case 'fontStyle':
708
725
  toggleItalic();
709
- editor.isActive('italic')
710
- ? (fontStyleChanges.fontStyle = 'italic')
711
- : (fontStyleChanges.fontStyle = undefined);
712
726
  break;
713
727
  case 'textDecorationLine':
714
728
  toggleUnderline();
715
- editor.isActive('underline')
716
- ? fontStyleChanges.textDecoration
717
- ? (fontStyleChanges.textDecoration =
718
- fontStyleChanges.textDecoration + ' underline')
719
- : (fontStyleChanges.textDecoration = 'underline')
720
- : (fontStyleChanges.textDecoration =
721
- fontStyleChanges.textDecoration?.replace('underline', ''));
722
-
723
729
  break;
724
730
  case 'textDecorationStrike':
725
731
  toggleStrike();
726
- editor.isActive('strike')
727
- ? fontStyleChanges.textDecoration
728
- ? (fontStyleChanges.textDecoration =
729
- fontStyleChanges.textDecoration + ' line-through')
730
- : (fontStyleChanges.textDecoration = 'line-through')
731
- : (fontStyleChanges.textDecoration =
732
- fontStyleChanges.textDecoration?.replace('line-through', ''));
733
732
  break;
734
733
  case 'textColor':
735
734
  toggleTextColor(editorOption.value as string);
736
- fontStyleChanges.color = editorOption.value as string;
737
735
  break;
738
736
  case 'backgroundColor':
739
737
  toggleBackgroundColor(editorOption.value as string);
740
- fontStyleChanges.backgroundColor = editorOption.value as string;
741
738
  break;
742
739
  case 'lineHeight':
743
740
  setLineHeightAll(editorOption.value as string);
744
- fontStyleChanges.lineHeight = editorOption.value as string;
745
741
  break;
746
742
  case 'letterSpacing':
747
743
  setLetterSpaceAll(editorOption.value as string);
748
- fontStyleChanges.letterSpacing = letterSpacingValueConvertor.convert({
749
- letterSpacing: editorOption.value as string,
750
- });
751
744
  break;
752
745
  case 'textAlign':
753
746
  toggleTextAlign();
754
- fontStyleChanges.textAlign = currentAlignment.value.alignment;
747
+ persistBlockTextAlign(currentAlignment.value.alignment);
755
748
  break;
756
749
  case 'hyperlink':
757
750
  setHyperlinkUrl(editorOption.value as string);
758
751
  break;
759
752
  }
760
-
761
- syncContentSettings(fontStyleChanges);
762
753
  };
763
754
 
764
- const syncContentSettings = (fontStyleChanges: TextStyleDeclaration) => {
755
+ // Alignment is block-level (not a per-run mark, not a role property), so it persists to
756
+ // the block/item style — writing ONLY `textAlign`, so the role link is untouched.
757
+ const persistBlockTextAlign = (textAlign: string | undefined) => {
758
+ if (!textAlign) return;
759
+
760
+ const value = { textAlign };
761
+
765
762
  if (props.name === 'textItems') {
766
763
  contentSettingStore.updateTextItemTextStyleTypeWithStyles({
767
764
  id: props.rawId,
768
765
  name: props.name,
769
- value: {
770
- ...fontStyleChanges,
771
- fontSize: fontSettings.selectedFontSize,
772
- },
766
+ value,
773
767
  index: props.textItemIndex,
774
768
  });
775
769
  } else {
776
770
  contentSettingStore.updateTextStyles({
777
771
  id: props.rawId,
778
772
  name: props.name,
779
- value: {
780
- ...fontStyleChanges,
781
- },
773
+ value,
782
774
  });
783
775
  }
784
776
  };
785
777
 
778
+ // The subset of the block's typography that DIFFERS from its linked role — the promotable
779
+ // override set. Empty when the block matches its role (colour is element-first, excluded).
780
+ const getTypographyOverride = (): Record<string, string> => {
781
+ const role = selectedGlobalTextStyles.value?.style as
782
+ | Record<string, string>
783
+ | undefined;
784
+
785
+ if (!role) return {};
786
+
787
+ const override: Record<string, string> = {};
788
+
789
+ Object.entries(readBlockTypography(editor)).forEach(([key, value]) => {
790
+ // A role-var mark is linked, not an override — the value IS the role.
791
+ if (isRoleVarRef(value)) return;
792
+
793
+ const promoted =
794
+ key === 'textDecoration'
795
+ ? composeTextDecoration(role[key], value)
796
+ : value;
797
+
798
+ if (
799
+ typeof promoted === 'string' &&
800
+ normTypographyValue(key, promoted) !==
801
+ normTypographyValue(key, role[key])
802
+ ) {
803
+ override[key] = promoted;
804
+ }
805
+ });
806
+
807
+ return override;
808
+ };
809
+
810
+ // Drives the role dropdown's `*` and the "modified style" toggle: true when any run of this
811
+ // linked block differs from its role's typography — one bold word counts.
812
+ const hasTypographyOverride = (): boolean => {
813
+ const role = getRoleStyle() as Record<string, unknown> | undefined;
814
+
815
+ return !!role && hasRunTypographyOverride(editor, role);
816
+ };
817
+
818
+ // A word-level override has no single value to promote, so "Apply changes to all" needs a
819
+ // whole-block one.
820
+ const canApplyTypographyToRole = (): boolean =>
821
+ isCanonicalLinked(props.styles) &&
822
+ Object.keys(getTypographyOverride()).length > 0;
823
+
824
+ // Reset to default: re-link this block's typography to its role (see `relinkTypographyChain`).
825
+ // skipStyleDetection keeps the role link so this doesn't read as a detaching style edit.
826
+ const resetTypographyToRole = () => {
827
+ const role = getRoleStyle() as Record<string, unknown> | undefined;
828
+
829
+ markUserCommand();
830
+ skipStyleDetection(relinkTypographyChain(editor, role)).run();
831
+ };
832
+
833
+ // A role switch resets only typography (ADR-0020): runs relink to the new role, colour stays.
834
+ // An undo/redo restores the doc that went with the role, so it only moves the dropdown tick —
835
+ // the apply bumps `snapshotAppliedToken` in the same tick as it writes the role.
836
+ watch(
837
+ [() => props.styles.textStyleType, () => historyStore.snapshotAppliedToken],
838
+ ([newType, token], [oldType, oldToken]) => {
839
+ if (!newType || newType === oldType) return;
840
+
841
+ textStyleTypeOptions.forEach((item) => {
842
+ item.isSelected = item.value === newType;
843
+ });
844
+
845
+ if (token === oldToken) resetTypographyToRole();
846
+ },
847
+ );
848
+
849
+ // Apply to all: promote this block's whole-block typography into the role definition (every
850
+ // block linked to the role adopts it), relink this block's runs that now equal the role —
851
+ // word-level overrides stay, unlike Reset — and re-normalize siblings against the new role.
852
+ // Mirrors the sidebar TextStyleField.saveToGlobal, sourcing the overrides from the RTE marks.
853
+ const applyTypographyToRole = () => {
854
+ const type = props.styles.textStyleType;
855
+ const role = selectedGlobalTextStyles.value?.style;
856
+ const override = getTypographyOverride();
857
+
858
+ if (!type || !role || !Object.keys(override).length) return;
859
+
860
+ const previousRole = { ...role } as unknown as Record<string, unknown>;
861
+ const nextRole = { ...role, ...override } as TextStyleDeclaration;
862
+
863
+ markUserCommand();
864
+ styleSettingsStore.setTextStyleOption({ type, style: nextRole });
865
+ skipStyleDetection(
866
+ relinkRoleEqualRunsChain(
867
+ editor,
868
+ nextRole as unknown as Record<string, unknown>,
869
+ previousRole,
870
+ ),
871
+ ).run();
872
+ contentSettingStore.renormalizeTextStylesForRole(type, previousRole, {
873
+ id: props.rawId,
874
+ name: props.name,
875
+ index: props.textItemIndex,
876
+ doc: sanitizeRteJson(editor.getJSON()),
877
+ });
878
+ };
879
+
786
880
  const onFocus = () => {
787
881
  emit('focus');
788
882
  };
@@ -871,6 +965,7 @@ export const useRichTextEditor = (
871
965
  ? { from: selection.from, to: selection.to }
872
966
  : undefined;
873
967
 
968
+ markUserCommand();
874
969
  applyHyperlinkToEditor(url, text, selectionRange);
875
970
  isHyperlinkPopupOpen.value = false;
876
971
  break;
@@ -883,6 +978,7 @@ export const useRichTextEditor = (
883
978
  content_type: props.contentType,
884
979
  },
885
980
  });
981
+ markUserCommand();
886
982
  removeHyperlinkFromComposable();
887
983
  isHyperlinkPopupOpen.value = false;
888
984
  break;
@@ -912,8 +1008,10 @@ export const useRichTextEditor = (
912
1008
  lineHeightValue,
913
1009
  selectedFont: computed(() => selectedFont.value),
914
1010
  setInitialStyleSettings,
915
- copyGlobalStyleSettings,
916
- unsetAllMarks,
1011
+ hasTypographyOverride,
1012
+ canApplyTypographyToRole,
1013
+ resetTypographyToRole,
1014
+ applyTypographyToRole,
917
1015
  handleHyperlink,
918
1016
  hyperlinkValue,
919
1017
  setHyperlinkUrlValue,