@openeditor/react-native-prose-editor 0.0.9 → 0.0.11
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.
- package/README.md +5 -9
- package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +23 -0
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +2 -2
- package/android/src/main/java/com/apollohg/editor/NativeBlockEditorSurface.kt +476 -53
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +110 -169
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +1 -1
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +124 -337
- package/dist/EditorTheme.d.ts +8 -0
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
- package/ios/Generated_editor_core.swift +15 -0
- package/ios/NativeBlockEditorSurface.swift +371 -60
- package/ios/NativeEditorExpoView.swift +114 -117
- package/ios/NativeInputSupport.swift +0 -302
- package/ios/PositionBridge.swift +22 -548
- package/ios/RichTextEditorView.swift +217 -4671
- package/ios/editor_coreFFI/editor_coreFFI.h +11 -0
- package/package.json +1 -1
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +31 -0
- package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +0 -50
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +0 -4068
- package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +0 -1009
- package/android/src/main/java/com/apollohg/editor/InputSnapshotSupport.kt +0 -6
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
import UIKit
|
|
2
|
-
|
|
3
|
-
extension Notification.Name {
|
|
4
|
-
static let editorImageAttachmentDidLoad = Notification.Name(
|
|
5
|
-
"com.openeditor.editor.imageAttachmentDidLoad"
|
|
6
|
-
)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
2
|
enum NativeImageLoader {
|
|
10
3
|
static let cache = NSCache<NSString, UIImage>()
|
|
11
4
|
static let stateQueue = DispatchQueue(label: "com.openeditor.editor.image-loader-state")
|
|
@@ -99,298 +92,3 @@ enum NativeImageLoader {
|
|
|
99
92
|
return Data(base64Encoded: payload, options: [.ignoreUnknownCharacters])
|
|
100
93
|
}
|
|
101
94
|
}
|
|
102
|
-
|
|
103
|
-
// MARK: - Constants
|
|
104
|
-
|
|
105
|
-
/// Custom NSAttributedString attribute keys for editor metadata.
|
|
106
|
-
enum InputCoordinatorAttributes {
|
|
107
|
-
/// Marks a character as a void element placeholder (hardBreak, horizontalRule).
|
|
108
|
-
/// The value is the node type string (e.g. "hardBreak", "horizontalRule").
|
|
109
|
-
static let voidNodeType = NSAttributedString.Key("com.openeditor.editor.voidNodeType")
|
|
110
|
-
|
|
111
|
-
/// Stores the Rust document position (UInt32) for void elements.
|
|
112
|
-
static let docPos = NSAttributedString.Key("com.openeditor.editor.docPos")
|
|
113
|
-
|
|
114
|
-
/// Marks a character as a block boundary (for block start/end tracking).
|
|
115
|
-
static let blockBoundary = NSAttributedString.Key("com.openeditor.editor.blockBoundary")
|
|
116
|
-
|
|
117
|
-
/// Stores the block node type (e.g. "paragraph", "listItem").
|
|
118
|
-
static let blockNodeType = NSAttributedString.Key("com.openeditor.editor.blockNodeType")
|
|
119
|
-
|
|
120
|
-
/// Stores the block depth (UInt8).
|
|
121
|
-
static let blockDepth = NSAttributedString.Key("com.openeditor.editor.blockDepth")
|
|
122
|
-
|
|
123
|
-
/// Stores list context info as a dictionary for list items.
|
|
124
|
-
static let listContext = NSAttributedString.Key("com.openeditor.editor.listContext")
|
|
125
|
-
|
|
126
|
-
/// Marks blocks that should render a visible list marker.
|
|
127
|
-
static let listMarkerContext = NSAttributedString.Key("com.openeditor.editor.listMarkerContext")
|
|
128
|
-
|
|
129
|
-
/// Stores the rendered list marker color for the paragraph marker.
|
|
130
|
-
static let listMarkerColor = NSAttributedString.Key("com.openeditor.editor.listMarkerColor")
|
|
131
|
-
|
|
132
|
-
/// Stores the rendered list marker scale for unordered bullets.
|
|
133
|
-
static let listMarkerScale = NSAttributedString.Key("com.openeditor.editor.listMarkerScale")
|
|
134
|
-
|
|
135
|
-
/// Stores the paragraph base font used to render the list marker.
|
|
136
|
-
static let listMarkerBaseFont = NSAttributedString.Key("com.openeditor.editor.listMarkerBaseFont")
|
|
137
|
-
|
|
138
|
-
/// Stores the reserved list marker gutter width.
|
|
139
|
-
static let listMarkerWidth = NSAttributedString.Key("com.openeditor.editor.listMarkerWidth")
|
|
140
|
-
|
|
141
|
-
/// Stores the rendered blockquote border color.
|
|
142
|
-
static let blockquoteBorderColor = NSAttributedString.Key("com.openeditor.editor.blockquoteBorderColor")
|
|
143
|
-
|
|
144
|
-
/// Stores the rendered blockquote border width.
|
|
145
|
-
static let blockquoteBorderWidth = NSAttributedString.Key("com.openeditor.editor.blockquoteBorderWidth")
|
|
146
|
-
|
|
147
|
-
/// Stores the rendered blockquote gap between border and text.
|
|
148
|
-
static let blockquoteMarkerGap = NSAttributedString.Key("com.openeditor.editor.blockquoteMarkerGap")
|
|
149
|
-
|
|
150
|
-
/// Marks code-block paragraphs for custom background drawing.
|
|
151
|
-
static let codeBlockBackgroundColor = NSAttributedString.Key("com.openeditor.editor.codeBlockBackgroundColor")
|
|
152
|
-
static let codeBlockBorderRadius = NSAttributedString.Key("com.openeditor.editor.codeBlockBorderRadius")
|
|
153
|
-
static let codeBlockPaddingHorizontal = NSAttributedString.Key("com.openeditor.editor.codeBlockPaddingHorizontal")
|
|
154
|
-
static let codeBlockPaddingVertical = NSAttributedString.Key("com.openeditor.editor.codeBlockPaddingVertical")
|
|
155
|
-
|
|
156
|
-
/// Marks synthetic zero-width placeholders used only for UIKit layout.
|
|
157
|
-
static let syntheticPlaceholder = NSAttributedString.Key("com.openeditor.editor.syntheticPlaceholder")
|
|
158
|
-
|
|
159
|
-
/// Stores the link href for visually styled link text without enabling UITextView's default link interaction.
|
|
160
|
-
static let linkHref = NSAttributedString.Key("com.openeditor.editor.linkHref")
|
|
161
|
-
|
|
162
|
-
/// Stores the owning top-level document child index for partial native patching.
|
|
163
|
-
static let topLevelChildIndex = NSAttributedString.Key("com.openeditor.editor.topLevelChildIndex")
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/// Layout constants for paragraph styles.
|
|
167
|
-
enum LayoutConstants {
|
|
168
|
-
/// Spacing between paragraphs (points).
|
|
169
|
-
static let paragraphSpacing: CGFloat = 8.0
|
|
170
|
-
|
|
171
|
-
/// Base indentation per depth level (points).
|
|
172
|
-
static let indentPerDepth: CGFloat = 24.0
|
|
173
|
-
|
|
174
|
-
/// Width reserved for the list bullet/number (points).
|
|
175
|
-
static let listMarkerWidth: CGFloat = 36.0
|
|
176
|
-
|
|
177
|
-
/// Gap between the list marker and the text that follows (points).
|
|
178
|
-
static let listMarkerTextGap: CGFloat = 8.0
|
|
179
|
-
|
|
180
|
-
/// Height of the horizontal rule separator line (points).
|
|
181
|
-
static let horizontalRuleHeight: CGFloat = 1.0
|
|
182
|
-
|
|
183
|
-
/// Vertical padding above and below the horizontal rule (points).
|
|
184
|
-
static let horizontalRuleVerticalPadding: CGFloat = 8.0
|
|
185
|
-
|
|
186
|
-
/// Total leading inset reserved for each blockquote depth.
|
|
187
|
-
static let blockquoteIndent: CGFloat = 18.0
|
|
188
|
-
|
|
189
|
-
/// Width of the rendered blockquote border bar.
|
|
190
|
-
static let blockquoteBorderWidth: CGFloat = 3.0
|
|
191
|
-
|
|
192
|
-
/// Gap between the blockquote border bar and the text that follows.
|
|
193
|
-
static let blockquoteMarkerGap: CGFloat = 8.0
|
|
194
|
-
|
|
195
|
-
/// Bullet character for unordered list items.
|
|
196
|
-
static let unorderedListBullet = "\u{2022} "
|
|
197
|
-
|
|
198
|
-
/// Scale factor applied only to unordered list marker glyphs.
|
|
199
|
-
static let unorderedListMarkerFontScale: CGFloat = 2.0
|
|
200
|
-
|
|
201
|
-
/// Object replacement character used for void block elements.
|
|
202
|
-
static let objectReplacementCharacter = "\u{FFFC}"
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
enum NativeListMarker {
|
|
206
|
-
static func text(for context: [String: Any]) -> String {
|
|
207
|
-
if context["task"] as? Bool == true {
|
|
208
|
-
return (context["checked"] as? Bool == true) ? "☑︎ " : "☐ "
|
|
209
|
-
}
|
|
210
|
-
if context["ordered"] as? Bool == true {
|
|
211
|
-
let index = context["index"] as? Int ?? 1
|
|
212
|
-
return "\(index). "
|
|
213
|
-
}
|
|
214
|
-
return LayoutConstants.unorderedListBullet
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
// MARK: - BlockContext
|
|
220
|
-
|
|
221
|
-
/// Transient context while rendering block elements. Pushed onto a stack
|
|
222
|
-
/// when a `blockStart` element is encountered and popped on `blockEnd`.
|
|
223
|
-
struct BlockContext {
|
|
224
|
-
let nodeType: String
|
|
225
|
-
let depth: UInt8
|
|
226
|
-
var listContext: [String: Any]?
|
|
227
|
-
var topLevelChildIndex: Int? = nil
|
|
228
|
-
var listMarkerContext: [String: Any]? = nil
|
|
229
|
-
var markerPending: Bool = false
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// MARK: - HorizontalRuleAttachment
|
|
233
|
-
|
|
234
|
-
/// NSTextAttachment subclass that draws a horizontal separator line.
|
|
235
|
-
///
|
|
236
|
-
/// The attachment renders as a thin line across the available width with
|
|
237
|
-
/// vertical padding. Used for `horizontalRule` void block elements.
|
|
238
|
-
final class HorizontalRuleAttachment: NSTextAttachment {
|
|
239
|
-
|
|
240
|
-
var lineColor: UIColor = .separator
|
|
241
|
-
var lineHeight: CGFloat = LayoutConstants.horizontalRuleHeight
|
|
242
|
-
var verticalPadding: CGFloat = LayoutConstants.horizontalRuleVerticalPadding
|
|
243
|
-
|
|
244
|
-
override func attachmentBounds(
|
|
245
|
-
for textContainer: NSTextContainer?,
|
|
246
|
-
proposedLineFragment lineFrag: CGRect,
|
|
247
|
-
glyphPosition position: CGPoint,
|
|
248
|
-
characterIndex charIndex: Int
|
|
249
|
-
) -> CGRect {
|
|
250
|
-
let totalHeight = lineHeight + (verticalPadding * 2)
|
|
251
|
-
return CGRect(
|
|
252
|
-
x: 0,
|
|
253
|
-
y: 0,
|
|
254
|
-
width: lineFrag.width,
|
|
255
|
-
height: totalHeight
|
|
256
|
-
)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
override func image(
|
|
260
|
-
forBounds imageBounds: CGRect,
|
|
261
|
-
textContainer: NSTextContainer?,
|
|
262
|
-
characterIndex charIndex: Int
|
|
263
|
-
) -> UIImage? {
|
|
264
|
-
let renderer = UIGraphicsImageRenderer(bounds: imageBounds)
|
|
265
|
-
return renderer.image { context in
|
|
266
|
-
lineColor.setFill()
|
|
267
|
-
let lineY = imageBounds.midY - (lineHeight / 2)
|
|
268
|
-
let lineRect = CGRect(
|
|
269
|
-
x: imageBounds.origin.x,
|
|
270
|
-
y: lineY,
|
|
271
|
-
width: imageBounds.width,
|
|
272
|
-
height: lineHeight
|
|
273
|
-
)
|
|
274
|
-
context.fill(lineRect)
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
final class BlockImageAttachment: NSTextAttachment {
|
|
280
|
-
private let source: String
|
|
281
|
-
private let placeholderTint: UIColor
|
|
282
|
-
private var preferredWidth: CGFloat?
|
|
283
|
-
private var preferredHeight: CGFloat?
|
|
284
|
-
private var loadedImage: UIImage?
|
|
285
|
-
|
|
286
|
-
init(
|
|
287
|
-
source: String,
|
|
288
|
-
placeholderTint: UIColor,
|
|
289
|
-
preferredWidth: CGFloat?,
|
|
290
|
-
preferredHeight: CGFloat?
|
|
291
|
-
) {
|
|
292
|
-
self.source = source
|
|
293
|
-
self.placeholderTint = placeholderTint
|
|
294
|
-
self.preferredWidth = preferredWidth
|
|
295
|
-
self.preferredHeight = preferredHeight
|
|
296
|
-
super.init(data: nil, ofType: nil)
|
|
297
|
-
loadImageIfNeeded()
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
required init?(coder: NSCoder) {
|
|
301
|
-
return nil
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
func setPreferredSize(width: CGFloat, height: CGFloat) {
|
|
305
|
-
preferredWidth = width
|
|
306
|
-
preferredHeight = height
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
func previewImage() -> UIImage? {
|
|
310
|
-
loadedImage ?? image
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
override func attachmentBounds(
|
|
314
|
-
for textContainer: NSTextContainer?,
|
|
315
|
-
proposedLineFragment lineFrag: CGRect,
|
|
316
|
-
glyphPosition position: CGPoint,
|
|
317
|
-
characterIndex charIndex: Int
|
|
318
|
-
) -> CGRect {
|
|
319
|
-
let lineFragmentWidth = lineFrag.width.isFinite ? lineFrag.width : 0
|
|
320
|
-
let containerWidth = textContainer.map {
|
|
321
|
-
max(0, $0.size.width - ($0.lineFragmentPadding * 2))
|
|
322
|
-
} ?? 0
|
|
323
|
-
let widthCandidates = [lineFragmentWidth, containerWidth].filter { $0.isFinite && $0 > 0 }
|
|
324
|
-
let maxWidth = max(160, widthCandidates.min() ?? 160)
|
|
325
|
-
let fallbackAspectRatio = loadedImage.flatMap { image -> CGFloat? in
|
|
326
|
-
let imageSize = image.size
|
|
327
|
-
guard imageSize.width > 0, imageSize.height > 0 else { return nil }
|
|
328
|
-
return imageSize.height / imageSize.width
|
|
329
|
-
} ?? 0.56
|
|
330
|
-
|
|
331
|
-
var resolvedWidth = preferredWidth
|
|
332
|
-
var resolvedHeight = preferredHeight
|
|
333
|
-
|
|
334
|
-
if resolvedWidth == nil, resolvedHeight == nil, let loadedImage {
|
|
335
|
-
let imageSize = loadedImage.size
|
|
336
|
-
if imageSize.width > 0, imageSize.height > 0 {
|
|
337
|
-
resolvedWidth = imageSize.width
|
|
338
|
-
resolvedHeight = imageSize.height
|
|
339
|
-
}
|
|
340
|
-
} else if resolvedWidth == nil, let resolvedHeight {
|
|
341
|
-
resolvedWidth = resolvedHeight / fallbackAspectRatio
|
|
342
|
-
} else if resolvedHeight == nil, let resolvedWidth {
|
|
343
|
-
resolvedHeight = resolvedWidth * fallbackAspectRatio
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
let width = max(1, resolvedWidth ?? maxWidth)
|
|
347
|
-
let height = max(1, resolvedHeight ?? min(180, maxWidth * fallbackAspectRatio))
|
|
348
|
-
let scale = min(1, maxWidth / width)
|
|
349
|
-
return CGRect(x: 0, y: 0, width: width * scale, height: height * scale)
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
override func image(
|
|
353
|
-
forBounds imageBounds: CGRect,
|
|
354
|
-
textContainer: NSTextContainer?,
|
|
355
|
-
characterIndex charIndex: Int
|
|
356
|
-
) -> UIImage? {
|
|
357
|
-
if let loadedImage {
|
|
358
|
-
return loadedImage
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
let renderer = UIGraphicsImageRenderer(bounds: imageBounds)
|
|
362
|
-
return renderer.image { _ in
|
|
363
|
-
let path = UIBezierPath(roundedRect: imageBounds, cornerRadius: 12)
|
|
364
|
-
UIColor.secondarySystemFill.setFill()
|
|
365
|
-
path.fill()
|
|
366
|
-
|
|
367
|
-
let iconSize = min(imageBounds.width, imageBounds.height) * 0.28
|
|
368
|
-
let iconOrigin = CGPoint(
|
|
369
|
-
x: imageBounds.midX - (iconSize / 2),
|
|
370
|
-
y: imageBounds.midY - (iconSize / 2)
|
|
371
|
-
)
|
|
372
|
-
let iconRect = CGRect(origin: iconOrigin, size: CGSize(width: iconSize, height: iconSize))
|
|
373
|
-
|
|
374
|
-
if #available(iOS 13.0, *) {
|
|
375
|
-
let config = UIImage.SymbolConfiguration(pointSize: iconSize, weight: .medium)
|
|
376
|
-
let icon = UIImage(systemName: "photo", withConfiguration: config)?
|
|
377
|
-
.withTintColor(placeholderTint.withAlphaComponent(0.7), renderingMode: .alwaysOriginal)
|
|
378
|
-
icon?.draw(in: iconRect)
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
private func loadImageIfNeeded() {
|
|
384
|
-
NativeImageLoader.load(source: source) { [weak self] image in
|
|
385
|
-
guard let self,
|
|
386
|
-
let image
|
|
387
|
-
else {
|
|
388
|
-
return
|
|
389
|
-
}
|
|
390
|
-
self.loadedImage = image
|
|
391
|
-
self.image = image
|
|
392
|
-
NotificationCenter.default.post(name: .editorImageAttachmentDidLoad, object: self)
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
}
|