@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
@@ -2,47 +2,544 @@ import type { ChainedCommands, Editor, JSONContent } from '@tiptap/vue-3';
2
2
  import type { EditorState, Transaction, Selection } from 'prosemirror-state';
3
3
  import {
4
4
  STYLE_THEME_KEYS,
5
- type BannerSectionContent,
6
- type BookingSectionContent,
7
- type Content,
8
- type ImagesSectionContent,
9
- type MailingListContent,
10
- type ProfileContent,
11
- type ShapesSectionContent,
12
- type TextContent,
5
+ CANONICAL_TEXT_STYLE_TYPES,
13
6
  type TextStyleDeclaration,
14
- type TextStyleType,
15
- type ContactFormContent,
16
- type AboutSectionContent,
17
- type TestimonialSectionContent,
18
- type SocialLinksContent,
19
- type RequestPaymentContent,
20
- type SupportMeContent,
21
- type ShopContent,
22
7
  type RichTextValue,
23
8
  } from '@brand-page';
24
- import type { PageSettingsState } from '@shared/page-settings/store';
25
- import type { StyleSettingsState } from '@shared/style-settings/store';
26
9
  import { collectRteTextColors } from '@shared/color-strategies/rte-color';
27
- import { ESCAPED_NEWLINE_CHARACTER } from '@src/constants/ui';
28
- import { getRenderText } from '@brand-page/utils';
29
- import type { Mark } from 'prosemirror-model';
10
+ import {
11
+ composeTextDecoration,
12
+ localTextDecoration,
13
+ roleVarRef,
14
+ } from '@shared/text-role-typography.util';
15
+ import type { Mark, Node as ProseMirrorNode } from 'prosemirror-model';
30
16
  import createDomPurify, { type DOMPurify } from 'dompurify';
31
17
  import Color from 'color';
32
- import { isHrefPageId } from '../composables/rich-text-editor/hyperlink';
18
+ import { isHrefPageId } from '@shared/rte-link.util';
33
19
  import {
34
20
  RTE_SECTION_FIELD_PAIRS,
35
- type RTEFieldPair,
21
+ PLAIN_TEXT_PAIR_SECTIONS,
36
22
  } from '@shared/color-strategies/rte-field-pairs';
37
23
  // Re-exported below so every existing caller keeps importing them from here; they
38
24
  // live in a leaf module because this one pulls dompurify at runtime.
