react-native-marcus 0.1.0

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 (304) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +227 -0
  3. package/RNMarcus.podspec +78 -0
  4. package/android/build.gradle +220 -0
  5. package/android/proguard-rules.pro +1 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/AndroidManifestNew.xml +2 -0
  8. package/android/src/main/cpp/CMakeLists.txt +41 -0
  9. package/android/src/main/cpp/MarkdownParser.cpp +49 -0
  10. package/android/src/main/cpp/MarkdownParser.h +56 -0
  11. package/android/src/main/cpp/OnLoad.cpp +17 -0
  12. package/android/src/main/java/app/getbullet/marcus/MarcusModule.kt +28 -0
  13. package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +31 -0
  14. package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +63 -0
  15. package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorViewManager.kt +32 -0
  16. package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorViewManagerSpec.kt +21 -0
  17. package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +333 -0
  18. package/android/src/main/java/app/getbullet/marcus/MarkdownParseResult.kt +12 -0
  19. package/android/src/main/java/app/getbullet/marcus/MarkdownParser.kt +89 -0
  20. package/android/src/main/java/app/getbullet/marcus/MarkdownRange.kt +11 -0
  21. package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +81 -0
  22. package/android/src/main/java/app/getbullet/marcus/MarkdownTextWatcher.kt +17 -0
  23. package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +84 -0
  24. package/android/src/main/java/app/getbullet/marcus/ReactLineHeight.java +29 -0
  25. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +6 -0
  26. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +105 -0
  27. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +67 -0
  28. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +6 -0
  29. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontFamilySpan.kt +28 -0
  30. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontSizeSpan.kt +7 -0
  31. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownForegroundColorSpan.kt +6 -0
  32. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +57 -0
  33. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +6 -0
  34. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLineHeightSpan.kt +21 -0
  35. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownSpan.kt +7 -0
  36. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownStrikethroughSpan.kt +5 -0
  37. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownUnderlineSpan.kt +5 -0
  38. package/android/src/main/new_arch/CMakeLists.txt +70 -0
  39. package/android/src/main/new_arch/RNMarcusSpec.cpp +43 -0
  40. package/android/src/main/new_arch/RNMarcusSpec.h +36 -0
  41. package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorShadowNode.cpp +218 -0
  42. package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorShadowNode.h +54 -0
  43. package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +156 -0
  44. package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +130 -0
  45. package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +205 -0
  46. package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +78 -0
  47. package/android/src/test/resources/render-model.txt +583 -0
  48. package/apple/MarcusModule.h +8 -0
  49. package/apple/MarcusModule.mm +26 -0
  50. package/apple/MarcusRange.h +15 -0
  51. package/apple/MarcusRange.mm +15 -0
  52. package/apple/MarcusTextInputDecoratorComponentView.h +12 -0
  53. package/apple/MarcusTextInputDecoratorComponentView.mm +121 -0
  54. package/apple/MarcusTextInputDecoratorShadowNode.cpp +251 -0
  55. package/apple/MarcusTextInputDecoratorShadowNode.h +65 -0
  56. package/apple/MarcusTextInputDecoratorShadowNode.mm +204 -0
  57. package/apple/MarcusTextInputDecoratorViewManager.h +8 -0
  58. package/apple/MarcusTextInputDecoratorViewManager.mm +11 -0
  59. package/apple/MarkdownAdaptiveImageGlyph.swift +44 -0
  60. package/apple/MarkdownAttributes.h +31 -0
  61. package/apple/MarkdownAttributes.m +9 -0
  62. package/apple/MarkdownBackedTextInputDelegate.swift +117 -0
  63. package/apple/MarkdownDecorator.swift +316 -0
  64. package/apple/MarkdownFonts.swift +21 -0
  65. package/apple/MarkdownFormatter+React.swift +51 -0
  66. package/apple/MarkdownFormatter.swift +509 -0
  67. package/apple/MarkdownLog.h +10 -0
  68. package/apple/MarkdownLog.mm +6 -0
  69. package/apple/MarkdownParser.swift +200 -0
  70. package/apple/MarkdownSwiftInterop.h +23 -0
  71. package/apple/MarkdownTextBackground.swift +17 -0
  72. package/apple/MarkdownTextFieldObserver.swift +90 -0
  73. package/apple/MarkdownTextLayoutFragment.swift +205 -0
  74. package/apple/MarkdownTextLayoutManagerDelegate.swift +84 -0
  75. package/apple/MarkdownTextStorageDelegate.swift +32 -0
  76. package/apple/MarkdownTextViewObserver.swift +30 -0
  77. package/apple/RCTMarcusStyle+Codegen.h +18 -0
  78. package/apple/RCTMarcusStyle+Codegen.mm +64 -0
  79. package/apple/RCTMarcusStyle.h +41 -0
  80. package/apple/RCTMarcusStyle.m +5 -0
  81. package/apple/RCTMarcusUtils.h +26 -0
  82. package/apple/RCTMarcusUtils.mm +26 -0
  83. package/apple/RCTTextInput+AdaptiveImageGlyph.h +16 -0
  84. package/apple/RCTTextInput+AdaptiveImageGlyph.mm +79 -0
  85. package/apple/RCTTextInputComponentView+Marcus.h +11 -0
  86. package/apple/RCTTextInputComponentView+Marcus.mm +79 -0
  87. package/cpp/MarkdownCxx.modulemap +8 -0
  88. package/cpp/MarkdownGlobal.cpp +48 -0
  89. package/cpp/MarkdownGlobal.h +33 -0
  90. package/cpp/MarkdownWorkletParser.cpp +93 -0
  91. package/cpp/MarkdownWorkletParser.h +45 -0
  92. package/cpp/RuntimeDecorator.cpp +39 -0
  93. package/cpp/RuntimeDecorator.h +14 -0
  94. package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorState.h +33 -0
  95. package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorViewComponentDescriptor.h +17 -0
  96. package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
  97. package/lib/commonjs/MarkdownTextInput.js +99 -0
  98. package/lib/commonjs/MarkdownTextInput.js.map +1 -0
  99. package/lib/commonjs/MarkdownTextInput.web.js +618 -0
  100. package/lib/commonjs/MarkdownTextInput.web.js.map +1 -0
  101. package/lib/commonjs/NativeMarcusModule.js +9 -0
  102. package/lib/commonjs/NativeMarcusModule.js.map +1 -0
  103. package/lib/commonjs/commonTypes.js +2 -0
  104. package/lib/commonjs/commonTypes.js.map +1 -0
  105. package/lib/commonjs/global.d.js +2 -0
  106. package/lib/commonjs/global.d.js.map +1 -0
  107. package/lib/commonjs/index.js +27 -0
  108. package/lib/commonjs/index.js.map +1 -0
  109. package/lib/commonjs/package.json +1 -0
  110. package/lib/commonjs/parser/index.js +7030 -0
  111. package/lib/commonjs/parser/index.js.map +7 -0
  112. package/lib/commonjs/rangeUtils.js +71 -0
  113. package/lib/commonjs/rangeUtils.js.map +1 -0
  114. package/lib/commonjs/react-native-web.d.js +2 -0
  115. package/lib/commonjs/react-native-web.d.js.map +1 -0
  116. package/lib/commonjs/styleUtils.js +91 -0
  117. package/lib/commonjs/styleUtils.js.map +1 -0
  118. package/lib/commonjs/web/InputHistory.js +111 -0
  119. package/lib/commonjs/web/InputHistory.js.map +1 -0
  120. package/lib/commonjs/web/MarkdownTextInput.css +72 -0
  121. package/lib/commonjs/web/utils/blockLayout.js +132 -0
  122. package/lib/commonjs/web/utils/blockLayout.js.map +1 -0
  123. package/lib/commonjs/web/utils/blockUtils.js +219 -0
  124. package/lib/commonjs/web/utils/blockUtils.js.map +1 -0
  125. package/lib/commonjs/web/utils/browserUtils.js +40 -0
  126. package/lib/commonjs/web/utils/browserUtils.js.map +1 -0
  127. package/lib/commonjs/web/utils/cursorUtils.js +157 -0
  128. package/lib/commonjs/web/utils/cursorUtils.js.map +1 -0
  129. package/lib/commonjs/web/utils/inputUtils.js +152 -0
  130. package/lib/commonjs/web/utils/inputUtils.js.map +1 -0
  131. package/lib/commonjs/web/utils/measureUtils.js +0 -0
  132. package/lib/commonjs/web/utils/measureUtils.js.map +1 -0
  133. package/lib/commonjs/web/utils/parserUtils.js +552 -0
  134. package/lib/commonjs/web/utils/parserUtils.js.map +1 -0
  135. package/lib/commonjs/web/utils/treeUtils.js +109 -0
  136. package/lib/commonjs/web/utils/treeUtils.js.map +1 -0
  137. package/lib/commonjs/web/utils/webStyleUtils.js +59 -0
  138. package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -0
  139. package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
  140. package/lib/module/MarkdownTextInput.js +94 -0
  141. package/lib/module/MarkdownTextInput.js.map +1 -0
  142. package/lib/module/MarkdownTextInput.web.js +612 -0
  143. package/lib/module/MarkdownTextInput.web.js.map +1 -0
  144. package/lib/module/NativeMarcusModule.js +5 -0
  145. package/lib/module/NativeMarcusModule.js.map +1 -0
  146. package/lib/module/commonTypes.js +2 -0
  147. package/lib/module/commonTypes.js.map +1 -0
  148. package/lib/module/global.d.js +2 -0
  149. package/lib/module/global.d.js.map +1 -0
  150. package/lib/module/index.js +5 -0
  151. package/lib/module/index.js.map +1 -0
  152. package/lib/module/package.json +1 -0
  153. package/lib/module/parser/index.js +7029 -0
  154. package/lib/module/parser/index.js.map +7 -0
  155. package/lib/module/rangeUtils.js +66 -0
  156. package/lib/module/rangeUtils.js.map +1 -0
  157. package/lib/module/react-native-web.d.js +2 -0
  158. package/lib/module/react-native-web.d.js.map +1 -0
  159. package/lib/module/styleUtils.js +88 -0
  160. package/lib/module/styleUtils.js.map +1 -0
  161. package/lib/module/web/InputHistory.js +106 -0
  162. package/lib/module/web/InputHistory.js.map +1 -0
  163. package/lib/module/web/MarkdownTextInput.css +72 -0
  164. package/lib/module/web/utils/blockLayout.js +128 -0
  165. package/lib/module/web/utils/blockLayout.js.map +1 -0
  166. package/lib/module/web/utils/blockUtils.js +210 -0
  167. package/lib/module/web/utils/blockUtils.js.map +1 -0
  168. package/lib/module/web/utils/browserUtils.js +36 -0
  169. package/lib/module/web/utils/browserUtils.js.map +1 -0
  170. package/lib/module/web/utils/cursorUtils.js +150 -0
  171. package/lib/module/web/utils/cursorUtils.js.map +1 -0
  172. package/lib/module/web/utils/inputUtils.js +144 -0
  173. package/lib/module/web/utils/inputUtils.js.map +1 -0
  174. package/lib/module/web/utils/measureUtils.js +0 -0
  175. package/lib/module/web/utils/measureUtils.js.map +1 -0
  176. package/lib/module/web/utils/parserUtils.js +547 -0
  177. package/lib/module/web/utils/parserUtils.js.map +1 -0
  178. package/lib/module/web/utils/treeUtils.js +103 -0
  179. package/lib/module/web/utils/treeUtils.js.map +1 -0
  180. package/lib/module/web/utils/webStyleUtils.js +54 -0
  181. package/lib/module/web/utils/webStyleUtils.js.map +1 -0
  182. package/lib/typescript/commonjs/e2e/checks.test.d.ts +2 -0
  183. package/lib/typescript/commonjs/e2e/checks.test.d.ts.map +1 -0
  184. package/lib/typescript/commonjs/e2e/editing.test.d.ts +2 -0
  185. package/lib/typescript/commonjs/e2e/editing.test.d.ts.map +1 -0
  186. package/lib/typescript/commonjs/e2e/helpers.d.ts +23 -0
  187. package/lib/typescript/commonjs/e2e/helpers.d.ts.map +1 -0
  188. package/lib/typescript/commonjs/e2e/smoke.test.d.ts +2 -0
  189. package/lib/typescript/commonjs/e2e/smoke.test.d.ts.map +1 -0
  190. package/lib/typescript/commonjs/package.json +1 -0
  191. package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +63 -0
  192. package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -0
  193. package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +22 -0
  194. package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -0
  195. package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +41 -0
  196. package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -0
  197. package/lib/typescript/commonjs/src/NativeMarcusModule.d.ts +7 -0
  198. package/lib/typescript/commonjs/src/NativeMarcusModule.d.ts.map +1 -0
  199. package/lib/typescript/commonjs/src/commonTypes.d.ts +10 -0
  200. package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -0
  201. package/lib/typescript/commonjs/src/index.d.ts +5 -0
  202. package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
  203. package/lib/typescript/commonjs/src/parser/index.d.ts +8 -0
  204. package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -0
  205. package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts +8 -0
  206. package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -0
  207. package/lib/typescript/commonjs/src/rangeUtils.d.ts +6 -0
  208. package/lib/typescript/commonjs/src/rangeUtils.d.ts.map +1 -0
  209. package/lib/typescript/commonjs/src/styleUtils.d.ts +8 -0
  210. package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -0
  211. package/lib/typescript/commonjs/src/web/InputHistory.d.ts +24 -0
  212. package/lib/typescript/commonjs/src/web/InputHistory.d.ts.map +1 -0
  213. package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +41 -0
  214. package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -0
  215. package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +33 -0
  216. package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -0
  217. package/lib/typescript/commonjs/src/web/utils/browserUtils.d.ts +7 -0
  218. package/lib/typescript/commonjs/src/web/utils/browserUtils.d.ts.map +1 -0
  219. package/lib/typescript/commonjs/src/web/utils/cursorUtils.d.ts +12 -0
  220. package/lib/typescript/commonjs/src/web/utils/cursorUtils.d.ts.map +1 -0
  221. package/lib/typescript/commonjs/src/web/utils/inputUtils.d.ts +12 -0
  222. package/lib/typescript/commonjs/src/web/utils/inputUtils.d.ts.map +1 -0
  223. package/lib/typescript/commonjs/src/web/utils/measureUtils.d.ts +22 -0
  224. package/lib/typescript/commonjs/src/web/utils/measureUtils.d.ts.map +1 -0
  225. package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +44 -0
  226. package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -0
  227. package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +25 -0
  228. package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -0
  229. package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +7 -0
  230. package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -0
  231. package/lib/typescript/module/e2e/checks.test.d.ts +2 -0
  232. package/lib/typescript/module/e2e/checks.test.d.ts.map +1 -0
  233. package/lib/typescript/module/e2e/editing.test.d.ts +2 -0
  234. package/lib/typescript/module/e2e/editing.test.d.ts.map +1 -0
  235. package/lib/typescript/module/e2e/helpers.d.ts +23 -0
  236. package/lib/typescript/module/e2e/helpers.d.ts.map +1 -0
  237. package/lib/typescript/module/e2e/smoke.test.d.ts +2 -0
  238. package/lib/typescript/module/e2e/smoke.test.d.ts.map +1 -0
  239. package/lib/typescript/module/package.json +1 -0
  240. package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +63 -0
  241. package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -0
  242. package/lib/typescript/module/src/MarkdownTextInput.d.ts +22 -0
  243. package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -0
  244. package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +41 -0
  245. package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -0
  246. package/lib/typescript/module/src/NativeMarcusModule.d.ts +7 -0
  247. package/lib/typescript/module/src/NativeMarcusModule.d.ts.map +1 -0
  248. package/lib/typescript/module/src/commonTypes.d.ts +10 -0
  249. package/lib/typescript/module/src/commonTypes.d.ts.map +1 -0
  250. package/lib/typescript/module/src/index.d.ts +5 -0
  251. package/lib/typescript/module/src/index.d.ts.map +1 -0
  252. package/lib/typescript/module/src/parser/index.d.ts +8 -0
  253. package/lib/typescript/module/src/parser/index.d.ts.map +1 -0
  254. package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts +8 -0
  255. package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -0
  256. package/lib/typescript/module/src/rangeUtils.d.ts +6 -0
  257. package/lib/typescript/module/src/rangeUtils.d.ts.map +1 -0
  258. package/lib/typescript/module/src/styleUtils.d.ts +8 -0
  259. package/lib/typescript/module/src/styleUtils.d.ts.map +1 -0
  260. package/lib/typescript/module/src/web/InputHistory.d.ts +24 -0
  261. package/lib/typescript/module/src/web/InputHistory.d.ts.map +1 -0
  262. package/lib/typescript/module/src/web/utils/blockLayout.d.ts +41 -0
  263. package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -0
  264. package/lib/typescript/module/src/web/utils/blockUtils.d.ts +33 -0
  265. package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -0
  266. package/lib/typescript/module/src/web/utils/browserUtils.d.ts +7 -0
  267. package/lib/typescript/module/src/web/utils/browserUtils.d.ts.map +1 -0
  268. package/lib/typescript/module/src/web/utils/cursorUtils.d.ts +12 -0
  269. package/lib/typescript/module/src/web/utils/cursorUtils.d.ts.map +1 -0
  270. package/lib/typescript/module/src/web/utils/inputUtils.d.ts +12 -0
  271. package/lib/typescript/module/src/web/utils/inputUtils.d.ts.map +1 -0
  272. package/lib/typescript/module/src/web/utils/measureUtils.d.ts +22 -0
  273. package/lib/typescript/module/src/web/utils/measureUtils.d.ts.map +1 -0
  274. package/lib/typescript/module/src/web/utils/parserUtils.d.ts +44 -0
  275. package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -0
  276. package/lib/typescript/module/src/web/utils/treeUtils.d.ts +25 -0
  277. package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -0
  278. package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +7 -0
  279. package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -0
  280. package/package.json +167 -0
  281. package/react-native.config.js +10 -0
  282. package/src/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
  283. package/src/MarkdownTextInput.tsx +139 -0
  284. package/src/MarkdownTextInput.web.tsx +958 -0
  285. package/src/NativeMarcusModule.ts +8 -0
  286. package/src/commonTypes.ts +30 -0
  287. package/src/global.d.ts +1 -0
  288. package/src/index.tsx +4 -0
  289. package/src/parser/index.ts +556 -0
  290. package/src/parser/micromark-extension-mentions.ts +82 -0
  291. package/src/rangeUtils.ts +68 -0
  292. package/src/react-native-web.d.ts +1 -0
  293. package/src/styleUtils.ts +100 -0
  294. package/src/web/InputHistory.ts +137 -0
  295. package/src/web/MarkdownTextInput.css +72 -0
  296. package/src/web/utils/blockLayout.ts +163 -0
  297. package/src/web/utils/blockUtils.ts +245 -0
  298. package/src/web/utils/browserUtils.ts +41 -0
  299. package/src/web/utils/cursorUtils.ts +204 -0
  300. package/src/web/utils/inputUtils.ts +206 -0
  301. package/src/web/utils/measureUtils.ts +0 -0
  302. package/src/web/utils/parserUtils.ts +813 -0
  303. package/src/web/utils/treeUtils.ts +149 -0
  304. package/src/web/utils/webStyleUtils.ts +69 -0
