@openeditor/react-native-prose-editor 0.0.7 → 0.0.9
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/android/build.gradle +35 -0
- package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +2 -2
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +138 -597
- package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +13 -3
- package/android/src/main/java/com/apollohg/editor/InputSnapshotSupport.kt +6 -0
- package/android/src/main/java/com/apollohg/editor/NativeBlockEditorSurface.kt +1086 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +1 -1
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +26 -50
- package/android/src/main/java/com/apollohg/editor/NativeImageLoader.kt +89 -0
- package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +31 -66
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +98 -33
- package/dist/EditorToolbar.d.ts +1 -1
- package/dist/EditorToolbar.js +19 -0
- package/dist/NativeEditorBridge.d.ts +62 -35
- package/dist/NativeEditorBridge.js +122 -79
- package/dist/NativeProseViewer.d.ts +13 -34
- package/dist/NativeProseViewer.js +40 -465
- package/dist/NativeRichTextEditor.d.ts +10 -1
- package/dist/NativeRichTextEditor.js +72 -34
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/schemas.d.ts +11 -0
- package/dist/schemas.js +149 -0
- package/dist/useNativeEditor.d.ts +3 -3
- package/dist/useNativeEditor.js +3 -3
- 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 +97 -2
- package/ios/NativeBlockEditorSurface.swift +1445 -0
- package/ios/NativeEditorExpoView.swift +3 -3
- package/ios/NativeEditorModule.swift +31 -46
- package/ios/NativeInputSupport.swift +396 -0
- package/ios/PositionBridge.swift +4 -4
- package/ios/RichTextEditorView.swift +352 -786
- package/ios/editor_coreFFI/editor_coreFFI.h +147 -70
- 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 +2087 -1399
- package/android/src/androidTest/AndroidManifest.xml +0 -8
- package/android/src/androidTest/java/com/apollohg/editor/MeasureHeightInstrumentedTest.kt +0 -53
- package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceCollaborationInitialSyncTest.kt +0 -241
- package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceImeRegressionTest.kt +0 -338
- package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceOutsideTapTest.kt +0 -789
- package/android/src/androidTest/java/com/apollohg/editor/NativeDevicePerformanceTest.kt +0 -350
- package/android/src/androidTest/java/com/apollohg/editor/NativeEditorOutsideTapActivity.kt +0 -5
- package/android/src/main/java/com/apollohg/editor/NativeProseViewerExpoView.kt +0 -315
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +0 -2172
- package/android/src/sharedTest/java/com/apollohg/editor/NativePerformanceSupport.kt +0 -482
- package/android/src/test/java/com/apollohg/editor/CaretGeometryTest.kt +0 -137
- package/android/src/test/java/com/apollohg/editor/EditorEditTextHardwareKeyTest.kt +0 -321
- package/android/src/test/java/com/apollohg/editor/EditorInputConnectionTest.kt +0 -4202
- package/android/src/test/java/com/apollohg/editor/NativeEditorExpoViewTest.kt +0 -2581
- package/android/src/test/java/com/apollohg/editor/NativeEditorModuleTest.kt +0 -27
- package/android/src/test/java/com/apollohg/editor/NativePerformanceTest.kt +0 -197
- package/android/src/test/java/com/apollohg/editor/NativeProseViewerExpoViewTest.kt +0 -84
- package/android/src/test/java/com/apollohg/editor/NativeToolbarTest.kt +0 -544
- package/android/src/test/java/com/apollohg/editor/PositionBridgeTest.kt +0 -461
- package/android/src/test/java/com/apollohg/editor/RenderBridgeTest.kt +0 -2054
- package/android/src/test/java/com/apollohg/editor/RichTextEditorViewTest.kt +0 -1367
- package/ios/EditorLayoutManager.swift +0 -774
- package/ios/NativeProseViewerExpoView.swift +0 -276
- package/ios/RenderBridge.swift +0 -1708
- package/ios/Tests/NativePerformanceTests.swift +0 -687
- package/ios/Tests/PositionBridgeTests.swift +0 -706
- package/ios/Tests/RenderBridgeTests.swift +0 -2236
- package/ios/Tests/RichTextEditorViewTests.swift +0 -6548
|
@@ -36,6 +36,7 @@ export interface NativeEditorModule {
|
|
|
36
36
|
editorToggleBlockquote(editorId: number): string;
|
|
37
37
|
editorToggleHeading(editorId: number, level: number): string;
|
|
38
38
|
editorSetSelection(editorId: number, anchor: number, head: number): void;
|
|
39
|
+
editorSetCellSelection?(editorId: number, anchor: number, head: number): void;
|
|
39
40
|
editorGetSelection(editorId: number): string;
|
|
40
41
|
editorGetSelectionState(editorId: number): string;
|
|
41
42
|
editorGetCurrentState(editorId: number): string;
|
|
@@ -61,6 +62,11 @@ export interface NativeEditorModule {
|
|
|
61
62
|
editorUnwrapFromList(editorId: number): string;
|
|
62
63
|
editorIndentListItem(editorId: number): string;
|
|
63
64
|
editorOutdentListItem(editorId: number): string;
|
|
65
|
+
editorInsertTableRow(editorId: number, after: boolean): string;
|
|
66
|
+
editorDeleteTableRow(editorId: number): string;
|
|
67
|
+
editorInsertTableColumn(editorId: number, after: boolean): string;
|
|
68
|
+
editorDeleteTableColumn(editorId: number): string;
|
|
69
|
+
editorMoveTableCell(editorId: number, forward: boolean): string;
|
|
64
70
|
editorInsertNode(editorId: number, nodeType: string): string;
|
|
65
71
|
editorUndo(editorId: number): string;
|
|
66
72
|
editorRedo(editorId: number): string;
|
|
@@ -68,42 +74,55 @@ export interface NativeEditorModule {
|
|
|
68
74
|
editorCanRedo(editorId: number): boolean;
|
|
69
75
|
}
|
|
70
76
|
export interface Selection {
|
|
71
|
-
type: 'text' | 'node' | 'all';
|
|
77
|
+
type: 'text' | 'node' | 'cell' | 'all';
|
|
72
78
|
anchor?: number;
|
|
73
79
|
head?: number;
|
|
74
80
|
pos?: number;
|
|
81
|
+
anchorScalar?: number;
|
|
82
|
+
headScalar?: number;
|
|
83
|
+
posScalar?: number;
|
|
75
84
|
}
|
|
76
|
-
export interface
|
|
77
|
-
ordered: boolean;
|
|
78
|
-
index: number;
|
|
79
|
-
total: number;
|
|
80
|
-
start: number;
|
|
81
|
-
isFirst: boolean;
|
|
82
|
-
isLast: boolean;
|
|
83
|
-
kind?: string | null;
|
|
84
|
-
checked?: boolean | null;
|
|
85
|
-
}
|
|
86
|
-
export interface RenderMarkWithAttrs {
|
|
85
|
+
export interface RenderTreeMarkWithAttrs {
|
|
87
86
|
type: string;
|
|
88
87
|
[key: string]: unknown;
|
|
89
88
|
}
|
|
90
|
-
export type
|
|
91
|
-
export
|
|
92
|
-
|
|
89
|
+
export type RenderTreeMark = string | RenderTreeMarkWithAttrs;
|
|
90
|
+
export type RenderTreeNodeKind = 'doc' | 'block' | 'textBlock' | 'list' | 'listItem' | 'columns' | 'column' | 'table' | 'tableRow' | 'tableCell' | 'text' | 'inlineAtom' | 'blockAtom' | 'inlineContainer';
|
|
91
|
+
export interface RenderTreeNode {
|
|
92
|
+
kind: RenderTreeNodeKind;
|
|
93
|
+
nodeType: string;
|
|
94
|
+
docPos: number;
|
|
95
|
+
docSize: number;
|
|
96
|
+
attrs?: Record<string, unknown>;
|
|
97
|
+
marks?: RenderTreeMark[];
|
|
93
98
|
text?: string;
|
|
94
|
-
marks?: RenderMark[];
|
|
95
|
-
nodeType?: string;
|
|
96
|
-
depth?: number;
|
|
97
|
-
docPos?: number;
|
|
98
99
|
label?: string;
|
|
99
|
-
attrs?: Record<string, unknown>;
|
|
100
100
|
mentionTheme?: EditorMentionTheme;
|
|
101
|
-
|
|
101
|
+
children?: RenderTreeNode[];
|
|
102
|
+
}
|
|
103
|
+
export interface RenderTree {
|
|
104
|
+
version: number;
|
|
105
|
+
root: RenderTreeNode;
|
|
102
106
|
}
|
|
103
|
-
|
|
107
|
+
export type RenderTreePatchOperation = {
|
|
108
|
+
type: 'updateMetadata';
|
|
109
|
+
path: number[];
|
|
110
|
+
docPos: number;
|
|
111
|
+
docSize: number;
|
|
112
|
+
} | {
|
|
113
|
+
type: 'replaceNode';
|
|
114
|
+
path: number[];
|
|
115
|
+
node: RenderTreeNode;
|
|
116
|
+
} | {
|
|
117
|
+
type: 'spliceChildren';
|
|
118
|
+
path: number[];
|
|
104
119
|
startIndex: number;
|
|
105
120
|
deleteCount: number;
|
|
106
|
-
|
|
121
|
+
children: RenderTreeNode[];
|
|
122
|
+
};
|
|
123
|
+
export interface RenderTreePatch {
|
|
124
|
+
version: number;
|
|
125
|
+
operations: RenderTreePatchOperation[];
|
|
107
126
|
}
|
|
108
127
|
export interface ActiveState {
|
|
109
128
|
marks: Record<string, boolean>;
|
|
@@ -118,9 +137,10 @@ export interface HistoryState {
|
|
|
118
137
|
canRedo: boolean;
|
|
119
138
|
}
|
|
120
139
|
export interface EditorUpdate {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
140
|
+
renderTree: RenderTree;
|
|
141
|
+
renderPatch?: RenderTreePatch;
|
|
142
|
+
/** Canonical flat IME snapshot; offsets match scalar selection fields. */
|
|
143
|
+
inputText: string;
|
|
124
144
|
selection: Selection;
|
|
125
145
|
activeState: ActiveState;
|
|
126
146
|
historyState: HistoryState;
|
|
@@ -156,7 +176,8 @@ export interface CommandBlockedInfo {
|
|
|
156
176
|
}
|
|
157
177
|
export type EncodedCollaborationStateInput = Uint8Array | readonly number[] | string;
|
|
158
178
|
export declare function normalizeActiveState(raw: unknown): ActiveState;
|
|
159
|
-
export declare function parseEditorUpdateJson(json: string,
|
|
179
|
+
export declare function parseEditorUpdateJson(json: string, previousRenderTree?: RenderTree): EditorUpdate | null;
|
|
180
|
+
export declare function applyRenderTreePatch(tree: RenderTree, patch: RenderTreePatch): RenderTree;
|
|
160
181
|
export declare function encodeCollaborationStateBase64(encodedState: EncodedCollaborationStateInput): string;
|
|
161
182
|
export declare function decodeCollaborationStateBase64(base64: string): Uint8Array;
|
|
162
183
|
export declare function parseCollaborationResultJson(json: string): CollaborationResult;
|
|
@@ -170,7 +191,7 @@ export declare class NativeEditorBridge {
|
|
|
170
191
|
private _documentVersion;
|
|
171
192
|
private _cachedHtml;
|
|
172
193
|
private _cachedJsonString;
|
|
173
|
-
private
|
|
194
|
+
private _renderTreeCache;
|
|
174
195
|
private _lastCommandBlocked;
|
|
175
196
|
private _lastCommandBlockedReason;
|
|
176
197
|
private _lastCommandPreflightUpdate;
|
|
@@ -194,16 +215,16 @@ export declare class NativeEditorBridge {
|
|
|
194
215
|
prepareForNativeCommand(): boolean;
|
|
195
216
|
/** Destroy the editor instance and free native resources. */
|
|
196
217
|
destroy(): void;
|
|
197
|
-
/** Set content from HTML. Returns
|
|
198
|
-
setHtml(html: string):
|
|
218
|
+
/** Set content from HTML. Returns the structural editor state. */
|
|
219
|
+
setHtml(html: string): EditorUpdate | null;
|
|
199
220
|
/** Get content as HTML. */
|
|
200
221
|
getHtml(): string;
|
|
201
222
|
/** Get cached HTML without making a native roundtrip. */
|
|
202
223
|
getCachedHtml(): string | null;
|
|
203
|
-
/** Set content from ProseMirror JSON. Returns
|
|
204
|
-
setJson(doc: DocumentJSON):
|
|
205
|
-
/** Set content from a serialized ProseMirror JSON string. Returns
|
|
206
|
-
setJsonString(jsonString: string):
|
|
224
|
+
/** Set content from ProseMirror JSON. Returns the structural editor state. */
|
|
225
|
+
setJson(doc: DocumentJSON): EditorUpdate | null;
|
|
226
|
+
/** Set content from a serialized ProseMirror JSON string. Returns the structural editor state. */
|
|
227
|
+
setJsonString(jsonString: string): EditorUpdate | null;
|
|
207
228
|
/** Get content as raw ProseMirror JSON string. */
|
|
208
229
|
getJsonString(): string;
|
|
209
230
|
/** Get cached raw ProseMirror JSON without making a native roundtrip. */
|
|
@@ -236,6 +257,8 @@ export declare class NativeEditorBridge {
|
|
|
236
257
|
toggleHeading(level: number): EditorUpdate | null;
|
|
237
258
|
/** Set the document selection by anchor and head positions. */
|
|
238
259
|
setSelection(anchor: number, head: number): void;
|
|
260
|
+
/** Set a table cell selection by cell document positions. */
|
|
261
|
+
setCellSelection(anchor: number, head: number): void;
|
|
239
262
|
/** Convert a document position to a scalar position used by native text views. */
|
|
240
263
|
docToScalar(docPos: number): number;
|
|
241
264
|
/** Convert a native scalar position back to a document position. */
|
|
@@ -282,6 +305,11 @@ export declare class NativeEditorBridge {
|
|
|
282
305
|
indentListItem(): EditorUpdate | null;
|
|
283
306
|
/** Outdent the current list item to the parent list level. */
|
|
284
307
|
outdentListItem(): EditorUpdate | null;
|
|
308
|
+
insertTableRow(after?: boolean): EditorUpdate | null;
|
|
309
|
+
deleteTableRow(): EditorUpdate | null;
|
|
310
|
+
insertTableColumn(after?: boolean): EditorUpdate | null;
|
|
311
|
+
deleteTableColumn(): EditorUpdate | null;
|
|
312
|
+
moveTableCell(forward?: boolean): EditorUpdate | null;
|
|
285
313
|
/** Insert a void node (e.g. 'horizontalRule') at the current selection. */
|
|
286
314
|
insertNode(nodeType: string): EditorUpdate | null;
|
|
287
315
|
parseUpdateJson(json: string, options?: ParseUpdateOptions): EditorUpdate | null;
|
|
@@ -322,4 +350,3 @@ export declare class NativeCollaborationBridge {
|
|
|
322
350
|
clearLocalAwareness(): CollaborationResult;
|
|
323
351
|
private assertNotDestroyed;
|
|
324
352
|
}
|
|
325
|
-
export {};
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.NativeCollaborationBridge = exports.NativeEditorBridge = void 0;
|
|
4
4
|
exports.normalizeActiveState = normalizeActiveState;
|
|
5
5
|
exports.parseEditorUpdateJson = parseEditorUpdateJson;
|
|
6
|
+
exports.applyRenderTreePatch = applyRenderTreePatch;
|
|
6
7
|
exports.encodeCollaborationStateBase64 = encodeCollaborationStateBase64;
|
|
7
8
|
exports.decodeCollaborationStateBase64 = decodeCollaborationStateBase64;
|
|
8
9
|
exports.parseCollaborationResultJson = parseCollaborationResultJson;
|
|
@@ -25,30 +26,7 @@ function normalizeActiveState(raw) {
|
|
|
25
26
|
insertableNodes: (obj.insertableNodes ?? []),
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
|
-
function
|
|
29
|
-
if (!json || json === '[]')
|
|
30
|
-
return [];
|
|
31
|
-
try {
|
|
32
|
-
const parsed = JSON.parse(json);
|
|
33
|
-
if (parsed != null &&
|
|
34
|
-
typeof parsed === 'object' &&
|
|
35
|
-
!Array.isArray(parsed) &&
|
|
36
|
-
'error' in parsed) {
|
|
37
|
-
throw new Error(`NativeEditorBridge: ${parsed.error}`);
|
|
38
|
-
}
|
|
39
|
-
if (!Array.isArray(parsed)) {
|
|
40
|
-
throw new Error(ERR_NATIVE_RESPONSE);
|
|
41
|
-
}
|
|
42
|
-
return parsed;
|
|
43
|
-
}
|
|
44
|
-
catch (e) {
|
|
45
|
-
if (e instanceof Error && e.message.startsWith('NativeEditorBridge:')) {
|
|
46
|
-
throw e;
|
|
47
|
-
}
|
|
48
|
-
throw new Error(ERR_NATIVE_RESPONSE);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function parseEditorUpdateJson(json, previousRenderBlocks) {
|
|
29
|
+
function parseEditorUpdateJson(json, previousRenderTree) {
|
|
52
30
|
if (!json || json === '')
|
|
53
31
|
return null;
|
|
54
32
|
try {
|
|
@@ -56,20 +34,21 @@ function parseEditorUpdateJson(json, previousRenderBlocks) {
|
|
|
56
34
|
if ('error' in parsed) {
|
|
57
35
|
throw new Error(`NativeEditorBridge: ${parsed.error}`);
|
|
58
36
|
}
|
|
59
|
-
const
|
|
60
|
-
? parsed.
|
|
61
|
-
: applyRenderBlocksPatch(previousRenderBlocks, parsed.renderPatch != null && typeof parsed.renderPatch === 'object'
|
|
62
|
-
? parsed.renderPatch
|
|
63
|
-
: undefined);
|
|
64
|
-
const renderPatch = parsed.renderPatch != null && typeof parsed.renderPatch === 'object'
|
|
65
|
-
? parsed.renderPatch
|
|
37
|
+
const renderPatch = parsed.renderPatch != null
|
|
38
|
+
? parseRenderTreePatch(parsed.renderPatch)
|
|
66
39
|
: undefined;
|
|
40
|
+
const renderTree = parsed.renderTree != null
|
|
41
|
+
? parseRenderTree(parsed.renderTree)
|
|
42
|
+
: previousRenderTree && renderPatch
|
|
43
|
+
? applyRenderTreePatch(previousRenderTree, renderPatch)
|
|
44
|
+
: previousRenderTree;
|
|
45
|
+
if (!renderTree) {
|
|
46
|
+
throw new Error(ERR_NATIVE_RESPONSE);
|
|
47
|
+
}
|
|
67
48
|
return {
|
|
68
|
-
|
|
69
|
-
? parsed.renderElements
|
|
70
|
-
: flattenRenderBlocks(renderBlocks),
|
|
71
|
-
renderBlocks,
|
|
49
|
+
renderTree,
|
|
72
50
|
renderPatch,
|
|
51
|
+
inputText: typeof parsed.inputText === 'string' ? parsed.inputText : '',
|
|
73
52
|
selection: (parsed.selection ?? { type: 'text', anchor: 0, head: 0 }),
|
|
74
53
|
activeState: normalizeActiveState(parsed.activeState),
|
|
75
54
|
historyState: (parsed.historyState ?? {
|
|
@@ -86,30 +65,79 @@ function parseEditorUpdateJson(json, previousRenderBlocks) {
|
|
|
86
65
|
throw new Error(ERR_NATIVE_RESPONSE);
|
|
87
66
|
}
|
|
88
67
|
}
|
|
89
|
-
function
|
|
90
|
-
if (
|
|
91
|
-
|
|
68
|
+
function parseRenderTreePatch(raw) {
|
|
69
|
+
if (raw == null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
70
|
+
throw new Error(ERR_NATIVE_RESPONSE);
|
|
92
71
|
}
|
|
93
|
-
|
|
72
|
+
const candidate = raw;
|
|
73
|
+
if (typeof candidate.version !== 'number' || !Array.isArray(candidate.operations)) {
|
|
74
|
+
throw new Error(ERR_NATIVE_RESPONSE);
|
|
75
|
+
}
|
|
76
|
+
return candidate;
|
|
94
77
|
}
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
78
|
+
function applyRenderTreePatch(tree, patch) {
|
|
79
|
+
let root = cloneRenderTreeNode(tree.root);
|
|
80
|
+
for (const operation of patch.operations) {
|
|
81
|
+
if (operation.type === 'replaceNode' && operation.path.length === 0) {
|
|
82
|
+
root = cloneRenderTreeNode(operation.node);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const target = resolveRenderTreeNode(root, operation.path);
|
|
86
|
+
if (!target)
|
|
87
|
+
throw new Error(ERR_NATIVE_RESPONSE);
|
|
88
|
+
switch (operation.type) {
|
|
89
|
+
case 'updateMetadata':
|
|
90
|
+
target.docPos = operation.docPos;
|
|
91
|
+
target.docSize = operation.docSize;
|
|
92
|
+
break;
|
|
93
|
+
case 'replaceNode': {
|
|
94
|
+
const parent = resolveRenderTreeNode(root, operation.path.slice(0, -1));
|
|
95
|
+
const index = operation.path[operation.path.length - 1];
|
|
96
|
+
if (!parent?.children || index == null || !parent.children[index]) {
|
|
97
|
+
throw new Error(ERR_NATIVE_RESPONSE);
|
|
98
|
+
}
|
|
99
|
+
parent.children[index] = cloneRenderTreeNode(operation.node);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case 'spliceChildren':
|
|
103
|
+
target.children ?? (target.children = []);
|
|
104
|
+
target.children.splice(operation.startIndex, operation.deleteCount, ...operation.children.map(cloneRenderTreeNode));
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return { version: tree.version, root };
|
|
109
|
+
}
|
|
110
|
+
function resolveRenderTreeNode(root, path) {
|
|
111
|
+
let current = root;
|
|
112
|
+
for (const index of path) {
|
|
113
|
+
const child = current.children?.[index];
|
|
114
|
+
if (!child)
|
|
115
|
+
return null;
|
|
116
|
+
current = child;
|
|
117
|
+
}
|
|
118
|
+
return current;
|
|
119
|
+
}
|
|
120
|
+
function cloneRenderTreeNode(node) {
|
|
121
|
+
return {
|
|
122
|
+
...node,
|
|
123
|
+
...(node.attrs ? { attrs: { ...node.attrs } } : {}),
|
|
124
|
+
...(node.marks ? { marks: node.marks.map((mark) => typeof mark === 'string' ? mark : { ...mark }) } : {}),
|
|
125
|
+
...(node.mentionTheme ? { mentionTheme: { ...node.mentionTheme } } : {}),
|
|
126
|
+
...(node.children ? { children: node.children.map(cloneRenderTreeNode) } : {}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function parseRenderTree(raw) {
|
|
130
|
+
if (raw == null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
131
|
+
throw new Error(ERR_NATIVE_RESPONSE);
|
|
132
|
+
}
|
|
133
|
+
const candidate = raw;
|
|
134
|
+
if (typeof candidate.version !== 'number' ||
|
|
135
|
+
candidate.root == null ||
|
|
136
|
+
typeof candidate.root !== 'object' ||
|
|
137
|
+
Array.isArray(candidate.root)) {
|
|
138
|
+
throw new Error(ERR_NATIVE_RESPONSE);
|
|
139
|
+
}
|
|
140
|
+
return candidate;
|
|
113
141
|
}
|
|
114
142
|
function parseContentSnapshotJson(json) {
|
|
115
143
|
try {
|
|
@@ -321,7 +349,7 @@ class NativeEditorBridge {
|
|
|
321
349
|
this._documentVersion = 0;
|
|
322
350
|
this._cachedHtml = null;
|
|
323
351
|
this._cachedJsonString = null;
|
|
324
|
-
this.
|
|
352
|
+
this._renderTreeCache = null;
|
|
325
353
|
this._lastCommandBlocked = false;
|
|
326
354
|
this._lastCommandBlockedReason = null;
|
|
327
355
|
this._lastCommandPreflightUpdate = null;
|
|
@@ -394,19 +422,18 @@ class NativeEditorBridge {
|
|
|
394
422
|
if (this._destroyed)
|
|
395
423
|
return;
|
|
396
424
|
this._destroyed = true;
|
|
397
|
-
this.
|
|
425
|
+
this._renderTreeCache = null;
|
|
398
426
|
this._lastCommandBlocked = false;
|
|
399
427
|
this._lastCommandBlockedReason = null;
|
|
400
428
|
this._lastCommandPreflightUpdate = null;
|
|
401
429
|
getNativeModule().editorDestroy(this._editorId);
|
|
402
430
|
}
|
|
403
|
-
/** Set content from HTML. Returns
|
|
431
|
+
/** Set content from HTML. Returns the structural editor state. */
|
|
404
432
|
setHtml(html) {
|
|
405
433
|
this.assertNotDestroyed();
|
|
406
434
|
this.invalidateContentCaches();
|
|
407
|
-
this.
|
|
408
|
-
|
|
409
|
-
return parseRenderElements(json);
|
|
435
|
+
getNativeModule().editorSetHtml(this._editorId, html);
|
|
436
|
+
return this.getCurrentState();
|
|
410
437
|
}
|
|
411
438
|
/** Get content as HTML. */
|
|
412
439
|
getHtml() {
|
|
@@ -423,18 +450,17 @@ class NativeEditorBridge {
|
|
|
423
450
|
this.assertNotDestroyed();
|
|
424
451
|
return this._cachedHtml?.value ?? null;
|
|
425
452
|
}
|
|
426
|
-
/** Set content from ProseMirror JSON. Returns
|
|
453
|
+
/** Set content from ProseMirror JSON. Returns the structural editor state. */
|
|
427
454
|
setJson(doc) {
|
|
428
455
|
return this.setJsonString(JSON.stringify((0, schemas_1.normalizeDocumentJson)(doc, this._schema)));
|
|
429
456
|
}
|
|
430
|
-
/** Set content from a serialized ProseMirror JSON string. Returns
|
|
457
|
+
/** Set content from a serialized ProseMirror JSON string. Returns the structural editor state. */
|
|
431
458
|
setJsonString(jsonString) {
|
|
432
459
|
this.assertNotDestroyed();
|
|
433
460
|
this.invalidateContentCaches();
|
|
434
|
-
this._renderBlocksCache = null;
|
|
435
461
|
const normalizedJsonString = normalizeDocumentJsonString(jsonString, this._schema);
|
|
436
|
-
|
|
437
|
-
return
|
|
462
|
+
getNativeModule().editorSetJson(this._editorId, normalizedJsonString);
|
|
463
|
+
return this.getCurrentState();
|
|
438
464
|
}
|
|
439
465
|
/** Get content as raw ProseMirror JSON string. */
|
|
440
466
|
getJsonString() {
|
|
@@ -563,6 +589,12 @@ class NativeEditorBridge {
|
|
|
563
589
|
getNativeModule().editorSetSelection(this._editorId, anchor, head);
|
|
564
590
|
this._lastSelection = { type: 'text', anchor, head };
|
|
565
591
|
}
|
|
592
|
+
/** Set a table cell selection by cell document positions. */
|
|
593
|
+
setCellSelection(anchor, head) {
|
|
594
|
+
this.assertNotDestroyed();
|
|
595
|
+
getNativeModule().editorSetCellSelection?.(this._editorId, anchor, head);
|
|
596
|
+
this._lastSelection = { type: 'cell', anchor, head };
|
|
597
|
+
}
|
|
566
598
|
/** Convert a document position to a scalar position used by native text views. */
|
|
567
599
|
docToScalar(docPos) {
|
|
568
600
|
this.assertNotDestroyed();
|
|
@@ -711,6 +743,26 @@ class NativeEditorBridge {
|
|
|
711
743
|
: getNativeModule().editorOutdentListItem(this._editorId);
|
|
712
744
|
}, { refreshSelectionAfterPreflight: true });
|
|
713
745
|
}
|
|
746
|
+
insertTableRow(after = true) {
|
|
747
|
+
this.assertNotDestroyed();
|
|
748
|
+
return this.runPreparedCommand(() => getNativeModule().editorInsertTableRow(this._editorId, after));
|
|
749
|
+
}
|
|
750
|
+
deleteTableRow() {
|
|
751
|
+
this.assertNotDestroyed();
|
|
752
|
+
return this.runPreparedCommand(() => getNativeModule().editorDeleteTableRow(this._editorId));
|
|
753
|
+
}
|
|
754
|
+
insertTableColumn(after = true) {
|
|
755
|
+
this.assertNotDestroyed();
|
|
756
|
+
return this.runPreparedCommand(() => getNativeModule().editorInsertTableColumn(this._editorId, after));
|
|
757
|
+
}
|
|
758
|
+
deleteTableColumn() {
|
|
759
|
+
this.assertNotDestroyed();
|
|
760
|
+
return this.runPreparedCommand(() => getNativeModule().editorDeleteTableColumn(this._editorId));
|
|
761
|
+
}
|
|
762
|
+
moveTableCell(forward = true) {
|
|
763
|
+
this.assertNotDestroyed();
|
|
764
|
+
return this.runPreparedCommand(() => getNativeModule().editorMoveTableCell(this._editorId, forward));
|
|
765
|
+
}
|
|
714
766
|
/** Insert a void node (e.g. 'horizontalRule') at the current selection. */
|
|
715
767
|
insertNode(nodeType) {
|
|
716
768
|
this.assertNotDestroyed();
|
|
@@ -730,9 +782,7 @@ class NativeEditorBridge {
|
|
|
730
782
|
return;
|
|
731
783
|
}
|
|
732
784
|
this._lastSelection = update.selection;
|
|
733
|
-
|
|
734
|
-
this._renderBlocksCache = update.renderBlocks;
|
|
735
|
-
}
|
|
785
|
+
this._renderTreeCache = update.renderTree;
|
|
736
786
|
if (typeof update.documentVersion !== 'number') {
|
|
737
787
|
this.invalidateContentCaches();
|
|
738
788
|
return;
|
|
@@ -765,17 +815,10 @@ class NativeEditorBridge {
|
|
|
765
815
|
return false;
|
|
766
816
|
}
|
|
767
817
|
parseAndNoteUpdate(json, options) {
|
|
768
|
-
|
|
818
|
+
const update = parseEditorUpdateJson(json, this._renderTreeCache ?? undefined);
|
|
769
819
|
if (this.shouldRejectUpdate(update, json, options)) {
|
|
770
820
|
return null;
|
|
771
821
|
}
|
|
772
|
-
if (update?.renderPatch && !update.renderBlocks) {
|
|
773
|
-
json = getNativeModule().editorGetCurrentState(this._editorId);
|
|
774
|
-
update = parseEditorUpdateJson(json, this._renderBlocksCache ?? undefined);
|
|
775
|
-
if (this.shouldRejectUpdate(update, json, options)) {
|
|
776
|
-
return null;
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
822
|
this.noteUpdate(update);
|
|
780
823
|
if (update) {
|
|
781
824
|
this._lastAcceptedUpdateJson = json;
|
|
@@ -1,53 +1,32 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
-
import
|
|
3
|
+
import type { EditorTheme } from './EditorTheme';
|
|
4
4
|
import type { DocumentJSON } from './NativeEditorBridge';
|
|
5
5
|
import { type SchemaDefinition } from './schemas';
|
|
6
|
-
export interface NativeProseViewerMentionRenderContext {
|
|
7
|
-
docPos: number;
|
|
8
|
-
label: string;
|
|
9
|
-
attrs: Record<string, unknown>;
|
|
10
|
-
}
|
|
11
|
-
export interface NativeProseViewerMentionPressEvent extends NativeProseViewerMentionRenderContext {
|
|
12
|
-
}
|
|
13
|
-
export interface NativeProseViewerLinkPressEvent {
|
|
14
|
-
href: string;
|
|
15
|
-
text: string;
|
|
16
|
-
}
|
|
17
|
-
type NativeProseViewerContent = DocumentJSON | string;
|
|
18
|
-
export type NativeProseViewerMentionPrefix = string | ((mention: NativeProseViewerMentionRenderContext) => string | null | undefined);
|
|
19
|
-
export interface NativeProseViewerMentionsAddonConfig {
|
|
20
|
-
trigger?: string;
|
|
21
|
-
prefix?: NativeProseViewerMentionPrefix;
|
|
22
|
-
theme?: EditorMentionTheme;
|
|
23
|
-
resolveTheme?: (mention: NativeProseViewerMentionRenderContext) => EditorMentionTheme | null | undefined;
|
|
24
|
-
onPress?: (event: NativeProseViewerMentionPressEvent) => void;
|
|
25
|
-
}
|
|
26
|
-
export interface NativeProseViewerAddons {
|
|
27
|
-
mentions?: NativeProseViewerMentionsAddonConfig;
|
|
28
|
-
}
|
|
29
6
|
interface NativeProseViewerBaseProps {
|
|
30
|
-
|
|
31
|
-
contentJSONRevision?: string | number;
|
|
7
|
+
revision?: string | number;
|
|
32
8
|
schema?: SchemaDefinition;
|
|
33
9
|
theme?: EditorTheme;
|
|
34
10
|
style?: StyleProp<ViewStyle>;
|
|
35
11
|
allowBase64Images?: boolean;
|
|
36
|
-
collapseTrailingEmptyParagraphs?: boolean;
|
|
37
|
-
enableLinkTaps?: boolean;
|
|
38
|
-
addons?: NativeProseViewerAddons;
|
|
39
|
-
onPressLink?: (event: NativeProseViewerLinkPressEvent) => void;
|
|
40
|
-
contentId?: string;
|
|
41
|
-
containerWidth?: number;
|
|
42
12
|
}
|
|
43
13
|
interface NativeProseViewerJsonProps extends NativeProseViewerBaseProps {
|
|
44
|
-
contentJSON:
|
|
14
|
+
contentJSON: DocumentJSON | string;
|
|
45
15
|
contentHTML?: never;
|
|
16
|
+
collapseWhenEmpty?: boolean;
|
|
46
17
|
}
|
|
47
18
|
interface NativeProseViewerHtmlProps extends NativeProseViewerBaseProps {
|
|
48
19
|
contentHTML: string;
|
|
49
20
|
contentJSON?: never;
|
|
50
21
|
}
|
|
51
22
|
export type NativeProseViewerProps = NativeProseViewerJsonProps | NativeProseViewerHtmlProps;
|
|
52
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Read-only presentation of the canonical block-editor surface.
|
|
25
|
+
*
|
|
26
|
+
* The viewer deliberately shares the editor's document parser, render tree,
|
|
27
|
+
* layout surface, theme implementation, and native views. Keeping it as a
|
|
28
|
+
* configured editor prevents the read-only product from becoming a second
|
|
29
|
+
* rendering engine with subtly different schema behavior.
|
|
30
|
+
*/
|
|
31
|
+
export declare function NativeProseViewer(props: NativeProseViewerProps): React.JSX.Element;
|
|
53
32
|
export {};
|