39
- import { isTiptapDocument, isRteHtmlString } from './rte-shape';
25
+ import {
26
+ getRteJsonPlainText,
27
+ isRteHtmlString,
28
+ isTiptapDocument,
29
+ } from '@shared/rte-shape.util';
30
+
31
+ export {
32
+ getRteJsonPlainText,
33
+ isRteHtmlString,
34
+ isTiptapDocument,
35
+ } from '@shared/rte-shape.util';
36
+
37
+ /**
38
+ * BP-5969 (ADR-0019, Option B): a block linked to a CANONICAL role gets its typography
39
+ * from the page-scoped CSS class (`text-role-css.util`), so authoring must NOT bake
40
+ * family/size/spacing/line-height/weight/style/decoration into the marks — a value baked
41
+ * post-migration would pin the block forever (migration-22 never re-runs to strip it).
42
+ * Colour is exempt (it isn't in the class). Legacy/unlinked types have no class, so they
43
+ * still bake inline. Mirrors `TextWrapper`'s canonical-only `roleClass`.
44
+ */
45
+ export const isCanonicalLinked = (
46
+ style: { textStyleType?: string | undefined } | undefined,
47
+ ): boolean =>
48
+ !!style?.textStyleType &&
49
+ (CANONICAL_TEXT_STYLE_TYPES as readonly string[]).includes(
50
+ style.textStyleType,
51
+ );
52
+
53
+ /** True when a mark value is a role reference (`var(--role-…)`) rather than a literal. */
54
+ export const isRoleVarRef = (value: unknown): value is string =>
55
+ typeof value === 'string' && value.startsWith('var(--role-');
56
+
57
+ // A prop's CSS "default" keyword that renders identically to the prop being absent — e.g.
58
+ // font-weight:normal == no font-weight. The toolbar writes these literals when a format is
59
+ // toggled OFF (bold→'normal', italic→'normal', underline/strike→'none'); a role that simply
60
+ // omits the prop must still read as equal, so both collapse to '' for the role-equality compare.
61
+ const DEFAULT_EQUIVALENT_VALUE: Record<string, string> = {
62
+ fontWeight: 'normal',
63
+ fontStyle: 'normal',
64
+ textDecoration: 'none',
65
+ lineHeight: 'normal',
66
+ letterSpacing: 'normal',
67
+ };
68
+
69
+ /**
70
+ * Normalise a typography value for a role-equality compare: quote-strip + trim for fontFamily,
71
+ * trim otherwise, then fold a prop's default keyword (`normal`/`none`) to '' so it matches a role
72
+ * that leaves the prop unset (`'normal'`/`'none'`/absent are the same override-wise).
73
+ */
74
+ export const normTypographyValue = (key: string, value?: string): string => {
75
+ const trimmed =
76
+ key === 'fontFamily'
77
+ ? (value ?? '').replace(/['"]/g, '').trim()
78
+ : (value ?? '').trim();
79
+
80
+ return trimmed === DEFAULT_EQUIVALENT_VALUE[key] ? '' : trimmed;
81
+ };
82
+
83
+ /**
84
+ * BP-5969 — the RTE mark value to write for a typography attr change: the role var (so the run
85
+ * stays linked and follows role edits) when the picked value equals the linked role, else the
86
+ * literal raw override. `roleStyle` undefined (legacy/unlinked block, no role class to resolve a
87
+ * var) → always the literal. The equality test runs at the mutation point, per ADR-0019.
88
+ */
89
+ export const roleVarOrLiteral = (
90
+ markAttr: string,
91
+ picked: string,
92
+ roleStyle: TextStyleDeclaration | undefined,
93
+ ): string => {
94
+ const ref = roleVarRef(markAttr);
95
+ const roleValue = (roleStyle as Record<string, unknown> | undefined)?.[
96
+ markAttr
97
+ ];
98
+
99
+ return ref &&
100
+ typeof roleValue === 'string' &&
101
+ normTypographyValue(markAttr, picked) ===
102
+ normTypographyValue(markAttr, roleValue)
103
+ ? ref
104
+ : picked;
105
+ };
106
+
107
+ const BLOCK_TYPOGRAPHY_ATTRS = [
108
+ 'fontFamily',
109
+ 'fontSize',
110
+ 'lineHeight',
111
+ 'letterSpacing',
112
+ 'fontWeight',
113
+ 'fontStyle',
114
+ 'textDecoration',
115
+ ] as const;
116
+
117
+ type BlockTypographyAttr = (typeof BLOCK_TYPOGRAPHY_ATTRS)[number];
118
+
119
+ const LINK_TYPOGRAPHY_ATTRS = BLOCK_TYPOGRAPHY_ATTRS.filter(
120
+ (attr) => attr !== 'textDecoration',
121
+ );
122
+
123
+ // A link's underline is the link's own look (the toolbar and AutoLink add it), not a
124
+ // typography choice, so it neither lights the asterisk nor joins the block's value.
125
+ const typographyAttrsOf = (
126
+ node: ProseMirrorNode,
127
+ ): readonly BlockTypographyAttr[] =>
128
+ node.marks.some((mark) => mark.type.name === 'link')
129
+ ? LINK_TYPOGRAPHY_ATTRS
130
+ : BLOCK_TYPOGRAPHY_ATTRS;
131
+
132
+ export const LEGACY_TYPOGRAPHY_MARKS: Partial<
133
+ Record<BlockTypographyAttr, string[]>
134
+ > = {
135
+ fontWeight: ['bold'],
136
+ fontStyle: ['italic'],
137
+ textDecoration: ['underline', 'strike'],
138
+ };
139
+
140
+ /**
141
+ * A run's marks without the legacy semantic marks (`<strong>`/`<em>`/`<u>`/`<s>`) that compete with
142
+ * `attrs`. A link keeps its underline, as Reset does (`restoreLinkUnderlines`).
143
+ */
144
+ export const withoutTypographyMarks = <M extends { type: string }>(
145
+ marks: readonly M[],
146
+ attrs: readonly BlockTypographyAttr[],
147
+ isLink: boolean,
148
+ ): M[] => {
149
+ const dropped = new Set(
150
+ attrs.flatMap((attr) => LEGACY_TYPOGRAPHY_MARKS[attr] ?? []),
151
+ );
152
+
153
+ return marks.filter(
154
+ (mark) => !dropped.has(mark.type) || (isLink && mark.type === 'underline'),
155
+ );
156
+ };
157
+
158
+ /**
159
+ * One text run's typography: its `textStyle` attrs, falling back to a legacy semantic mark
160
+ * (`<strong>`/`<em>`/`<u>`/`<s>`) for un-migrated / pasted runs (mirrors `formatting.ts` `effective`).
161
+ * An absent attr is undefined — the run inherits the role class.
162
+ */
163
+ export const runTypography = (
164
+ node: ProseMirrorNode,
165
+ ): Record<BlockTypographyAttr, string | undefined> => {
166
+ const textStyle = node.marks.find((mark) => mark.type.name === 'textStyle');
167
+ const attrOf = (attr: string): string | undefined => {
168
+ const value = textStyle?.attrs?.[attr];
169
+
170
+ return typeof value === 'string' && value !== '' ? value : undefined;
171
+ };
172
+ const has = (markName: string) =>
173
+ node.marks.some((mark) => mark.type.name === markName);
174
+ const legacyDecoration =
175
+ [has('underline') && 'underline', has('strike') && 'line-through']
176
+ .filter(Boolean)
177
+ .join(' ') || undefined;
178
+
179
+ return {
180
+ fontFamily: attrOf('fontFamily'),
181
+ fontSize: attrOf('fontSize'),
182
+ lineHeight: attrOf('lineHeight'),
183
+ letterSpacing: attrOf('letterSpacing'),
184
+ fontWeight: attrOf('fontWeight') ?? (has('bold') ? 'bold' : undefined),
185
+ fontStyle: attrOf('fontStyle') ?? (has('italic') ? 'italic' : undefined),
186
+ textDecoration: localTextDecoration(
187
+ attrOf('textDecoration') ?? legacyDecoration,
188
+ textStyle?.attrs?.textDecorationReset === true,
189
+ ),
190
+ };
191
+ };
192
+
193
+ const isRoleEqualLiteral = (
194
+ attr: BlockTypographyAttr,
195
+ value: string | undefined,
196
+ role: Record<string, unknown>,
197
+ previousRole?: Record<string, unknown>,
198
+ ): value is string =>
199
+ value !== undefined &&
200
+ !isRoleVarRef(value) &&
201
+ normTypographyValue(
202
+ attr,
203
+ attr === 'textDecoration'
204
+ ? composeTextDecoration(
205
+ previousRole?.textDecoration as string | undefined,
206
+ value,
207
+ )
208
+ : value,
209
+ ) === normTypographyValue(attr, role[attr] as string | undefined);
210
+
211
+ /**
212
+ * True when any run's typography differs from `role` — the role dropdown's asterisk. Unlike
213
+ * `readBlockTypography`, one bold word counts. Absent attrs and role refs follow the role.
214
+ */
215
+ export const hasRunTypographyOverride = (
216
+ editor: Editor,
217
+ role: Record<string, unknown>,
218
+ ): boolean => {
219
+ let found = false;
220
+
221
+ editor.state.doc.descendants((node) => {
222
+ if (found) return false;
223
+
224
+ if (!node.isText || !node.text) return;
225
+
226
+ const run = runTypography(node);
227
+
228
+ found = typographyAttrsOf(node).some(
229
+ (attr) =>
230
+ run[attr] !== undefined &&
231
+ !isRoleVarRef(run[attr]) &&
232
+ !isRoleEqualLiteral(attr, run[attr], role),
233
+ );
234
+ });
235
+
236
+ return found;
237
+ };
238
+
239
+ /**
240
+ * "Apply changes to all", on the live editor: every run value now equal to `role` becomes its
241
+ * `var(--role-*)` ref (or is unset when the role leaves the attr undefined), and the legacy mark it
242
+ * came from is dropped. A value that still differs — a word-level override — stays. Colour and the
243
+ * selection are untouched. The editor twin of `relinkRoleEqualMarks`; the caller runs the chain.
244
+ */
245
+ export const relinkRoleEqualRunsChain = (
246
+ editor: Editor,
247
+ role: Record<string, unknown>,
248
+ previousRole?: Record<string, unknown>,
249
+ ): ChainedCommands =>
250
+ editor.chain().command(({ tr }) => {
251
+ const { schema } = tr.doc.type;
252
+ const textStyleType = schema.marks.textStyle;
253
+
254
+ // Mark steps never shift positions, so walking the pre-edit doc stays valid.
255
+ tr.doc.descendants((node, pos) => {
256
+ if (!node.isText || !node.text) return;
257
+
258
+ const run = runTypography(node);
259
+ const relinked = typographyAttrsOf(node).filter((attr) =>
260
+ isRoleEqualLiteral(attr, run[attr], role, previousRole),
261
+ );
262
+
263
+ if (!relinked.length) return;
264
+
265
+ const from = pos;
266
+ const to = pos + node.nodeSize;
267
+ const attrs: Record<string, unknown> = {
268
+ ...node.marks.find((mark) => mark.type === textStyleType)?.attrs,
269
+ };
270
+
271
+ relinked.forEach((attr) => {
272
+ attrs[attr] = role[attr] ? roleVarRef(attr) : null;
273
+
274
+ if (attr === 'textDecoration') attrs.textDecorationReset = null;
275
+
276
+ LEGACY_TYPOGRAPHY_MARKS[attr]?.forEach((name) => {
277
+ if (schema.marks[name]) tr.removeMark(from, to, schema.marks[name]);
278
+ });
279
+ });
280
+
281
+ // Attr defaults are '' or null, so any falsy value is empty (as Tiptap's `removeEmptyTextStyle`).
282
+ if (Object.values(attrs).some(Boolean))
283
+ tr.addMark(from, to, textStyleType.create(attrs));
284
+ else tr.removeMark(from, to, textStyleType);
285
+ });
286
+
287
+ return true;
288
+ });
289
+
290
+ /**
291
+ * The block's whole-box typography — what "Apply changes to all" can promote into the role. A prop
292
+ * is included only when UNIFORM across every text run: one bold word has no single value to promote
293
+ * (`hasRunTypographyOverride` still flags it). A role-var ref rides through as the block value; the
294
+ * caller (`getTypographyOverride`) treats it as "linked", not an override.
295
+ */
296
+ export const readBlockTypography = (editor: Editor): Record<string, string> => {
297
+ const seen: Record<string, Set<string | undefined>> = Object.fromEntries(
298
+ BLOCK_TYPOGRAPHY_ATTRS.map((attr) => [attr, new Set<string | undefined>()]),
299
+ );
300
+ let hasText = false;
301
+
302
+ editor.state.doc.descendants((node) => {
303
+ if (!node.isText || !node.text) return;
304
+
305
+ hasText = true;
306
+
307
+ const effective = runTypography(node);
308
+
309
+ typographyAttrsOf(node).forEach((attr) => seen[attr].add(effective[attr]));
310
+ });
311
+
312
+ if (!hasText) return {};
313
+
314
+ const typography: Record<string, string> = {};
315
+
316
+ BLOCK_TYPOGRAPHY_ATTRS.forEach((attr) => {
317
+ const value =
318
+ seen[attr].size === 1
319
+ ? ([...seen[attr]][0] as string | undefined)
320
+ : undefined;
321
+
322
+ if (value) typography[attr] = value;
323
+ });
324
+
325
+ return typography;
326
+ };
327
+
328
+ /**
329
+ * BP-5969 — build the "reset to role" chain for the whole selection: re-link every typography attr
330
+ * the role DEFINES to its `var(--role-*)` token (self-describing, follows future role edits), unset
331
+ * the attrs the role leaves undefined, and clear the legacy semantic marks (bold/italic/underline/
332
+ * strike) that the attr resets don't touch. Returns the un-run chain so the caller can wrap it in
333
+ * `skipStyleDetection` (keeping the role link) before running. Colour marks are left alone.
334
+ */
335
+ export const relinkTypographyChain = (
336
+ editor: Editor,
337
+ role: Record<string, unknown> | undefined,
338
+ ): ChainedCommands => {
339
+ const chain = editor.chain().selectAll();
340
+
341
+ if (role?.fontFamily) chain.setFontFamily(roleVarRef('fontFamily') as string);
342
+ else chain.unsetFontFamily();
343
+
344
+ if (role?.fontSize) chain.setFontSize(roleVarRef('fontSize') as string);
345
+ else chain.unsetFontSize();
346
+
347
+ if (role?.lineHeight) chain.setLineHeight(roleVarRef('lineHeight') as string);
348
+ else chain.unsetLineHeight();
349
+
350
+ if (role?.letterSpacing)
351
+ chain.setLetterSpacing(roleVarRef('letterSpacing') as string);
352
+ else chain.unsetLetterSpacing();
353
+
354
+ if (role?.fontWeight) chain.setFontWeight(roleVarRef('fontWeight') as string);
355
+ else chain.unsetFontWeight();
356
+
357
+ if (role?.fontStyle) chain.setFontStyle(roleVarRef('fontStyle') as string);
358
+ else chain.unsetFontStyle();
359
+
360
+ if (role?.textDecoration)
361
+ chain.setTextDecoration(roleVarRef('textDecoration') as string);
362
+ else chain.unsetTextDecoration();
363
+
364
+ return chain
365
+ .unsetBold()
366
+ .unsetItalic()
367
+ .unsetUnderline()
368
+ .unsetStrike()
369
+ .command(restoreLinkUnderlines(editor));
370
+ };
371
+
372
+ /**
373
+ * A chain step that puts the underline back on link text that had one before the chain cleared
374
+ * every underline: a link's underline is its own look, not a typography choice (`typographyAttrsOf`).
375
+ * Captures the link ranges now, so build it before the chain changes the doc; mark steps keep
376
+ * positions, so the ranges still hold when it runs.
377
+ */
378
+ export const restoreLinkUnderlines = (
379
+ editor: Editor,
380
+ ): ((props: { tr: Transaction }) => boolean) => {
381
+ const ranges: [number, number][] = [];
382
+
383
+ editor.state.doc.descendants((node, pos) => {
384
+ if (
385
+ node.isText &&
386
+ node.marks.some((mark) => mark.type.name === 'link') &&
387
+ node.marks.some((mark) => mark.type.name === 'underline')
388
+ )
389
+ ranges.push([pos, pos + node.nodeSize]);
390
+ });
391
+
392
+ return ({ tr }) => {
393
+ const underline = tr.doc.type.schema.marks.underline;
394
+
395
+ if (underline)
396
+ ranges.forEach(([from, to]) => tr.addMark(from, to, underline.create()));
397
+
398
+ return true;
399
+ };
400
+ };
401
+
402
+ type RteRunNode = {
403
+ type?: string;
404
+ marks?: { type: string; attrs?: Record<string, unknown> }[];
405
+ content?: RteRunNode[];
406
+ };
407
+
408
+ /**
409
+ * The document-level twin of `relinkTypographyChain`, for a block that isn't being edited: each run's
410
+ * typography literal that equals any of `roles` (a literal the asterisk never counted as an override)
411
+ * becomes the `var(--role-*)` ref when `roles[0]` — the role the block links now — defines the attr,
412
+ * else it's unset. Literals that differ from every role, role refs, colour and link marks are left
413
+ * alone. Returns the same doc when nothing changes.
414
+ */
415
+ export const relinkRoleEqualMarks = (
416
+ doc: JSONContent,
417
+ roles: readonly (Record<string, unknown> | undefined)[],
418
+ ): JSONContent => {
419
+ const [linkedRole] = roles;
420
+ const clone = JSON.parse(JSON.stringify(doc)) as RteRunNode;
421
+ let changed = false;
422
+
423
+ const visit = (node: RteRunNode): void => {
424
+ const mark = node.marks?.find((m) => m.type === 'textStyle');
425
+
426
+ if (mark?.attrs) {
427
+ for (const attr of BLOCK_TYPOGRAPHY_ATTRS) {
428
+ const value = mark.attrs[attr];
429
+
430
+ if (typeof value !== 'string' || value === '' || isRoleVarRef(value))
431
+ continue;
432
+
433
+ const equalsARole = roles.some(
434
+ (role) =>
435
+ role &&
436
+ normTypographyValue(attr, value) ===
437
+ normTypographyValue(attr, role[attr] as string | undefined),
438
+ );
439
+
440
+ if (!equalsARole) continue;
441
+
442
+ mark.attrs[attr] = linkedRole?.[attr] ? roleVarRef(attr) : null;
443
+
444
+ if (attr === 'textDecoration') delete mark.attrs.textDecorationReset;
445
+
446
+ changed = true;
447
+ }
448
+
449
+ if (Object.values(mark.attrs).every((v) => v === null || v === undefined))
450
+ node.marks = node.marks?.filter((m) => m !== mark);
451
+ }
452
+
453
+ node.content?.forEach(visit);
454
+ };
455
+
456
+ visit(clone);
457
+
458
+ return changed ? (clone as JSONContent) : doc;
459
+ };
460
+
461
+ /**
462
+ * BP-5969 (R19) — the stored-document twin of `relinkTypographyChain`'s reset-to-role chain, for a
463
+ * doc that isn't mounted in an editor (the AI boundary's role switch). Unlike `relinkRoleEqualMarks`
464
+ * (which only relinks a literal that already equals a role), every run's typography is reset to
465
+ * `role` — a deliberate accent override included — mirroring the maker's Reset: a `var(--role-*)`
466
+ * ref where `role` defines the attr, else unset. The legacy semantic marks (bold/italic/underline/
467
+ * strike) that the attr resets don't touch are dropped too. Colour and link marks are left alone.
468
+ *
469
+ * `styleOverrides` is the call's own surviving style-object override — e.g. a switch call that
470
+ * also sends `fontFamily: 'Playfair Display'`. Those attrs are nulled, not ref'd: the render fill
471
+ * (`fillRteRunTypography`) treats a non-null mark value as final, so baking the ref here would bury
472
+ * the override under it; nulling leaves the render fill free to resolve the literal from the style
473
+ * object, same as any other block-level override.
474
+ */
475
+ export const relinkDocToRole = (
476
+ doc: JSONContent,
477
+ role: Record<string, unknown> | undefined,
478
+ styleOverrides?: Record<string, unknown>,
479
+ ): JSONContent => {
480
+ const clone = JSON.parse(JSON.stringify(doc)) as RteRunNode;
481
+
482
+ const visit = (node: RteRunNode): void => {
483
+ if (node.type === 'text' && node.marks?.length) {
484
+ const marks = withoutTypographyMarks(
485
+ node.marks,
486
+ BLOCK_TYPOGRAPHY_ATTRS,
487
+ node.marks.some((mark) => mark.type === 'link'),
488
+ );
489
+ const textStyle = marks.find((mark) => mark.type === 'textStyle');
490
+ const attrs: Record<string, unknown> = { ...textStyle?.attrs };
491
+
492
+ for (const attr of BLOCK_TYPOGRAPHY_ATTRS) {
493
+ const override = styleOverrides?.[attr];
494
+ const isOverridden =
495
+ typeof override === 'string' &&
496
+ override !== '' &&
497
+ !isRoleVarRef(override);
498
+
499
+ attrs[attr] = !isOverridden && role?.[attr] ? roleVarRef(attr) : null;
500
+ }
501
+
502
+ delete attrs.textDecorationReset;
503
+
504
+ // A mark with no colour and every typography attr null (role undefined, or every attr
505
+ // overridden by a non-role value) carries nothing — drop it rather than leave a no-op
506
+ // `textStyle` mark behind, same as `relinkRoleEqualMarks` above.
507
+ const isEmptyMark = Object.values(attrs).every(
508
+ (v) => v === null || v === undefined,
509
+ );
510
+
511
+ if (textStyle) {
512
+ if (isEmptyMark) node.marks = marks.filter((m) => m !== textStyle);
513
+ else {
514
+ textStyle.attrs = attrs;
515
+ node.marks = marks;
516
+ }
517
+ } else if (!isEmptyMark) {
518
+ marks.push({ type: 'textStyle', attrs });
519
+ node.marks = marks;
520
+ } else {
521
+ node.marks = marks;
522
+ }
523
+ }
524
+
525
+ node.content?.forEach(visit);
526
+ };
40
527
 
41
- export { isTiptapDocument, isRteHtmlString };
528
+ visit(clone);
42
529
 
43
- // 'text' has special textItems handling, so it's not in the map but is RTE-enabled
530
+ return clone as JSONContent;
531
+ };
532
+
533
+ // The sections the maker mounts an RTE editor on. Derived from the pairs map, but the
534
+ // PLAIN_TEXT_PAIR_SECTIONS are excluded: they appear in the map only for the colour strategy's
535
+ // pairing lookup — their title/description are plain strings whose colour lives on `*.style.color`
536
+ // (rendered by TextWrapper's plain path), so mounting an editor would convert them to a Tiptap doc
537
+ // and strand that colour (the doc renders from the mark, which the plain bake never sets).
538
+ // 'text' has special textItems handling, so it's not in the map but is RTE-enabled.
44
539
  export const RTE_ALLOWED_SECTION_TYPES = [
45
- ...Object.keys(RTE_SECTION_FIELD_PAIRS),
540
+ ...Object.keys(RTE_SECTION_FIELD_PAIRS).filter(
541
+ (type) => !PLAIN_TEXT_PAIR_SECTIONS.has(type),
542
+ ),
46
543
  'text',
47
544
  ];
48
545
 
@@ -59,22 +556,6 @@ export const isRTEManagedStyleField = (
59
556
  return pairs.some((pair) => pair.styleField === fieldName);
60
557
  };
61
558
 
62
- // Union of all section content types that support RTE field-pair processing
63
- type RTESectionContent =
64
- | BannerSectionContent
65
- | BookingSectionContent
66
- | ImagesSectionContent
67
- | ShapesSectionContent
68
- | MailingListContent
69
- | ProfileContent
70
- | ContactFormContent
71
- | AboutSectionContent
72
- | TestimonialSectionContent
73
- | SocialLinksContent
74
- | RequestPaymentContent
75
- | SupportMeContent
76
- | ShopContent;
77
-
78
559
  // Only allow tags used by the enabled Tiptap extensions
79
560
  const allowedTags = [
80
561
  'p',
@@ -202,24 +683,6 @@ export const skipStyleDetection = (chain: ChainedCommands, marks?: Mark[]) =>
202
683
  return true;
203
684
  });
204
685
 
205
- /**
206
- * Extract visible plain text from a Tiptap JSON document (concatenated text
207
- * nodes). Dependency-free — no editor instantiation.
208
- */
209
- export const getRteJsonPlainText = (doc: JSONContent): string => {
210
- const parts: string[] = [];
211
-
212
- const walk = (node: JSONContent): void => {
213
- if (typeof node.text === 'string') parts.push(node.text);
214
-
215
- node.content?.forEach(walk);
216
- };
217
-
218
- walk(doc);
219
-
220
- return parts.join(' ').replace(/\s+/g, ' ').trim();
221
- };
222
-
223
686
  // True for RTE content in EITHER shape: a Tiptap JSON document (post-migration)
224
687
  // or a legacy inline-HTML string (pre-migration, or a not-yet-migrated site).
225
688
  export const hasRTEContent = (content: unknown): boolean =>
@@ -429,435 +892,15 @@ export const replaceTextInRTE = (
429
892
  };
430
893
 
431
894
  /**
432
- * Whether a rewrap should (re)stamp this linked RTE field, kept in one place so the three
433
- * structurally-identical rewrap blocks in `transformToRTE` (section field-pairs, the
434
- * `textItems` loop, and the single-block `text` path) can't drift apart (BP-5886, ADR-0016):
435
- * - **type-driven** (a global-style edit): re-stamp existing RTE to propagate the change;
436
- * - **store-driven** (section-select / add-content): only convert genuine plain text, so an
437
- * already-authored mark (the migration-baked frame colour) is never overwritten on select.
895
+ * Helper to build stored marks from a style declaration. BP-5969: when `bakeTypography`
896
+ * is false (a canonical-role block — the class supplies typography), only colour and
897
+ * highlight are baked; family/size/spacing/line-height/weight/style/decoration are left
898
+ * to the role class so freshly-typed text follows the role and repaints on a role edit.
438
899
  */
439
- const shouldRewrapRteField = (
440
- textStyleType: TextStyleType | undefined,
441
- type: TextStyleType | undefined,
442
- uiStyleSettingsStore: StyleSettingsState | undefined,
443
- textValue: unknown,
444
- ): boolean =>
445
- !!textStyleType &&
446
- (textStyleType === type ||
447
- (!!uiStyleSettingsStore && !hasRTEContent(textValue)));
448
-
449
- const transformRTEField = (
450
- content: RTESectionContent,
451
- pair: RTEFieldPair,
452
- externalEditor: Editor,
453
- uiStyleSettingsStore: StyleSettingsState | undefined,
454
- newContent: Content | undefined,
455
- type: TextStyleType | undefined,
456
- newStyle: TextStyleDeclaration | undefined,
457
- ) => {
458
- const fields = content as unknown as Record<string, unknown>;
459
- const style = fields[pair.styleField] as TextStyleDeclaration | undefined;
460
- const textStyleType = style?.textStyleType;
461
-
462
- if (
463
- shouldRewrapRteField(
464
- textStyleType,
465
- type,
466
- uiStyleSettingsStore,
467
- fields[pair.textField],
468
- )
469
- ) {
470
- let globalContentStyle = uiStyleSettingsStore
471
- ? uiStyleSettingsStore.textStyles.textStyleOptions[textStyleType!].style
472
- : (newStyle ?? ({} as TextStyleDeclaration));
473
-
474
- globalContentStyle = {
475
- ...globalContentStyle,
476
- textAlign: style?.textAlign,
477
- // Element-first colour: a creation-time contrast bake (or a colour picked while
478
- // linked) lives on the style field and must survive the rewrap into rich text.
479
- // The linked global colour applies only when the element has none — mirroring
480
- // `resolveTextColor`'s element-first cascade, so the rewrap can't overpaint the
481
- // override with the global colour.
482
- color: style?.color || globalContentStyle.color,
483
- };
484
-
485
- fields[pair.textField] = getUpdatedHTMLFromEditor(
486
- externalEditor,
487
- fields[pair.textField] as string,
488
- globalContentStyle,
489
- );
490
- } else if (
491
- !textStyleType &&
492
- newContent &&
493
- newContent.id === content.id &&
494
- !hasRTEContent(fields[pair.textField] as string)
495
- ) {
496
- const newContentFields = newContent as unknown as Record<string, unknown>;
497
- const newContentStyle =
498
- (newContentFields[pair.styleField] as TextStyleDeclaration | undefined) ||
499
- style;
500
-
501
- if (newContentStyle) {
502
- fields[pair.textField] = getUpdatedHTMLFromEditor(
503
- externalEditor,
504
- fields[pair.textField] as string,
505
- newContentStyle,
506
- );
507
- }
508
- }
509
- };
510
-
511
- const switchRTEField = (
512
- content: RTESectionContent,
513
- pair: RTEFieldPair,
514
- externalEditor: Editor,
515
- uiStyleSettingsStore: StyleSettingsState | undefined,
516
- ) => {
517
- const fields = content as unknown as Record<string, unknown>;
518
- const style = fields[pair.styleField] as TextStyleDeclaration | undefined;
519
- const textStyleType = style?.textStyleType;
520
-
521
- if (textStyleType && uiStyleSettingsStore) {
522
- let globalContentStyle = uiStyleSettingsStore.textStyles.textStyleOptions[
523
- textStyleType
524
- ].style as TextStyleDeclaration;
525
-
526
- globalContentStyle = {
527
- ...globalContentStyle,
528
- textAlign: style?.textAlign,
529
- };
530
-
531
- if (!hasRTEContent(fields[pair.textField] as string))
532
- fields[pair.textField] = getUpdatedHTMLFromEditor(
533
- externalEditor,
534
- fields[pair.textField] as string,
535
- globalContentStyle,
536
- );
537
- } else if (
538
- !textStyleType &&
539
- style &&
540
- !hasRTEContent(fields[pair.textField] as string)
541
- ) {
542
- fields[pair.textField] = getUpdatedHTMLFromEditor(
543
- externalEditor,
544
- fields[pair.textField] as string,
545
- style,
546
- );
547
- }
548
- };
549
-
550
- export const transformToRTE = (
551
- uiPageSettingStore: PageSettingsState,
552
- externalEditor: Editor,
553
- uiStyleSettingsStore?: StyleSettingsState,
554
- newContent?: Content,
555
- type?: TextStyleType,
556
- newStyle?: TextStyleDeclaration,
557
- ) => {
558
- for (const page of uiPageSettingStore.pages) {
559
- for (const contentItem of page.contents) {
560
- const fieldPairs = RTE_SECTION_FIELD_PAIRS[contentItem.type];
561
-
562
- if (fieldPairs) {
563
- const content = contentItem.settings.content as RTESectionContent;
564
-
565
- for (const pair of fieldPairs) {
566
- transformRTEField(
567
- content,
568
- pair,
569
- externalEditor,
570
- uiStyleSettingsStore,
571
- newContent,
572
- type,
573
- newStyle,
574
- );
575
- }
576
- }
577
-
578
- if (contentItem.type === 'text') {
579
- const textContent = contentItem.settings.content as TextContent;
580
-
581
- if (textContent.textItems && textContent.textItems.length > 0) {
582
- for (const [
583
- itemIndex,
584
- textItemContent,
585
- ] of textContent.textItems.entries()) {
586
- const textItemStyleType = textItemContent.textStyle.textStyleType;
587
-
588
- if (
589
- shouldRewrapRteField(
590
- textItemStyleType,
591
- type,
592
- uiStyleSettingsStore,
593
- textItemContent.text,
594
- )
595
- ) {
596
- let globalContentStyle = uiStyleSettingsStore
597
- ? uiStyleSettingsStore.textStyles.textStyleOptions[
598
- textItemStyleType!
599
- ].style
600
- : (newStyle ?? ({} as TextStyleDeclaration));
601
-
602
- globalContentStyle = {
603
- ...globalContentStyle,
604
- textAlign: textItemContent.textStyle.textAlign,
605
- // Element-first colour — see `transformRTEField`.
606
- color:
607
- textItemContent.textStyle.color || globalContentStyle.color,
608
- };
609
-
610
- textItemContent.text = getUpdatedHTMLFromEditor(
611
- externalEditor,
612
- textItemContent.text,
613
- globalContentStyle,
614
- );
615
- } else if (
616
- !textItemStyleType &&
617
- newContent &&
618
- newContent.id === textContent.id &&
619
- !hasRTEContent(textItemContent.text)
620
- ) {
621
- const newContentStyle =
622
- (newContent as TextContent)?.textItems?.[itemIndex]
623
- ?.textStyle ?? (newContent as TextContent).textStyle;
624
-
625
- textItemContent.text = getUpdatedHTMLFromEditor(
626
- externalEditor,
627
- textItemContent.text,
628
- newContentStyle,
629
- );
630
- }
631
- }
632
- }
633
-
634
- if (
635
- (!textContent.textItems || textContent.textItems.length === 0) &&
636
- textContent.textStyle
637
- ) {
638
- const textStyleType = textContent.textStyle.textStyleType;
639
-
640
- if (
641
- shouldRewrapRteField(
642
- textStyleType,
643
- type,
644
- uiStyleSettingsStore,
645
- textContent.text,
646
- )
647
- ) {
648
- let globalContentStyle = uiStyleSettingsStore
649
- ? uiStyleSettingsStore.textStyles.textStyleOptions[textStyleType!]
650
- .style
651
- : (newStyle ?? ({} as TextStyleDeclaration));
652
-
653
- globalContentStyle = {
654
- ...globalContentStyle,
655
- textAlign: textContent.textStyle.textAlign,
656
- // Element-first colour — see `transformRTEField`.
657
- color: textContent.textStyle.color || globalContentStyle.color,
658
- };
659
-
660
- textContent.text = getUpdatedHTMLFromEditor(
661
- externalEditor,
662
- textContent.text,
663
- globalContentStyle,
664
- );
665
- } else if (
666
- !textStyleType &&
667
- newContent &&
668
- newContent.id === textContent.id &&
669
- !hasRTEContent(textContent.text)
670
- ) {
671
- const newContentStyle = (newContent as TextContent).textStyle;
672
-
673
- textContent.text = getUpdatedHTMLFromEditor(
674
- externalEditor,
675
- textContent.text,
676
- newContentStyle,
677
- );
678
- }
679
- }
680
- }
681
- }
682
- }
683
- };
684
-
685
- export const switchAllContentToRTE = (
686
- uiPageSettingStore: PageSettingsState,
687
- externalEditor: Editor,
688
- uiStyleSettingsStore?: StyleSettingsState,
689
- ) => {
690
- for (const page of uiPageSettingStore.pages) {
691
- for (const contentItem of page.contents) {
692
- const fieldPairs = RTE_SECTION_FIELD_PAIRS[contentItem.type];
693
-
694
- if (fieldPairs) {
695
- const content = contentItem.settings.content as RTESectionContent;
696
-
697
- for (const pair of fieldPairs) {
698
- switchRTEField(content, pair, externalEditor, uiStyleSettingsStore);
699
- }
700
- }
701
-
702
- if (contentItem.type === 'text') {
703
- const textContent = contentItem.settings.content as TextContent;
704
-
705
- if (textContent.textItems && textContent.textItems.length > 0) {
706
- for (const [
707
- _itemIndex,
708
- textItemContent,
709
- ] of textContent.textItems.entries()) {
710
- const textItemStyleType = textItemContent.textStyle.textStyleType;
711
-
712
- if (textItemStyleType && uiStyleSettingsStore) {
713
- let globalContentStyle = uiStyleSettingsStore.textStyles
714
- .textStyleOptions[textItemStyleType]
715
- .style as TextStyleDeclaration;
716
-
717
- globalContentStyle = {
718
- ...globalContentStyle,
719
- textAlign: textItemContent.textStyle.textAlign,
720
- };
721
-
722
- if (!hasRTEContent(textItemContent.text))
723
- textItemContent.text = getUpdatedHTMLFromEditor(
724
- externalEditor,
725
- textItemContent.text,
726
- globalContentStyle,
727
- );
728
- } else if (
729
- !textItemStyleType &&
730
- !hasRTEContent(textItemContent.text)
731
- ) {
732
- const newContentStyle = textItemContent.textStyle;
733
-
734
- textItemContent.text = getUpdatedHTMLFromEditor(
735
- externalEditor,
736
- textItemContent.text,
737
- newContentStyle,
738
- );
739
- }
740
- }
741
- }
742
-
743
- if (
744
- (!textContent.textItems || textContent.textItems.length === 0) &&
745
- textContent.textStyle
746
- ) {
747
- const textStyleType = textContent.textStyle.textStyleType;
748
-
749
- if (textStyleType && uiStyleSettingsStore) {
750
- let globalContentStyle = uiStyleSettingsStore.textStyles
751
- .textStyleOptions[textStyleType].style as TextStyleDeclaration;
752
-
753
- globalContentStyle = {
754
- ...globalContentStyle,
755
- textAlign: textContent.textStyle.textAlign,
756
- };
757
-
758
- if (!hasRTEContent(textContent.text))
759
- textContent.text = getUpdatedHTMLFromEditor(
760
- externalEditor,
761
- textContent.text,
762
- globalContentStyle,
763
- );
764
- } else if (!textStyleType && !hasRTEContent(textContent.text)) {
765
- const newContentStyle = textContent.textStyle;
766
-
767
- textContent.text = getUpdatedHTMLFromEditor(
768
- externalEditor,
769
- textContent.text,
770
- newContentStyle,
771
- );
772
- }
773
- }
774
- }
775
- }
776
- }
777
- };
778
-
779
- export const getUpdatedHTMLFromEditor = (
780
- externalEditor: Editor,
781
- contentText: RichTextValue,
782
- newContentStyle: TextStyleDeclaration,
783
- ) => {
784
- if (!contentText) {
785
- return contentText;
786
- }
787
-
788
- // BP-5787: accept a string (legacy HTML) or a Tiptap JSON doc. setContent
789
- // handles both; only the newline-escaping shim applies to strings.
790
- const content =
791
- typeof contentText === 'string' &&
792
- contentText.includes(ESCAPED_NEWLINE_CHARACTER)
793
- ? getRenderText(contentText)
794
- : contentText;
795
-
796
- externalEditor.commands.setContent(content);
797
-
798
- const currentAlignment = externalEditor.getAttributes(
799
- externalEditor.state.selection.$anchor.node().type.name,
800
- );
801
-
802
- let chain = externalEditor.chain().selectAll();
803
-
804
- if (newContentStyle?.fontFamily) {
805
- chain = chain.setFontFamily(newContentStyle.fontFamily);
806
- }
807
-
808
- if (newContentStyle?.fontSize) {
809
- chain = chain.setFontSize(newContentStyle.fontSize);
810
- }
811
-
812
- if (newContentStyle?.letterSpacing) {
813
- chain = chain.setLetterSpacing(newContentStyle.letterSpacing);
814
- }
815
-
816
- if (newContentStyle?.lineHeight) {
817
- chain = chain.setLineHeight(newContentStyle.lineHeight);
818
- }
819
-
820
- if (newContentStyle?.color) {
821
- chain = chain.setMark('textStyle', {
822
- color: getRTEColor(newContentStyle.color, 'text'),
823
- });
824
- }
825
-
826
- // Handle backgroundColor
827
- if ('backgroundColor' in (newContentStyle || {})) {
828
- chain = newContentStyle.backgroundColor
829
- ? chain.setMark('highlight', {
830
- color: getRTEColor(newContentStyle.backgroundColor, 'background'),
831
- })
832
- : chain.unsetHighlight();
833
- }
834
-
835
- if (!currentAlignment.textAlign && newContentStyle?.textAlign) {
836
- chain = chain.setTextAlign(newContentStyle.textAlign);
837
- }
838
-
839
- const hasStrike = newContentStyle?.textDecoration?.includes('line-through');
840
- const hasUnderline = newContentStyle?.textDecoration?.includes('underline');
841
-
842
- chain = hasStrike ? chain.setStrike() : chain.unsetStrike();
843
- chain = hasUnderline ? chain.setUnderline() : chain.unsetUnderline();
844
-
845
- chain =
846
- newContentStyle?.fontWeight === 'bold'
847
- ? chain.setBold()
848
- : chain.unsetBold();
849
- chain = newContentStyle?.fontStyle ? chain.setItalic() : chain.unsetItalic();
850
-
851
- chain.run();
852
-
853
- // BP-5787: return the JSON form, consistent with how RTE is now stored.
854
- return externalEditor.getJSON();
855
- };
856
-
857
- // Helper to build stored marks from a style declaration
858
900
  export const buildInitialMarks = (
859
901
  state: EditorState,
860
902
  style: TextStyleDeclaration,
903
+ bakeTypography = true,
861
904
  ) => {
862
905
  const marks: any[] = [];
863
906
  const {
@@ -875,19 +918,46 @@ export const buildInitialMarks = (
875
918
  // TextStyle mark attributes
876
919
  const textStyleAttrs: Record<string, any> = {};
877
920
 
878
- if (fontFamily) textStyleAttrs.fontFamily = fontFamily;
921
+ // BP-5969: a canonical-linked block (bakeTypography === false) references the role via
922
+ // cascade vars — self-describing marks that follow role edits — instead of baking a
923
+ // literal that would pin the run. Legacy/unlinked bakes the resolved literal as before.
924
+ // `style` here is the role's resolved style, so the guards write a ref only for the attrs
925
+ // the role actually defines.
926
+ if (bakeTypography) {
927
+ if (fontFamily) textStyleAttrs.fontFamily = fontFamily;
928
+
929
+ if (fontSize) textStyleAttrs.fontSize = fontSize;
930
+
931
+ if (letterSpacing) textStyleAttrs.letterSpacing = letterSpacing;
932
+
933
+ if (lineHeight) textStyleAttrs.lineHeight = lineHeight;
934
+ } else {
935
+ if (fontFamily) textStyleAttrs.fontFamily = roleVarRef('fontFamily');
936
+
937
+ if (fontSize) textStyleAttrs.fontSize = roleVarRef('fontSize');
938
+
939
+ if (letterSpacing)
940
+ textStyleAttrs.letterSpacing = roleVarRef('letterSpacing');
941
+
942
+ if (lineHeight) textStyleAttrs.lineHeight = roleVarRef('lineHeight');
943
+
944
+ // BP-5969 (Phase A): weight/style/decoration are var-backed textStyle attrs too, so newly-typed
945
+ // linked text follows the role for these as well (not baked as semantic <strong>/<u> marks).
946
+ if (fontWeight) textStyleAttrs.fontWeight = roleVarRef('fontWeight');
879
947
 
880
- if (fontSize) textStyleAttrs.fontSize = fontSize;
948
+ if (fontStyle) textStyleAttrs.fontStyle = roleVarRef('fontStyle');
949
+
950
+ if (textDecoration)
951
+ textStyleAttrs.textDecoration = roleVarRef('textDecoration');
952
+ }
881
953
 
882
954
  if (color) {
883
955
  textStyleAttrs.color = getRTEColor(color, 'text');
884
956
  }
885
957
 
886
- if (letterSpacing) textStyleAttrs.letterSpacing = letterSpacing;
887
-
888
- if (lineHeight) textStyleAttrs.lineHeight = lineHeight;
889
-
890
- marks.push(state.schema.marks.textStyle.create(textStyleAttrs));
958
+ if (Object.keys(textStyleAttrs).length > 0) {
959
+ marks.push(state.schema.marks.textStyle.create(textStyleAttrs));
960
+ }
891
961
 
892
962
  if (backgroundColor) {
893
963
  marks.push(
@@ -897,27 +967,61 @@ export const buildInitialMarks = (
897
967
  );
898
968
  }
899
969
 
900
- // Bold and italic
901
- if (fontWeight === 'bold') {
902
- marks.push(state.schema.marks.bold.create());
903
- }
970
+ if (bakeTypography) {
971
+ // Bold and italic
972
+ if (fontWeight === 'bold') {
973
+ marks.push(state.schema.marks.bold.create());
974
+ }
904
975
 
905
- if (fontStyle) {
906
- marks.push(state.schema.marks.italic.create());
907
- }
976
+ if (fontStyle) {
977
+ marks.push(state.schema.marks.italic.create());
978
+ }
908
979
 
909
- // Underline and strike-through
910
- if (textDecoration?.includes('underline')) {
911
- marks.push(state.schema.marks.underline.create());
912
- }
980
+ // Underline and strike-through
981
+ if (textDecoration?.includes('underline')) {
982
+ marks.push(state.schema.marks.underline.create());
983
+ }
913
984
 
914
- if (textDecoration?.includes('line-through')) {
915
- marks.push(state.schema.marks.strike.create());
985
+ if (textDecoration?.includes('line-through')) {
986
+ marks.push(state.schema.marks.strike.create());
987
+ }
916
988
  }
917
989
 
918
990
  return marks;
919
991
  };
920
992
 
993
+ /**
994
+ * The first-type seed for an empty field, with what the customer picked before typing laid over it:
995
+ * a picked mark wins per attribute (its unset attrs keep the seed's), and a picked mark the seed
996
+ * lacks is added. With nothing picked, the seed as is.
997
+ */
998
+ export const mergePickedMarks = (
999
+ seed: readonly Mark[],
1000
+ picked: readonly Mark[] | null | undefined,
1001
+ ): Mark[] => {
1002
+ if (!picked?.length) return [...seed];
1003
+
1004
+ const merged = seed.map((mark) => {
1005
+ const pick = picked.find((p) => p.type === mark.type);
1006
+
1007
+ if (!pick) return mark;
1008
+
1009
+ const pickedAttrs = Object.fromEntries(
1010
+ Object.entries(pick.attrs).filter(
1011
+ ([, value]) => value !== null && value !== undefined,
1012
+ ),
1013
+ );
1014
+
1015
+ return mark.type.create({ ...mark.attrs, ...pickedAttrs });
1016
+ });
1017
+
1018
+ const added = picked.filter(
1019
+ (pick) => !seed.some((mark) => mark.type === pick.type),
1020
+ );
1021
+
1022
+ return [...merged, ...added];
1023
+ };
1024
+
921
1025
  export const retainStyleOnEmptyLine = (
922
1026
  editor: Editor,
923
1027
  selection: Selection,