@openeditor/react-native-prose-editor 0.0.2

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 (91) hide show
  1. package/LICENSE +160 -0
  2. package/README.md +180 -0
  3. package/android/build.gradle +69 -0
  4. package/android/consumer-rules.pro +18 -0
  5. package/android/src/androidTest/AndroidManifest.xml +8 -0
  6. package/android/src/androidTest/java/com/apollohg/editor/MeasureHeightInstrumentedTest.kt +53 -0
  7. package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceCollaborationInitialSyncTest.kt +241 -0
  8. package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceImeRegressionTest.kt +338 -0
  9. package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceOutsideTapTest.kt +789 -0
  10. package/android/src/androidTest/java/com/apollohg/editor/NativeDevicePerformanceTest.kt +350 -0
  11. package/android/src/androidTest/java/com/apollohg/editor/NativeEditorOutsideTapActivity.kt +5 -0
  12. package/android/src/main/assets/editor-icons/MaterialIcons.json +2236 -0
  13. package/android/src/main/assets/editor-icons/MaterialIcons.ttf +0 -0
  14. package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +50 -0
  15. package/android/src/main/java/com/apollohg/editor/EditorAddons.kt +135 -0
  16. package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +4527 -0
  17. package/android/src/main/java/com/apollohg/editor/EditorHeightBehavior.kt +14 -0
  18. package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +999 -0
  19. package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +474 -0
  20. package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +199 -0
  21. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +3137 -0
  22. package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +561 -0
  23. package/android/src/main/java/com/apollohg/editor/NativeProseViewerExpoView.kt +315 -0
  24. package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +1413 -0
  25. package/android/src/main/java/com/apollohg/editor/PositionBridge.kt +133 -0
  26. package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +315 -0
  27. package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +2172 -0
  28. package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +399 -0
  29. package/android/src/sharedTest/java/com/apollohg/editor/NativePerformanceSupport.kt +482 -0
  30. package/android/src/test/java/com/apollohg/editor/CaretGeometryTest.kt +137 -0
  31. package/android/src/test/java/com/apollohg/editor/EditorEditTextHardwareKeyTest.kt +321 -0
  32. package/android/src/test/java/com/apollohg/editor/EditorInputConnectionTest.kt +4202 -0
  33. package/android/src/test/java/com/apollohg/editor/NativeEditorExpoViewTest.kt +2581 -0
  34. package/android/src/test/java/com/apollohg/editor/NativeEditorModuleTest.kt +27 -0
  35. package/android/src/test/java/com/apollohg/editor/NativePerformanceTest.kt +197 -0
  36. package/android/src/test/java/com/apollohg/editor/NativeProseViewerExpoViewTest.kt +84 -0
  37. package/android/src/test/java/com/apollohg/editor/NativeToolbarTest.kt +544 -0
  38. package/android/src/test/java/com/apollohg/editor/PositionBridgeTest.kt +461 -0
  39. package/android/src/test/java/com/apollohg/editor/RenderBridgeTest.kt +2054 -0
  40. package/android/src/test/java/com/apollohg/editor/RichTextEditorViewTest.kt +1367 -0
  41. package/app.plugin.js +62 -0
  42. package/dist/EditorTheme.d.ts +113 -0
  43. package/dist/EditorTheme.js +29 -0
  44. package/dist/EditorToolbar.d.ts +191 -0
  45. package/dist/EditorToolbar.js +1095 -0
  46. package/dist/NativeEditorBridge.d.ts +325 -0
  47. package/dist/NativeEditorBridge.js +933 -0
  48. package/dist/NativeProseViewer.d.ts +53 -0
  49. package/dist/NativeProseViewer.js +480 -0
  50. package/dist/NativeRichTextEditor.d.ts +183 -0
  51. package/dist/NativeRichTextEditor.js +2574 -0
  52. package/dist/YjsCollaboration.d.ts +90 -0
  53. package/dist/YjsCollaboration.js +743 -0
  54. package/dist/addons.d.ts +100 -0
  55. package/dist/addons.js +82 -0
  56. package/dist/heightCache.d.ts +6 -0
  57. package/dist/heightCache.js +45 -0
  58. package/dist/index.d.ts +10 -0
  59. package/dist/index.js +30 -0
  60. package/dist/schemas.d.ts +37 -0
  61. package/dist/schemas.js +263 -0
  62. package/dist/useNativeEditor.d.ts +42 -0
  63. package/dist/useNativeEditor.js +124 -0
  64. package/expo-module.config.json +9 -0
  65. package/ios/EditorAddons.swift +327 -0
  66. package/ios/EditorCore.xcframework/Info.plist +44 -0
  67. package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
  68. package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
  69. package/ios/EditorLayoutManager.swift +774 -0
  70. package/ios/EditorTheme.swift +515 -0
  71. package/ios/Generated_editor_core.swift +1513 -0
  72. package/ios/NativeEditorExpoView.swift +3593 -0
  73. package/ios/NativeEditorModule.swift +624 -0
  74. package/ios/NativeProseViewerExpoView.swift +276 -0
  75. package/ios/PositionBridge.swift +558 -0
  76. package/ios/ReactNativeProseEditor.podspec +49 -0
  77. package/ios/RenderBridge.swift +1708 -0
  78. package/ios/RichTextEditorView.swift +6150 -0
  79. package/ios/Tests/NativePerformanceTests.swift +687 -0
  80. package/ios/Tests/PositionBridgeTests.swift +706 -0
  81. package/ios/Tests/RenderBridgeTests.swift +2236 -0
  82. package/ios/Tests/RichTextEditorViewTests.swift +6548 -0
  83. package/ios/editor_coreFFI/editor_coreFFI.h +1289 -0
  84. package/ios/editor_coreFFI/module.modulemap +7 -0
  85. package/ios/editor_coreFFI.h +904 -0
  86. package/ios/editor_coreFFI.modulemap +7 -0
  87. package/package.json +74 -0
  88. package/rust/android/arm64-v8a/libeditor_core.so +0 -0
  89. package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
  90. package/rust/android/x86_64/libeditor_core.so +0 -0
  91. package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +2563 -0
