@apollohg/react-native-prose-editor 0.3.0 → 0.4.1
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 +18 -0
- package/android/build.gradle +23 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +515 -39
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +58 -28
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +25 -0
- package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +232 -62
- package/android/src/main/java/com/apollohg/editor/PositionBridge.kt +57 -27
- package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +147 -78
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +249 -71
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +7 -6
- package/dist/EditorToolbar.d.ts +26 -6
- package/dist/EditorToolbar.js +299 -65
- package/dist/NativeEditorBridge.d.ts +40 -1
- package/dist/NativeEditorBridge.js +184 -90
- package/dist/NativeRichTextEditor.d.ts +5 -1
- package/dist/NativeRichTextEditor.js +201 -78
- package/dist/YjsCollaboration.d.ts +2 -0
- package/dist/YjsCollaboration.js +142 -20
- package/dist/index.d.ts +1 -1
- package/dist/schemas.js +12 -0
- package/dist/useNativeEditor.d.ts +2 -0
- package/dist/useNativeEditor.js +7 -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/EditorLayoutManager.swift +3 -3
- package/ios/Generated_editor_core.swift +87 -0
- package/ios/NativeEditorExpoView.swift +488 -178
- package/ios/NativeEditorModule.swift +25 -0
- package/ios/PositionBridge.swift +310 -75
- package/ios/RenderBridge.swift +362 -27
- package/ios/RichTextEditorView.swift +2001 -189
- package/ios/editor_coreFFI/editor_coreFFI.h +55 -0
- package/package.json +11 -2
- 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 +128 -0
package/dist/YjsCollaboration.js
CHANGED
|
@@ -17,11 +17,119 @@ const EMPTY_DOCUMENT = {
|
|
|
17
17
|
],
|
|
18
18
|
};
|
|
19
19
|
const SELECTION_AWARENESS_DEBOUNCE_MS = 40;
|
|
20
|
-
function
|
|
21
|
-
|
|
20
|
+
function cloneJsonValue(value) {
|
|
21
|
+
if (Array.isArray(value)) {
|
|
22
|
+
return value.map((item) => cloneJsonValue(item));
|
|
23
|
+
}
|
|
24
|
+
if (value != null && typeof value === 'object') {
|
|
25
|
+
const clone = {};
|
|
26
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
27
|
+
clone[key] = cloneJsonValue(nestedValue);
|
|
28
|
+
}
|
|
29
|
+
return clone;
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
function acceptingGroupsForContent(content, existingChildCount) {
|
|
34
|
+
const tokens = content
|
|
35
|
+
.trim()
|
|
36
|
+
.split(/\s+/)
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.map((token) => {
|
|
39
|
+
const quantifier = token[token.length - 1];
|
|
40
|
+
if (quantifier === '+' || quantifier === '*' || quantifier === '?') {
|
|
41
|
+
return {
|
|
42
|
+
group: token.slice(0, -1),
|
|
43
|
+
min: quantifier === '+' ? 1 : 0,
|
|
44
|
+
max: quantifier === '?' ? 1 : null,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
group: token,
|
|
49
|
+
min: 1,
|
|
50
|
+
max: 1,
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
let remaining = existingChildCount;
|
|
54
|
+
const acceptingGroups = [];
|
|
55
|
+
for (const token of tokens) {
|
|
56
|
+
if (remaining >= token.min) {
|
|
57
|
+
const consumed = token.max == null ? remaining : Math.min(remaining, token.max);
|
|
58
|
+
remaining = Math.max(0, remaining - consumed);
|
|
59
|
+
const atMax = token.max != null && consumed >= token.max;
|
|
60
|
+
if (!atMax) {
|
|
61
|
+
acceptingGroups.push(token.group);
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
acceptingGroups.push(token.group);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
return acceptingGroups;
|
|
69
|
+
}
|
|
70
|
+
function defaultEmptyDocument(schema) {
|
|
71
|
+
if (!schema) {
|
|
72
|
+
return {
|
|
73
|
+
type: 'doc',
|
|
74
|
+
content: [{ type: 'paragraph' }],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const docNode = schema.nodes.find((node) => node.role === 'doc' || node.name === 'doc');
|
|
78
|
+
const acceptingGroups = docNode == null ? [] : acceptingGroupsForContent(docNode.content ?? '', 0);
|
|
79
|
+
const matchingTextBlocks = schema.nodes.filter((node) => node.role === 'textBlock' &&
|
|
80
|
+
acceptingGroups.some((group) => node.name === group || node.group === group));
|
|
81
|
+
const preferredTextBlock = matchingTextBlocks.find((node) => node.htmlTag === 'p' || node.name === 'paragraph') ??
|
|
82
|
+
matchingTextBlocks[0] ??
|
|
83
|
+
schema.nodes.find((node) => node.htmlTag === 'p' || node.name === 'paragraph') ??
|
|
84
|
+
schema.nodes.find((node) => node.role === 'textBlock');
|
|
85
|
+
if (!preferredTextBlock) {
|
|
86
|
+
return {
|
|
87
|
+
type: 'doc',
|
|
88
|
+
content: [{ type: 'paragraph' }],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
type: 'doc',
|
|
93
|
+
content: [{ type: preferredTextBlock.name }],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function initialFallbackDocument(options) {
|
|
97
|
+
return options.initialDocumentJson
|
|
98
|
+
? cloneJsonValue(options.initialDocumentJson)
|
|
99
|
+
: defaultEmptyDocument(options.schema);
|
|
100
|
+
}
|
|
101
|
+
function shouldUseFallbackForNativeDocument(doc, options) {
|
|
102
|
+
if (options.initialDocumentJson != null || options.initialEncodedState != null) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (doc.type !== 'doc') {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return !Array.isArray(doc.content) || doc.content.length === 0;
|
|
22
109
|
}
|
|
23
110
|
function awarenessToRecord(awareness) {
|
|
24
|
-
return
|
|
111
|
+
return awareness;
|
|
112
|
+
}
|
|
113
|
+
function localAwarenessEquals(left, right) {
|
|
114
|
+
const leftSelection = left.selection;
|
|
115
|
+
const rightSelection = right.selection;
|
|
116
|
+
if (leftSelection == null || rightSelection == null) {
|
|
117
|
+
if (leftSelection !== rightSelection) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (leftSelection.anchor !== rightSelection.anchor ||
|
|
122
|
+
leftSelection.head !== rightSelection.head) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const leftUser = left.user;
|
|
126
|
+
const rightUser = right.user;
|
|
127
|
+
return (left.focused === right.focused &&
|
|
128
|
+
leftUser.userId === rightUser.userId &&
|
|
129
|
+
leftUser.name === rightUser.name &&
|
|
130
|
+
leftUser.color === rightUser.color &&
|
|
131
|
+
leftUser.avatarUrl === rightUser.avatarUrl &&
|
|
132
|
+
JSON.stringify(leftUser.extra ?? null) === JSON.stringify(rightUser.extra ?? null));
|
|
25
133
|
}
|
|
26
134
|
function normalizeMessageBytes(data) {
|
|
27
135
|
if (data instanceof ArrayBuffer) {
|
|
@@ -109,11 +217,6 @@ function encodeInitialStateKey(encodedState) {
|
|
|
109
217
|
return '';
|
|
110
218
|
return (0, NativeEditorBridge_1.encodeCollaborationStateBase64)(encodedState);
|
|
111
219
|
}
|
|
112
|
-
function encodeDocumentKey(doc) {
|
|
113
|
-
if (doc == null)
|
|
114
|
-
return '';
|
|
115
|
-
return JSON.stringify(doc);
|
|
116
|
-
}
|
|
117
220
|
class YjsCollaborationControllerImpl {
|
|
118
221
|
constructor(options, callbacks = {}) {
|
|
119
222
|
this.socket = null;
|
|
@@ -126,23 +229,26 @@ class YjsCollaborationControllerImpl {
|
|
|
126
229
|
this.callbacks = callbacks;
|
|
127
230
|
this.createWebSocket = options.createWebSocket;
|
|
128
231
|
this.retryIntervalMs = options.retryIntervalMs;
|
|
129
|
-
const hasInitialEncodedState = options.initialEncodedState != null;
|
|
130
232
|
this.localAwarenessState = {
|
|
131
233
|
user: options.localAwareness,
|
|
132
234
|
focused: false,
|
|
133
235
|
};
|
|
134
236
|
this.bridge = NativeEditorBridge_1.NativeCollaborationBridge.create({
|
|
135
237
|
fragmentName: options.fragmentName ?? DEFAULT_YJS_FRAGMENT_NAME,
|
|
238
|
+
schema: options.schema,
|
|
136
239
|
initialEncodedState: options.initialEncodedState,
|
|
137
240
|
localAwareness: awarenessToRecord(this.localAwarenessState),
|
|
138
241
|
});
|
|
242
|
+
const nativeDocumentJson = this.bridge.getDocumentJson();
|
|
139
243
|
this._state = {
|
|
140
244
|
documentId: options.documentId,
|
|
141
245
|
status: 'idle',
|
|
142
246
|
isConnected: false,
|
|
143
|
-
documentJson:
|
|
144
|
-
?
|
|
145
|
-
:
|
|
247
|
+
documentJson: options.initialDocumentJson != null
|
|
248
|
+
? cloneJsonValue(options.initialDocumentJson)
|
|
249
|
+
: shouldUseFallbackForNativeDocument(nativeDocumentJson, options)
|
|
250
|
+
? defaultEmptyDocument(options.schema)
|
|
251
|
+
: nativeDocumentJson,
|
|
146
252
|
};
|
|
147
253
|
this._peers = this.bridge.getPeers();
|
|
148
254
|
if (options.connect !== false) {
|
|
@@ -314,10 +420,10 @@ class YjsCollaborationControllerImpl {
|
|
|
314
420
|
destroy() {
|
|
315
421
|
if (this.destroyed)
|
|
316
422
|
return;
|
|
317
|
-
this.destroyed = true;
|
|
318
423
|
this.cancelRetry();
|
|
319
424
|
this.cancelPendingAwarenessSync();
|
|
320
425
|
this.disconnect();
|
|
426
|
+
this.destroyed = true;
|
|
321
427
|
this.bridge.destroy();
|
|
322
428
|
}
|
|
323
429
|
updateLocalAwareness(partial) {
|
|
@@ -336,22 +442,33 @@ class YjsCollaborationControllerImpl {
|
|
|
336
442
|
handleSelectionChange(selection) {
|
|
337
443
|
if (this.destroyed)
|
|
338
444
|
return;
|
|
339
|
-
|
|
445
|
+
const nextAwareness = this.mergeLocalAwareness({
|
|
340
446
|
focused: true,
|
|
341
447
|
selection: selectionToAwarenessRange(selection),
|
|
342
448
|
});
|
|
449
|
+
if (localAwarenessEquals(nextAwareness, this.localAwarenessState)) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
this.localAwarenessState = nextAwareness;
|
|
343
453
|
this.scheduleAwarenessSync();
|
|
344
454
|
}
|
|
345
455
|
handleFocusChange(focused) {
|
|
346
456
|
if (this.destroyed)
|
|
347
457
|
return;
|
|
348
|
-
|
|
458
|
+
const nextAwareness = this.mergeLocalAwareness({ focused });
|
|
459
|
+
if (localAwarenessEquals(nextAwareness, this.localAwarenessState)) {
|
|
460
|
+
if (this.pendingAwarenessTimer != null) {
|
|
461
|
+
this.commitLocalAwareness();
|
|
462
|
+
}
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
this.localAwarenessState = nextAwareness;
|
|
349
466
|
this.commitLocalAwareness();
|
|
350
467
|
}
|
|
351
468
|
applyResult(result) {
|
|
352
469
|
if (result.documentChanged && result.documentJson) {
|
|
353
470
|
this.setState({
|
|
354
|
-
documentJson:
|
|
471
|
+
documentJson: result.documentJson,
|
|
355
472
|
});
|
|
356
473
|
}
|
|
357
474
|
if (result.peersChanged && result.peers) {
|
|
@@ -489,14 +606,14 @@ function useYjsCollaboration(options) {
|
|
|
489
606
|
const createWebSocketRef = (0, react_1.useRef)(options.createWebSocket);
|
|
490
607
|
createWebSocketRef.current = options.createWebSocket;
|
|
491
608
|
const controllerRef = (0, react_1.useRef)(null);
|
|
492
|
-
const initialDocumentKey = encodeDocumentKey(options.initialDocumentJson);
|
|
493
609
|
const initialEncodedStateKey = encodeInitialStateKey(options.initialEncodedState);
|
|
494
610
|
const localAwarenessKey = JSON.stringify(options.localAwareness);
|
|
611
|
+
const schemaKey = JSON.stringify(options.schema ?? null);
|
|
495
612
|
const [state, setState] = (0, react_1.useState)({
|
|
496
613
|
documentId: options.documentId,
|
|
497
614
|
status: 'idle',
|
|
498
615
|
isConnected: false,
|
|
499
|
-
documentJson:
|
|
616
|
+
documentJson: initialFallbackDocument(options),
|
|
500
617
|
});
|
|
501
618
|
const [peers, setPeers] = (0, react_1.useState)([]);
|
|
502
619
|
(0, react_1.useEffect)(() => {
|
|
@@ -529,7 +646,7 @@ function useYjsCollaboration(options) {
|
|
|
529
646
|
documentId: options.documentId,
|
|
530
647
|
status: 'error',
|
|
531
648
|
isConnected: false,
|
|
532
|
-
documentJson:
|
|
649
|
+
documentJson: initialFallbackDocument(options),
|
|
533
650
|
lastError: nextError,
|
|
534
651
|
};
|
|
535
652
|
controllerRef.current = null;
|
|
@@ -543,7 +660,12 @@ function useYjsCollaboration(options) {
|
|
|
543
660
|
controllerRef.current = null;
|
|
544
661
|
};
|
|
545
662
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
546
|
-
}, [
|
|
663
|
+
}, [
|
|
664
|
+
options.documentId,
|
|
665
|
+
options.fragmentName,
|
|
666
|
+
schemaKey,
|
|
667
|
+
initialEncodedStateKey,
|
|
668
|
+
]);
|
|
547
669
|
(0, react_1.useEffect)(() => {
|
|
548
670
|
controllerRef.current?.updateLocalAwareness({
|
|
549
671
|
user: options.localAwareness,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { NativeRichTextEditor, type NativeRichTextEditorProps, type NativeRichTextEditorRef, type NativeRichTextEditorHeightBehavior, type NativeRichTextEditorToolbarPlacement, type RemoteSelectionDecoration, type LinkRequestContext, type ImageRequestContext, } from './NativeRichTextEditor';
|
|
2
|
-
export { EditorToolbar, DEFAULT_EDITOR_TOOLBAR_ITEMS, type EditorToolbarProps, type EditorToolbarItem, type EditorToolbarIcon, type EditorToolbarDefaultIconId, type EditorToolbarSFSymbolIcon, type EditorToolbarMaterialIcon, type EditorToolbarCommand, type EditorToolbarListType, } from './EditorToolbar';
|
|
2
|
+
export { EditorToolbar, DEFAULT_EDITOR_TOOLBAR_ITEMS, type EditorToolbarProps, type EditorToolbarItem, type EditorToolbarLeafItem, type EditorToolbarGroupChildItem, type EditorToolbarGroupItem, type EditorToolbarGroupPresentation, type EditorToolbarIcon, type EditorToolbarDefaultIconId, type EditorToolbarSFSymbolIcon, type EditorToolbarMaterialIcon, type EditorToolbarCommand, type EditorToolbarHeadingLevel, type EditorToolbarListType, } from './EditorToolbar';
|
|
3
3
|
export type { EditorContentInsets, EditorTheme, EditorTextStyle, EditorHeadingTheme, EditorListTheme, EditorHorizontalRuleTheme, EditorMentionTheme, EditorToolbarTheme, EditorToolbarAppearance, EditorFontStyle, EditorFontWeight, } from './EditorTheme';
|
|
4
4
|
export { MENTION_NODE_NAME, mentionNodeSpec, withMentionsSchema, buildMentionFragmentJson, type EditorAddons, type MentionsAddonConfig, type MentionSuggestion, type MentionQueryChangeEvent, type MentionSelectEvent, type EditorAddonEvent, } from './addons';
|
|
5
5
|
export { tiptapSchema, prosemirrorSchema, IMAGE_NODE_NAME, imageNodeSpec, withImagesSchema, buildImageFragmentJson, type SchemaDefinition, type NodeSpec, type MarkSpec, type AttrSpec, type ImageNodeAttributes, } from './schemas';
|
package/dist/schemas.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.imageNodeSpec = imageNodeSpec;
|
|
|
5
5
|
exports.withImagesSchema = withImagesSchema;
|
|
6
6
|
exports.buildImageFragmentJson = buildImageFragmentJson;
|
|
7
7
|
exports.IMAGE_NODE_NAME = 'image';
|
|
8
|
+
const HEADING_LEVELS = [1, 2, 3, 4, 5, 6];
|
|
8
9
|
function imageNodeSpec(name = exports.IMAGE_NODE_NAME) {
|
|
9
10
|
return {
|
|
10
11
|
name,
|
|
@@ -22,6 +23,15 @@ function imageNodeSpec(name = exports.IMAGE_NODE_NAME) {
|
|
|
22
23
|
isVoid: true,
|
|
23
24
|
};
|
|
24
25
|
}
|
|
26
|
+
function headingNodeSpec(level) {
|
|
27
|
+
return {
|
|
28
|
+
name: `h${level}`,
|
|
29
|
+
content: 'inline*',
|
|
30
|
+
group: 'block',
|
|
31
|
+
role: 'textBlock',
|
|
32
|
+
htmlTag: `h${level}`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
25
35
|
function withImagesSchema(schema) {
|
|
26
36
|
const hasImageNode = schema.nodes.some((node) => node.name === exports.IMAGE_NODE_NAME);
|
|
27
37
|
if (hasImageNode) {
|
|
@@ -64,6 +74,7 @@ exports.tiptapSchema = {
|
|
|
64
74
|
role: 'textBlock',
|
|
65
75
|
htmlTag: 'p',
|
|
66
76
|
},
|
|
77
|
+
...HEADING_LEVELS.map((level) => headingNodeSpec(level)),
|
|
67
78
|
{
|
|
68
79
|
name: 'blockquote',
|
|
69
80
|
content: 'block+',
|
|
@@ -132,6 +143,7 @@ exports.prosemirrorSchema = {
|
|
|
132
143
|
role: 'textBlock',
|
|
133
144
|
htmlTag: 'p',
|
|
134
145
|
},
|
|
146
|
+
...HEADING_LEVELS.map((level) => headingNodeSpec(level)),
|
|
135
147
|
{
|
|
136
148
|
name: 'blockquote',
|
|
137
149
|
content: 'block+',
|
|
@@ -30,6 +30,8 @@ export interface UseNativeEditorReturn {
|
|
|
30
30
|
redo: () => void;
|
|
31
31
|
/** Toggle blockquote wrapping around the current block selection. */
|
|
32
32
|
toggleBlockquote: () => void;
|
|
33
|
+
/** Toggle a heading level on the current block selection. */
|
|
34
|
+
toggleHeading: (level: 1 | 2 | 3 | 4 | 5 | 6) => void;
|
|
33
35
|
/** Insert text at a position. */
|
|
34
36
|
insertText: (pos: number, text: string) => void;
|
|
35
37
|
/** Delete a range [from, to). */
|
package/dist/useNativeEditor.js
CHANGED
|
@@ -82,6 +82,12 @@ function useNativeEditor(options = {}) {
|
|
|
82
82
|
const update = bridgeRef.current.toggleBlockquote();
|
|
83
83
|
applyUpdate(update);
|
|
84
84
|
}, [applyUpdate]);
|
|
85
|
+
const toggleHeading = (0, react_1.useCallback)((level) => {
|
|
86
|
+
if (!bridgeRef.current || bridgeRef.current.isDestroyed)
|
|
87
|
+
return;
|
|
88
|
+
const update = bridgeRef.current.toggleHeading(level);
|
|
89
|
+
applyUpdate(update);
|
|
90
|
+
}, [applyUpdate]);
|
|
85
91
|
const insertText = (0, react_1.useCallback)((pos, text) => {
|
|
86
92
|
if (!bridgeRef.current || bridgeRef.current.isDestroyed)
|
|
87
93
|
return;
|
|
@@ -110,6 +116,7 @@ function useNativeEditor(options = {}) {
|
|
|
110
116
|
undo,
|
|
111
117
|
redo,
|
|
112
118
|
toggleBlockquote,
|
|
119
|
+
toggleHeading,
|
|
113
120
|
insertText,
|
|
114
121
|
deleteRange,
|
|
115
122
|
getHtml,
|
|
Binary file
|
|
Binary file
|
|
@@ -23,7 +23,7 @@ final class EditorLayoutManager: NSLayoutManager {
|
|
|
23
23
|
textStorage.enumerateAttribute(
|
|
24
24
|
RenderBridgeAttributes.blockquoteBorderColor,
|
|
25
25
|
in: characterRange,
|
|
26
|
-
options: []
|
|
26
|
+
options: [.longestEffectiveRangeNotRequired]
|
|
27
27
|
) { value, range, _ in
|
|
28
28
|
guard range.length > 0, let color = value as? UIColor else { return }
|
|
29
29
|
|
|
@@ -68,7 +68,7 @@ final class EditorLayoutManager: NSLayoutManager {
|
|
|
68
68
|
textStorage.enumerateAttribute(
|
|
69
69
|
RenderBridgeAttributes.listMarkerContext,
|
|
70
70
|
in: characterRange,
|
|
71
|
-
options: []
|
|
71
|
+
options: [.longestEffectiveRangeNotRequired]
|
|
72
72
|
) { value, range, _ in
|
|
73
73
|
guard range.length > 0, let listContext = value as? [String: Any] else { return }
|
|
74
74
|
|
|
@@ -90,7 +90,7 @@ final class EditorLayoutManager: NSLayoutManager {
|
|
|
90
90
|
textStorage.enumerateAttribute(
|
|
91
91
|
RenderBridgeAttributes.blockquoteBorderColor,
|
|
92
92
|
in: characterRange,
|
|
93
|
-
options: []
|
|
93
|
+
options: [.longestEffectiveRangeNotRequired]
|
|
94
94
|
) { value, range, _ in
|
|
95
95
|
guard range.length > 0, let color = value as? UIColor else { return }
|
|
96
96
|
|
|
@@ -397,6 +397,22 @@ fileprivate final class UniffiHandleMap<T>: @unchecked Sendable {
|
|
|
397
397
|
// Public interface members begin here.
|
|
398
398
|
|
|
399
399
|
|
|
400
|
+
#if swift(>=5.8)
|
|
401
|
+
@_documentation(visibility: private)
|
|
402
|
+
#endif
|
|
403
|
+
fileprivate struct FfiConverterUInt8: FfiConverterPrimitive {
|
|
404
|
+
typealias FfiType = UInt8
|
|
405
|
+
typealias SwiftType = UInt8
|
|
406
|
+
|
|
407
|
+
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt8 {
|
|
408
|
+
return try lift(readInt(&buf))
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
public static func write(_ value: UInt8, into buf: inout [UInt8]) {
|
|
412
|
+
writeInt(&buf, lower(value))
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
400
416
|
#if swift(>=5.8)
|
|
401
417
|
@_documentation(visibility: private)
|
|
402
418
|
#endif
|
|
@@ -678,6 +694,18 @@ public func editorDeleteAndSplitScalar(id: UInt64, scalarFrom: UInt32, scalarTo:
|
|
|
678
694
|
)
|
|
679
695
|
})
|
|
680
696
|
}
|
|
697
|
+
/**
|
|
698
|
+
* Delete backward relative to an explicit scalar selection. Returns an update JSON string.
|
|
699
|
+
*/
|
|
700
|
+
public func editorDeleteBackwardAtSelectionScalar(id: UInt64, scalarAnchor: UInt32, scalarHead: UInt32) -> String {
|
|
701
|
+
return try! FfiConverterString.lift(try! rustCall() {
|
|
702
|
+
uniffi_editor_core_fn_func_editor_delete_backward_at_selection_scalar(
|
|
703
|
+
FfiConverterUInt64.lower(id),
|
|
704
|
+
FfiConverterUInt32.lower(scalarAnchor),
|
|
705
|
+
FfiConverterUInt32.lower(scalarHead),$0
|
|
706
|
+
)
|
|
707
|
+
})
|
|
708
|
+
}
|
|
681
709
|
/**
|
|
682
710
|
* Delete a range. Returns an update JSON string.
|
|
683
711
|
*/
|
|
@@ -722,6 +750,16 @@ public func editorDocToScalar(id: UInt64, docPos: UInt32) -> UInt32 {
|
|
|
722
750
|
)
|
|
723
751
|
})
|
|
724
752
|
}
|
|
753
|
+
/**
|
|
754
|
+
* Get both HTML and ProseMirror JSON content in one payload.
|
|
755
|
+
*/
|
|
756
|
+
public func editorGetContentSnapshot(id: UInt64) -> String {
|
|
757
|
+
return try! FfiConverterString.lift(try! rustCall() {
|
|
758
|
+
uniffi_editor_core_fn_func_editor_get_content_snapshot(
|
|
759
|
+
FfiConverterUInt64.lower(id),$0
|
|
760
|
+
)
|
|
761
|
+
})
|
|
762
|
+
}
|
|
725
763
|
/**
|
|
726
764
|
* Get the current editor state (render elements, selection, active state,
|
|
727
765
|
* history state) without performing any edits. Used by native views to pull
|
|
@@ -764,6 +802,16 @@ public func editorGetSelection(id: UInt64) -> String {
|
|
|
764
802
|
)
|
|
765
803
|
})
|
|
766
804
|
}
|
|
805
|
+
/**
|
|
806
|
+
* Get the current selection-related editor state without render elements.
|
|
807
|
+
*/
|
|
808
|
+
public func editorGetSelectionState(id: UInt64) -> String {
|
|
809
|
+
return try! FfiConverterString.lift(try! rustCall() {
|
|
810
|
+
uniffi_editor_core_fn_func_editor_get_selection_state(
|
|
811
|
+
FfiConverterUInt64.lower(id),$0
|
|
812
|
+
)
|
|
813
|
+
})
|
|
814
|
+
}
|
|
767
815
|
/**
|
|
768
816
|
* Indent the current list item into a nested list. Returns an update JSON string.
|
|
769
817
|
*/
|
|
@@ -1085,6 +1133,30 @@ public func editorToggleBlockquoteAtSelectionScalar(id: UInt64, scalarAnchor: UI
|
|
|
1085
1133
|
)
|
|
1086
1134
|
})
|
|
1087
1135
|
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Toggle a heading level on the current text-block selection. Returns an update JSON string.
|
|
1138
|
+
*/
|
|
1139
|
+
public func editorToggleHeading(id: UInt64, level: UInt8) -> String {
|
|
1140
|
+
return try! FfiConverterString.lift(try! rustCall() {
|
|
1141
|
+
uniffi_editor_core_fn_func_editor_toggle_heading(
|
|
1142
|
+
FfiConverterUInt64.lower(id),
|
|
1143
|
+
FfiConverterUInt8.lower(level),$0
|
|
1144
|
+
)
|
|
1145
|
+
})
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Toggle a heading level at an explicit scalar selection. Returns an update JSON string.
|
|
1149
|
+
*/
|
|
1150
|
+
public func editorToggleHeadingAtSelectionScalar(id: UInt64, scalarAnchor: UInt32, scalarHead: UInt32, level: UInt8) -> String {
|
|
1151
|
+
return try! FfiConverterString.lift(try! rustCall() {
|
|
1152
|
+
uniffi_editor_core_fn_func_editor_toggle_heading_at_selection_scalar(
|
|
1153
|
+
FfiConverterUInt64.lower(id),
|
|
1154
|
+
FfiConverterUInt32.lower(scalarAnchor),
|
|
1155
|
+
FfiConverterUInt32.lower(scalarHead),
|
|
1156
|
+
FfiConverterUInt8.lower(level),$0
|
|
1157
|
+
)
|
|
1158
|
+
})
|
|
1159
|
+
}
|
|
1088
1160
|
/**
|
|
1089
1161
|
* Toggle a mark on the current selection. Returns an update JSON string.
|
|
1090
1162
|
*/
|
|
@@ -1256,6 +1328,9 @@ private let initializationResult: InitializationResult = {
|
|
|
1256
1328
|
if (uniffi_editor_core_checksum_func_editor_delete_and_split_scalar() != 13764) {
|
|
1257
1329
|
return InitializationResult.apiChecksumMismatch
|
|
1258
1330
|
}
|
|
1331
|
+
if (uniffi_editor_core_checksum_func_editor_delete_backward_at_selection_scalar() != 7697) {
|
|
1332
|
+
return InitializationResult.apiChecksumMismatch
|
|
1333
|
+
}
|
|
1259
1334
|
if (uniffi_editor_core_checksum_func_editor_delete_range() != 6109) {
|
|
1260
1335
|
return InitializationResult.apiChecksumMismatch
|
|
1261
1336
|
}
|
|
@@ -1268,6 +1343,9 @@ private let initializationResult: InitializationResult = {
|
|
|
1268
1343
|
if (uniffi_editor_core_checksum_func_editor_doc_to_scalar() != 48291) {
|
|
1269
1344
|
return InitializationResult.apiChecksumMismatch
|
|
1270
1345
|
}
|
|
1346
|
+
if (uniffi_editor_core_checksum_func_editor_get_content_snapshot() != 32837) {
|
|
1347
|
+
return InitializationResult.apiChecksumMismatch
|
|
1348
|
+
}
|
|
1271
1349
|
if (uniffi_editor_core_checksum_func_editor_get_current_state() != 13946) {
|
|
1272
1350
|
return InitializationResult.apiChecksumMismatch
|
|
1273
1351
|
}
|
|
@@ -1280,6 +1358,9 @@ private let initializationResult: InitializationResult = {
|
|
|
1280
1358
|
if (uniffi_editor_core_checksum_func_editor_get_selection() != 20571) {
|
|
1281
1359
|
return InitializationResult.apiChecksumMismatch
|
|
1282
1360
|
}
|
|
1361
|
+
if (uniffi_editor_core_checksum_func_editor_get_selection_state() != 16471) {
|
|
1362
|
+
return InitializationResult.apiChecksumMismatch
|
|
1363
|
+
}
|
|
1283
1364
|
if (uniffi_editor_core_checksum_func_editor_indent_list_item() != 10818) {
|
|
1284
1365
|
return InitializationResult.apiChecksumMismatch
|
|
1285
1366
|
}
|
|
@@ -1364,6 +1445,12 @@ private let initializationResult: InitializationResult = {
|
|
|
1364
1445
|
if (uniffi_editor_core_checksum_func_editor_toggle_blockquote_at_selection_scalar() != 58523) {
|
|
1365
1446
|
return InitializationResult.apiChecksumMismatch
|
|
1366
1447
|
}
|
|
1448
|
+
if (uniffi_editor_core_checksum_func_editor_toggle_heading() != 7099) {
|
|
1449
|
+
return InitializationResult.apiChecksumMismatch
|
|
1450
|
+
}
|
|
1451
|
+
if (uniffi_editor_core_checksum_func_editor_toggle_heading_at_selection_scalar() != 54315) {
|
|
1452
|
+
return InitializationResult.apiChecksumMismatch
|
|
1453
|
+
}
|
|
1367
1454
|
if (uniffi_editor_core_checksum_func_editor_toggle_mark() != 30661) {
|
|
1368
1455
|
return InitializationResult.apiChecksumMismatch
|
|
1369
1456
|
}
|