@actualwave/react-native-codeditor 1.0.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 (210) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +790 -0
  3. package/app.plugin.js +133 -0
  4. package/lib/module/BlockingView.js +25 -0
  5. package/lib/module/CodeEditor.js +239 -0
  6. package/lib/module/EditorAPI.js +2 -0
  7. package/lib/module/WebViewAPI.js +133 -0
  8. package/lib/module/index.js +6 -0
  9. package/lib/module/package.json +1 -0
  10. package/lib/typescript/app.plugin.d.ts +3 -0
  11. package/lib/typescript/babel.config.d.ts +10 -0
  12. package/lib/typescript/package.json +1 -0
  13. package/lib/typescript/react-native.config.d.ts +2 -0
  14. package/lib/typescript/scripts/copy-assets.d.ts +2 -0
  15. package/lib/typescript/src/BlockingView.d.ts +3 -0
  16. package/lib/typescript/src/CodeEditor.d.ts +33 -0
  17. package/lib/typescript/src/EditorAPI.d.ts +66 -0
  18. package/lib/typescript/src/WebViewAPI.d.ts +46 -0
  19. package/lib/typescript/src/index.d.ts +7 -0
  20. package/package.json +106 -0
  21. package/react-native.config.js +3 -0
  22. package/src/assets/codemirror/@actualwave_codemirror-lang-sksl.js +15 -0
  23. package/src/assets/codemirror/@babel_runtime_helpers_interopRequireDefault.js +1 -0
  24. package/src/assets/codemirror/@babel_runtime_helpers_objectSpread2.js +1 -0
  25. package/src/assets/codemirror/@babel_runtime_helpers_toConsumableArray.js +1 -0
  26. package/src/assets/codemirror/@codemirror_autocomplete.js +206 -0
  27. package/src/assets/codemirror/@codemirror_collab.js +31 -0
  28. package/src/assets/codemirror/@codemirror_commands.js +411 -0
  29. package/src/assets/codemirror/@codemirror_lang-angular.js +7 -0
  30. package/src/assets/codemirror/@codemirror_lang-cpp.js +7 -0
  31. package/src/assets/codemirror/@codemirror_lang-css.js +14 -0
  32. package/src/assets/codemirror/@codemirror_lang-go.js +13 -0
  33. package/src/assets/codemirror/@codemirror_lang-html.js +20 -0
  34. package/src/assets/codemirror/@codemirror_lang-java.js +7 -0
  35. package/src/assets/codemirror/@codemirror_lang-javascript.js +54 -0
  36. package/src/assets/codemirror/@codemirror_lang-jinja.js +15 -0
  37. package/src/assets/codemirror/@codemirror_lang-json.js +10 -0
  38. package/src/assets/codemirror/@codemirror_lang-less.js +10 -0
  39. package/src/assets/codemirror/@codemirror_lang-lezer.js +7 -0
  40. package/src/assets/codemirror/@codemirror_lang-liquid.js +15 -0
  41. package/src/assets/codemirror/@codemirror_lang-markdown.js +55 -0
  42. package/src/assets/codemirror/@codemirror_lang-php.js +7 -0
  43. package/src/assets/codemirror/@codemirror_lang-python.js +19 -0
  44. package/src/assets/codemirror/@codemirror_lang-rust.js +7 -0
  45. package/src/assets/codemirror/@codemirror_lang-sass.js +10 -0
  46. package/src/assets/codemirror/@codemirror_lang-sql.js +52 -0
  47. package/src/assets/codemirror/@codemirror_lang-vue.js +6 -0
  48. package/src/assets/codemirror/@codemirror_lang-wast.js +2 -0
  49. package/src/assets/codemirror/@codemirror_lang-xml.js +13 -0
  50. package/src/assets/codemirror/@codemirror_lang-yaml.js +13 -0
  51. package/src/assets/codemirror/@codemirror_language-data.js +5 -0
  52. package/src/assets/codemirror/@codemirror_language.js +524 -0
  53. package/src/assets/codemirror/@codemirror_legacy-modes_mode_apl.js +1 -0
  54. package/src/assets/codemirror/@codemirror_legacy-modes_mode_asciiarmor.js +1 -0
  55. package/src/assets/codemirror/@codemirror_legacy-modes_mode_asn1.js +3 -0
  56. package/src/assets/codemirror/@codemirror_legacy-modes_mode_asterisk.js +9 -0
  57. package/src/assets/codemirror/@codemirror_legacy-modes_mode_brainfuck.js +13 -0
  58. package/src/assets/codemirror/@codemirror_legacy-modes_mode_clike.js +31 -0
  59. package/src/assets/codemirror/@codemirror_legacy-modes_mode_clojure.js +5 -0
  60. package/src/assets/codemirror/@codemirror_legacy-modes_mode_cmake.js +6 -0
  61. package/src/assets/codemirror/@codemirror_legacy-modes_mode_cobol.js +7 -0
  62. package/src/assets/codemirror/@codemirror_legacy-modes_mode_coffeescript.js +14 -0
  63. package/src/assets/codemirror/@codemirror_legacy-modes_mode_commonlisp.js +1 -0
  64. package/src/assets/codemirror/@codemirror_legacy-modes_mode_crystal.js +18 -0
  65. package/src/assets/codemirror/@codemirror_legacy-modes_mode_css.js +5 -0
  66. package/src/assets/codemirror/@codemirror_legacy-modes_mode_cypher.js +1 -0
  67. package/src/assets/codemirror/@codemirror_legacy-modes_mode_d.js +2 -0
  68. package/src/assets/codemirror/@codemirror_legacy-modes_mode_diff.js +1 -0
  69. package/src/assets/codemirror/@codemirror_legacy-modes_mode_dockerfile.js +9 -0
  70. package/src/assets/codemirror/@codemirror_legacy-modes_mode_dtd.js +3 -0
  71. package/src/assets/codemirror/@codemirror_legacy-modes_mode_dylan.js +35 -0
  72. package/src/assets/codemirror/@codemirror_legacy-modes_mode_ebnf.js +9 -0
  73. package/src/assets/codemirror/@codemirror_legacy-modes_mode_ecl.js +3 -0
  74. package/src/assets/codemirror/@codemirror_legacy-modes_mode_eiffel.js +1 -0
  75. package/src/assets/codemirror/@codemirror_legacy-modes_mode_elm.js +3 -0
  76. package/src/assets/codemirror/@codemirror_legacy-modes_mode_erlang.js +52 -0
  77. package/src/assets/codemirror/@codemirror_legacy-modes_mode_factor.js +14 -0
  78. package/src/assets/codemirror/@codemirror_legacy-modes_mode_fcl.js +2 -0
  79. package/src/assets/codemirror/@codemirror_legacy-modes_mode_forth.js +5 -0
  80. package/src/assets/codemirror/@codemirror_legacy-modes_mode_fortran.js +2 -0
  81. package/src/assets/codemirror/@codemirror_legacy-modes_mode_gas.js +13 -0
  82. package/src/assets/codemirror/@codemirror_legacy-modes_mode_gherkin.js +5 -0
  83. package/src/assets/codemirror/@codemirror_legacy-modes_mode_go.js +2 -0
  84. package/src/assets/codemirror/@codemirror_legacy-modes_mode_groovy.js +3 -0
  85. package/src/assets/codemirror/@codemirror_legacy-modes_mode_haskell.js +5 -0
  86. package/src/assets/codemirror/@codemirror_legacy-modes_mode_haxe.js +12 -0
  87. package/src/assets/codemirror/@codemirror_legacy-modes_mode_http.js +1 -0
  88. package/src/assets/codemirror/@codemirror_legacy-modes_mode_idl.js +7 -0
  89. package/src/assets/codemirror/@codemirror_legacy-modes_mode_javascript.js +23 -0
  90. package/src/assets/codemirror/@codemirror_legacy-modes_mode_jinja2.js +8 -0
  91. package/src/assets/codemirror/@codemirror_legacy-modes_mode_julia.js +17 -0
  92. package/src/assets/codemirror/@codemirror_legacy-modes_mode_livescript.js +1 -0
  93. package/src/assets/codemirror/@codemirror_legacy-modes_mode_lua.js +2 -0
  94. package/src/assets/codemirror/@codemirror_legacy-modes_mode_mathematica.js +26 -0
  95. package/src/assets/codemirror/@codemirror_legacy-modes_mode_mbox.js +11 -0
  96. package/src/assets/codemirror/@codemirror_legacy-modes_mode_mirc.js +1 -0
  97. package/src/assets/codemirror/@codemirror_legacy-modes_mode_mllike.js +8 -0
  98. package/src/assets/codemirror/@codemirror_legacy-modes_mode_modelica.js +12 -0
  99. package/src/assets/codemirror/@codemirror_legacy-modes_mode_mscgen.js +3 -0
  100. package/src/assets/codemirror/@codemirror_legacy-modes_mode_mumps.js +16 -0
  101. package/src/assets/codemirror/@codemirror_legacy-modes_mode_nginx.js +1 -0
  102. package/src/assets/codemirror/@codemirror_legacy-modes_mode_nsis.js +20 -0
  103. package/src/assets/codemirror/@codemirror_legacy-modes_mode_ntriples.js +1 -0
  104. package/src/assets/codemirror/@codemirror_legacy-modes_mode_octave.js +8 -0
  105. package/src/assets/codemirror/@codemirror_legacy-modes_mode_oz.js +17 -0
  106. package/src/assets/codemirror/@codemirror_legacy-modes_mode_pascal.js +2 -0
  107. package/src/assets/codemirror/@codemirror_legacy-modes_mode_pegjs.js +25 -0
  108. package/src/assets/codemirror/@codemirror_legacy-modes_mode_perl.js +247 -0
  109. package/src/assets/codemirror/@codemirror_legacy-modes_mode_pig.js +6 -0
  110. package/src/assets/codemirror/@codemirror_legacy-modes_mode_powershell.js +7 -0
  111. package/src/assets/codemirror/@codemirror_legacy-modes_mode_properties.js +5 -0
  112. package/src/assets/codemirror/@codemirror_legacy-modes_mode_protobuf.js +7 -0
  113. package/src/assets/codemirror/@codemirror_legacy-modes_mode_pug.js +24 -0
  114. package/src/assets/codemirror/@codemirror_legacy-modes_mode_puppet.js +42 -0
  115. package/src/assets/codemirror/@codemirror_legacy-modes_mode_python.js +15 -0
  116. package/src/assets/codemirror/@codemirror_legacy-modes_mode_q.js +2 -0
  117. package/src/assets/codemirror/@codemirror_legacy-modes_mode_r.js +1 -0
  118. package/src/assets/codemirror/@codemirror_legacy-modes_mode_rpm.js +9 -0
  119. package/src/assets/codemirror/@codemirror_legacy-modes_mode_ruby.js +4 -0
  120. package/src/assets/codemirror/@codemirror_legacy-modes_mode_rust.js +5 -0
  121. package/src/assets/codemirror/@codemirror_legacy-modes_mode_sas.js +23 -0
  122. package/src/assets/codemirror/@codemirror_legacy-modes_mode_sass.js +26 -0
  123. package/src/assets/codemirror/@codemirror_legacy-modes_mode_scheme.js +14 -0
  124. package/src/assets/codemirror/@codemirror_legacy-modes_mode_shell.js +1 -0
  125. package/src/assets/codemirror/@codemirror_legacy-modes_mode_sieve.js +7 -0
  126. package/src/assets/codemirror/@codemirror_legacy-modes_mode_simple-mode.js +1 -0
  127. package/src/assets/codemirror/@codemirror_legacy-modes_mode_smalltalk.js +1 -0
  128. package/src/assets/codemirror/@codemirror_legacy-modes_mode_solr.js +1 -0
  129. package/src/assets/codemirror/@codemirror_legacy-modes_mode_sparql.js +1 -0
  130. package/src/assets/codemirror/@codemirror_legacy-modes_mode_spreadsheet.js +6 -0
  131. package/src/assets/codemirror/@codemirror_legacy-modes_mode_sql.js +84 -0
  132. package/src/assets/codemirror/@codemirror_legacy-modes_mode_stex.js +11 -0
  133. package/src/assets/codemirror/@codemirror_legacy-modes_mode_stylus.js +45 -0
  134. package/src/assets/codemirror/@codemirror_legacy-modes_mode_swift.js +2 -0
  135. package/src/assets/codemirror/@codemirror_legacy-modes_mode_tcl.js +1 -0
  136. package/src/assets/codemirror/@codemirror_legacy-modes_mode_textile.js +2 -0
  137. package/src/assets/codemirror/@codemirror_legacy-modes_mode_tiddlywiki.js +33 -0
  138. package/src/assets/codemirror/@codemirror_legacy-modes_mode_tiki.js +20 -0
  139. package/src/assets/codemirror/@codemirror_legacy-modes_mode_toml.js +4 -0
  140. package/src/assets/codemirror/@codemirror_legacy-modes_mode_troff.js +1 -0
  141. package/src/assets/codemirror/@codemirror_legacy-modes_mode_ttcn-cfg.js +3 -0
  142. package/src/assets/codemirror/@codemirror_legacy-modes_mode_ttcn.js +3 -0
  143. package/src/assets/codemirror/@codemirror_legacy-modes_mode_turtle.js +1 -0
  144. package/src/assets/codemirror/@codemirror_legacy-modes_mode_vb.js +11 -0
  145. package/src/assets/codemirror/@codemirror_legacy-modes_mode_vbscript.js +26 -0
  146. package/src/assets/codemirror/@codemirror_legacy-modes_mode_velocity.js +15 -0
  147. package/src/assets/codemirror/@codemirror_legacy-modes_mode_verilog.js +67 -0
  148. package/src/assets/codemirror/@codemirror_legacy-modes_mode_vhdl.js +2 -0
  149. package/src/assets/codemirror/@codemirror_legacy-modes_mode_wast.js +7 -0
  150. package/src/assets/codemirror/@codemirror_legacy-modes_mode_webidl.js +25 -0
  151. package/src/assets/codemirror/@codemirror_legacy-modes_mode_xml.js +6 -0
  152. package/src/assets/codemirror/@codemirror_legacy-modes_mode_xquery.js +51 -0
  153. package/src/assets/codemirror/@codemirror_legacy-modes_mode_yacas.js +16 -0
  154. package/src/assets/codemirror/@codemirror_legacy-modes_mode_yaml.js +1 -0
  155. package/src/assets/codemirror/@codemirror_legacy-modes_mode_z80.js +1 -0
  156. package/src/assets/codemirror/@codemirror_lint.js +46 -0
  157. package/src/assets/codemirror/@codemirror_merge.js +167 -0
  158. package/src/assets/codemirror/@codemirror_search.js +121 -0
  159. package/src/assets/codemirror/@codemirror_state.js +793 -0
  160. package/src/assets/codemirror/@codemirror_theme-one-dark.js +12 -0
  161. package/src/assets/codemirror/@codemirror_view.js +1210 -0
  162. package/src/assets/codemirror/@lezer_common.js +407 -0
  163. package/src/assets/codemirror/@lezer_cpp.js +4 -0
  164. package/src/assets/codemirror/@lezer_css.js +4 -0
  165. package/src/assets/codemirror/@lezer_go.js +3 -0
  166. package/src/assets/codemirror/@lezer_highlight.js +380 -0
  167. package/src/assets/codemirror/@lezer_html.js +20 -0
  168. package/src/assets/codemirror/@lezer_java.js +2 -0
  169. package/src/assets/codemirror/@lezer_javascript.js +7 -0
  170. package/src/assets/codemirror/@lezer_json.js +2 -0
  171. package/src/assets/codemirror/@lezer_lezer.js +2 -0
  172. package/src/assets/codemirror/@lezer_lr.js +325 -0
  173. package/src/assets/codemirror/@lezer_markdown.js +286 -0
  174. package/src/assets/codemirror/@lezer_php.js +3 -0
  175. package/src/assets/codemirror/@lezer_python.js +5 -0
  176. package/src/assets/codemirror/@lezer_rust.js +3 -0
  177. package/src/assets/codemirror/@lezer_sass.js +5 -0
  178. package/src/assets/codemirror/@lezer_xml.js +3 -0
  179. package/src/assets/codemirror/@lezer_yaml.js +12 -0
  180. package/src/assets/codemirror/@marijn_find-cluster-break.js +10 -0
  181. package/src/assets/codemirror/@uiw_codemirror-theme-androidstudio.js +3 -0
  182. package/src/assets/codemirror/@uiw_codemirror-theme-andromeda.js +1 -0
  183. package/src/assets/codemirror/@uiw_codemirror-theme-atomone.js +6 -0
  184. package/src/assets/codemirror/@uiw_codemirror-theme-aura.js +1 -0
  185. package/src/assets/codemirror/@uiw_codemirror-theme-basic.js +1 -0
  186. package/src/assets/codemirror/@uiw_codemirror-theme-bbedit.js +1 -0
  187. package/src/assets/codemirror/@uiw_codemirror-theme-copilot.js +1 -0
  188. package/src/assets/codemirror/@uiw_codemirror-theme-darcula.js +6 -0
  189. package/src/assets/codemirror/@uiw_codemirror-theme-dracula.js +6 -0
  190. package/src/assets/codemirror/@uiw_codemirror-theme-duotone.js +5 -0
  191. package/src/assets/codemirror/@uiw_codemirror-theme-eclipse.js +1 -0
  192. package/src/assets/codemirror/@uiw_codemirror-theme-github.js +3 -0
  193. package/src/assets/codemirror/@uiw_codemirror-theme-material.js +1 -0
  194. package/src/assets/codemirror/@uiw_codemirror-theme-monokai.js +1 -0
  195. package/src/assets/codemirror/@uiw_codemirror-theme-nord.js +2 -0
  196. package/src/assets/codemirror/@uiw_codemirror-theme-okaidia.js +2 -0
  197. package/src/assets/codemirror/@uiw_codemirror-theme-solarized.js +1 -0
  198. package/src/assets/codemirror/@uiw_codemirror-theme-sublime.js +2 -0
  199. package/src/assets/codemirror/@uiw_codemirror-theme-tokyo-night.js +1 -0
  200. package/src/assets/codemirror/@uiw_codemirror-theme-vscode.js +5 -0
  201. package/src/assets/codemirror/@uiw_codemirror-theme-xcode.js +3 -0
  202. package/src/assets/codemirror/@uiw_codemirror-themes.js +1 -0
  203. package/src/assets/codemirror/_core.js +4494 -0
  204. package/src/assets/codemirror/codemirror.js +46 -0
  205. package/src/assets/codemirror/crelt.js +1 -0
  206. package/src/assets/codemirror/style-mod.js +36 -0
  207. package/src/assets/codemirror/w3c-keyname.js +8 -0
  208. package/src/assets/codemirror-editor.umd.js +415 -0
  209. package/src/assets/editor.html +377 -0
  210. package/src/assets/webview-interface.umd.js +2 -0
