react-native-enriched-html 1.0.0 → 1.0.1

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 (272) hide show
  1. package/android/src/main/java/com/swmansion/enriched/common/EnrichedSpanFlags.kt +39 -0
  2. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +43 -20
  3. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +1 -0
  4. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextSpanFactory.kt +1 -0
  5. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt +2 -1
  6. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +2 -1
  7. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +8 -5
  8. package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +12 -2
  9. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +6 -0
  10. package/android/src/main/java/com/swmansion/enriched/textinput/styles/AlignmentStyles.kt +6 -7
  11. package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +13 -1
  12. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +4 -3
  13. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +4 -3
  14. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +38 -11
  15. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +2 -0
  16. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +2 -1
  17. package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +2 -1
  18. package/cpp/parser/GumboNormalizer.c +61 -3
  19. package/cpp/tests/GumboParserTest.cpp +54 -0
  20. package/ios/EnrichedTextInputView.mm +37 -25
  21. package/ios/extensions/LayoutManagerExtension.mm +73 -62
  22. package/ios/extensions/StringExtension.mm +75 -0
  23. package/ios/htmlParser/HtmlParser.h +4 -1
  24. package/ios/htmlParser/HtmlParser.mm +23 -5
  25. package/ios/inputHtmlParser/InputHtmlParser.mm +6 -3
  26. package/ios/interfaces/StyleHeaders.h +4 -0
  27. package/ios/styles/LinkStyle.mm +20 -34
  28. package/ios/styles/MentionStyle.mm +55 -4
  29. package/ios/textHtmlParser/TextHtmlParser.mm +2 -1
  30. package/ios/utils/ParagraphAttributesUtils.mm +7 -0
  31. package/lib/module/index.js +1 -0
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/module/utils/expandMentionStylesForIndicators.js +2 -3
  34. package/lib/module/utils/expandMentionStylesForIndicators.js.map +1 -1
  35. package/lib/module/utils/normalizeHtmlStyle.js +1 -1
  36. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -1
  37. package/lib/module/web/EnrichedText.css +343 -0
  38. package/lib/module/web/EnrichedText.js +55 -0
  39. package/lib/module/web/EnrichedText.js.map +1 -0
  40. package/lib/module/web/EnrichedTextInput.js +54 -52
  41. package/lib/module/web/EnrichedTextInput.js.map +1 -1
  42. package/lib/module/web/constants/brokenImageGlyph.js +9 -0
  43. package/lib/module/web/constants/brokenImageGlyph.js.map +1 -0
  44. package/lib/module/web/constants/classNames.js +5 -0
  45. package/lib/module/web/constants/classNames.js.map +1 -0
  46. package/lib/module/web/emitLinkDetected.js +12 -0
  47. package/lib/module/web/emitLinkDetected.js.map +1 -0
  48. package/lib/module/web/formats/EnrichedBlockquote.js +1 -1
  49. package/lib/module/web/formats/EnrichedBlockquote.js.map +1 -1
  50. package/lib/module/web/formats/EnrichedCheckboxList.js +2 -2
  51. package/lib/module/web/formats/EnrichedCheckboxList.js.map +1 -1
  52. package/lib/module/web/formats/EnrichedCodeBlock.js +1 -1
  53. package/lib/module/web/formats/EnrichedCodeBlock.js.map +1 -1
  54. package/lib/module/web/formats/EnrichedHeading.js +1 -1
  55. package/lib/module/web/formats/EnrichedHeading.js.map +1 -1
  56. package/lib/module/web/formats/EnrichedImageNodeView.js +1 -1
  57. package/lib/module/web/formats/EnrichedImageNodeView.js.map +1 -1
  58. package/lib/module/web/formats/EnrichedLink.js +43 -3
  59. package/lib/module/web/formats/EnrichedLink.js.map +1 -1
  60. package/lib/module/web/formats/EnrichedOrderedList.js +2 -2
  61. package/lib/module/web/formats/EnrichedOrderedList.js.map +1 -1
  62. package/lib/module/web/formats/EnrichedTextAlign.js +82 -0
  63. package/lib/module/web/formats/EnrichedTextAlign.js.map +1 -0
  64. package/lib/module/web/formats/EnrichedUnorderedList.js +2 -2
  65. package/lib/module/web/formats/EnrichedUnorderedList.js.map +1 -1
  66. package/lib/module/web/formats/applyWrappingListToSelection.js +3 -2
  67. package/lib/module/web/formats/applyWrappingListToSelection.js.map +1 -1
  68. package/lib/module/web/formats/formatRules.js +39 -3
  69. package/lib/module/web/formats/formatRules.js.map +1 -1
  70. package/lib/module/web/formats/listKeyboard.js +4 -1
  71. package/lib/module/web/formats/listKeyboard.js.map +1 -1
  72. package/lib/module/web/normalization/checkboxHtmlNormalizer.js.map +1 -0
  73. package/lib/module/web/normalization/htmlNormalizer.js +514 -0
  74. package/lib/module/web/normalization/htmlNormalizer.js.map +1 -0
  75. package/lib/module/web/normalization/prepareHtmlForWeb.js +78 -0
  76. package/lib/module/web/normalization/prepareHtmlForWeb.js.map +1 -0
  77. package/lib/module/web/{tiptapHtmlNormalizer.js → normalization/tiptapHtmlNormalizer.js} +6 -2
  78. package/lib/module/web/normalization/tiptapHtmlNormalizer.js.map +1 -0
  79. package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js +27 -0
  80. package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js.map +1 -0
  81. package/lib/module/web/pmPlugins/AutolinkPlugin/index.js +178 -0
  82. package/lib/module/web/pmPlugins/AutolinkPlugin/index.js.map +1 -0
  83. package/lib/module/web/pmPlugins/MentionPlugin/index.js +1 -1
  84. package/lib/module/web/pmPlugins/MentionPlugin/index.js.map +1 -1
  85. package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js +23 -4
  86. package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js.map +1 -1
  87. package/lib/module/web/pmPlugins/MentionPlugin/setMention.js +17 -18
  88. package/lib/module/web/pmPlugins/MentionPlugin/setMention.js.map +1 -1
  89. package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js +105 -0
  90. package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js.map +1 -0
  91. package/lib/module/web/pmPlugins/ShortcutPlugin.js +11 -0
  92. package/lib/module/web/pmPlugins/ShortcutPlugin.js.map +1 -1
  93. package/lib/module/web/pmPlugins/StrictMarksPlugin.js +2 -0
  94. package/lib/module/web/pmPlugins/StrictMarksPlugin.js.map +1 -1
  95. package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js.map +1 -1
  96. package/lib/module/web/sanitization/htmlSanitizer.js +10 -0
  97. package/lib/module/web/sanitization/htmlSanitizer.js.map +1 -0
  98. package/lib/module/web/styleConversion/buildMentionRulesCSS.js +12 -6
  99. package/lib/module/web/styleConversion/buildMentionRulesCSS.js.map +1 -1
  100. package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js +159 -0
  101. package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js.map +1 -0
  102. package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js +3 -155
  103. package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js.map +1 -1
  104. package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js +44 -0
  105. package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js.map +1 -0
  106. package/lib/module/web/styleConversion/{enrichedInputThemingToCSSProperties.js → enrichedThemingToCSSProperties.js} +3 -3
  107. package/lib/module/web/styleConversion/enrichedThemingToCSSProperties.js.map +1 -0
  108. package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js +87 -64
  109. package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js.map +1 -1
  110. package/lib/module/web/styleConversion/inlineImageCSSVariables.js +7 -0
  111. package/lib/module/web/styleConversion/inlineImageCSSVariables.js.map +1 -0
  112. package/lib/module/web/useImageErrorFallback.js +33 -0
  113. package/lib/module/web/useImageErrorFallback.js.map +1 -0
  114. package/lib/module/web/useOnChangeHtml.js +1 -1
  115. package/lib/module/web/useOnChangeHtml.js.map +1 -1
  116. package/lib/module/web/useOnChangeState.js +6 -3
  117. package/lib/module/web/useOnChangeState.js.map +1 -1
  118. package/lib/module/web/useOnLinkDetected.js +32 -45
  119. package/lib/module/web/useOnLinkDetected.js.map +1 -1
  120. package/lib/module/web/usePressInteractions.js +19 -0
  121. package/lib/module/web/usePressInteractions.js.map +1 -0
  122. package/lib/module/web/useStableRef.js +13 -0
  123. package/lib/module/web/useStableRef.js.map +1 -0
  124. package/lib/typescript/src/index.d.ts +1 -0
  125. package/lib/typescript/src/index.d.ts.map +1 -1
  126. package/lib/typescript/src/native/EnrichedText.d.ts +1 -1
  127. package/lib/typescript/src/native/EnrichedText.d.ts.map +1 -1
  128. package/lib/typescript/src/native/EnrichedTextInput.d.ts +1 -1
  129. package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -1
  130. package/lib/typescript/src/types.d.ts +1 -1
  131. package/lib/typescript/src/types.d.ts.map +1 -1
  132. package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts +1 -1
  133. package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts.map +1 -1
  134. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -1
  135. package/lib/typescript/src/web/EnrichedText.d.ts +4 -0
  136. package/lib/typescript/src/web/EnrichedText.d.ts.map +1 -0
  137. package/lib/typescript/src/web/EnrichedTextInput.d.ts +2 -2
  138. package/lib/typescript/src/web/EnrichedTextInput.d.ts.map +1 -1
  139. package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts +3 -0
  140. package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts.map +1 -0
  141. package/lib/typescript/src/web/constants/classNames.d.ts +3 -0
  142. package/lib/typescript/src/web/constants/classNames.d.ts.map +1 -0
  143. package/lib/typescript/src/web/emitLinkDetected.d.ts +10 -0
  144. package/lib/typescript/src/web/emitLinkDetected.d.ts.map +1 -0
  145. package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts.map +1 -1
  146. package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts.map +1 -1
  147. package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts.map +1 -1
  148. package/lib/typescript/src/web/formats/EnrichedHeading.d.ts.map +1 -1
  149. package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts +1 -1
  150. package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts.map +1 -1
  151. package/lib/typescript/src/web/formats/EnrichedLink.d.ts +4 -1
  152. package/lib/typescript/src/web/formats/EnrichedLink.d.ts.map +1 -1
  153. package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts.map +1 -1
  154. package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts +2 -0
  155. package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts.map +1 -0
  156. package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts.map +1 -1
  157. package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts.map +1 -1
  158. package/lib/typescript/src/web/formats/formatRules.d.ts +3 -1
  159. package/lib/typescript/src/web/formats/formatRules.d.ts.map +1 -1
  160. package/lib/typescript/src/web/formats/listKeyboard.d.ts.map +1 -1
  161. package/lib/typescript/src/web/normalization/checkboxHtmlNormalizer.d.ts.map +1 -0
  162. package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts +2 -0
  163. package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts.map +1 -0
  164. package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts +2 -0
  165. package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts.map +1 -0
  166. package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts +3 -0
  167. package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts.map +1 -0
  168. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts +7 -0
  169. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts.map +1 -0
  170. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts +8 -0
  171. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts.map +1 -0
  172. package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts +1 -1
  173. package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts.map +1 -1
  174. package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts.map +1 -1
  175. package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts.map +1 -1
  176. package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts +4 -0
  177. package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts.map +1 -0
  178. package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts.map +1 -1
  179. package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts.map +1 -1
  180. package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts +6 -1
  181. package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts.map +1 -1
  182. package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts +2 -0
  183. package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts.map +1 -0
  184. package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts +2 -2
  185. package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts.map +1 -1
  186. package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts +8 -0
  187. package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts.map +1 -0
  188. package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts +2 -5
  189. package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts.map +1 -1
  190. package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts +4 -0
  191. package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts.map +1 -0
  192. package/lib/typescript/src/web/styleConversion/{enrichedInputThemingToCSSProperties.d.ts → enrichedThemingToCSSProperties.d.ts} +1 -1
  193. package/lib/typescript/src/web/styleConversion/enrichedThemingToCSSProperties.d.ts.map +1 -0
  194. package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts +5 -4
  195. package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts.map +1 -1
  196. package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts +3 -0
  197. package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts.map +1 -0
  198. package/lib/typescript/src/web/useImageErrorFallback.d.ts +3 -0
  199. package/lib/typescript/src/web/useImageErrorFallback.d.ts.map +1 -0
  200. package/lib/typescript/src/web/useOnChangeState.d.ts.map +1 -1
  201. package/lib/typescript/src/web/useOnLinkDetected.d.ts +2 -2
  202. package/lib/typescript/src/web/useOnLinkDetected.d.ts.map +1 -1
  203. package/lib/typescript/src/web/usePressInteractions.d.ts +2 -0
  204. package/lib/typescript/src/web/usePressInteractions.d.ts.map +1 -0
  205. package/lib/typescript/src/web/useStableRef.d.ts +3 -0
  206. package/lib/typescript/src/web/useStableRef.d.ts.map +1 -0
  207. package/package.json +54 -28
  208. package/src/index.tsx +1 -0
  209. package/src/types.ts +5 -3
  210. package/src/utils/expandMentionStylesForIndicators.ts +3 -3
  211. package/src/utils/normalizeHtmlStyle.ts +2 -1
  212. package/src/web/EnrichedText.css +343 -0
  213. package/src/web/EnrichedText.tsx +82 -0
  214. package/src/web/EnrichedTextInput.tsx +69 -55
  215. package/src/web/constants/brokenImageGlyph.ts +7 -0
  216. package/src/web/constants/classNames.ts +2 -0
  217. package/src/web/emitLinkDetected.ts +29 -0
  218. package/src/web/formats/EnrichedBlockquote.ts +2 -1
  219. package/src/web/formats/EnrichedCheckboxList.ts +5 -2
  220. package/src/web/formats/EnrichedCodeBlock.ts +2 -1
  221. package/src/web/formats/EnrichedHeading.ts +2 -1
  222. package/src/web/formats/EnrichedImageNodeView.tsx +1 -3
  223. package/src/web/formats/EnrichedLink.ts +59 -4
  224. package/src/web/formats/EnrichedOrderedList.ts +5 -2
  225. package/src/web/formats/EnrichedTextAlign.ts +88 -0
  226. package/src/web/formats/EnrichedUnorderedList.ts +5 -2
  227. package/src/web/formats/applyWrappingListToSelection.ts +4 -4
  228. package/src/web/formats/formatRules.ts +48 -4
  229. package/src/web/formats/listKeyboard.ts +6 -1
  230. package/src/web/normalization/htmlNormalizer.ts +656 -0
  231. package/src/web/normalization/prepareHtmlForWeb.ts +93 -0
  232. package/src/web/{tiptapHtmlNormalizer.ts → normalization/tiptapHtmlNormalizer.ts} +12 -2
  233. package/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.ts +43 -0
  234. package/src/web/pmPlugins/AutolinkPlugin/index.ts +274 -0
  235. package/src/web/pmPlugins/MentionPlugin/index.ts +1 -1
  236. package/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.ts +32 -5
  237. package/src/web/pmPlugins/MentionPlugin/setMention.ts +19 -29
  238. package/src/web/pmPlugins/MentionPlugin/useMentionEvents.ts +114 -0
  239. package/src/web/pmPlugins/ShortcutPlugin.ts +7 -0
  240. package/src/web/pmPlugins/StrictMarksPlugin.ts +3 -0
  241. package/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.ts +41 -36
  242. package/src/web/sanitization/htmlSanitizer.ts +8 -0
  243. package/src/web/styleConversion/buildMentionRulesCSS.ts +30 -11
  244. package/src/web/styleConversion/enrichedBaseStyleToCSSProperties.ts +228 -0
  245. package/src/web/styleConversion/enrichedInputStyleToCSSProperties.ts +6 -217
  246. package/src/web/styleConversion/enrichedTextStyleToCSSProperties.ts +53 -0
  247. package/src/web/styleConversion/{enrichedInputThemingToCSSProperties.ts → enrichedThemingToCSSProperties.ts} +4 -4
  248. package/src/web/styleConversion/htmlStyleToCSSVariables.ts +121 -70
  249. package/src/web/styleConversion/inlineImageCSSVariables.ts +6 -0
  250. package/src/web/useImageErrorFallback.ts +37 -0
  251. package/src/web/useOnChangeHtml.ts +1 -1
  252. package/src/web/useOnChangeState.ts +7 -5
  253. package/src/web/useOnLinkDetected.ts +38 -50
  254. package/src/web/usePressInteractions.tsx +22 -0
  255. package/src/web/useStableRef.ts +12 -0
  256. package/lib/module/web/EnrichedTextInput.css +0 -201
  257. package/lib/module/web/checkboxHtmlNormalizer.js.map +0 -1
  258. package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js +0 -100
  259. package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js.map +0 -1
  260. package/lib/module/web/styleConversion/enrichedInputThemingToCSSProperties.js.map +0 -1
  261. package/lib/module/web/tiptapHtmlNormalizer.js.map +0 -1
  262. package/lib/typescript/src/web/checkboxHtmlNormalizer.d.ts.map +0 -1
  263. package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts +0 -4
  264. package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts.map +0 -1
  265. package/lib/typescript/src/web/styleConversion/enrichedInputThemingToCSSProperties.d.ts.map +0 -1
  266. package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts +0 -3
  267. package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts.map +0 -1
  268. package/src/web/EnrichedTextInput.css +0 -201
  269. package/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.ts +0 -105
  270. /package/lib/module/web/{checkboxHtmlNormalizer.js → normalization/checkboxHtmlNormalizer.js} +0 -0
  271. /package/lib/typescript/src/web/{checkboxHtmlNormalizer.d.ts → normalization/checkboxHtmlNormalizer.d.ts} +0 -0
  272. /package/src/web/{checkboxHtmlNormalizer.ts → normalization/checkboxHtmlNormalizer.ts} +0 -0