@@ -0,0 +1,50 @@
1
+ package com.openeditor.editor
2
+
3
+ import android.graphics.Paint
4
+ import android.text.Layout
5
+
6
+ /**
7
+ * Vertical geometry for the text caret, clipped to the rendered glyph height.
8
+ *
9
+ * Android's [android.widget.Editor] draws the native caret from
10
+ * `Layout.getLineTop(line)` to `Layout.getLineBottom(line)`. When a
11
+ * [ParagraphSpacerSpan] inflates a line's descent to create inter-block
12
+ * spacing, `getLineBottom` includes that gap and the caret stretches into it.
13
+ * `getLineBottomWithoutSpacing` cannot help: the inflation lives in the line's
14
+ * DESCENT column, not the line-spacing EXTRA column it subtracts.
15
+ *
16
+ * The baseline is provably independent of descent inflation
17
+ * (`getLineBaseline(line) == getLineTop(line) - ascent`), so anchoring the
18
+ * caret bottom at `baseline + raw font descent` clips it to the glyph height.
19
+ * This mirrors the trim already used for blockquote stripes
20
+ * ([BlockquoteSpan.resolvedStripeBottom]).
21
+ */
22
+ object CaretGeometry {
23
+ data class VerticalBounds(val top: Float, val bottom: Float)
24
+
25
+ /**
26
+ * Whether the manually-drawn caret should be visible. The native caret is
27
+ * suppressed, so this gates our replacement: only when the field is focused,
28
+ * its window is focused, and the selection is a collapsed insertion point
29
+ * (a range selection shows the selection highlight instead).
30
+ */
31
+ fun shouldRender(
32
+ focused: Boolean,
33
+ windowFocused: Boolean,
34
+ selectionStart: Int,
35
+ selectionEnd: Int
36
+ ): Boolean = focused &&
37
+ windowFocused &&
38
+ selectionStart >= 0 &&
39
+ selectionStart == selectionEnd
40
+
41
+ fun verticalBounds(layout: Layout, offset: Int, paint: Paint): VerticalBounds {
42
+ val line = layout.getLineForOffset(offset.coerceIn(0, layout.text.length))
43
+ val top = layout.getLineTop(line).toFloat()
44
+ // Anchor the bottom at the glyph descent below the baseline. The baseline
45
+ // is independent of any ReplacementSpan descent inflation, so this clips
46
+ // the caret to the rendered text height instead of the inflated line bottom.
47
+ val bottom = layout.getLineBaseline(line) + paint.fontMetrics.descent
48
+ return VerticalBounds(top, bottom)
49
+ }
50
+ }
@@ -0,0 +1,135 @@
1
+ package com.openeditor.editor
2
+
3
+ import org.json.JSONObject
4
+
5
+ data class NativeMentionSuggestion(
6
+ val key: String,
7
+ val title: String,
8
+ val subtitle: String?,
9
+ val label: String,
10
+ val attrs: JSONObject
11
+ ) {
12
+ companion object {
13
+ fun fromJson(json: JSONObject?): NativeMentionSuggestion? {
14
+ json ?: return null
15
+ val key = json.optString("key", "")
16
+ val title = json.optString("title", "")
17
+ val label = json.optString("label", "")
18
+ if (key.isBlank() || title.isBlank() || label.isBlank()) return null
19
+ return NativeMentionSuggestion(
20
+ key = key,
21
+ title = title,
22
+ subtitle = json.takeUnless { it.isNull("subtitle") }?.optString("subtitle"),
23
+ label = label,
24
+ attrs = json.optJSONObject("attrs") ?: JSONObject()
25
+ )
26
+ }
27
+ }
28
+ }
29
+
30
+ data class NativeMentionsAddonConfig(
31
+ val trigger: String,
32
+ val suggestions: List<NativeMentionSuggestion>,
33
+ val theme: EditorMentionTheme?,
34
+ val resolveSelectionAttrs: Boolean,
35
+ val resolveTheme: Boolean
36
+ ) {
37
+ companion object {
38
+ fun fromJson(json: JSONObject?): NativeMentionsAddonConfig? {
39
+ json ?: return null
40
+ val trigger = json.optString("trigger", "@").ifBlank { "@" }
41
+ val rawSuggestions = json.optJSONArray("suggestions")
42
+ val suggestions = mutableListOf<NativeMentionSuggestion>()
43
+ if (rawSuggestions != null) {
44
+ for (index in 0 until rawSuggestions.length()) {
45
+ val suggestion = NativeMentionSuggestion.fromJson(
46
+ rawSuggestions.optJSONObject(index)
47
+ )
48
+ if (suggestion != null) {
49
+ suggestions.add(suggestion)
50
+ }
51
+ }
52
+ }
53
+ return NativeMentionsAddonConfig(
54
+ trigger = trigger,
55
+ suggestions = suggestions,
56
+ theme = EditorMentionTheme.fromJson(json.optJSONObject("theme")),
57
+ resolveSelectionAttrs = json.optBoolean("resolveSelectionAttrs", false),
58
+ resolveTheme = json.optBoolean("resolveTheme", false)
59
+ )
60
+ }
61
+ }
62
+ }
63
+
64
+ data class NativeEditorAddons(
65
+ val mentions: NativeMentionsAddonConfig?
66
+ ) {
67
+ companion object {
68
+ fun fromJson(json: String?): NativeEditorAddons {
69
+ if (json.isNullOrBlank()) return NativeEditorAddons(null)
70
+ val root = try {
71
+ JSONObject(json)
72
+ } catch (_: Exception) {
73
+ return NativeEditorAddons(null)
74
+ }
75
+ return NativeEditorAddons(
76
+ mentions = NativeMentionsAddonConfig.fromJson(root.optJSONObject("mentions"))
77
+ )
78
+ }
79
+ }
80
+ }
81
+
82
+ data class MentionQueryState(
83
+ val query: String,
84
+ val trigger: String,
85
+ val anchor: Int,
86
+ val head: Int
87
+ )
88
+
89
+ internal fun isMentionIdentifierCodePoint(codePoint: Int): Boolean {
90
+ return Character.isLetterOrDigit(codePoint) || codePoint == '_'.code || codePoint == '-'.code
91
+ }
92
+
93
+ internal fun resolveMentionQueryState(
94
+ text: String,
95
+ cursorScalar: Int,
96
+ trigger: String,
97
+ isCaretInsideMention: Boolean
98
+ ): MentionQueryState? {
99
+ if (isCaretInsideMention) return null
100
+
101
+ val scalars = text.codePoints().toArray()
102
+ if (cursorScalar > scalars.size) return null
103
+ val triggerCodePoint = trigger.codePointAt(0)
104
+
105
+ var start = cursorScalar
106
+ while (start > 0) {
107
+ val previous = scalars[start - 1]
108
+ if (Character.isWhitespace(previous) ||
109
+ previous == '\n'.code ||
110
+ previous == 0xFFFC ||
111
+ (!isMentionIdentifierCodePoint(previous) && previous != triggerCodePoint)
112
+ ) {
113
+ break
114
+ }
115
+ start -= 1
116
+ }
117
+
118
+ if (start >= scalars.size || scalars[start] != triggerCodePoint) return null
119
+ if (start > 0) {
120
+ val previous = scalars[start - 1]
121
+ if (isMentionIdentifierCodePoint(previous)) {
122
+ return null
123
+ }
124
+ }
125
+
126
+ val query = String(scalars, start + 1, cursorScalar - (start + 1))
127
+ if (query.any { it.isWhitespace() }) return null
128
+
129
+ return MentionQueryState(
130
+ query = query,
131
+ trigger = trigger,
132
+ anchor = start,
133
+ head = cursorScalar
134
+ )
135
+ }