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,121 @@
1
+ #import <React/RCTFabricComponentsPlugins.h>
2
+ #import <React/RCTTextInputComponentView.h>
3
+ #import <react/debug/react_native_assert.h>
4
+ #import <react/renderer/components/RNMarcusSpec/Props.h>
5
+
6
+ #import <RNMarcus/MarkdownSwiftInterop.h>
7
+ #import <RNMarcus/MarcusTextInputDecoratorComponentView.h>
8
+ #import <RNMarcus/MarcusTextInputDecoratorViewComponentDescriptor.h>
9
+ #import <RNMarcus/RCTMarcusStyle+Codegen.h>
10
+ #import <RNMarcus/RCTTextInput+AdaptiveImageGlyph.h>
11
+
12
+ using namespace facebook::react;
13
+
14
+ // Thin Objective-C++ shell around MarkdownDecorator.
15
+ //
16
+ // Only the parts that RCTComponentViewProtocol forces into Objective-C++ live
17
+ // here -- the component descriptor, the C++ props update, and view recycling.
18
+ // Everything else is delegated to Swift.
19
+ @implementation MarcusTextInputDecoratorComponentView {
20
+ MarkdownDecorator *_decorator;
21
+ }
22
+
23
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
24
+ return concreteComponentDescriptorProvider<
25
+ MarcusTextInputDecoratorViewComponentDescriptor>();
26
+ }
27
+
28
+ // Needed because of this: https://github.com/facebook/react-native/pull/37274
29
+ + (void)load {
30
+ [super load];
31
+ }
32
+
33
+ - (instancetype)initWithFrame:(CGRect)frame {
34
+ if (self = [super initWithFrame:frame]) {
35
+ static const auto defaultProps =
36
+ std::make_shared<const MarcusTextInputDecoratorViewProps>();
37
+ _props = defaultProps;
38
+ _decorator = [MarkdownDecorator new];
39
+ }
40
+
41
+ return self;
42
+ }
43
+
44
+ - (void)didAddSubview:(UIView *)subview {
45
+ [super didAddSubview:subview];
46
+
47
+ react_native_assert(!_decorator.isAttached && "MarcusTextInputDecoratorComponentView tried to add "
48
+ "TextInput observers while they were attached");
49
+ react_native_assert(
50
+ [subview isKindOfClass:[RCTTextInputComponentView class]] &&
51
+ "Child component of MarcusTextInputDecoratorComponentView is not an "
52
+ "instance of RCTTextInputComponentView."
53
+ );
54
+
55
+ [_decorator attachTo:subview];
56
+ }
57
+
58
+ - (void)willRemoveSubview:(UIView *)subview {
59
+ [_decorator detach];
60
+ [super willRemoveSubview:subview];
61
+ }
62
+
63
+ - (void)reattachTextInputObservers {
64
+ // React Native replaces its backed text input view in place when `multiline`
65
+ // changes, which never touches this view's own subviews. Called from the
66
+ // RCTTextInputComponentView swizzle so the observers follow the new view
67
+ // instead of staying bound to the discarded one.
68
+ UIView *textInputComponentView = self.subviews.firstObject;
69
+ if (textInputComponentView == nil) {
70
+ return;
71
+ }
72
+
73
+ [_decorator attachTo:textInputComponentView];
74
+ }
75
+
76
+ - (void)updateProps:(Props::Shared const &)props
77
+ oldProps:(Props::Shared const &)oldProps {
78
+ const auto &oldViewProps =
79
+ *std::static_pointer_cast<MarcusTextInputDecoratorViewProps const>(
80
+ _props
81
+ );
82
+ const auto &newViewProps =
83
+ *std::static_pointer_cast<MarcusTextInputDecoratorViewProps const>(
84
+ props
85
+ );
86
+
87
+ if (oldViewProps.parserId != newViewProps.parserId) {
88
+ _decorator.parserId = @(newViewProps.parserId);
89
+ }
90
+
91
+ // Codegen only emits operator== for the markdownStyle struct under
92
+ // RN_SERIALIZABLE_STATE, which is not defined on iOS, so there is no cheap
93
+ // way to tell whether the style actually changed. Rebuilding it and
94
+ // re-formatting on every props update is wasteful -- Fabric emits one
95
+ // whenever the props object is recreated -- but hand-written equality over
96
+ // ~27 fields would rot silently the moment a style prop is added.
97
+ _decorator.markdownStyle =
98
+ [[RCTMarcusStyle alloc] initWithStruct:newViewProps.markdownStyle];
99
+
100
+ [_decorator applyNewStyles];
101
+
102
+ [super updateProps:props oldProps:oldProps];
103
+ }
104
+
105
+ - (void)prepareForRecycle {
106
+ react_native_assert(!_decorator.isAttached && "MarcusTextInputDecoratorComponentView was being "
107
+ "recycled with TextInput observers still attached");
108
+ [super prepareForRecycle];
109
+
110
+ static const auto defaultProps =
111
+ std::make_shared<const MarcusTextInputDecoratorViewProps>();
112
+ _props = defaultProps;
113
+ [_decorator reset];
114
+ }
115
+
116
+ Class<RCTComponentViewProtocol>
117
+ MarcusTextInputDecoratorViewCls(void) {
118
+ return MarcusTextInputDecoratorComponentView.class;
119
+ }
120
+
121
+ @end
@@ -0,0 +1,251 @@
1
+ #include "MarcusTextInputDecoratorShadowNode.h"
2
+
3
+ #include <react/renderer/components/view/conversions.h>
4
+ #include <react/renderer/core/ComponentDescriptor.h>
5
+ #include <yoga/Yoga.h>
6
+
7
+ namespace facebook {
8
+ namespace react {
9
+
10
+ extern const char MarcusTextInputDecoratorViewComponentName[] =
11
+ "MarcusTextInputDecoratorView";
12
+
13
+ MarcusTextInputDecoratorShadowNode::MarcusTextInputDecoratorShadowNode(
14
+ ShadowNodeFragment const &fragment,
15
+ ShadowNodeFamily::Shared const &family,
16
+ ShadowNodeTraits traits
17
+ )
18
+ : ConcreteViewShadowNode(fragment, family, traits) {
19
+ initialize();
20
+ makeChildNodeMutable();
21
+
22
+ if (fragment.children) {
23
+ overwriteMeasureCallbackConnector();
24
+ }
25
+ }
26
+
27
+ MarcusTextInputDecoratorShadowNode::MarcusTextInputDecoratorShadowNode(
28
+ ShadowNode const &sourceShadowNode,
29
+ ShadowNodeFragment const &fragment
30
+ )
31
+ : ConcreteViewShadowNode(sourceShadowNode, fragment) {
32
+ // Carry the re-measure flag over from the node being cloned. It has to be set
33
+ // before makeChildNodeMutable() below, which is what reads it.
34
+ const auto &source =
35
+ static_cast<const MarcusTextInputDecoratorShadowNode &>(sourceShadowNode);
36
+ needsRemeasure_ = source.needsRemeasure_;
37
+
38
+ initialize();
39
+ makeChildNodeMutable();
40
+
41
+ if (fragment.children) {
42
+ overwriteMeasureCallbackConnector();
43
+ }
44
+ }
45
+
46
+ void
47
+ MarcusTextInputDecoratorShadowNode::initialize() {
48
+ // Setting display: contents style results in ForceFlattenView trait being set
49
+ // on the shadow node. This trait causes the node not to have a host view. By
50
+ // removing the trait, it's possible to force RN to create a host view, layout
51
+ // of which can then be customized.
52
+ ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView);
53
+ }
54
+
55
+ void
56
+ MarcusTextInputDecoratorShadowNode::makeChildNodeMutable() {
57
+ // When the decorator is cloned and has a child node, the child node should be
58
+ // cloned as well to ensure it is mutable.
59
+ const auto &children = getChildren();
60
+ if (!children.empty()) {
61
+ react_native_assert(
62
+ children.size() == 1 &&
63
+ "MarcusTextInputDecoratorView received more than one child"
64
+ );
65
+
66
+ const auto clonedChild = children[0]->clone({});
67
+ replaceChild(*children[0], clonedChild);
68
+ }
69
+ }
70
+
71
+ void
72
+ MarcusTextInputDecoratorShadowNode::overwriteMeasureCallbackConnector() {
73
+ const auto &children = getChildren();
74
+ if (children.empty()) {
75
+ return;
76
+ }
77
+ react_native_assert(
78
+ children.size() == 1 &&
79
+ "MarcusTextInputDecoratorView received more than one child"
80
+ );
81
+
82
+ const auto child = std::dynamic_pointer_cast<const TextInputShadowNode>(children[0]);
83
+ react_native_assert(
84
+ child != nullptr &&
85
+ "MarcusTextInputDecoratorView received child other than a TextInput"
86
+ );
87
+ child->ensureUnsealed();
88
+
89
+ // This is obviously not correct, but since both MarcusTextInputDecoratorShadowNode and
90
+ // TextInputShadowNode inherit from YogaLayoutableShadowNode by doing this cast it's
91
+ // possible to access protected members from TextInputShadowNode like yogaNode_.
92
+ // As only things from YogaLayoutableShadowNode are accessed, it should be safe,
93
+ // since the vtable should be the same between them.
94
+ const auto &nodeWithAccessibleYogaNode =
95
+ std::reinterpret_pointer_cast<const MarcusTextInputDecoratorShadowNode>(child);
96
+
97
+ // decorator node cannot have a measure function since it's not a leaf node
98
+ // but we can redirect measuring of the child input to call measureContent
99
+ // on the decorator
100
+ const auto &yogaNode = &nodeWithAccessibleYogaNode->yogaNode_;
101
+ YGNodeSetMeasureFunc(yogaNode, yogaNodeMeasureCallbackConnector);
102
+
103
+ // If a background parse finished since the last layout, the size Yoga has for
104
+ // the child was measured from unformatted text. Nothing dirties this part of
105
+ // the tree on its own -- Yoga never measures the decorator -- so the stale
106
+ // height would stick until something else changed the layout.
107
+ //
108
+ // Both nodes are marked by hand rather than via YGNodeMarkDirty(): the child
109
+ // is usually already dirty from completeClone(), which stops the flag
110
+ // propagating upwards and would leave the decorator clean. Parents pick it up
111
+ // themselves when updateYogaChildren() runs.
112
+ if (needsRemeasure_ != nullptr && needsRemeasure_->exchange(false)) {
113
+ yogaNode->setDirty(true);
114
+ yogaNode_.setDirty(true);
115
+ }
116
+ }
117
+
118
+ void
119
+ MarcusTextInputDecoratorShadowNode::appendChild(
120
+ const std::shared_ptr<const ShadowNode> &child
121
+ ) {
122
+ YogaLayoutableShadowNode::appendChild(child);
123
+
124
+ overwriteMeasureCallbackConnector();
125
+ }
126
+
127
+ void
128
+ MarcusTextInputDecoratorShadowNode::replaceChild(
129
+ const ShadowNode &oldChild, const std::shared_ptr<const ShadowNode> &newChild, size_t suggestedIndex
130
+ ) {
131
+ YogaLayoutableShadowNode::replaceChild(oldChild, newChild, suggestedIndex);
132
+
133
+ overwriteMeasureCallbackConnector();
134
+ };
135
+
136
+ Size
137
+ MarcusTextInputDecoratorShadowNode::measureContent(
138
+ const LayoutContext &layoutContext,
139
+ const LayoutConstraints &layoutConstraints
140
+ ) const {
141
+ const auto &children = getChildren();
142
+ react_native_assert(
143
+ children.size() == 1 &&
144
+ "MarcusTextInputDecoratorView received wrong number of children"
145
+ );
146
+
147
+ const auto child =
148
+ std::static_pointer_cast<const TextInputShadowNode>(children[0]);
149
+
150
+ child->ensureUnsealed();
151
+
152
+ // apply markdown formatting before measuring the child
153
+ const auto &mutableChild =
154
+ std::const_pointer_cast<TextInputShadowNode>(child);
155
+ applyMarkdownFormattingToTextInputState(mutableChild, layoutContext);
156
+
157
+ const auto childWithMeasureContentAccess =
158
+ std::static_pointer_cast<const YogaLayoutableShadowNode>(child);
159
+ return childWithMeasureContentAccess->measureContent(layoutContext, layoutConstraints);
160
+ }
161
+
162
+ void
163
+ MarcusTextInputDecoratorShadowNode::layout(LayoutContext layoutContext) {
164
+ YogaLayoutableShadowNode::layout(layoutContext);
165
+
166
+ const auto &children = getChildren();
167
+ react_native_assert(
168
+ children.size() == 1 &&
169
+ "MarcusTextInputDecoratorView didn't receive exactly one child"
170
+ );
171
+
172
+ const auto child =
173
+ std::static_pointer_cast<const TextInputShadowNode>(children[0]);
174
+
175
+ child->ensureUnsealed();
176
+
177
+ const auto &mutableChild =
178
+ std::const_pointer_cast<TextInputShadowNode>(child);
179
+
180
+ // TODO: this may not be the correct way to do this
181
+ // Since nodes with display: contents are skipped during layout, they have
182
+ // zero-layout. To properly display the view, assign the layout metrics from
183
+ // the child (text input, which was calculated by Yoga) to the decorator view.
184
+ auto childMetrics = child->getLayoutMetrics();
185
+ setLayoutMetrics(childMetrics);
186
+
187
+ // Then, it's also needed to update the metrics on the child as the position
188
+ // is relative to the parent, which was just moved above. By zeroing the
189
+ // origin, the child is effectively moved to the same position it was before
190
+ // the manipulation here.
191
+ childMetrics.frame.origin = Point{};
192
+ mutableChild->setLayoutMetrics(childMetrics);
193
+ }
194
+
195
+ // this is private in YogaLayoutableShadowNode
196
+ YGSize
197
+ MarcusTextInputDecoratorShadowNode::yogaNodeMeasureCallbackConnector(
198
+ YGNodeConstRef yogaNode, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode
199
+ ) {
200
+
201
+ auto minimumSize = Size{0, 0};
202
+ auto maximumSize = Size{std::numeric_limits<Float>::infinity(), std::numeric_limits<Float>::infinity()};
203
+
204
+ switch (widthMode) {
205
+ case YGMeasureModeUndefined:
206
+ break;
207
+ case YGMeasureModeExactly:
208
+ minimumSize.width = floatFromYogaFloat(width);
209
+ maximumSize.width = floatFromYogaFloat(width);
210
+ break;
211
+ case YGMeasureModeAtMost:
212
+ maximumSize.width = floatFromYogaFloat(width);
213
+ break;
214
+ }
215
+
216
+ switch (heightMode) {
217
+ case YGMeasureModeUndefined:
218
+ break;
219
+ case YGMeasureModeExactly:
220
+ minimumSize.height = floatFromYogaFloat(height);
221
+ maximumSize.height = floatFromYogaFloat(height);
222
+ break;
223
+ case YGMeasureModeAtMost:
224
+ maximumSize.height = floatFromYogaFloat(height);
225
+ break;
226
+ }
227
+
228
+ // This is where changes begin compared to the copied code
229
+ const auto &decoratorYogaNode = YGNodeGetParent(const_cast<YGNodeRef>(yogaNode));
230
+ const auto &decoratorShadowNode = shadowNodeFromContext(decoratorYogaNode);
231
+
232
+ LayoutContext context{};
233
+ context.fontSizeMultiplier = fontSizeMultiplier();
234
+
235
+ const auto size = decoratorShadowNode.measureContent(context, {minimumSize, maximumSize});
236
+
237
+ return YGSize{yogaFloatFromFloat(size.width), yogaFloatFromFloat(size.height)};
238
+ }
239
+
240
+ // this is private in YogaLayoutableShadowNode
241
+ YogaLayoutableShadowNode &
242
+ MarcusTextInputDecoratorShadowNode::shadowNodeFromContext(
243
+ YGNodeConstRef yogaNode
244
+ ) {
245
+ return dynamic_cast<YogaLayoutableShadowNode &>(
246
+ *static_cast<ShadowNode *>(YGNodeGetContext(yogaNode))
247
+ );
248
+ }
249
+
250
+ } // namespace react
251
+ } // namespace facebook
@@ -0,0 +1,65 @@
1
+ #pragma once
2
+
3
+ #include <react/renderer/components/RNMarcusSpec/EventEmitters.h>
4
+ #include <react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorState.h>
5
+ #include <react/renderer/components/RNMarcusSpec/Props.h>
6
+ #include <react/renderer/components/iostextinput/TextInputShadowNode.h>
7
+
8
+ #include <react/renderer/components/view/ConcreteViewShadowNode.h>
9
+ #include <react/renderer/core/LayoutContext.h>
10
+
11
+ #include <atomic>
12
+ #include <memory>
13
+
14
+ namespace facebook {
15
+ namespace react {
16
+
17
+ JSI_EXPORT extern const char MarcusTextInputDecoratorViewComponentName[];
18
+
19
+ class JSI_EXPORT MarcusTextInputDecoratorShadowNode final
20
+ : public ConcreteViewShadowNode<MarcusTextInputDecoratorViewComponentName, MarcusTextInputDecoratorViewProps, MarcusTextInputDecoratorViewEventEmitter, MarcusTextInputDecoratorState> {
21
+ public:
22
+ MarcusTextInputDecoratorShadowNode(ShadowNodeFragment const &fragment, ShadowNodeFamily::Shared const &family, ShadowNodeTraits traits);
23
+
24
+ MarcusTextInputDecoratorShadowNode(ShadowNode const &sourceShadowNode, ShadowNodeFragment const &fragment);
25
+
26
+ void
27
+ appendChild(const std::shared_ptr<const ShadowNode> &child) override;
28
+ void
29
+ replaceChild(const ShadowNode &oldChild, const std::shared_ptr<const ShadowNode> &newChild, size_t suggestedIndex = SIZE_MAX) override;
30
+ void
31
+ layout(LayoutContext layoutContext) override;
32
+ Size
33
+ measureContent(const LayoutContext &layoutContext, const LayoutConstraints &layoutConstraints) const override;
34
+
35
+ private:
36
+ void
37
+ initialize();
38
+ void
39
+ overwriteMeasureCallbackConnector();
40
+ void
41
+ makeChildNodeMutable();
42
+ void
43
+ applyMarkdownFormattingToTextInputState(
44
+ std::shared_ptr<TextInputShadowNode> node,
45
+ const LayoutContext &layoutContext
46
+ ) const;
47
+ static YGSize
48
+ yogaNodeMeasureCallbackConnector(YGNodeConstRef yogaNode, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode);
49
+ static YogaLayoutableShadowNode &
50
+ shadowNodeFromContext(YGNodeConstRef yogaNode);
51
+
52
+ // Defined in the Objective-C++ half of this class; wraps
53
+ // RCTFontSizeMultiplier so the Yoga measure connector can stay in plain C++.
54
+ static Float
55
+ fontSizeMultiplier();
56
+
57
+ // Set from any thread when a background parse finishes. The next
58
+ // overwriteMeasureCallbackConnector() marks the child's Yoga node dirty so it
59
+ // is measured again, this time with markdown applied. Shared across clones so
60
+ // a parse that lands mid-layout is not lost.
61
+ mutable std::shared_ptr<std::atomic_bool> needsRemeasure_;
62
+ };
63
+
64
+ } // namespace react
65
+ } // namespace facebook
@@ -0,0 +1,204 @@
1
+ // Objective-C++ half of MarcusTextInputDecoratorShadowNode.
2
+ //
3
+ // Everything that needs to cross into Objective-C lives here; the rest of the
4
+ // class is plain C++ in MarcusTextInputDecoratorShadowNode.cpp. Splitting a
5
+ // class across translation units is fine -- these are just two object files
6
+ // that link together.
7
+
8
+ #include "MarcusTextInputDecoratorShadowNode.h"
9
+
10
+ #import <React/RCTUtils.h>
11
+ #include <react/renderer/core/ConcreteState.h>
12
+ #include <react/renderer/textlayoutmanager/RCTAttributedTextUtils.h>
13
+
14
+ #include <atomic>
15
+ #include <memory>
16
+
17
+ #import "RCTMarcusStyle+Codegen.h"
18
+ #import <RNMarcus/MarkdownSwiftInterop.h>
19
+
20
+ namespace facebook {
21
+ namespace react {
22
+
23
+ namespace {
24
+
25
+ using DecoratorState = ConcreteState<MarcusTextInputDecoratorState>;
26
+
27
+ // Formats `string` in place.
28
+ //
29
+ // On the main thread only already-cached ranges are used; the parser is never
30
+ // run there. Running it waits on the markdown runtime, and on a Yoga measure
31
+ // pass that wait can outlast the watchdog
32
+ // (Expensify/react-native-live-markdown#772). On a cache miss the text is left
33
+ // unformatted for this pass and parsed in the background instead, then
34
+ // `needsRemeasure` and a state update ask for another measure so the size
35
+ // measured from unformatted text -- wrong for h1, code fonts, blockquote
36
+ // indents and emoji -- does not stick.
37
+ //
38
+ // This is rare in practice: text changes are normally parsed off the main
39
+ // thread first, so the main thread finds the ranges already cached.
40
+ void
41
+ ApplyMarkdownFormatting(
42
+ NSMutableAttributedString *string,
43
+ NSDictionary<NSAttributedStringKey, id> *defaultTextAttributes,
44
+ RCTMarcusStyle *markdownStyle,
45
+ NSNumber *parserId,
46
+ MarkdownParser *parser,
47
+ std::shared_ptr<std::atomic_bool> needsRemeasure,
48
+ std::shared_ptr<const DecoratorState> state
49
+ ) {
50
+ NSString *text = string.string;
51
+ NSArray<MarcusRange *> *markdownRanges =
52
+ [parser cachedRangesForText:text
53
+ withParserId:parserId];
54
+
55
+ if (markdownRanges == nil) {
56
+ if ([NSThread isMainThread]) {
57
+ [parser
58
+ warmCacheAsyncForText:text
59
+ withParserId:parserId
60
+ completion:^{
61
+ // Only ever runs for the newest text, so this cannot
62
+ // loop: the next measure finds the ranges cached, or the
63
+ // text changed again and a new measure was needed
64
+ // anyway.
65
+ if (needsRemeasure != nullptr) {
66
+ needsRemeasure->store(true);
67
+ }
68
+ if (state != nullptr) {
69
+ // Changes nothing; it exists only to schedule a
70
+ // commit. updateState() is safe from any thread and
71
+ // handles an already-gone family.
72
+ state->updateState(MarcusTextInputDecoratorState{});
73
+ }
74
+ }];
75
+ return;
76
+ }
77
+
78
+ // Background threads may parse inline: the watchdog only applies to the
79
+ // main thread, and parsing no longer holds a lock the main thread waits on.
80
+ markdownRanges = [parser parse:text withParserId:parserId];
81
+ }
82
+
83
+ [MarkdownFormatter formatAttributedString:string
84
+ defaultTextAttributes:defaultTextAttributes
85
+ ranges:markdownRanges
86
+ style:markdownStyle];
87
+ }
88
+
89
+ } // namespace
90
+
91
+ Float
92
+ MarcusTextInputDecoratorShadowNode::fontSizeMultiplier() {
93
+ return RCTFontSizeMultiplier();
94
+ }
95
+
96
+ void
97
+ MarcusTextInputDecoratorShadowNode::
98
+ applyMarkdownFormattingToTextInputState(
99
+ std::shared_ptr<TextInputShadowNode> textInput,
100
+ const LayoutContext &layoutContext
101
+ ) const {
102
+
103
+ const auto &textInputState =
104
+ *std::static_pointer_cast<const react::ConcreteState<TextInputState>>(
105
+ textInput->getState()
106
+ );
107
+ const auto &stateData = textInputState.getData();
108
+ const auto fontSizeMultiplier = layoutContext.fontSizeMultiplier;
109
+
110
+ const auto &decoratorProps =
111
+ *std::static_pointer_cast<MarcusTextInputDecoratorViewProps const>(
112
+ getProps()
113
+ );
114
+ const auto &textInputProps =
115
+ *std::static_pointer_cast<TextInputProps const>(textInput->getProps());
116
+
117
+ const auto defaultTextAttributes =
118
+ textInputProps.getEffectiveTextAttributes(fontSizeMultiplier);
119
+ const auto defaultNSTextAttributes =
120
+ RCTNSTextAttributesFromTextAttributes(defaultTextAttributes);
121
+
122
+ // The parser is shared process-wide, so its cache survives the cloning that
123
+ // happens during layout without this node having to hold on to it.
124
+ MarkdownParser *parser = [MarkdownParser sharedParser];
125
+
126
+ if (!needsRemeasure_) {
127
+ needsRemeasure_ = std::make_shared<std::atomic_bool>(false);
128
+ }
129
+
130
+ RCTMarcusStyle *markdownStyle =
131
+ [[RCTMarcusStyle alloc] initWithStruct:decoratorProps.markdownStyle];
132
+ NSNumber *parserId = [NSNumber numberWithInt:decoratorProps.parserId];
133
+
134
+ // convert the attibuted string stored in state to
135
+ // NSAttributedString
136
+ auto nsAttributedString = RCTNSAttributedStringFromAttributedStringBox(
137
+ stateData.attributedStringBox
138
+ );
139
+
140
+ auto newStateData = TextInputState(stateData);
141
+
142
+ if (stateData.attributedStringBox.getMode() ==
143
+ AttributedStringBox::Mode::Value) {
144
+
145
+ // Handles the first render, where the text stored in props is
146
+ // different than the one stored in state. The one in state is empty,
147
+ // while the one in props is passed from JS. If we don't update the
148
+ // state here, we'll end up with a one-default-line-sized text input
149
+ if (textInputState.getRevision() == State::initialRevisionValue) {
150
+ auto plainStringFromState =
151
+ std::string([[nsAttributedString string] UTF8String]);
152
+
153
+ if (plainStringFromState != textInputProps.text) {
154
+ // creates new AttributedString from props, adapted from
155
+ // TextInputShadowNode (ios one, text inputs are
156
+ // platform-specific)
157
+ auto attributedString = AttributedString{};
158
+ attributedString.appendFragment(AttributedString::Fragment{textInputProps.text, defaultTextAttributes});
159
+
160
+ auto attachments = BaseTextShadowNode::Attachments{};
161
+ BaseTextShadowNode::buildAttributedString(
162
+ defaultTextAttributes, *textInput, attributedString, attachments
163
+ );
164
+
165
+ // convert the newly created attributed string to
166
+ // NSAttributedString
167
+ nsAttributedString = RCTNSAttributedStringFromAttributedStringBox(
168
+ AttributedStringBox{attributedString}
169
+ );
170
+ }
171
+ }
172
+
173
+ // apply markdown
174
+ NSMutableAttributedString *newString = [nsAttributedString mutableCopy];
175
+ ApplyMarkdownFormatting(
176
+ newString, defaultNSTextAttributes, markdownStyle, parserId, parser, needsRemeasure_, std::static_pointer_cast<const DecoratorState>(getState())
177
+ );
178
+
179
+ // create a clone of the old TextInputState and update the
180
+ // attributed string box to point to the string with markdown
181
+ // applied
182
+ newStateData.attributedStringBox =
183
+ RCTAttributedStringBoxFromNSAttributedString(newString);
184
+ } else if (stateData.attributedStringBox.getMode() ==
185
+ AttributedStringBox::Mode::OpaquePointer) {
186
+
187
+ // apply markdown
188
+ NSMutableAttributedString *newString = [nsAttributedString mutableCopy];
189
+ ApplyMarkdownFormatting(
190
+ newString, defaultNSTextAttributes, markdownStyle, parserId, parser, needsRemeasure_, std::static_pointer_cast<const DecoratorState>(getState())
191
+ );
192
+
193
+ // create a clone of the old TextInputState and update the
194
+ // attributed string box to point to the string with markdown
195
+ // applied
196
+ newStateData.attributedStringBox =
197
+ RCTAttributedStringBoxFromNSAttributedString(newString);
198
+ }
199
+
200
+ textInput->setStateData(std::move(newStateData));
201
+ }
202
+
203
+ } // namespace react
204
+ } // namespace facebook
@@ -0,0 +1,8 @@
1
+ #import <React/RCTViewManager.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ @interface MarcusTextInputDecoratorViewManager : RCTViewManager
6
+ @end
7
+
8
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,11 @@
1
+ #import <RNMarcus/MarcusTextInputDecoratorViewManager.h>
2
+
3
+ @implementation MarcusTextInputDecoratorViewManager
4
+
5
+ RCT_EXPORT_MODULE(MarcusTextInputDecoratorView)
6
+
7
+ RCT_EXPORT_VIEW_PROPERTY(markdownStyle, NSDictionary)
8
+
9
+ RCT_EXPORT_VIEW_PROPERTY(parserId, NSNumber)
10
+
11
+ @end