@@ -0,0 +1,8 @@
1
+ import type { TurboModule } from "react-native"
2
+ import { TurboModuleRegistry } from "react-native"
3
+
4
+ interface Spec extends TurboModule {
5
+ install: () => boolean
6
+ }
7
+
8
+ export default TurboModuleRegistry.get<Spec>("MarcusModule")
@@ -0,0 +1,30 @@
1
+ type MarkdownType =
2
+ | "bold"
3
+ | "italic"
4
+ | "strikethrough"
5
+ | "emoji"
6
+ | "mention-here"
7
+ | "mention-user"
8
+ | "mention-short"
9
+ | "mention-report"
10
+ | "link"
11
+ | "code"
12
+ | "pre"
13
+ | "blockquote"
14
+ | "list-ordered"
15
+ | "list-unordered"
16
+ | "block-prefix"
17
+ | "heading"
18
+ | "syntax"
19
+ | "inline-image"
20
+ | "codeblock"
21
+
22
+ interface MarkdownRange {
23
+ type: MarkdownType
24
+ start: number
25
+ length: number
26
+ depth?: number
27
+ syntaxType?: "opening" | "closing"
28
+ }
29
+
30
+ export type { MarkdownType, MarkdownRange }
@@ -0,0 +1 @@
1
+ declare module "*.css"
package/src/index.tsx ADDED
@@ -0,0 +1,4 @@
1
+ export { default as MarkdownTextInput, getWorkletRuntime } from "./MarkdownTextInput"
2
+ export type { MarkdownTextInputProps, MarkdownStyle } from "./MarkdownTextInput"
3
+ export type { MarkdownType, MarkdownRange } from "./commonTypes"
4
+ export { parse as parser } from "./parser"
@@ -0,0 +1,556 @@
1
+ import emojiRegex from "emoji-regex"
2
+ import { parse, postprocess, preprocess } from "micromark"
3
+ import { gfmAutolinkLiteral } from "micromark-extension-gfm-autolink-literal"
4
+ import { gfmStrikethrough } from "micromark-extension-gfm-strikethrough"
5
+ import type { Token, TokenType } from "micromark-util-types"
6
+
7
+ import type { MarkdownRange, MarkdownType } from "../commonTypes"
8
+ import { mentions } from "./micromark-extension-mentions"
9
+
10
+ const EMOJI_TYPE = "emoji"
11
+ const SYNTAX_TYPE = "syntax"
12
+
13
+ const MAX_DEPTH = 6
14
+
15
+ const TOKENS: {
16
+ syntax: Set<TokenType>
17
+ block: Partial<Record<TokenType, MarkdownType>>
18
+ special: Partial<Record<TokenType, MarkdownType>>
19
+ scope: Partial<Record<TokenType, MarkdownType>>
20
+ content: Partial<Record<TokenType, MarkdownType>>
21
+ link: Partial<Record<TokenType, MarkdownType>>
22
+ } = {
23
+ syntax: new Set([
24
+ // Emphasis.
25
+ "emphasisSequence",
26
+ "strongSequence",
27
+ "strikethroughSequence",
28
+
29
+ // Headings.
30
+ "atxHeadingSequence",
31
+ "setextHeadingLineSequence",
32
+
33
+ // Block containers.
34
+ "blockQuoteMarker",
35
+
36
+ // Lists.
37
+ "listItemMarker",
38
+ "listItemValue",
39
+
40
+ // Thematic breaks.
41
+ "thematicBreakSequence",
42
+
43
+ // Links/images.
44
+ "labelMarker",
45
+ "labelImageMarker",
46
+ "referenceMarker",
47
+ "resourceMarker",
48
+
49
+ // Link/image destinations.
50
+ "resourceDestinationLiteralMarker",
51
+
52
+ // Titles.
53
+ "resourceTitleMarker",
54
+
55
+ // Definitions.
56
+ "definitionLabelMarker",
57
+ "definitionMarker",
58
+ "definitionDestinationLiteralMarker",
59
+ "definitionTitleMarker",
60
+
61
+ // Autolinks.
62
+ "autolinkMarker",
63
+
64
+ // Escapes.
65
+ "escapeMarker",
66
+
67
+ // Hard breaks.
68
+ "hardBreakEscape",
69
+ "hardBreakTrailing",
70
+
71
+ // Code.
72
+ "codeTextSequence",
73
+ "codeFencedFenceSequence",
74
+
75
+ // Character references.
76
+ "characterReferenceMarker",
77
+ "characterReferenceMarkerNumeric",
78
+ "characterReferenceMarkerHexadecimal",
79
+ ]),
80
+ block: {
81
+ blockQuote: "blockquote",
82
+
83
+ // Ordered and unordered stay separate types so a renderer can tell them
84
+ // apart. They nest independently: a line inside both carries one range of
85
+ // each, and the indents add up.
86
+ listOrdered: "list-ordered",
87
+ listUnordered: "list-unordered",
88
+ },
89
+ special: {
90
+ codeFenced: "codeblock",
91
+ codeIndented: "codeblock",
92
+ image: "inline-image",
93
+ },
94
+ scope: {
95
+ emphasis: "italic",
96
+ strong: "bold",
97
+ strikethrough: "strikethrough",
98
+
99
+ // Headings are absent: their level is only known once the marker token
100
+ // arrives, so they are emitted from emitHeading() instead.
101
+ },
102
+ content: {
103
+ mention: "mention-user",
104
+
105
+ codeTextData: "code",
106
+ codeFlowValue: "pre",
107
+
108
+ // TODO:
109
+ // htmlFlow: 'html',
110
+ // htmlText: 'html',
111
+ },
112
+ link: {
113
+ autolinkProtocol: "link",
114
+ autolinkEmail: "link",
115
+
116
+ literalAutolinkEmail: "link",
117
+ literalAutolinkHttp: "link",
118
+ literalAutolinkWww: "link",
119
+
120
+ resourceDestinationString: "link",
121
+ definitionDestinationString: "link",
122
+ },
123
+ }
124
+
125
+ const BLOCK_PREFIX = "block-prefix"
126
+
127
+ const EMOJI: {
128
+ scan: Set<TokenType>
129
+ exclude: Set<MarkdownType>
130
+ pattern: RegExp
131
+ } = {
132
+ scan: new Set([
133
+ // Normal text.
134
+ "data",
135
+
136
+ // Inline code content.
137
+ "codeTextData",
138
+
139
+ // Fenced/indented code content.
140
+ "codeFlowValue",
141
+
142
+ // HTML source.
143
+ "htmlFlowData",
144
+ "htmlTextData",
145
+
146
+ // Link destinations.
147
+ "resourceDestinationString",
148
+ "definitionDestinationString",
149
+
150
+ // Link/image titles, if present.
151
+ "resourceTitleString",
152
+ "definitionTitleString",
153
+
154
+ // Autolinks.
155
+ "autolinkProtocol",
156
+ "autolinkEmail",
157
+ ]),
158
+ exclude: new Set(["bold", "italic", "strikethrough"]),
159
+ pattern: emojiRegex(),
160
+ }
161
+
162
+ // Bundler shenanigans: when bundling esm export ends up in wrapper function body
163
+ // which is a syntax error so don't export anything at all.
164
+ // Instead declare export so typing still works.
165
+ declare module "./index" {
166
+ export function parse(markdown: string): MarkdownRange[]
167
+ }
168
+
169
+ declare global {
170
+ function __parse__micromark(markdown: string): MarkdownRange[]
171
+ }
172
+
173
+ globalThis.__parse__micromark = function (markdown: string): MarkdownRange[] {
174
+ // micromark's preprocessor drops a leading BOM, so every offset it reports is
175
+ // one short of the caller's string -- a document that opens with one had every
176
+ // range shifted left by a character, leaving the last one uncovered. Parse the
177
+ // stripped text and shift the ranges back at the end rather than second-
178
+ // guessing offsets at each of the two dozen places they are read.
179
+ const bomLength = markdown.charCodeAt(0) === 0xfeff ? 1 : 0
180
+
181
+ if (bomLength > 0) {
182
+ markdown = markdown.slice(bomLength)
183
+ }
184
+
185
+ const extensions = [gfmAutolinkLiteral(), gfmStrikethrough(), mentions()]
186
+ const ranges: MarkdownRange[] = []
187
+ // Counted block containers, innermost last. Never exceeds MAX_DEPTH per type.
188
+ const openBlocks: MarkdownType[] = []
189
+ // One entry per open container, recording whether it made it into openBlocks.
190
+ const countedBlocks: boolean[] = []
191
+ // Heading awaiting the marker that states its level.
192
+ let pendingHeading: Token | null = null
193
+ // The container markers on the line being built, in the order they appear.
194
+ // Positional rather than keyed by type: one type can open more than once on a
195
+ // line, and the occurrences need not be adjacent (`> - > `). Cleared per
196
+ // line -- a container whose marker was on an earlier line is being continued,
197
+ // not opened.
198
+ const markers: { type: MarkdownType; start: number; end: number }[] = []
199
+ const events = postprocess(
200
+ parse({ extensions }).document().write(preprocess()(markdown, "utf-8", true)),
201
+ )
202
+ let lineStart = 0
203
+
204
+ for (const event of events) {
205
+ if (event[0] === "enter") {
206
+ enter(event[1])
207
+ } else {
208
+ exit(event[1])
209
+ }
210
+ }
211
+
212
+ excludeEmoji()
213
+
214
+ if (bomLength > 0) {
215
+ for (const range of ranges) {
216
+ range.start += bomLength
217
+ }
218
+ }
219
+
220
+ return ranges
221
+
222
+ function enter(token: Token) {
223
+ if (EMOJI.scan.has(token.type)) {
224
+ emitEmoji(token)
225
+ }
226
+
227
+ // The heading itself opens before its marker, so it is held until the
228
+ // marker states the level. Both marker types also emit `syntax`, so this
229
+ // falls through rather than returning.
230
+ if (token.type === "atxHeading" || token.type === "setextHeading") {
231
+ pendingHeading = token
232
+ } else if (token.type === "atxHeadingSequence") {
233
+ // ATX level is the run of `#`. A closing run is ignored: the heading has
234
+ // already been emitted by then.
235
+ emitHeading(token.end.offset - token.start.offset)
236
+ } else if (token.type === "setextHeadingLineSequence") {
237
+ emitHeading(markdown.charCodeAt(token.start.offset) === 61 ? 1 : 2)
238
+ } else if (token.type === "listItemPrefix") {
239
+ const list = openBlocks[openBlocks.length - 1]
240
+
241
+ // Past the depth cap the item's own list was never opened, so its marker
242
+ // would be recorded against whatever encloses it.
243
+ if (list === "list-ordered" || list === "list-unordered") {
244
+ markers.push({ type: list, start: token.start.offset, end: token.end.offset })
245
+ }
246
+ } else if (token.type === "blockQuotePrefix") {
247
+ // One per level, so `>>> ` arrives as three markers; flushLine folds a
248
+ // contiguous run back into a single prefix.
249
+ markers.push({
250
+ type: "blockquote",
251
+ start: token.start.offset,
252
+ end: token.end.offset,
253
+ })
254
+ }
255
+
256
+ const block = TOKENS.block[token.type]
257
+
258
+ if (block) {
259
+ // Past the cap the container is ignored outright rather than counted and
260
+ // clamped on the way out, so nesting cannot grow the stack or the work
261
+ // flushLine does per line.
262
+ const counted = depthOf(block) < MAX_DEPTH
263
+ countedBlocks.push(counted)
264
+
265
+ if (counted) {
266
+ openBlocks.push(block)
267
+ }
268
+
269
+ return
270
+ }
271
+
272
+ if (token.type === "lineEnding" || token.type === "lineEndingBlank") {
273
+ flushLine(token.start.offset)
274
+
275
+ // The token can reach past the break itself: micromark folds the next
276
+ // line's container prefix into it, so `\n>` arrives as one lineEnding.
277
+ // Resuming at token.end would swallow that line, leaving a gap in the
278
+ // quote. Resume just after the break instead.
279
+ let next = token.start.offset
280
+ if (markdown.charCodeAt(next) === 13) {
281
+ next++
282
+ }
283
+ if (markdown.charCodeAt(next) === 10) {
284
+ next++
285
+ }
286
+ lineStart = next > token.start.offset ? next : token.end.offset
287
+
288
+ return
289
+ }
290
+
291
+ if (TOKENS.special[token.type]) {
292
+ emit(token, TOKENS.special[token.type]!)
293
+ return
294
+ }
295
+
296
+ if (TOKENS.syntax.has(token.type)) {
297
+ emit(token, SYNTAX_TYPE)
298
+ return
299
+ }
300
+
301
+ if (TOKENS.scope[token.type]) {
302
+ emit(token, TOKENS.scope[token.type]!)
303
+ return
304
+ }
305
+
306
+ if (TOKENS.content[token.type]) {
307
+ if (token.type === "codeFlowValue") {
308
+ emitCodeBlock(token, TOKENS.content[token.type]!)
309
+ } else {
310
+ emit(token, TOKENS.content[token.type]!)
311
+ }
312
+
313
+ return
314
+ }
315
+
316
+ if (TOKENS.link[token.type]) {
317
+ emit(token, TOKENS.link[token.type]!)
318
+ return
319
+ }
320
+ }
321
+
322
+ function exit(token: Token) {
323
+ if (!TOKENS.block[token.type]) {
324
+ return
325
+ }
326
+
327
+ flushLine(token.end.offset)
328
+
329
+ // Ignored containers were never pushed, so only counted ones pop. Skipped
330
+ // ones are always innermost, so this stays balanced.
331
+ if (countedBlocks.pop()) {
332
+ openBlocks.pop()
333
+ }
334
+ }
335
+
336
+ function emitHeading(level: number) {
337
+ if (!pendingHeading) {
338
+ return
339
+ }
340
+
341
+ push(
342
+ pendingHeading.start.offset,
343
+ pendingHeading.end.offset,
344
+ "heading",
345
+ level > MAX_DEPTH ? MAX_DEPTH : level,
346
+ )
347
+ pendingHeading = null
348
+ }
349
+
350
+ function depthOf(type: MarkdownType) {
351
+ let depth = 0
352
+
353
+ for (let i = 0; i < openBlocks.length; i++) {
354
+ if (openBlocks[i] === type) {
355
+ depth++
356
+ }
357
+ }
358
+
359
+ return depth
360
+ }
361
+
362
+ /**
363
+ * Emits the containers the finished line sits in, each preceded by the run of
364
+ * text its own marker occupies there.
365
+ *
366
+ * The markers are real characters that the containers nested inside them are
367
+ * laid out around, and how wide they render is a question only the platform
368
+ * can answer -- so the offsets are handed over and the arithmetic is done
369
+ * where the font is known.
370
+ *
371
+ * This prefix-then-container order is the contract the native formatters read
372
+ * (`apple/MarkdownFormatter.swift`, `android/.../MarkdownFormatter.kt`): each
373
+ * holds the `block-prefix` it just saw and hands it to the next container, so
374
+ * laying out a line is one left-to-right walk. It is deliberately *not* tree
375
+ * order -- a container arrives after the marker it encloses -- so the web
376
+ * builder re-sorts into containment order via `sortRanges`. Reordering the
377
+ * emission to suit web would break the pairing on both native platforms.
378
+ */
379
+ function flushLine(end: number) {
380
+ if (openBlocks.length > 0 && end > lineStart) {
381
+ let prefixStart = lineStart
382
+
383
+ for (let i = 0; i < openBlocks.length; i++) {
384
+ const type = openBlocks[i]!
385
+
386
+ // Emit each distinct type once, at its first occurrence.
387
+ if (openBlocks.indexOf(type) !== i) {
388
+ continue
389
+ }
390
+
391
+ // This type's next marker at or after the walk position. Taking the
392
+ // last one on the line instead -- which is what keying by type alone
393
+ // did -- stretches the prefix across any marker that sits between two
394
+ // occurrences, so `> - > ` gave the quote a `"> - > "` prefix and left
395
+ // the list with none at all.
396
+ const index = markers.findIndex((m) => m.type === type && m.start >= prefixStart)
397
+
398
+ if (index !== -1) {
399
+ // A contiguous run of one type is a single prefix: `>>> ` is three
400
+ // markers but one step over on the way to the content.
401
+ let markerEnd = markers[index]!.end
402
+
403
+ for (let j = index + 1; j < markers.length; j++) {
404
+ const marker = markers[j]!
405
+
406
+ if (marker.type !== type || marker.start !== markerEnd) {
407
+ break
408
+ }
409
+
410
+ markerEnd = marker.end
411
+ }
412
+
413
+ push(prefixStart, markerEnd, BLOCK_PREFIX)
414
+ prefixStart = markerEnd
415
+ }
416
+
417
+ push(lineStart, end, type, depthOf(type))
418
+ }
419
+ }
420
+
421
+ if (end > lineStart) {
422
+ lineStart = end
423
+ }
424
+
425
+ markers.length = 0
426
+ }
427
+
428
+ function emit(token: Token, type: MarkdownType) {
429
+ push(token.start.offset, token.end.offset, type)
430
+ }
431
+
432
+ function emitCodeBlock(token: Token, type: MarkdownType) {
433
+ let end = token.end.offset
434
+
435
+ if (markdown.charCodeAt(end) === 13) {
436
+ // CRLF
437
+ end += markdown.charCodeAt(end + 1) === 10 ? 2 : 1
438
+ } else if (markdown.charCodeAt(end) === 10) {
439
+ // LF
440
+ end++
441
+ }
442
+
443
+ push(token.start.offset, end, type)
444
+ }
445
+
446
+ function emitEmoji(token: Token) {
447
+ const start = token.start.offset
448
+ const end = token.end.offset
449
+
450
+ if (start >= end) return
451
+
452
+ const value = markdown.slice(start, end)
453
+
454
+ EMOJI.pattern.lastIndex = 0
455
+
456
+ let match: RegExpExecArray | null = null
457
+
458
+ while ((match = EMOJI.pattern.exec(value)) !== null) {
459
+ const emojiStart = start + match.index
460
+ const emojiEnd = emojiStart + match[0].length
461
+
462
+ push(emojiStart, emojiEnd, EMOJI_TYPE)
463
+
464
+ if (match[0].length === 0) {
465
+ EMOJI.pattern.lastIndex++
466
+ }
467
+ }
468
+ }
469
+
470
+ function push(start: number, end: number, type: MarkdownType, depth?: number) {
471
+ if (start >= end) return
472
+
473
+ const previous = ranges[ranges.length - 1]
474
+
475
+ if (
476
+ depth === undefined &&
477
+ // Two containers opening on one line put their prefixes back to back.
478
+ // Merging them would lose which prefix belongs to which container.
479
+ type !== BLOCK_PREFIX &&
480
+ // Adjacent syntax belongs to whatever encloses it, and neighbors often sit in different
481
+ // enclosures: `` >` `` is a quote marker then a code fence, `` ~a~` `` closes a
482
+ // strikethrough then opens one. A merged range would cover both and so straddle the end of
483
+ // the first one's container -- overlapping it without containing it or being contained by
484
+ // it, which is not a tree and renders the shared characters once per range. Runs that do
485
+ // share an enclosure, like `>>`, cost only an extra span.
486
+ type !== SYNTAX_TYPE &&
487
+ previous &&
488
+ previous.type === type &&
489
+ previous.start + previous.length === start
490
+ ) {
491
+ previous.length = end - previous.start
492
+ return
493
+ }
494
+
495
+ const range: MarkdownRange = {
496
+ start,
497
+ length: end - start,
498
+ type,
499
+ }
500
+
501
+ if (depth !== undefined) {
502
+ range.depth = depth
503
+ }
504
+
505
+ ranges.push(range)
506
+ }
507
+
508
+ function excludeEmoji() {
509
+ if (!ranges.find((r) => r.type === "emoji")) return
510
+
511
+ const result: MarkdownRange[] = []
512
+
513
+ for (const range of ranges) {
514
+ if (!EMOJI.exclude.has(range.type)) {
515
+ result.push(range)
516
+ continue
517
+ }
518
+
519
+ const end = range.start + range.length
520
+ let cursor = range.start
521
+
522
+ for (const emoji of ranges) {
523
+ if (emoji.type !== "emoji") continue
524
+
525
+ const emojiStart = emoji.start
526
+ const emojiEnd = emojiStart + emoji.length
527
+
528
+ if (emojiEnd <= cursor) continue
529
+ if (emojiStart >= end) break
530
+
531
+ if (emojiStart > cursor) {
532
+ result.push({
533
+ start: cursor,
534
+ length: emojiStart - cursor,
535
+ type: range.type,
536
+ })
537
+ }
538
+
539
+ cursor = Math.max(cursor, emojiEnd)
540
+
541
+ if (cursor >= end) break
542
+ }
543
+
544
+ if (cursor < end) {
545
+ result.push({
546
+ start: cursor,
547
+ length: end - cursor,
548
+ type: range.type,
549
+ })
550
+ }
551
+ }
552
+
553
+ ranges.length = 0
554
+ ranges.push(...result)
555
+ }
556
+ }
@@ -0,0 +1,82 @@
1
+ import type { Code, Effects, Extension, State } from "micromark-util-types"
2
+
3
+ const LETTER = /^\p{L}$/u
4
+ const WORD = /^[\p{L}\p{M}\p{N}_]$/u
5
+ const ALPHANUMERIC = /^[\p{L}\p{M}\p{N}]$/u
6
+
7
+ declare module "micromark-util-types" {
8
+ interface TokenTypeMap {
9
+ mention: "mention"
10
+ }
11
+ }
12
+
13
+ const mention = {
14
+ name: "mention",
15
+ tokenize,
16
+ } as const
17
+
18
+ export function mentions(): Extension {
19
+ return {
20
+ text: {
21
+ 64: mention, // @
22
+ },
23
+ }
24
+ }
25
+
26
+ function tokenize(effects: Effects, ok: State, nok: State): State {
27
+ return start
28
+
29
+ function start(code: Code): State {
30
+ if (code === null) {
31
+ return nok(code)!
32
+ }
33
+
34
+ effects.enter("mention")
35
+ effects.consume(code)
36
+
37
+ return first
38
+ }
39
+
40
+ function first(code: Code): State {
41
+ if (code === null || !test(LETTER, code)) {
42
+ return nok(code)!
43
+ }
44
+
45
+ effects.consume(code)
46
+
47
+ return body
48
+ }
49
+
50
+ function body(code: Code): State {
51
+ if (code !== null && test(WORD, code)) {
52
+ effects.consume(code)
53
+ return body
54
+ }
55
+
56
+ if (code === 45) {
57
+ // `-` is allowed only when followed by an alphanumeric character.
58
+ return hyphen
59
+ }
60
+
61
+ effects.exit("mention")
62
+ return ok(code)!
63
+ }
64
+
65
+ function hyphen(code: Code): State {
66
+ if (code === null || !test(ALPHANUMERIC, code)) {
67
+ return nok(code)!
68
+ }
69
+
70
+ effects.consume(code)
71
+
72
+ return body
73
+ }
74
+ }
75
+
76
+ function test(regex: RegExp, code: number): boolean {
77
+ if (code < 0 || code > 0x10ffff || (code >= 0xd800 && code <= 0xdfff)) {
78
+ return false
79
+ }
80
+
81
+ return regex.test(String.fromCodePoint(code))
82
+ }