@apollohg/react-native-prose-editor 0.1.1 → 0.3.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.
- package/README.md +12 -7
- package/android/build.gradle +7 -2
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +289 -2
- package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +51 -1
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +199 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +16 -3
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +82 -1
- package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +403 -45
- package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +246 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +841 -155
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +125 -8
- package/{src/EditorTheme.ts → dist/EditorTheme.d.ts} +12 -52
- package/dist/EditorTheme.js +29 -0
- package/dist/EditorToolbar.d.ts +129 -0
- package/dist/EditorToolbar.js +394 -0
- package/dist/NativeEditorBridge.d.ts +242 -0
- package/dist/NativeEditorBridge.js +647 -0
- package/dist/NativeRichTextEditor.d.ts +142 -0
- package/dist/NativeRichTextEditor.js +649 -0
- package/dist/YjsCollaboration.d.ts +83 -0
- package/dist/YjsCollaboration.js +585 -0
- package/dist/addons.d.ts +70 -0
- package/dist/addons.js +77 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +26 -0
- package/dist/schemas.d.ts +35 -0
- package/{src/schemas.ts → dist/schemas.js} +62 -27
- package/dist/useNativeEditor.d.ts +40 -0
- package/dist/useNativeEditor.js +117 -0
- package/ios/EditorAddons.swift +26 -3
- package/ios/EditorCore.xcframework/Info.plist +5 -5
- 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 +236 -0
- package/ios/EditorTheme.swift +51 -1
- package/ios/Generated_editor_core.swift +270 -2
- package/ios/NativeEditorExpoView.swift +612 -45
- package/ios/NativeEditorModule.swift +81 -0
- package/ios/PositionBridge.swift +22 -0
- package/ios/RenderBridge.swift +427 -39
- package/ios/RichTextEditorView.swift +1342 -18
- package/ios/editor_coreFFI/editor_coreFFI.h +209 -0
- package/package.json +80 -64
- 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 +404 -4
- package/src/EditorToolbar.tsx +0 -620
- package/src/NativeEditorBridge.ts +0 -607
- package/src/NativeRichTextEditor.tsx +0 -951
- package/src/addons.ts +0 -158
- package/src/index.ts +0 -63
- package/src/useNativeEditor.ts +0 -173
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_EDITOR_TOOLBAR_ITEMS = void 0;
|
|
4
|
+
exports.EditorToolbar = EditorToolbar;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const vector_icons_1 = require("@expo/vector-icons");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const react_native_1 = require("react-native");
|
|
9
|
+
function defaultIcon(id) {
|
|
10
|
+
return { type: 'default', id };
|
|
11
|
+
}
|
|
12
|
+
exports.DEFAULT_EDITOR_TOOLBAR_ITEMS = [
|
|
13
|
+
{ type: 'mark', mark: 'bold', label: 'Bold', icon: defaultIcon('bold') },
|
|
14
|
+
{ type: 'mark', mark: 'italic', label: 'Italic', icon: defaultIcon('italic') },
|
|
15
|
+
{ type: 'mark', mark: 'underline', label: 'Underline', icon: defaultIcon('underline') },
|
|
16
|
+
{ type: 'mark', mark: 'strike', label: 'Strikethrough', icon: defaultIcon('strike') },
|
|
17
|
+
{ type: 'blockquote', label: 'Blockquote', icon: defaultIcon('blockquote') },
|
|
18
|
+
{ type: 'separator' },
|
|
19
|
+
{ type: 'list', listType: 'bulletList', label: 'Bullet List', icon: defaultIcon('bulletList') },
|
|
20
|
+
{
|
|
21
|
+
type: 'list',
|
|
22
|
+
listType: 'orderedList',
|
|
23
|
+
label: 'Ordered List',
|
|
24
|
+
icon: defaultIcon('orderedList'),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'command',
|
|
28
|
+
command: 'indentList',
|
|
29
|
+
label: 'Indent List',
|
|
30
|
+
icon: defaultIcon('indentList'),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: 'command',
|
|
34
|
+
command: 'outdentList',
|
|
35
|
+
label: 'Outdent List',
|
|
36
|
+
icon: defaultIcon('outdentList'),
|
|
37
|
+
},
|
|
38
|
+
{ type: 'node', nodeType: 'hardBreak', label: 'Line Break', icon: defaultIcon('lineBreak') },
|
|
39
|
+
{
|
|
40
|
+
type: 'node',
|
|
41
|
+
nodeType: 'horizontalRule',
|
|
42
|
+
label: 'Horizontal Rule',
|
|
43
|
+
icon: defaultIcon('horizontalRule'),
|
|
44
|
+
},
|
|
45
|
+
{ type: 'separator' },
|
|
46
|
+
{ type: 'command', command: 'undo', label: 'Undo', icon: defaultIcon('undo') },
|
|
47
|
+
{ type: 'command', command: 'redo', label: 'Redo', icon: defaultIcon('redo') },
|
|
48
|
+
];
|
|
49
|
+
const BUTTON_HIT = 44;
|
|
50
|
+
const BUTTON_VISIBLE = 32;
|
|
51
|
+
const TOOLBAR_PADDING_H = 12;
|
|
52
|
+
const TOOLBAR_PADDING_V = 4;
|
|
53
|
+
const ACTIVE_BG = 'rgba(0, 122, 255, 0.12)';
|
|
54
|
+
const ACTIVE_COLOR = '#007AFF';
|
|
55
|
+
const DEFAULT_COLOR = '#666666';
|
|
56
|
+
const DISABLED_COLOR = '#C7C7CC';
|
|
57
|
+
const SEPARATOR_COLOR = '#E5E5EA';
|
|
58
|
+
const TOOLBAR_BG = '#FFFFFF';
|
|
59
|
+
const TOOLBAR_BORDER = '#E5E5EA';
|
|
60
|
+
const TOOLBAR_RADIUS = 0;
|
|
61
|
+
const BUTTON_RADIUS = 6;
|
|
62
|
+
const DEFAULT_GLYPH_ICONS = {
|
|
63
|
+
bold: 'B',
|
|
64
|
+
italic: 'I',
|
|
65
|
+
underline: 'U',
|
|
66
|
+
strike: 'S',
|
|
67
|
+
link: '🔗',
|
|
68
|
+
image: '🖼',
|
|
69
|
+
blockquote: '❝',
|
|
70
|
+
bulletList: '•≡',
|
|
71
|
+
orderedList: '1.',
|
|
72
|
+
indentList: '→',
|
|
73
|
+
outdentList: '←',
|
|
74
|
+
lineBreak: '↵',
|
|
75
|
+
horizontalRule: '—',
|
|
76
|
+
undo: '↩',
|
|
77
|
+
redo: '↪',
|
|
78
|
+
};
|
|
79
|
+
const DEFAULT_MATERIAL_ICONS = {
|
|
80
|
+
bold: 'format-bold',
|
|
81
|
+
italic: 'format-italic',
|
|
82
|
+
underline: 'format-underlined',
|
|
83
|
+
strike: 'strikethrough-s',
|
|
84
|
+
link: 'link',
|
|
85
|
+
image: 'image',
|
|
86
|
+
blockquote: 'format-quote',
|
|
87
|
+
bulletList: 'format-list-bulleted',
|
|
88
|
+
orderedList: 'format-list-numbered',
|
|
89
|
+
indentList: 'format-indent-increase',
|
|
90
|
+
outdentList: 'format-indent-decrease',
|
|
91
|
+
lineBreak: 'keyboard-return',
|
|
92
|
+
horizontalRule: 'horizontal-rule',
|
|
93
|
+
undo: 'undo',
|
|
94
|
+
redo: 'redo',
|
|
95
|
+
};
|
|
96
|
+
function EditorToolbar({ activeState, historyState, onToggleBold, onToggleItalic, onToggleUnderline, onToggleStrike, onToggleBulletList, onToggleBlockquote, onToggleOrderedList, onIndentList, onOutdentList, onInsertHorizontalRule, onInsertLineBreak, onUndo, onRedo, onToggleMark, onToggleListType, onInsertNodeType, onRunCommand, onToolbarAction, onRequestLink, onRequestImage, toolbarItems = exports.DEFAULT_EDITOR_TOOLBAR_ITEMS, theme, showTopBorder = true, }) {
|
|
97
|
+
const marks = activeState.marks ?? {};
|
|
98
|
+
const nodes = activeState.nodes ?? {};
|
|
99
|
+
const commands = activeState.commands ?? {};
|
|
100
|
+
const allowedMarks = activeState.allowedMarks ?? [];
|
|
101
|
+
const insertableNodes = activeState.insertableNodes ?? [];
|
|
102
|
+
const isMarkActive = (0, react_1.useCallback)((mark) => !!marks[mark], [marks]);
|
|
103
|
+
const isInList = !!nodes['bulletList'] || !!nodes['orderedList'];
|
|
104
|
+
const canIndentList = isInList && !!commands['indentList'];
|
|
105
|
+
const canOutdentList = isInList && !!commands['outdentList'];
|
|
106
|
+
const getActionForItem = (0, react_1.useCallback)((item) => {
|
|
107
|
+
switch (item.type) {
|
|
108
|
+
case 'separator':
|
|
109
|
+
return null;
|
|
110
|
+
case 'mark':
|
|
111
|
+
if (onToggleMark) {
|
|
112
|
+
return () => onToggleMark(item.mark);
|
|
113
|
+
}
|
|
114
|
+
switch (item.mark) {
|
|
115
|
+
case 'bold':
|
|
116
|
+
return onToggleBold;
|
|
117
|
+
case 'italic':
|
|
118
|
+
return onToggleItalic;
|
|
119
|
+
case 'underline':
|
|
120
|
+
return onToggleUnderline;
|
|
121
|
+
case 'strike':
|
|
122
|
+
return onToggleStrike;
|
|
123
|
+
default:
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
case 'list':
|
|
127
|
+
if (onToggleListType) {
|
|
128
|
+
return () => onToggleListType(item.listType);
|
|
129
|
+
}
|
|
130
|
+
return item.listType === 'bulletList'
|
|
131
|
+
? (onToggleBulletList ?? null)
|
|
132
|
+
: (onToggleOrderedList ?? null);
|
|
133
|
+
case 'link':
|
|
134
|
+
return onRequestLink ?? null;
|
|
135
|
+
case 'image':
|
|
136
|
+
return onRequestImage ?? null;
|
|
137
|
+
case 'blockquote':
|
|
138
|
+
return onToggleBlockquote ?? null;
|
|
139
|
+
case 'node':
|
|
140
|
+
if (onInsertNodeType) {
|
|
141
|
+
return () => onInsertNodeType(item.nodeType);
|
|
142
|
+
}
|
|
143
|
+
switch (item.nodeType) {
|
|
144
|
+
case 'hardBreak':
|
|
145
|
+
return onInsertLineBreak ?? null;
|
|
146
|
+
case 'horizontalRule':
|
|
147
|
+
return onInsertHorizontalRule ?? null;
|
|
148
|
+
default:
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
case 'command':
|
|
152
|
+
if (onRunCommand) {
|
|
153
|
+
return () => onRunCommand(item.command);
|
|
154
|
+
}
|
|
155
|
+
switch (item.command) {
|
|
156
|
+
case 'indentList':
|
|
157
|
+
return onIndentList ?? null;
|
|
158
|
+
case 'outdentList':
|
|
159
|
+
return onOutdentList ?? null;
|
|
160
|
+
case 'undo':
|
|
161
|
+
return onUndo;
|
|
162
|
+
case 'redo':
|
|
163
|
+
return onRedo;
|
|
164
|
+
}
|
|
165
|
+
case 'action':
|
|
166
|
+
return onToolbarAction ? () => onToolbarAction(item.key) : null;
|
|
167
|
+
}
|
|
168
|
+
}, [
|
|
169
|
+
onIndentList,
|
|
170
|
+
onInsertHorizontalRule,
|
|
171
|
+
onInsertLineBreak,
|
|
172
|
+
onInsertNodeType,
|
|
173
|
+
onOutdentList,
|
|
174
|
+
onRedo,
|
|
175
|
+
onRunCommand,
|
|
176
|
+
onRequestLink,
|
|
177
|
+
onRequestImage,
|
|
178
|
+
onToolbarAction,
|
|
179
|
+
onToggleBold,
|
|
180
|
+
onToggleBlockquote,
|
|
181
|
+
onToggleBulletList,
|
|
182
|
+
onToggleItalic,
|
|
183
|
+
onToggleListType,
|
|
184
|
+
onToggleMark,
|
|
185
|
+
onToggleOrderedList,
|
|
186
|
+
onToggleStrike,
|
|
187
|
+
onToggleUnderline,
|
|
188
|
+
onUndo,
|
|
189
|
+
]);
|
|
190
|
+
const makeButtonKey = (0, react_1.useCallback)((item, index) => item.key ??
|
|
191
|
+
(item.type === 'mark'
|
|
192
|
+
? `mark:${item.mark}:${index}`
|
|
193
|
+
: item.type === 'link'
|
|
194
|
+
? `link:${index}`
|
|
195
|
+
: item.type === 'image'
|
|
196
|
+
? `image:${index}`
|
|
197
|
+
: item.type === 'blockquote'
|
|
198
|
+
? `blockquote:${index}`
|
|
199
|
+
: item.type === 'list'
|
|
200
|
+
? `list:${item.listType}:${index}`
|
|
201
|
+
: item.type === 'command'
|
|
202
|
+
? `command:${item.command}:${index}`
|
|
203
|
+
: item.type === 'node'
|
|
204
|
+
? `node:${item.nodeType}:${index}`
|
|
205
|
+
: `action:${item.key}:${index}`), []);
|
|
206
|
+
const renderedItems = [];
|
|
207
|
+
for (let index = 0; index < toolbarItems.length; index += 1) {
|
|
208
|
+
const item = toolbarItems[index];
|
|
209
|
+
if (item.type === 'separator') {
|
|
210
|
+
renderedItems.push({
|
|
211
|
+
type: 'separator',
|
|
212
|
+
key: item.key ?? `separator:${index}`,
|
|
213
|
+
});
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const action = getActionForItem(item);
|
|
217
|
+
if (!action) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
let isActive = false;
|
|
221
|
+
let isDisabled = false;
|
|
222
|
+
switch (item.type) {
|
|
223
|
+
case 'mark':
|
|
224
|
+
isActive = isMarkActive(item.mark);
|
|
225
|
+
isDisabled = !allowedMarks.includes(item.mark);
|
|
226
|
+
break;
|
|
227
|
+
case 'link':
|
|
228
|
+
isActive = isMarkActive('link');
|
|
229
|
+
isDisabled = !allowedMarks.includes('link') || !onRequestLink;
|
|
230
|
+
break;
|
|
231
|
+
case 'image':
|
|
232
|
+
isDisabled = !insertableNodes.includes('image') || !onRequestImage;
|
|
233
|
+
break;
|
|
234
|
+
case 'blockquote':
|
|
235
|
+
isActive = !!nodes['blockquote'];
|
|
236
|
+
isDisabled = !commands['toggleBlockquote'];
|
|
237
|
+
break;
|
|
238
|
+
case 'list':
|
|
239
|
+
isActive = !!nodes[item.listType];
|
|
240
|
+
isDisabled =
|
|
241
|
+
!commands[item.listType === 'bulletList' ? 'wrapBulletList' : 'wrapOrderedList'];
|
|
242
|
+
break;
|
|
243
|
+
case 'command':
|
|
244
|
+
switch (item.command) {
|
|
245
|
+
case 'indentList':
|
|
246
|
+
isDisabled = !canIndentList;
|
|
247
|
+
break;
|
|
248
|
+
case 'outdentList':
|
|
249
|
+
isDisabled = !canOutdentList;
|
|
250
|
+
break;
|
|
251
|
+
case 'undo':
|
|
252
|
+
isDisabled = !historyState.canUndo;
|
|
253
|
+
break;
|
|
254
|
+
case 'redo':
|
|
255
|
+
isDisabled = !historyState.canRedo;
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
break;
|
|
259
|
+
case 'action':
|
|
260
|
+
isActive = !!item.isActive;
|
|
261
|
+
isDisabled = !!item.isDisabled || !onToolbarAction;
|
|
262
|
+
break;
|
|
263
|
+
case 'node':
|
|
264
|
+
isActive = !!nodes[item.nodeType];
|
|
265
|
+
isDisabled = !insertableNodes.includes(item.nodeType);
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
renderedItems.push({
|
|
269
|
+
type: 'button',
|
|
270
|
+
button: {
|
|
271
|
+
key: makeButtonKey(item, index),
|
|
272
|
+
label: item.label,
|
|
273
|
+
icon: item.icon,
|
|
274
|
+
action,
|
|
275
|
+
isActive,
|
|
276
|
+
isDisabled,
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
const compactItems = renderedItems.filter((entry, index, list) => {
|
|
281
|
+
if (entry.type !== 'separator') {
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
const previous = list[index - 1];
|
|
285
|
+
const next = list[index + 1];
|
|
286
|
+
return previous?.type === 'button' && next?.type === 'button';
|
|
287
|
+
});
|
|
288
|
+
const renderButton = ({ key, label, icon, action, isActive, isDisabled }) => {
|
|
289
|
+
const activeColor = theme?.buttonActiveColor ?? ACTIVE_COLOR;
|
|
290
|
+
const defaultColor = theme?.buttonColor ?? DEFAULT_COLOR;
|
|
291
|
+
const disabledColor = theme?.buttonDisabledColor ?? DISABLED_COLOR;
|
|
292
|
+
const color = isActive ? activeColor : isDisabled ? disabledColor : defaultColor;
|
|
293
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { onPress: action, disabled: isDisabled, style: [
|
|
294
|
+
styles.button,
|
|
295
|
+
{
|
|
296
|
+
borderRadius: theme?.buttonBorderRadius ?? BUTTON_RADIUS,
|
|
297
|
+
},
|
|
298
|
+
isActive && {
|
|
299
|
+
backgroundColor: theme?.buttonActiveBackgroundColor ?? ACTIVE_BG,
|
|
300
|
+
},
|
|
301
|
+
], activeOpacity: 0.5, accessibilityRole: 'button', accessibilityLabel: label, accessibilityState: { selected: isActive, disabled: isDisabled }, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { children: (0, jsx_runtime_1.jsx)(ToolbarIcon, { icon: icon, color: color }) }) }, key));
|
|
302
|
+
};
|
|
303
|
+
const renderSeparator = (key) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
304
|
+
styles.separator,
|
|
305
|
+
theme?.separatorColor != null ? { backgroundColor: theme.separatorColor } : null,
|
|
306
|
+
] }, key));
|
|
307
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
308
|
+
styles.container,
|
|
309
|
+
!showTopBorder && styles.containerWithoutTopBorder,
|
|
310
|
+
theme?.backgroundColor != null ? { backgroundColor: theme.backgroundColor } : null,
|
|
311
|
+
theme?.borderColor != null
|
|
312
|
+
? showTopBorder
|
|
313
|
+
? { borderTopColor: theme.borderColor }
|
|
314
|
+
: null
|
|
315
|
+
: null,
|
|
316
|
+
theme?.borderWidth != null
|
|
317
|
+
? showTopBorder
|
|
318
|
+
? { borderTopWidth: theme.borderWidth }
|
|
319
|
+
: null
|
|
320
|
+
: null,
|
|
321
|
+
{
|
|
322
|
+
borderRadius: theme?.borderRadius ?? TOOLBAR_RADIUS,
|
|
323
|
+
},
|
|
324
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, contentContainerStyle: styles.scrollContent, keyboardShouldPersistTaps: 'always', children: compactItems.map((item) => item.type === 'separator'
|
|
325
|
+
? renderSeparator(item.key)
|
|
326
|
+
: renderButton(item.button)) }) }));
|
|
327
|
+
}
|
|
328
|
+
function ToolbarIcon({ icon, color }) {
|
|
329
|
+
const materialIconName = resolveMaterialIconName(icon);
|
|
330
|
+
if (materialIconName) {
|
|
331
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.iconContainer, children: (0, jsx_runtime_1.jsx)(vector_icons_1.MaterialIcons, { name: materialIconName, size: 20, color: color }) }));
|
|
332
|
+
}
|
|
333
|
+
const glyph = resolveGlyphText(icon) ?? '?';
|
|
334
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.iconContainer, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.iconText, { color }], children: glyph }) }));
|
|
335
|
+
}
|
|
336
|
+
function resolveMaterialIconName(icon) {
|
|
337
|
+
switch (icon.type) {
|
|
338
|
+
case 'default':
|
|
339
|
+
return DEFAULT_MATERIAL_ICONS[icon.id];
|
|
340
|
+
case 'platform':
|
|
341
|
+
return icon.android?.type === 'material' ? icon.android.name : undefined;
|
|
342
|
+
case 'glyph':
|
|
343
|
+
return undefined;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function resolveGlyphText(icon) {
|
|
347
|
+
switch (icon.type) {
|
|
348
|
+
case 'default':
|
|
349
|
+
return DEFAULT_GLYPH_ICONS[icon.id];
|
|
350
|
+
case 'glyph':
|
|
351
|
+
return icon.text;
|
|
352
|
+
case 'platform':
|
|
353
|
+
return icon.fallbackText;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const styles = react_native_1.StyleSheet.create({
|
|
357
|
+
container: {
|
|
358
|
+
backgroundColor: TOOLBAR_BG,
|
|
359
|
+
borderTopWidth: react_native_1.StyleSheet.hairlineWidth,
|
|
360
|
+
borderTopColor: TOOLBAR_BORDER,
|
|
361
|
+
paddingVertical: TOOLBAR_PADDING_V,
|
|
362
|
+
overflow: 'hidden',
|
|
363
|
+
},
|
|
364
|
+
containerWithoutTopBorder: {
|
|
365
|
+
borderTopWidth: 0,
|
|
366
|
+
},
|
|
367
|
+
scrollContent: {
|
|
368
|
+
flexDirection: 'row',
|
|
369
|
+
alignItems: 'center',
|
|
370
|
+
paddingHorizontal: TOOLBAR_PADDING_H,
|
|
371
|
+
minWidth: '100%',
|
|
372
|
+
},
|
|
373
|
+
button: {
|
|
374
|
+
width: BUTTON_HIT,
|
|
375
|
+
height: BUTTON_VISIBLE,
|
|
376
|
+
justifyContent: 'center',
|
|
377
|
+
alignItems: 'center',
|
|
378
|
+
borderRadius: BUTTON_RADIUS,
|
|
379
|
+
},
|
|
380
|
+
separator: {
|
|
381
|
+
width: react_native_1.StyleSheet.hairlineWidth,
|
|
382
|
+
height: 20,
|
|
383
|
+
marginHorizontal: 4,
|
|
384
|
+
backgroundColor: SEPARATOR_COLOR,
|
|
385
|
+
},
|
|
386
|
+
iconContainer: {
|
|
387
|
+
justifyContent: 'center',
|
|
388
|
+
alignItems: 'center',
|
|
389
|
+
},
|
|
390
|
+
iconText: {
|
|
391
|
+
fontSize: 16,
|
|
392
|
+
fontWeight: '600',
|
|
393
|
+
},
|
|
394
|
+
});
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
export interface NativeEditorModule {
|
|
2
|
+
editorCreate(configJson: string): number;
|
|
3
|
+
editorDestroy(editorId: number): void;
|
|
4
|
+
collaborationSessionCreate(configJson: string): number;
|
|
5
|
+
collaborationSessionDestroy(sessionId: number): void;
|
|
6
|
+
collaborationSessionGetDocumentJson(sessionId: number): string;
|
|
7
|
+
collaborationSessionGetEncodedState(sessionId: number): string;
|
|
8
|
+
collaborationSessionGetPeersJson(sessionId: number): string;
|
|
9
|
+
collaborationSessionStart(sessionId: number): string;
|
|
10
|
+
collaborationSessionApplyLocalDocumentJson(sessionId: number, json: string): string;
|
|
11
|
+
collaborationSessionApplyEncodedState(sessionId: number, encodedStateJson: string): string;
|
|
12
|
+
collaborationSessionReplaceEncodedState(sessionId: number, encodedStateJson: string): string;
|
|
13
|
+
collaborationSessionHandleMessage(sessionId: number, messageJson: string): string;
|
|
14
|
+
collaborationSessionSetLocalAwareness(sessionId: number, awarenessJson: string): string;
|
|
15
|
+
collaborationSessionClearLocalAwareness(sessionId: number): string;
|
|
16
|
+
editorSetHtml(editorId: number, html: string): string;
|
|
17
|
+
editorGetHtml(editorId: number): string;
|
|
18
|
+
editorSetJson(editorId: number, json: string): string;
|
|
19
|
+
editorGetJson(editorId: number): string;
|
|
20
|
+
editorReplaceHtml(editorId: number, html: string): string;
|
|
21
|
+
editorReplaceJson(editorId: number, json: string): string;
|
|
22
|
+
editorInsertText(editorId: number, pos: number, text: string): string;
|
|
23
|
+
editorReplaceSelectionText(editorId: number, text: string): string;
|
|
24
|
+
editorDeleteRange(editorId: number, from: number, to: number): string;
|
|
25
|
+
editorSplitBlock(editorId: number, pos: number): string;
|
|
26
|
+
editorInsertContentHtml(editorId: number, html: string): string;
|
|
27
|
+
editorInsertContentJson(editorId: number, json: string): string;
|
|
28
|
+
editorInsertContentJsonAtSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number, json: string): string;
|
|
29
|
+
editorToggleMark(editorId: number, markName: string): string;
|
|
30
|
+
editorSetMark(editorId: number, markName: string, attrsJson: string): string;
|
|
31
|
+
editorUnsetMark(editorId: number, markName: string): string;
|
|
32
|
+
editorToggleBlockquote(editorId: number): string;
|
|
33
|
+
editorSetSelection(editorId: number, anchor: number, head: number): void;
|
|
34
|
+
editorGetSelection(editorId: number): string;
|
|
35
|
+
editorGetCurrentState(editorId: number): string;
|
|
36
|
+
editorInsertTextScalar(editorId: number, scalarPos: number, text: string): string;
|
|
37
|
+
editorDeleteScalarRange(editorId: number, scalarFrom: number, scalarTo: number): string;
|
|
38
|
+
editorReplaceTextScalar(editorId: number, scalarFrom: number, scalarTo: number, text: string): string;
|
|
39
|
+
editorSplitBlockScalar(editorId: number, scalarPos: number): string;
|
|
40
|
+
editorDeleteAndSplitScalar(editorId: number, scalarFrom: number, scalarTo: number): string;
|
|
41
|
+
editorSetSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number): void;
|
|
42
|
+
editorToggleMarkAtSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number, markName: string): string;
|
|
43
|
+
editorSetMarkAtSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number, markName: string, attrsJson: string): string;
|
|
44
|
+
editorUnsetMarkAtSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number, markName: string): string;
|
|
45
|
+
editorToggleBlockquoteAtSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number): string;
|
|
46
|
+
editorWrapInListAtSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number, listType: string): string;
|
|
47
|
+
editorUnwrapFromListAtSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number): string;
|
|
48
|
+
editorIndentListItemAtSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number): string;
|
|
49
|
+
editorOutdentListItemAtSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number): string;
|
|
50
|
+
editorInsertNodeAtSelectionScalar(editorId: number, scalarAnchor: number, scalarHead: number, nodeType: string): string;
|
|
51
|
+
editorDocToScalar(editorId: number, docPos: number): number;
|
|
52
|
+
editorScalarToDoc(editorId: number, scalar: number): number;
|
|
53
|
+
editorWrapInList(editorId: number, listType: string): string;
|
|
54
|
+
editorUnwrapFromList(editorId: number): string;
|
|
55
|
+
editorIndentListItem(editorId: number): string;
|
|
56
|
+
editorOutdentListItem(editorId: number): string;
|
|
57
|
+
editorInsertNode(editorId: number, nodeType: string): string;
|
|
58
|
+
editorUndo(editorId: number): string;
|
|
59
|
+
editorRedo(editorId: number): string;
|
|
60
|
+
editorCanUndo(editorId: number): boolean;
|
|
61
|
+
editorCanRedo(editorId: number): boolean;
|
|
62
|
+
}
|
|
63
|
+
export interface Selection {
|
|
64
|
+
type: 'text' | 'node' | 'all';
|
|
65
|
+
anchor?: number;
|
|
66
|
+
head?: number;
|
|
67
|
+
pos?: number;
|
|
68
|
+
}
|
|
69
|
+
export interface ListContext {
|
|
70
|
+
ordered: boolean;
|
|
71
|
+
index: number;
|
|
72
|
+
total: number;
|
|
73
|
+
start: number;
|
|
74
|
+
isFirst: boolean;
|
|
75
|
+
isLast: boolean;
|
|
76
|
+
}
|
|
77
|
+
export interface RenderMarkWithAttrs {
|
|
78
|
+
type: string;
|
|
79
|
+
[key: string]: unknown;
|
|
80
|
+
}
|
|
81
|
+
export type RenderMark = string | RenderMarkWithAttrs;
|
|
82
|
+
export interface RenderElement {
|
|
83
|
+
type: 'textRun' | 'blockStart' | 'blockEnd' | 'voidInline' | 'voidBlock' | 'opaqueInlineAtom' | 'opaqueBlockAtom';
|
|
84
|
+
text?: string;
|
|
85
|
+
marks?: RenderMark[];
|
|
86
|
+
nodeType?: string;
|
|
87
|
+
depth?: number;
|
|
88
|
+
docPos?: number;
|
|
89
|
+
label?: string;
|
|
90
|
+
attrs?: Record<string, unknown>;
|
|
91
|
+
listContext?: ListContext;
|
|
92
|
+
}
|
|
93
|
+
export interface ActiveState {
|
|
94
|
+
marks: Record<string, boolean>;
|
|
95
|
+
markAttrs: Record<string, Record<string, unknown>>;
|
|
96
|
+
nodes: Record<string, boolean>;
|
|
97
|
+
commands: Record<string, boolean>;
|
|
98
|
+
allowedMarks: string[];
|
|
99
|
+
insertableNodes: string[];
|
|
100
|
+
}
|
|
101
|
+
export interface HistoryState {
|
|
102
|
+
canUndo: boolean;
|
|
103
|
+
canRedo: boolean;
|
|
104
|
+
}
|
|
105
|
+
export interface EditorUpdate {
|
|
106
|
+
renderElements: RenderElement[];
|
|
107
|
+
selection: Selection;
|
|
108
|
+
activeState: ActiveState;
|
|
109
|
+
historyState: HistoryState;
|
|
110
|
+
}
|
|
111
|
+
export interface DocumentJSON {
|
|
112
|
+
[key: string]: unknown;
|
|
113
|
+
}
|
|
114
|
+
export interface CollaborationPeer {
|
|
115
|
+
clientId: number;
|
|
116
|
+
isLocal: boolean;
|
|
117
|
+
state: Record<string, unknown> | null;
|
|
118
|
+
}
|
|
119
|
+
export interface CollaborationResult {
|
|
120
|
+
messages: number[][];
|
|
121
|
+
documentChanged: boolean;
|
|
122
|
+
documentJson?: DocumentJSON;
|
|
123
|
+
peersChanged: boolean;
|
|
124
|
+
peers?: CollaborationPeer[];
|
|
125
|
+
}
|
|
126
|
+
export type EncodedCollaborationStateInput = Uint8Array | readonly number[] | string;
|
|
127
|
+
export declare function normalizeActiveState(raw: unknown): ActiveState;
|
|
128
|
+
export declare function parseEditorUpdateJson(json: string): EditorUpdate | null;
|
|
129
|
+
export declare function encodeCollaborationStateBase64(encodedState: EncodedCollaborationStateInput): string;
|
|
130
|
+
export declare function decodeCollaborationStateBase64(base64: string): Uint8Array;
|
|
131
|
+
export declare function parseCollaborationResultJson(json: string): CollaborationResult;
|
|
132
|
+
/** @internal Reset the cached native module reference. For testing only. */
|
|
133
|
+
export declare function _resetNativeModuleCache(): void;
|
|
134
|
+
export declare class NativeEditorBridge {
|
|
135
|
+
private _editorId;
|
|
136
|
+
private _destroyed;
|
|
137
|
+
private _lastSelection;
|
|
138
|
+
private constructor();
|
|
139
|
+
/** Create a new editor instance backed by the Rust engine. */
|
|
140
|
+
static create(config?: {
|
|
141
|
+
maxLength?: number;
|
|
142
|
+
schemaJson?: string;
|
|
143
|
+
allowBase64Images?: boolean;
|
|
144
|
+
}): NativeEditorBridge;
|
|
145
|
+
/** The underlying native editor ID. */
|
|
146
|
+
get editorId(): number;
|
|
147
|
+
/** Whether this bridge has been destroyed. */
|
|
148
|
+
get isDestroyed(): boolean;
|
|
149
|
+
/** Destroy the editor instance and free native resources. */
|
|
150
|
+
destroy(): void;
|
|
151
|
+
/** Set content from HTML. Returns render elements for display. */
|
|
152
|
+
setHtml(html: string): RenderElement[];
|
|
153
|
+
/** Get content as HTML. */
|
|
154
|
+
getHtml(): string;
|
|
155
|
+
/** Set content from ProseMirror JSON. Returns render elements. */
|
|
156
|
+
setJson(doc: DocumentJSON): RenderElement[];
|
|
157
|
+
/** Get content as ProseMirror JSON. */
|
|
158
|
+
getJson(): DocumentJSON;
|
|
159
|
+
/** Insert text at a document position. Returns the full update. */
|
|
160
|
+
insertText(pos: number, text: string): EditorUpdate | null;
|
|
161
|
+
/** Delete a range [from, to). Returns the full update. */
|
|
162
|
+
deleteRange(from: number, to: number): EditorUpdate | null;
|
|
163
|
+
/** Replace the current selection with text atomically. */
|
|
164
|
+
replaceSelectionText(text: string): EditorUpdate | null;
|
|
165
|
+
/** Toggle a mark (bold, italic, etc.) on the current selection. */
|
|
166
|
+
toggleMark(markType: string): EditorUpdate | null;
|
|
167
|
+
/** Set a mark with attrs on the current selection. */
|
|
168
|
+
setMark(markType: string, attrs: Record<string, unknown>): EditorUpdate | null;
|
|
169
|
+
/** Remove a mark from the current selection. */
|
|
170
|
+
unsetMark(markType: string): EditorUpdate | null;
|
|
171
|
+
/** Toggle blockquote wrapping for the current block selection. */
|
|
172
|
+
toggleBlockquote(): EditorUpdate | null;
|
|
173
|
+
/** Set the document selection by anchor and head positions. */
|
|
174
|
+
setSelection(anchor: number, head: number): void;
|
|
175
|
+
/** Get the current selection from the Rust engine (synchronous native call).
|
|
176
|
+
* Always returns the live selection, not a stale cache. */
|
|
177
|
+
getSelection(): Selection;
|
|
178
|
+
/** Update the cached selection from native events (scalar offsets).
|
|
179
|
+
* Called by the React component when native selection change events arrive. */
|
|
180
|
+
updateSelectionFromNative(anchor: number, head: number): void;
|
|
181
|
+
/** Get the current full state from Rust (render elements, selection, etc.). */
|
|
182
|
+
getCurrentState(): EditorUpdate | null;
|
|
183
|
+
/** Split the block at a position (Enter key). */
|
|
184
|
+
splitBlock(pos: number): EditorUpdate | null;
|
|
185
|
+
/** Insert HTML content at the current selection. */
|
|
186
|
+
insertContentHtml(html: string): EditorUpdate | null;
|
|
187
|
+
/** Insert JSON content at the current selection. */
|
|
188
|
+
insertContentJson(doc: DocumentJSON): EditorUpdate | null;
|
|
189
|
+
/** Insert JSON content at an explicit scalar selection. */
|
|
190
|
+
insertContentJsonAtSelectionScalar(scalarAnchor: number, scalarHead: number, doc: DocumentJSON): EditorUpdate | null;
|
|
191
|
+
/** Replace entire document with HTML via transaction (preserves undo history). */
|
|
192
|
+
replaceHtml(html: string): EditorUpdate | null;
|
|
193
|
+
/** Replace entire document with JSON via transaction (preserves undo history). */
|
|
194
|
+
replaceJson(doc: DocumentJSON): EditorUpdate | null;
|
|
195
|
+
/** Undo the last operation. Returns update or null if nothing to undo. */
|
|
196
|
+
undo(): EditorUpdate | null;
|
|
197
|
+
/** Redo the last undone operation. Returns update or null if nothing to redo. */
|
|
198
|
+
redo(): EditorUpdate | null;
|
|
199
|
+
/** Check if undo is available. */
|
|
200
|
+
canUndo(): boolean;
|
|
201
|
+
/** Check if redo is available. */
|
|
202
|
+
canRedo(): boolean;
|
|
203
|
+
/** Toggle a list type on the current selection. Wraps if not in list, unwraps if already in that list type. */
|
|
204
|
+
toggleList(listType: string): EditorUpdate | null;
|
|
205
|
+
/** Unwrap the current list item back to a paragraph. */
|
|
206
|
+
unwrapFromList(): EditorUpdate | null;
|
|
207
|
+
/** Indent the current list item into a nested list. */
|
|
208
|
+
indentListItem(): EditorUpdate | null;
|
|
209
|
+
/** Outdent the current list item to the parent list level. */
|
|
210
|
+
outdentListItem(): EditorUpdate | null;
|
|
211
|
+
/** Insert a void node (e.g. 'horizontalRule') at the current selection. */
|
|
212
|
+
insertNode(nodeType: string): EditorUpdate | null;
|
|
213
|
+
private assertNotDestroyed;
|
|
214
|
+
private currentScalarSelection;
|
|
215
|
+
}
|
|
216
|
+
export declare class NativeCollaborationBridge {
|
|
217
|
+
private _sessionId;
|
|
218
|
+
private _destroyed;
|
|
219
|
+
private constructor();
|
|
220
|
+
static create(config?: {
|
|
221
|
+
clientId?: number;
|
|
222
|
+
fragmentName?: string;
|
|
223
|
+
initialDocumentJson?: DocumentJSON;
|
|
224
|
+
initialEncodedState?: EncodedCollaborationStateInput;
|
|
225
|
+
localAwareness?: Record<string, unknown>;
|
|
226
|
+
}): NativeCollaborationBridge;
|
|
227
|
+
get sessionId(): number;
|
|
228
|
+
get isDestroyed(): boolean;
|
|
229
|
+
destroy(): void;
|
|
230
|
+
getDocumentJson(): DocumentJSON;
|
|
231
|
+
getEncodedState(): Uint8Array;
|
|
232
|
+
getEncodedStateBase64(): string;
|
|
233
|
+
getPeers(): CollaborationPeer[];
|
|
234
|
+
start(): CollaborationResult;
|
|
235
|
+
applyLocalDocumentJson(doc: DocumentJSON): CollaborationResult;
|
|
236
|
+
applyEncodedState(encodedState: EncodedCollaborationStateInput): CollaborationResult;
|
|
237
|
+
replaceEncodedState(encodedState: EncodedCollaborationStateInput): CollaborationResult;
|
|
238
|
+
handleMessage(bytes: readonly number[]): CollaborationResult;
|
|
239
|
+
setLocalAwareness(state: Record<string, unknown>): CollaborationResult;
|
|
240
|
+
clearLocalAwareness(): CollaborationResult;
|
|
241
|
+
private assertNotDestroyed;
|
|
242
|
+
}
|