@@ -1,6 +1,7 @@
1
1
  import { isTextSelection, type Editor, type JSONContent } from '@tiptap/core';
2
2
 
3
3
  import { lineStartBackspace } from './wrappedBlockKeyboard';
4
+ import { withPreservedAlignment } from './formatRules';
4
5
 
5
6
  function emptyListItemContent(itemName: string): JSONContent {
6
7
  return {
@@ -51,7 +52,11 @@ export function listBackspace(
51
52
  ): boolean {
52
53
  return lineStartBackspace(editor, {
53
54
  isActive: () => editor.isActive(itemName),
54
- lift: () => editor.chain().focus().liftListItem(itemName).run(),
55
+ lift: () => {
56
+ return withPreservedAlignment(editor, editor.chain(), (c) =>
57
+ c.focus().liftListItem(itemName)
58
+ );
59
+ },
55
60
  shouldJoinBefore: (beforeName) =>
56
61
  beforeName != null && wrapperNames.includes(beforeName),
57
62
  });
@@ -0,0 +1,656 @@
1
+ /*
2
+ * Custom HTML normalizer for TipTap input.
3
+ * Mirrors the native GumboNormalizer (cpp/parser/GumboNormalizer.c)
4
+ */
5
+
6
+ type CssStyles = {
7
+ bold: boolean;
8
+ italic: boolean;
9
+ underline: boolean;
10
+ strikethrough: boolean;
11
+ };
12
+
13
+ const INLINE_TAGS = new Set([
14
+ 'b',
15
+ 'i',
16
+ 'u',
17
+ 's',
18
+ 'code',
19
+ 'a',
20
+ 'strong',
21
+ 'em',
22
+ 'del',
23
+ 'strike',
24
+ 'ins',
25
+ 'mention',
26
+ ]);
27
+
28
+ const BLOCK_TAGS = new Set([
29
+ 'p',
30
+ 'h1',
31
+ 'h2',
32
+ 'h3',
33
+ 'h4',
34
+ 'h5',
35
+ 'h6',
36
+ 'ul',
37
+ 'ol',
38
+ 'li',
39
+ 'blockquote',
40
+ 'codeblock',
41
+ 'pre',
42
+ ]);
43
+
44
+ const SELF_CLOSING_TAGS = new Set(['br', 'img']);
45
+
46
+ const PASS_TAGS = new Set(['html', 'head', 'body']);
47
+
48
+ const STRIPPED_TAGS = new Set(['meta', 'style', 'script', 'title', 'link']);
49
+
50
+ const TABLE_TAGS = new Set([
51
+ 'table',
52
+ 'thead',
53
+ 'tbody',
54
+ 'tfoot',
55
+ 'tr',
56
+ 'td',
57
+ 'th',
58
+ 'caption',
59
+ 'colgroup',
60
+ 'col',
61
+ ]);
62
+
63
+ function canonicalName(name: string): string {
64
+ switch (name) {
65
+ case 'strong':
66
+ return 'b';
67
+ case 'em':
68
+ return 'i';
69
+ case 'del':
70
+ case 'strike':
71
+ return 's';
72
+ case 'ins':
73
+ return 'u';
74
+ case 'pre':
75
+ return 'codeblock';
76
+ default:
77
+ return name;
78
+ }
79
+ }
80
+
81
+ type TagClass = 'skip' | 'inline' | 'block' | 'self-closing' | 'pass';
82
+
83
+ function classifyTag(name: string): TagClass {
84
+ if (INLINE_TAGS.has(name)) return 'inline';
85
+ if (BLOCK_TAGS.has(name)) return 'block';
86
+ if (SELF_CLOSING_TAGS.has(name)) return 'self-closing';
87
+ if (PASS_TAGS.has(name)) return 'pass';
88
+ return 'skip';
89
+ }
90
+
91
+ function isElement(node: Node | null): node is Element {
92
+ return !!node && node.nodeType === Node.ELEMENT_NODE;
93
+ }
94
+
95
+ function isText(node: Node | null): boolean {
96
+ return !!node && node.nodeType === Node.TEXT_NODE;
97
+ }
98
+
99
+ function tagName(node: Node | null): string | null {
100
+ if (!isElement(node)) return null;
101
+ return node.tagName.toLowerCase();
102
+ }
103
+
104
+ function isListNode(node: Node | null): boolean {
105
+ const n = tagName(node);
106
+ return n === 'ul' || n === 'ol';
107
+ }
108
+
109
+ function isBlockquoteNode(node: Node | null): boolean {
110
+ return tagName(node) === 'blockquote';
111
+ }
112
+
113
+ function isBrNode(node: Node | null): boolean {
114
+ return tagName(node) === 'br';
115
+ }
116
+
117
+ function isBlockProducing(node: Node | null): boolean {
118
+ const n = tagName(node);
119
+ if (!n) return false;
120
+ if (classifyTag(n) === 'block') return true;
121
+ return n === 'div' || n === 'table' || n === 'tr';
122
+ }
123
+
124
+ function isPurelyInline(node: Element): boolean {
125
+ for (const child of Array.from(node.childNodes)) {
126
+ if (isBlockProducing(child)) return false;
127
+ }
128
+ return true;
129
+ }
130
+
131
+ function hasBlockOrBqChild(node: Element): boolean {
132
+ for (const child of Array.from(node.childNodes)) {
133
+ if (isBlockProducing(child) || isBlockquoteNode(child)) return true;
134
+ }
135
+ return false;
136
+ }
137
+
138
+ function findCssValue(style: string, prop: string): string | null {
139
+ // Returns the value of the last declaration with this property name.
140
+ // Mirrors GumboNormalizer's find_css_value scanning behavior.
141
+ const re = new RegExp(
142
+ `(?:^|;)\\s*${prop.replace(/[-/\\^$*+?.()|[\\]{}]/g, '\\$&')}\\s*:\\s*([^;]*)`,
143
+ 'i'
144
+ );
145
+
146
+ const m = re.exec(style);
147
+ if (m !== null) {
148
+ return (m[1] ?? '').trim();
149
+ }
150
+ return null;
151
+ }
152
+
153
+ function findAllCssValues(style: string, prop: string): string[] {
154
+ const re = new RegExp(
155
+ `(?:^|;)\\s*${prop.replace(/[-/\\^$*+?.()|[\\]{}]/g, '\\$&')}\\s*:\\s*([^;]*)`,
156
+ 'gi'
157
+ );
158
+ const out: string[] = [];
159
+ let m: RegExpExecArray | null;
160
+ while ((m = re.exec(style)) !== null) {
161
+ out.push((m[1] ?? '').trim());
162
+ }
163
+ return out;
164
+ }
165
+
166
+ function parseCssStyle(style: string | null): CssStyles {
167
+ const result: CssStyles = {
168
+ bold: false,
169
+ italic: false,
170
+ underline: false,
171
+ strikethrough: false,
172
+ };
173
+ if (!style) return result;
174
+
175
+ const fw = findCssValue(style, 'font-weight');
176
+ if (fw) {
177
+ const lower = fw.toLowerCase();
178
+ if (lower.includes('bold') || lower.includes('bolder')) {
179
+ result.bold = true;
180
+ } else {
181
+ const num = parseInt(fw, 10);
182
+ if (!Number.isNaN(num) && num >= 700) result.bold = true;
183
+ }
184
+ }
185
+
186
+ const fs = findCssValue(style, 'font-style');
187
+ if (fs) {
188
+ const lower = fs.toLowerCase();
189
+ if (lower.includes('italic') || lower.includes('oblique'))
190
+ result.italic = true;
191
+ }
192
+
193
+ // text-decoration / text-decoration-line: scan ALL declarations
194
+ const decorations = [
195
+ ...findAllCssValues(style, 'text-decoration-line'),
196
+ ...findAllCssValues(style, 'text-decoration'),
197
+ ];
198
+ for (const v of decorations) {
199
+ const lower = v.toLowerCase();
200
+ if (lower.includes('underline')) result.underline = true;
201
+ if (lower.includes('line-through')) result.strikethrough = true;
202
+ }
203
+
204
+ return result;
205
+ }
206
+
207
+ function extraStyles(s: CssStyles, tag: string): CssStyles {
208
+ return {
209
+ bold: s.bold && tag !== 'b',
210
+ italic: s.italic && tag !== 'i',
211
+ underline: s.underline && tag !== 'u',
212
+ strikethrough: s.strikethrough && tag !== 's',
213
+ };
214
+ }
215
+
216
+ function emitStylesOpen(s: CssStyles): string {
217
+ let out = '';
218
+ if (s.bold) out += '<b>';
219
+ if (s.italic) out += '<i>';
220
+ if (s.underline) out += '<u>';
221
+ if (s.strikethrough) out += '<s>';
222
+ return out;
223
+ }
224
+
225
+ function emitStylesClose(s: CssStyles): string {
226
+ let out = '';
227
+ if (s.strikethrough) out += '</s>';
228
+ if (s.underline) out += '</u>';
229
+ if (s.italic) out += '</i>';
230
+ if (s.bold) out += '</b>';
231
+ return out;
232
+ }
233
+
234
+ function emitOneAttr(el: Element, attr: string): string {
235
+ const val = el.getAttribute(attr);
236
+ if (val == null || val === '') return '';
237
+ const escaped = escapeText(val);
238
+ return ` ${attr}="${escaped}"`;
239
+ }
240
+
241
+ function emitAttributes(el: Element, name: string): string {
242
+ switch (name) {
243
+ case 'a':
244
+ return emitOneAttr(el, 'href');
245
+ case 'img':
246
+ return (
247
+ emitOneAttr(el, 'src') +
248
+ emitOneAttr(el, 'alt') +
249
+ emitOneAttr(el, 'width') +
250
+ emitOneAttr(el, 'height')
251
+ );
252
+ case 'ul':
253
+ return isCheckboxList(el) ? ' data-type="checkbox"' : '';
254
+ case 'li':
255
+ // "" is U+F0FE (MS Word checked box); often encoded as "\xEF\x83\xBE" in UTF-8.
256
+ const isChecked =
257
+ el.hasAttribute('checked') ||
258
+ el.getAttribute('data-checked') === 'true' ||
259
+ el.getAttribute('aria-checked') === 'true' ||
260
+ el.getAttribute('data-leveltext') === ''; // MS Word checked box
261
+ return isChecked ? ' checked' : '';
262
+ case 'mention':
263
+ return (
264
+ emitOneAttr(el, 'id') +
265
+ emitOneAttr(el, 'text') +
266
+ emitOneAttr(el, 'indicator')
267
+ );
268
+ default:
269
+ return '';
270
+ }
271
+ }
272
+
273
+ function isCheckboxList(el: Element): boolean {
274
+ if (
275
+ el.getAttribute('data-type') === 'checkbox' ||
276
+ el.getAttribute('data-type') === 'checkboxList'
277
+ ) {
278
+ return true;
279
+ }
280
+
281
+ // In Google Docs and MS Word the <li> elements define if it is a checkbox
282
+ // list. We only need to check the first <li>.
283
+ const firstLi = Array.from(el.children).find(
284
+ (c) => c.tagName.toLowerCase() === 'li'
285
+ );
286
+ if (firstLi) {
287
+ const role = firstLi.getAttribute('role');
288
+ const className = firstLi.getAttribute('class') || '';
289
+
290
+ // Matches Google Docs (role="checkbox") OR MS Word (class includes "checklist")
291
+ if (role === 'checkbox' || className.includes('checklist')) {
292
+ return true;
293
+ }
294
+ }
295
+
296
+ return false;
297
+ }
298
+
299
+ function isGoogleDocsWrapper(el: Element, tag: string): boolean {
300
+ if (tag !== 'b') return false;
301
+ const id = el.getAttribute('id');
302
+ return !!id && id.startsWith('docs-internal-guid-') && id.length > 20;
303
+ }
304
+
305
+ function escapeText(s: string): string {
306
+ return s.replace(/[&<>"']/g, (match) => {
307
+ switch (match) {
308
+ case '&':
309
+ return '&amp;';
310
+ case '<':
311
+ return '&lt;';
312
+ case '>':
313
+ return '&gt;';
314
+ case '"':
315
+ return '&quot;';
316
+ case "'":
317
+ return '&#39;';
318
+ default:
319
+ return match;
320
+ }
321
+ });
322
+ }
323
+
324
+ // --- Blockquote content flattening ---
325
+
326
+ function flushInlineP(ib: { buf: string }, out: { buf: string }): void {
327
+ if (ib.buf.length > 0) {
328
+ out.buf += `<p>${ib.buf}</p>`;
329
+ ib.buf = '';
330
+ }
331
+ }
332
+
333
+ function flattenBqChildren(
334
+ node: Element,
335
+ ib: { buf: string },
336
+ out: { buf: string }
337
+ ): void {
338
+ for (const child of Array.from(node.childNodes)) {
339
+ flattenBqNode(child, ib, out);
340
+ }
341
+ }
342
+
343
+ function flattenBqNode(
344
+ node: Node,
345
+ ib: { buf: string },
346
+ out: { buf: string }
347
+ ): void {
348
+ if (isText(node)) {
349
+ const t = node.textContent ?? '';
350
+ ib.buf += escapeText(t);
351
+ return;
352
+ }
353
+ if (!isElement(node)) return;
354
+ if (isBrNode(node)) {
355
+ flushInlineP(ib, out);
356
+ return;
357
+ }
358
+ if (isBlockProducing(node) || isBlockquoteNode(node)) {
359
+ flushInlineP(ib, out);
360
+ flattenBqChildren(node, ib, out);
361
+ flushInlineP(ib, out);
362
+ return;
363
+ }
364
+ walkNode(node, ib);
365
+ }
366
+
367
+ // --- List item content flattening ---
368
+
369
+ type LiCtx = {
370
+ el: Element;
371
+ styles: CssStyles;
372
+ nestedLists: Element[];
373
+ hasEmitted: boolean;
374
+ };
375
+
376
+ function flushLiBuffer(
377
+ ib: { buf: string },
378
+ out: { buf: string },
379
+ ctx: LiCtx
380
+ ): void {
381
+ if (ib.buf.length === 0) return;
382
+ out.buf += `<li${emitAttributes(ctx.el, 'li')}>`;
383
+ out.buf += emitStylesOpen(ctx.styles);
384
+ out.buf += ib.buf;
385
+ out.buf += emitStylesClose(ctx.styles);
386
+ out.buf += '</li>';
387
+ ib.buf = '';
388
+ ctx.hasEmitted = true;
389
+ }
390
+
391
+ function flattenLiChildren(
392
+ node: Element,
393
+ ib: { buf: string },
394
+ out: { buf: string },
395
+ ctx: LiCtx
396
+ ): void {
397
+ for (const child of Array.from(node.childNodes)) {
398
+ flattenLiNode(child, ib, out, ctx);
399
+ }
400
+ }
401
+
402
+ function flattenLiNode(
403
+ node: Node,
404
+ ib: { buf: string },
405
+ out: { buf: string },
406
+ ctx: LiCtx
407
+ ): void {
408
+ if (isText(node)) {
409
+ ib.buf += escapeText(node.textContent ?? '');
410
+ return;
411
+ }
412
+ if (!isElement(node)) return;
413
+
414
+ if (tagName(node) === 'img') {
415
+ const role = ctx.el.getAttribute('role');
416
+ // strip the <img> that Google Docs uses for the display of a checkbox icon
417
+ if (role === 'checkbox') {
418
+ return;
419
+ }
420
+ }
421
+
422
+ if (isListNode(node)) {
423
+ ctx.nestedLists.push(node);
424
+ return;
425
+ }
426
+ if (isBrNode(node)) {
427
+ flushLiBuffer(ib, out, ctx);
428
+ return;
429
+ }
430
+ if (isBlockProducing(node) || isBlockquoteNode(node)) {
431
+ flushLiBuffer(ib, out, ctx);
432
+ flattenLiChildren(node, ib, out, ctx);
433
+ flushLiBuffer(ib, out, ctx);
434
+ return;
435
+ }
436
+ walkNode(node, ib);
437
+ }
438
+
439
+ // --- Main walker ---
440
+
441
+ function walkChildren(node: Element, out: { buf: string }): void {
442
+ const children = Array.from(node.childNodes);
443
+ const parentIsList = isListNode(node);
444
+
445
+ let hasBlock = false;
446
+ for (const c of children) {
447
+ if (isBlockProducing(c)) {
448
+ hasBlock = true;
449
+ break;
450
+ }
451
+ }
452
+
453
+ let i = 0;
454
+ while (i < children.length) {
455
+ const child = children[i]!;
456
+
457
+ // Flatten list-inside-list
458
+ if (parentIsList && isElement(child) && isListNode(child)) {
459
+ walkChildren(child, out);
460
+ i++;
461
+ continue;
462
+ }
463
+
464
+ // Merge consecutive blockquotes
465
+ if (isElement(child) && isBlockquoteNode(child)) {
466
+ out.buf += '<blockquote>';
467
+ const bqIb = { buf: '' };
468
+ while (i < children.length) {
469
+ const cur = children[i];
470
+ if (!cur || !isElement(cur) || !isBlockquoteNode(cur)) break;
471
+ flattenBqChildren(cur, bqIb, out);
472
+ i++;
473
+ }
474
+ flushInlineP(bqIb, out);
475
+ out.buf += '</blockquote>';
476
+ continue;
477
+ }
478
+
479
+ // Auto-paragraph: group inline runs into <p> when mixed with blocks
480
+ if (
481
+ hasBlock &&
482
+ !parentIsList &&
483
+ !isBlockProducing(child) &&
484
+ !(isElement(child) && isBlockquoteNode(child))
485
+ ) {
486
+ const ib = { buf: '' };
487
+ while (i < children.length) {
488
+ const cur = children[i]!;
489
+ if (
490
+ isBlockProducing(cur) ||
491
+ (isElement(cur) && isBlockquoteNode(cur))
492
+ ) {
493
+ break;
494
+ }
495
+ if (isElement(cur) && isBrNode(cur)) {
496
+ if (ib.buf.length > 0) {
497
+ flushInlineP(ib, out);
498
+ } else {
499
+ out.buf += '<br>';
500
+ }
501
+ i++;
502
+ continue;
503
+ }
504
+ // Transparent inline wrapper for block/bq children
505
+ if (isElement(cur) && hasBlockOrBqChild(cur)) {
506
+ flushInlineP(ib, out);
507
+ walkChildren(cur, out);
508
+ i++;
509
+ continue;
510
+ }
511
+ walkNode(cur, ib);
512
+ i++;
513
+ }
514
+ flushInlineP(ib, out);
515
+ continue;
516
+ }
517
+
518
+ walkNode(child, out);
519
+ i++;
520
+ }
521
+ }
522
+
523
+ function walkNode(node: Node, out: { buf: string }): void {
524
+ if (isText(node)) {
525
+ out.buf += escapeText(node.textContent ?? '');
526
+ return;
527
+ }
528
+ if (!isElement(node)) return;
529
+
530
+ const name = node.tagName.toLowerCase();
531
+
532
+ if (STRIPPED_TAGS.has(name)) return;
533
+
534
+ if (isGoogleDocsWrapper(node, name)) {
535
+ walkChildren(node, out);
536
+ return;
537
+ }
538
+
539
+ const outName = canonicalName(name);
540
+ const cls = classifyTag(name);
541
+
542
+ // <span>: CSS style → inline tags
543
+ if (name === 'span') {
544
+ const s = parseCssStyle(node.getAttribute('style'));
545
+ out.buf += emitStylesOpen(s);
546
+ walkChildren(node, out);
547
+ out.buf += emitStylesClose(s);
548
+ return;
549
+ }
550
+
551
+ // <div>: becomes <p> or passes through
552
+ if (name === 'div') {
553
+ const s = parseCssStyle(node.getAttribute('style'));
554
+ if (isPurelyInline(node)) {
555
+ const pb = { buf: '' };
556
+ for (const dc of Array.from(node.childNodes)) {
557
+ if (isElement(dc) && isBrNode(dc)) {
558
+ if (pb.buf.length > 0) {
559
+ out.buf += `<p>${emitStylesOpen(s)}${pb.buf}${emitStylesClose(s)}</p>`;
560
+ } else {
561
+ out.buf += '<br>';
562
+ }
563
+ pb.buf = '';
564
+ continue;
565
+ }
566
+ walkNode(dc, pb);
567
+ }
568
+ if (pb.buf.length > 0) {
569
+ out.buf += `<p>${emitStylesOpen(s)}${pb.buf}${emitStylesClose(s)}</p>`;
570
+ }
571
+ } else {
572
+ out.buf += emitStylesOpen(s);
573
+ walkChildren(node, out);
574
+ out.buf += emitStylesClose(s);
575
+ }
576
+ return;
577
+ }
578
+
579
+ // Table elements
580
+ if (TABLE_TAGS.has(name)) {
581
+ if (name === 'td' || name === 'th') {
582
+ walkChildren(node, out);
583
+ // Append space if there is a next element sibling
584
+ let sib: Node | null = node.nextSibling;
585
+ while (sib && !isElement(sib)) sib = sib.nextSibling;
586
+ if (sib) out.buf += ' ';
587
+ } else if (name === 'tr') {
588
+ const row = { buf: '' };
589
+ walkChildren(node, row);
590
+ if (row.buf.length > 0) {
591
+ out.buf += `<p>${row.buf}</p>`;
592
+ }
593
+ } else {
594
+ walkChildren(node, out);
595
+ }
596
+ return;
597
+ }
598
+
599
+ if (cls === 'pass' || cls === 'skip') {
600
+ walkChildren(node, out);
601
+ return;
602
+ }
603
+
604
+ if (cls === 'self-closing') {
605
+ out.buf += `<${outName}${emitAttributes(node, outName)}`;
606
+ out.buf += outName === 'img' ? ' />' : '>';
607
+ return;
608
+ }
609
+
610
+ // inline or block
611
+ const es = extraStyles(parseCssStyle(node.getAttribute('style')), outName);
612
+
613
+ // <li>: flatten
614
+ if (outName === 'li') {
615
+ const nestedLists: Element[] = [];
616
+ const liIb = { buf: '' };
617
+ const ctx: LiCtx = { el: node, styles: es, nestedLists, hasEmitted: false };
618
+ flattenLiChildren(node, liIb, out, ctx);
619
+ flushLiBuffer(liIb, out, ctx);
620
+
621
+ // if nothing emitted - the <li> is empty, we add it manually
622
+ if (!ctx.hasEmitted) {
623
+ out.buf += `<li${emitAttributes(ctx.el, 'li')}></li>`;
624
+ }
625
+
626
+ for (const nl of nestedLists) walkChildren(nl, out);
627
+ return;
628
+ }
629
+
630
+ // <codeblock>: wrap inline content in <p>
631
+ if (outName === 'codeblock') {
632
+ const wrap = isPurelyInline(node);
633
+ out.buf += '<codeblock>';
634
+ if (wrap) out.buf += '<p>';
635
+ walkChildren(node, out);
636
+ if (wrap) out.buf += '</p>';
637
+ out.buf += '</codeblock>';
638
+ return;
639
+ }
640
+
641
+ // Generic block/inline
642
+ out.buf += `<${outName}${emitAttributes(node, outName)}>`;
643
+ out.buf += emitStylesOpen(es);
644
+ walkChildren(node, out);
645
+ out.buf += emitStylesClose(es);
646
+ out.buf += `</${outName}>`;
647
+ }
648
+
649
+ export function normalizeHtml(html: string): string {
650
+ const parser = new DOMParser();
651
+ const doc = parser.parseFromString(`<body>${html}</body>`, 'text/html');
652
+ const body = doc.body;
653
+ const out = { buf: '' };
654
+ walkChildren(body, out);
655
+ return out.buf;
656
+ }