package/README.md ADDED
@@ -0,0 +1,790 @@
1
+ # @actualwave/react-native-codeditor
2
+
3
+ A React Native code editor component powered by [CodeMirror 6](https://codemirror.net/), embedded inside a `<WebView>`
4
+ with full bidirectional RPC. Syntax highlighting, themes, history, cursor/selection control —
5
+ all offline, no CDN required.
6
+
7
+ ---
8
+
9
+ ## Features
10
+
11
+ - CodeMirror 6 with mobile-optimized setup (line numbers, bracket matching, search, autocomplete, …)
12
+ - 20+ syntax languages loaded on demand (`javascript`, `python`, `rust`, `sql`, …)
13
+ - 20+ themes from `@uiw/codemirror-themes` (`darcula`, `monokai`, `github`, `nord`, …)
14
+ - Fully offline — all CM6 assets bundled into the library
15
+ - Real-time `onChange` content updates (no polling)
16
+ - Full editor API: getValue, setValue, cursor, selection, history undo/redo, viewport
17
+ - Transparent RPC bridge via `@actualwave/webview-interface` (DDA)
18
+
19
+ ---
20
+
21
+ ## Requirements
22
+
23
+ - React Native ≥ 0.71
24
+ - `react-native-webview` ≥ 11
25
+ - Android: WebView 61+ (Chromium-based)
26
+ - iOS: WebView with file:// access; bundle path must be provided via the `editorUri` prop
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ ### 1. Install the packages
33
+
34
+ ```sh
35
+ npm install @actualwave/react-native-codeditor react-native-webview
36
+ ```
37
+
38
+ ### 2. Copy native assets
39
+
40
+ The library ships CodeMirror 6 as static files that must be copied into your app's
41
+ native asset directories.
42
+
43
+ #### Expo managed / prebuild (recommended)
44
+
45
+ The library ships an Expo config plugin. Add it to your `app.json` or `app.config.js`:
46
+
47
+ ```json
48
+ {
49
+ "expo": {
50
+ "plugins": ["@actualwave/react-native-codeditor"]
51
+ }
52
+ }
53
+ ```
54
+
55
+ Then run `expo prebuild` — the plugin handles everything:
56
+ - Copies assets → `android/app/src/main/assets/codeditor/`
57
+ - Copies assets → `ios/<ProjectName>/assets/codeditor/`
58
+ - Adds a folder reference to `project.pbxproj` so Xcode bundles the files (iOS)
59
+
60
+ The plugin is idempotent — safe to re-run on subsequent prebuilds.
61
+
62
+ #### Manual copy (bare RN or without expo prebuild)
63
+
64
+ **Android:**
65
+
66
+ ```sh
67
+ mkdir -p android/app/src/main/assets/codeditor
68
+ cp -r node_modules/@actualwave/react-native-codeditor/src/assets/* android/app/src/main/assets/codeditor/
69
+ ```
70
+
71
+ Re-run after every `@actualwave/react-native-codeditor` upgrade.
72
+
73
+ #### iOS
74
+
75
+ **Step 1 — copy files**
76
+
77
+ ```sh
78
+ mkdir -p ios/<YourProject>/assets/codeditor
79
+ cp -r node_modules/@actualwave/react-native-codeditor/src/assets/* ios/<YourProject>/assets/codeditor/
80
+ ```
81
+
82
+ **Step 2 — add a folder reference in Xcode**
83
+
84
+ Copied files are not bundled automatically — Xcode must know about the folder:
85
+
86
+ 1. Open your `.xcworkspace` in Xcode.
87
+ 2. In the Project Navigator, right-click your app group → **Add Files to "\<YourProject\>"…**
88
+ 3. Select the `assets` folder inside `ios/<YourProject>/`.
89
+ 4. In the options sheet, set **Added folders** to **Create folder references** (blue icon).
90
+ Make sure **Add to targets: \<YourProject\>** is checked.
91
+ 5. Click **Add**.
92
+
93
+ The folder appears with a blue icon. A yellow group would break when new language or
94
+ theme files are added; a blue folder reference copies the whole tree automatically.
95
+
96
+ > Re-run the file copy after every `@actualwave/react-native-codeditor` upgrade. The Xcode folder
97
+ > reference only needs to be added once.
98
+
99
+ #### After upgrading `@actualwave/react-native-codeditor`
100
+
101
+ Re-run the asset copy after every upgrade — the bundled CodeMirror files may change
102
+ between versions:
103
+
104
+ **Expo (recommended):**
105
+ ```sh
106
+ npx expo prebuild
107
+ ```
108
+
109
+ **Manual — Android:**
110
+ ```sh
111
+ cp -r node_modules/@actualwave/react-native-codeditor/src/assets/* android/app/src/main/assets/codeditor/
112
+ ```
113
+
114
+ **Manual — iOS:**
115
+ ```sh
116
+ cp -r node_modules/@actualwave/react-native-codeditor/src/assets/* ios/<YourProject>/assets/codeditor/
117
+ ```
118
+
119
+ The Xcode folder reference only needs to be added once (see iOS steps above).
120
+
121
+ ---
122
+
123
+ ### 3. Android: keyboard resize mode
124
+
125
+ Add `adjustResize` to your Activity in `AndroidManifest.xml` so the editor shrinks when the
126
+ soft keyboard appears (instead of being hidden behind it):
127
+
128
+ ```xml
129
+ <activity
130
+ android:windowSoftInputMode="adjustResize"
131
+ ...
132
+ />
133
+ ```
134
+
135
+ ### 4. iOS: provide the editor URI
136
+
137
+ On Android the editor page loads from `file:///android_asset/codeditor/editor.html` (the
138
+ default). On iOS the `.app` bundle path varies per device and build. Compute it at
139
+ runtime using `expo-file-system` and pass it via the `editorUri` prop:
140
+
141
+ ```sh
142
+ npm install expo-file-system
143
+ ```
144
+
145
+ ```tsx
146
+ import { Platform } from 'react-native';
147
+ import * as FileSystem from 'expo-file-system';
148
+
149
+ const IOS_EDITOR_URI = Platform.OS === 'ios'
150
+ ? (FileSystem.bundleDirectory ?? '') + 'assets/codeditor/editor.html'
151
+ : undefined;
152
+
153
+ <CodeEditor editorUri={IOS_EDITOR_URI} ... />
154
+ ```
155
+
156
+ `FileSystem.bundleDirectory` returns the `file://` path to the `.app` folder (e.g.
157
+ `file:///var/containers/Bundle/Application/<UUID>/MyApp.app/`), which is stable on both
158
+ simulators and real devices regardless of where iOS installed the app.
159
+
160
+ After installing `expo-file-system`, re-run `pod install` to link its native module:
161
+
162
+ ```sh
163
+ cd ios && pod install
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Basic usage
169
+
170
+ ```tsx
171
+ import { useCallback, useRef } from 'react';
172
+ import { KeyboardAvoidingView, Platform } from 'react-native';
173
+ import CodeEditor from '@actualwave/react-native-codeditor';
174
+ import type { WebViewAPI, HistorySize } from '@actualwave/react-native-codeditor';
175
+
176
+ export default function EditorScreen() {
177
+ const apiRef = useRef<WebViewAPI | null>(null);
178
+
179
+ const handleInitialized = useCallback((api: WebViewAPI) => {
180
+ apiRef.current = api;
181
+ void api.focus(); // show soft keyboard on Android
182
+ }, []);
183
+
184
+ return (
185
+ // Shrinks the editor when the soft keyboard appears.
186
+ <KeyboardAvoidingView
187
+ style={{ flex: 1 }}
188
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
189
+ >
190
+ <CodeEditor
191
+ content="const x = 42;"
192
+ language="javascript"
193
+ theme="darcula"
194
+ onInitialized={handleInitialized}
195
+ onContentUpdate={(content) => console.log('length:', content.length)}
196
+ onHistorySizeUpdate={(size) => console.log('undo:', size.undo)}
197
+ onLog={(...args) => console.log('[editor]', ...args)}
198
+ onError={(err) => console.error('[editor]', err)}
199
+ />
200
+ </KeyboardAvoidingView>
201
+ );
202
+ }
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Props
208
+
209
+ ### Required callbacks
210
+
211
+ | Prop | Type | Description |
212
+ |---|---|---|
213
+ | `onInitialized` | `(api: WebViewAPI) => void` | Called once the editor is fully ready — after the theme and language modules have loaded and the editor is painted. Store the `api` handle here. Call `api.focus()` to open the Android keyboard. |
214
+ | `onContentUpdate` | `(content: string) => void` | Called on every keystroke with the full document text. |
215
+ | `onHistorySizeUpdate` | `(size: HistorySize) => void` | Called on every keystroke with `{ undo: number, redo: number }`. |
216
+ | `onLog` | `(...args: unknown[]) => void` | Receives `window.log(...)` calls from inside the WebView. |
217
+ | `onError` | `(error: unknown) => void` | Receives `window.onerror` events from inside the WebView. |
218
+ | `onSelectionChange` | `(text: string) => void` | Optional. Called when the user changes the selection; receives the selected text (empty string when selection collapses). |
219
+
220
+ ### Editor configuration
221
+
222
+ | Prop | Type | Default | Description |
223
+ |---|---|---|---|
224
+ | `content` | `string` | `''` | Initial document content (see [Content model](#content-model) below). |
225
+ | `language` | `string` | `undefined` | Syntax language, e.g. `'javascript'`, `'python'`, `'sql'`. The language module is loaded from the bundled assets on first use. |
226
+ | `extensions` | `ExtensionSpec[]` | `[]` | Additional CodeMirror 6 extension specs. See [Extension specs](#extension-specs). |
227
+ | `theme` | `string` | `undefined` | Theme name. See [Themes](#themes). |
228
+ | `viewport` | `ViewportSettings` | `undefined` | Controls the `<meta name="viewport">` tag inside the WebView. |
229
+
230
+ ### WebView / layout
231
+
232
+ | Prop | Type | Default | Description |
233
+ |---|---|---|---|
234
+ | `editorUri` | `string` | `'file:///android_asset/codeditor/editor.html'` | URI of the editor HTML page. Override for iOS — see [iOS: provide the editor URI](#4-ios-provide-the-editor-uri). |
235
+ | `allowFileAccess` | `boolean` | `true` | Passed to `<WebView>`. Required for `file://` asset loading. |
236
+ | `renderBlockingView` | `() => ReactNode` | `() => <BlockingView />` | Overlay rendered while the editor is initialising. Replace with your own loading UI. |
237
+ | `onWebViewRefUpdated` | `(ref) => void` | — | Called when the internal WebView ref changes. |
238
+ | `onLoad` | func | — | WebView `onLoad` pass-through. |
239
+ | `onLoadStart` | func | — | WebView `onLoadStart` pass-through. |
240
+ | `onLoadProgress` | func | — | WebView `onLoadProgress` pass-through. |
241
+ | `onLoadEnd` | func | — | WebView `onLoadEnd` pass-through. |
242
+ | `onNavigationStateChange` | func | — | WebView `onNavigationStateChange` pass-through. |
243
+
244
+ ---
245
+
246
+ ## Content model
247
+
248
+ `CodeEditor` is **uncontrolled** — the editor owns its document state after the first render,
249
+ similar to an `<input defaultValue="…">`. The `content` prop sets the initial document content
250
+ once (delivered via the DDA handshake), but it is not kept in sync with what the user types.
251
+
252
+ ```tsx
253
+ // Initial content is set once; subsequent user edits are not reflected back to the prop.
254
+ <CodeEditor content="const x = 42;" ... />
255
+ ```
256
+
257
+ Changes are reported out via `onContentUpdate` on every keystroke, but writing them back to
258
+ `content` would create a feedback loop where every keystroke triggers a `setValue` call that
259
+ resets the cursor — so **don't** pass user-typed content back as the `content` prop.
260
+
261
+ To push new content into the editor programmatically, use the API:
262
+
263
+ ```ts
264
+ // Replace content, preserve undo/redo history:
265
+ await api.editor.setValue(newCode);
266
+
267
+ // Replace content AND clear history (e.g. when opening a new file):
268
+ await api.editor.resetValue(newCode);
269
+ ```
270
+
271
+ `content` prop changes after the initial render do call `api.editor.setValue` internally, so
272
+ prop-driven replacement works (e.g. switching between files stored in React state). Just don't
273
+ do it in response to `onContentUpdate` — only in response to an external event like a file load
274
+ or language switch.
275
+
276
+ ---
277
+
278
+ ## `WebViewAPI` — editor control
279
+
280
+ The `api` object received in `onInitialized` provides:
281
+
282
+ - `api.focus()` — focuses the editor and triggers the Android soft keyboard.
283
+ - `api.editor` — a DDA proxy to the live CM6 editor inside the WebView. All `api.editor.*`
284
+ methods return Promises resolved by the WebView. **Always `await` these calls** — DDA
285
+ operates in lazy mode and only dispatches the command when `.then` is accessed on the
286
+ returned Promise. Calling without `await` (or `void`) silently does nothing.
287
+ - `api.injectJavaScript(code)` / `api.requestFocus()` — synchronous, React Native side only.
288
+
289
+ ### Focus
290
+
291
+ ```ts
292
+ api.focus(): Promise<void>
293
+ // Focuses the editor. Also calls webView.requestFocus() to trigger the Android keyboard.
294
+ ```
295
+
296
+ ### Content
297
+
298
+ ```ts
299
+ api.editor.getValue(): Promise<string>
300
+ // Returns the current document text.
301
+
302
+ api.editor.setValue(value: string): Promise<void>
303
+ // Replaces the document content. Preserves history.
304
+
305
+ api.editor.resetValue(value?: string): Promise<void>
306
+ // Replaces content AND clears undo/redo history.
307
+ ```
308
+
309
+ ### Language, extensions, theme
310
+
311
+ ```ts
312
+ api.editor.setLanguage(name: string): Promise<void>
313
+ // Switches syntax language. Loads the language module on demand.
314
+ // e.g. await api.editor.setLanguage('python')
315
+
316
+ api.editor.setExtensions(specs: ExtensionSpec[]): Promise<void>
317
+ // Replaces the active extension set.
318
+ // e.g. await api.editor.setExtensions(['@codemirror/search'])
319
+
320
+ api.editor.setTheme(themeName?: string): Promise<void>
321
+ // Switches theme. Pass undefined to remove the theme.
322
+ // e.g. await api.editor.setTheme('monokai')
323
+ ```
324
+
325
+ ### Viewport
326
+
327
+ ```ts
328
+ api.editor.setViewport(options: ViewportSettings): Promise<void>
329
+ // Updates <meta name="viewport"> inside the WebView.
330
+ // options: { intialScale?, maximumScale?, minimumScale?, userScalable?, viewportWidth? }
331
+ ```
332
+
333
+ ### Cursor and selection
334
+
335
+ ```ts
336
+ api.editor.getCursor(where?: 'from' | 'to' | 'head'): Promise<CursorPosition>
337
+ // Returns { line, ch, index } of the cursor (or selection boundary).
338
+ // line is 0-based; index is the absolute character offset.
339
+
340
+ api.editor.setCursor(line: number, ch?: number): Promise<void>
341
+ // Moves the cursor to line (0-based) + character offset.
342
+
343
+ api.editor.getSelection(): Promise<string>
344
+ // Returns the currently selected text.
345
+
346
+ api.editor.setSelection(anchor: number, head?: number): Promise<void>
347
+ // Sets the selection by absolute character offsets.
348
+
349
+ api.editor.replaceSelection(text: string): Promise<void>
350
+ // Replaces the current selection with text.
351
+
352
+ api.editor.cancelSelection(): Promise<void>
353
+ // Collapses the selection to the cursor position.
354
+ ```
355
+
356
+ ### History
357
+
358
+ ```ts
359
+ api.editor.historyUndo(): Promise<boolean>
360
+ // Undoes the last change. Returns true if an undo was performed.
361
+
362
+ api.editor.historyRedo(): Promise<boolean>
363
+ // Redoes the last undone change. Returns true if a redo was performed.
364
+
365
+ api.editor.historyClear(): Promise<void>
366
+ // Clears the undo/redo history without changing the document.
367
+
368
+ api.editor.historySize(): Promise<HistorySize>
369
+ // Returns the current { undo, redo } depth.
370
+ // Note: history size is also reported automatically via onHistorySizeUpdate.
371
+ ```
372
+
373
+ ### Scroll
374
+
375
+ ```ts
376
+ api.editor.scrollToCursor(margin?: number): Promise<void>
377
+ // Scrolls the editor so the cursor is visible.
378
+ ```
379
+
380
+ ### Editing commands
381
+
382
+ ```ts
383
+ api.editor.indentMore(): Promise<boolean>
384
+ api.editor.indentLess(): Promise<boolean>
385
+ // Indent / unindent the current line or selection.
386
+
387
+ api.editor.toggleComment(): Promise<boolean>
388
+ // Toggle line comments on the current selection.
389
+
390
+ api.editor.moveLineUp(): Promise<boolean>
391
+ api.editor.moveLineDown(): Promise<boolean>
392
+ // Move the current line (or selected lines) up or down.
393
+
394
+ api.editor.deleteLine(): Promise<boolean>
395
+ // Delete the current line.
396
+
397
+ api.editor.selectLine(): Promise<boolean>
398
+ // Select the entire current line.
399
+
400
+ api.editor.selectParentSyntax(): Promise<boolean>
401
+ // Expand the selection to the enclosing syntax node.
402
+ ```
403
+
404
+ ### Autocomplete
405
+
406
+ ```ts
407
+ api.editor.startCompletion(): Promise<void>
408
+ // Explicitly trigger the autocomplete popup.
409
+
410
+ api.editor.setCompletions(items: CompletionItem[]): Promise<void>
411
+ // Replace the static completions list used by the built-in completion source.
412
+ // Each item: { label, type?, detail?, info? }
413
+ ```
414
+
415
+ ### Font size
416
+
417
+ ```ts
418
+ api.editor.setFontSize(size: number): Promise<void>
419
+ // Sets the editor font size in pixels.
420
+ ```
421
+
422
+ ### Soft keyboard
423
+
424
+ ```ts
425
+ api.editor.setSoftKeyboard(enabled: boolean): Promise<void>
426
+ // Enable or disable the soft keyboard for the WebView editor area.
427
+ ```
428
+
429
+ ### Advanced
430
+
431
+ ```ts
432
+ api.editor.loadExtension(moduleName: string): Promise<object>
433
+ // Loads a CM6 module by package name and returns its raw exports.
434
+ // Useful for building custom features on top of bundled modules.
435
+
436
+ api.editor.destroy(): Promise<void>
437
+ // Destroys the CM6 EditorView and removes it from the DOM.
438
+
439
+ api.injectJavaScript(code: string): void
440
+ // Runs arbitrary JavaScript inside the WebView. Synchronous, no return value.
441
+
442
+ api.requestFocus(): void
443
+ // Calls webView.requestFocus() on the native WebView ref (Android keyboard hint).
444
+ ```
445
+
446
+ ---
447
+
448
+ ## Extension specs
449
+
450
+ The `extensions` prop and `api.editor.setExtensions()` accept an array of **extension specs**.
451
+ Each item can be:
452
+
453
+ | Form | Example | Behaviour |
454
+ |---|---|---|
455
+ | Package name string | `'@codemirror/search'` | Loaded and resolved via the built-in registry |
456
+ | `[packageName, exportName]` | `['@uiw/codemirror-theme-nord', 'nord']` | `mod[exportName]` returned directly |
457
+ | `[packageName, options]` | `['@codemirror/search', { top: true }]` | Resolver called with options |
458
+ | Any other value | `myCustomExtension` | Used as-is (a pre-built CM6 `Extension`) |
459
+
460
+ ### Built-in extension registry
461
+
462
+ | Package | Resolver |
463
+ |---|---|
464
+ | `@codemirror/autocomplete` | `mod.autocompletion(options)` |
465
+ | `@codemirror/search` | `mod.search(options)` |
466
+ | `@codemirror/lint` | `mod.lintGutter(options)` |
467
+ | `@codemirror/collab` | `mod.collab(options)` |
468
+ | `@codemirror/theme-one-dark` | `mod.oneDark` |
469
+
470
+ ### Examples
471
+
472
+ ```tsx
473
+ // Search panel at the top + autocomplete
474
+ <CodeEditor
475
+ extensions={[
476
+ ['@codemirror/search', { top: true }],
477
+ '@codemirror/autocomplete',
478
+ ]}
479
+ ...
480
+ />
481
+
482
+ // One Dark theme (from @codemirror, not @uiw)
483
+ <CodeEditor
484
+ extensions={[['@codemirror/theme-one-dark', 'oneDark']]}
485
+ ...
486
+ />
487
+
488
+ // @uiw theme via per-package spec (NOT the meta-package @uiw/codemirror-themes)
489
+ <CodeEditor
490
+ extensions={[['@uiw/codemirror-theme-monokai', 'monokai']]}
491
+ ...
492
+ />
493
+ ```
494
+
495
+ ---
496
+
497
+ ## Themes
498
+
499
+ Pass a theme name string to the `theme` prop:
500
+
501
+ | | | | |
502
+ |---|---|---|---|
503
+ | `androidstudio` | `andromeda` | `atomone` | `aura` |
504
+ | `basic` | `bbedit` | `copilot` | `darcula` |
505
+ | `dracula` | `duotone` | `eclipse` | `github` |
506
+ | `material` | `monokai` | `nord` | `okaidia` |
507
+ | `solarized` | `sublime` | `vscode` | `xcode` |
508
+
509
+ ```tsx
510
+ <CodeEditor theme="nord" ... />
511
+ ```
512
+
513
+ Switch theme at runtime:
514
+
515
+ ```ts
516
+ await api.editor.setTheme('monokai');
517
+ await api.editor.setTheme(undefined); // remove theme, use CM6 default
518
+ ```
519
+
520
+ ---
521
+
522
+ ## Supported languages
523
+
524
+ Pass any of these to the `language` prop or `api.editor.setLanguage()`:
525
+
526
+ `angular` `cpp` `css` `go` `html` `java` `javascript` `jinja` `json`
527
+ `less` `lezer` `liquid` `markdown` `php` `python` `rust` `sass` `sql`
528
+ `vue` `wast` `xml` `yaml`
529
+
530
+ Legacy modes (100+ additional languages via `@codemirror/legacy-modes`) can be loaded
531
+ manually with `api.editor.loadExtension()`:
532
+
533
+ ```ts
534
+ const { swift } = await api.editor.loadExtension('@codemirror/legacy-modes/mode/swift');
535
+ ```
536
+
537
+ ---
538
+
539
+ ## Viewport settings
540
+
541
+ ```ts
542
+ interface ViewportSettings {
543
+ intialScale?: number; // note: matches viewport meta attribute spelling
544
+ maximumScale?: number;
545
+ minimumScale?: number;
546
+ userScalable?: boolean;
547
+ viewportWidth?: string | number; // e.g. 'device-width'
548
+ }
549
+ ```
550
+
551
+ ```tsx
552
+ <CodeEditor
553
+ viewport={{
554
+ intialScale: 1,
555
+ minimumScale: 0.5,
556
+ maximumScale: 3,
557
+ userScalable: true,
558
+ viewportWidth: 'device-width',
559
+ }}
560
+ ...
561
+ />
562
+ ```
563
+
564
+ ---
565
+
566
+ ## Full example
567
+
568
+ ```tsx
569
+ import { useCallback, useRef, useState } from 'react';
570
+ import { KeyboardAvoidingView, Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
571
+ import CodeEditor from '@actualwave/react-native-codeditor';
572
+ import type { WebViewAPI, HistorySize } from '@actualwave/react-native-codeditor';
573
+
574
+ const INITIAL_CODE = `function greet(name) {
575
+ return 'Hello, ' + name + '!';
576
+ }
577
+ `;
578
+
579
+ const VIEWPORT = {
580
+ intialScale: 1,
581
+ minimumScale: 0.5,
582
+ maximumScale: 3,
583
+ userScalable: true,
584
+ viewportWidth: 'device-width',
585
+ };
586
+
587
+ export default function EditorScreen() {
588
+ const apiRef = useRef<WebViewAPI | null>(null);
589
+ const [status, setStatus] = useState('Loading…');
590
+ const [historySize, setHistorySize] = useState<HistorySize | null>(null);
591
+
592
+ const handleInitialized = useCallback((api: WebViewAPI) => {
593
+ apiRef.current = api;
594
+ setStatus('Ready');
595
+ void api.focus();
596
+ }, []);
597
+
598
+ const handleUndo = useCallback(async () => { await apiRef.current?.editor?.historyUndo(); }, []);
599
+ const handleRedo = useCallback(async () => { await apiRef.current?.editor?.historyRedo(); }, []);
600
+
601
+ const handleSwitchLanguage = useCallback(async () => {
602
+ await apiRef.current?.editor.setLanguage('python');
603
+ await apiRef.current?.editor.setTheme('github');
604
+ }, []);
605
+
606
+ return (
607
+ // KeyboardAvoidingView shrinks the editor when the soft keyboard appears.
608
+ // Requires android:windowSoftInputMode="adjustResize" in AndroidManifest.xml.
609
+ <KeyboardAvoidingView
610
+ style={styles.container}
611
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
612
+ >
613
+ <View style={styles.toolbar}>
614
+ <TouchableOpacity style={styles.btn} onPress={handleUndo}>
615
+ <Text style={styles.btnText}>Undo</Text>
616
+ </TouchableOpacity>
617
+ <TouchableOpacity style={styles.btn} onPress={handleRedo}>
618
+ <Text style={styles.btnText}>Redo</Text>
619
+ </TouchableOpacity>
620
+ <TouchableOpacity style={styles.btn} onPress={handleSwitchLanguage}>
621
+ <Text style={styles.btnText}>→ Python</Text>
622
+ </TouchableOpacity>
623
+ <Text style={styles.status}>
624
+ {status}{historySize ? ` ↩${historySize.undo} ↪${historySize.redo}` : ''}
625
+ </Text>
626
+ </View>
627
+
628
+ <CodeEditor
629
+ content={INITIAL_CODE}
630
+ language="javascript"
631
+ theme="darcula"
632
+ viewport={VIEWPORT}
633
+ onInitialized={handleInitialized}
634
+ onContentUpdate={(text) => setStatus(`${text.length} chars`)}
635
+ onHistorySizeUpdate={setHistorySize}
636
+ onLog={(...args) => console.log('[editor]', ...args)}
637
+ onError={(err) => console.error('[editor]', err)}
638
+ />
639
+ </KeyboardAvoidingView>
640
+ );
641
+ }
642
+
643
+ const styles = StyleSheet.create({
644
+ container: { flex: 1, backgroundColor: '#1e1e1e' },
645
+ toolbar: {
646
+ flexDirection: 'row',
647
+ alignItems: 'center',
648
+ padding: 8,
649
+ gap: 8,
650
+ backgroundColor: '#2d2d2d',
651
+ },
652
+ btn: {
653
+ backgroundColor: '#0e639c',
654
+ paddingHorizontal: 12,
655
+ paddingVertical: 6,
656
+ borderRadius: 4,
657
+ },
658
+ btnText: { color: '#fff', fontSize: 13 },
659
+ status: { color: '#aaa', fontSize: 12, flex: 1, textAlign: 'right' },
660
+ });
661
+ ```
662
+
663
+ ---
664
+
665
+ ## Contributing
666
+
667
+ ### Initial setup
668
+
669
+ ```bash
670
+ git clone https://github.com/burdiuz/react-native-codeditor.git
671
+ cd react-native-codeditor
672
+ npm install
673
+ ```
674
+
675
+ ### Regenerate WebView assets
676
+
677
+ The `src/assets/` files are pre-built and committed. Run this when `@actualwave/codemirror-package`
678
+ is updated to a new version:
679
+
680
+ ```bash
681
+ npm run copy-assets
682
+ ```
683
+
684
+ `copy-assets` reads from `node_modules/@actualwave/codemirror-package/dist/` and writes:
685
+ - `src/assets/codemirror-editor.umd.js` — plain IIFE bundle (no `import`/`export`)
686
+ - `src/assets/codemirror/` — individual CM6 module files
687
+
688
+ ### Build the library
689
+
690
+ ```bash
691
+ # Compile TypeScript → lib/ (ESM + type declarations):
692
+ npm run prepare
693
+
694
+ # Type-check only:
695
+ npm run typecheck
696
+ ```
697
+
698
+ ### Run the example app
699
+
700
+ ```bash
701
+ # 1. Build library
702
+ npm run prepare
703
+
704
+ # 2. Copy assets to the example's Android project
705
+ cp -r src/assets/* example/android/app/src/main/assets/codeditor/
706
+
707
+ # 3. Copy assets to the example's iOS project
708
+ cp -r src/assets/* example/ios/CodeditorExample/assets/codeditor/
709
+
710
+ # 4. Launch
711
+ cd example && npx expo run:android # or run:ios
712
+ ```
713
+
714
+ For iOS, also ensure the `assets` folder reference exists in Xcode (`project.pbxproj`).
715
+ See `COPY_ASSETS.md` for the full manual and automated steps.
716
+
717
+ Re-run steps 2–4 after any change to `src/assets/`. Re-run step 1 after any TypeScript
718
+ source change.
719
+
720
+ Metro resolves `@actualwave/react-native-codeditor` from the workspace root via `lib/module/index.js`.
721
+
722
+ Do NOT use `./gradlew clean` — it fails because `react-native-webview` codegen JNI
723
+ directories don't exist until after the first successful build. Use `npx expo run:android` directly.
724
+
725
+ ### Type-check
726
+
727
+ ```bash
728
+ npx tsc --noEmit
729
+ ```
730
+
731
+ ---
732
+
733
+ ## Updating dependencies (contributors)
734
+
735
+ ### Updating CodeMirror assets
736
+
737
+ `src/assets/codemirror/` and `src/assets/codemirror-editor.umd.js` are generated from
738
+ `@actualwave/js-codemirror-package` (a devDependency, published to npm). To update:
739
+
740
+ ```bash
741
+ # 1. Update the version in package.json, then:
742
+ npm install
743
+
744
+ # 2. Regenerate the assets:
745
+ npm run copy-assets
746
+
747
+ # 3. Copy to the example app:
748
+ cp -r src/assets/* example/android/app/src/main/assets/codeditor/
749
+ cp -r src/assets/* example/ios/CodeditorExample/assets/codeditor/
750
+ ```
751
+
752
+ Library consumers copy from `node_modules/@actualwave/react-native-codeditor/src/assets/`
753
+ into their own project (see [Copy native assets](#2-copy-native-assets)).
754
+
755
+ ---
756
+
757
+ ## Known issues
758
+
759
+ ### Android: characters appearing after the cursor when typing fast
760
+
761
+ Two separate issues can cause this, both fixed in the library:
762
+
763
+ **1. `drawSelection()` hiding the native cursor**
764
+
765
+ CM6's `drawSelection()` extension replaces the native browser cursor with a custom overlay.
766
+ Android's IME tracks the native cursor to know where to insert text — hiding it causes characters
767
+ to appear to the right of the cursor instead of advancing it.
768
+
769
+ **Fixed**: The library uses a custom `mobileSetup` that omits `drawSelection()`, keeping the
770
+ native cursor visible. If you add `drawSelection()` via a custom extension, this issue returns.
771
+
772
+ **2. EditContext API race condition (Chrome 126+ WebView)**
773
+
774
+ Chrome 126 introduced the [EditContext API](https://developer.chrome.com/docs/web-platform/editcontext/), which CM6 v6.42+ activates automatically on Android Chrome. In Chrome 147 there is a race condition where successive IME `textupdate` events arrive faster than CM6 can sync back via `editContext.updateText/updateSelection`, again placing characters after the cursor during fast typing.
775
+
776
+ **Fixed**: The library sets `EditorView.EDIT_CONTEXT = false` in `createEditor` (after the CM6
777
+ modules load but before the `EditorView` is constructed), falling back to the `contenteditable` +
778
+ MutationObserver path which is stable at any typing speed when `drawSelection()` is omitted.
779
+
780
+ ### iOS: editor URI must be provided manually
781
+
782
+ The default `editorUri` points to `file:///android_asset/codeditor/editor.html` (Android
783
+ only). On iOS you must compute the `.app` bundle path at runtime and pass it via the
784
+ `editorUri` prop (see [iOS: provide the editor URI](#4-ios-provide-the-editor-uri)).
785
+
786
+ ---
787
+
788
+ ## License
789
+
790
+ MIT