@apollohg/react-native-rich-text-editor 2.0.0 → 2.0.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 +1 -1
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/android/build.gradle +3 -0
- package/app.plugin.js +42 -39
- package/dist/AtomHost.js +18 -11
- package/dist/EditorAddon.js +4 -2
- package/dist/EditorAddonNormalization.js +17 -9
- package/dist/EditorMentionThemeNormalization.js +6 -3
- package/dist/EditorMentionThemeValidation.js +28 -11
- package/dist/EditorStyleSheetNormalization.js +84 -37
- package/dist/EditorTheme.d.ts +4 -0
- package/dist/EditorTheme.js +5 -3
- package/dist/EditorToolbarComponent.d.ts +1 -1
- package/dist/EditorToolbarComponent.js +1 -1
- package/dist/EditorToolbarRegistry.js +5 -5
- package/dist/EditorToolbarTypes.d.ts +12 -12
- package/dist/EditorToolbarVisuals.js +1 -1
- package/dist/ExternalTextComposition.js +89 -43
- package/dist/ImageLoadingPolicy.d.ts +1 -1
- package/dist/NativeEditorBoundaryError.d.ts +7 -7
- package/dist/NativeEditorBoundaryError.js +9 -5
- package/dist/NativeEditorCollaborationTransport.d.ts +7 -7
- package/dist/NativeEditorCollaborationTransport.js +28 -15
- package/dist/NativeEditorCreateConfig.js +11 -6
- package/dist/NativeEditorCreateJson.js +51 -25
- package/dist/NativeEditorCreateValidation.js +11 -6
- package/dist/NativeEditorDocumentBridge.d.ts +1 -1
- package/dist/NativeEditorDocumentBridge.js +32 -18
- package/dist/NativeEditorDocumentHandle.d.ts +4 -4
- package/dist/NativeEditorDocumentHandle.js +1 -1
- package/dist/NativeEditorLocalAwareness.js +25 -13
- package/dist/NativeEditorNativeModule.d.ts +8 -1
- package/dist/NativeEditorNativeModule.js +7 -5
- package/dist/NativeEditorRenderNormalization.js +66 -25
- package/dist/NativeEditorResultNormalization.js +53 -29
- package/dist/NativeEditorTypes.d.ts +10 -10
- package/dist/NativeEditorV2Decimal.js +6 -3
- package/dist/NativeProseViewer.js +11 -3
- package/dist/ResourceLimits.d.ts +2 -2
- package/dist/ResourceLimits.js +7 -2
- package/dist/RichTextEditor.js +1 -1
- package/dist/RichTextEditorNativeTypes.d.ts +1 -1
- package/dist/RichTextEditorNativeView.d.ts +1 -3
- package/dist/RichTextEditorSerialization.js +24 -14
- package/dist/RichTextEditorTypes.d.ts +1 -1
- package/dist/YjsCollaboration.d.ts +7 -7
- package/dist/YjsCollaboration.js +42 -26
- package/dist/addons.d.ts +4 -4
- package/dist/addons.js +3 -3
- package/dist/atomInstances.js +6 -3
- package/dist/atomUpdates.js +7 -4
- package/dist/atoms.js +20 -12
- package/dist/attributeValidation.js +55 -24
- package/dist/contentExpression.js +80 -42
- package/dist/listMarkers.d.ts +1 -1
- package/dist/listMarkers.js +1 -1
- package/dist/schemaDefinition.d.ts +1 -1
- package/dist/schemaDefinition.js +23 -14
- package/dist/schemaDocument.js +1 -1
- package/dist/schemaDocumentConstruction.js +25 -14
- package/dist/schemaNormalization.js +51 -29
- package/dist/schemaPresets.d.ts +1 -1
- package/dist/schemaPresets.js +9 -4
- package/dist/schemaResolution.js +46 -25
- package/dist/specs/PreparedProseViewerNativeComponent.d.ts +4 -0
- package/dist/useEditorToolbarInteractions.d.ts +2 -2
- package/dist/useEditorToolbarInteractions.js +45 -23
- package/dist/useEditorToolbarItems.d.ts +1 -1
- package/dist/useEditorToolbarItems.js +4 -3
- package/dist/useEditorToolbarPresentation.d.ts +3 -3
- package/dist/useEditorToolbarPresentation.js +4 -4
- package/dist/useEditorToolbarState.d.ts +1 -1
- package/dist/useEditorToolbarState.js +1 -1
- package/dist/useFocusPreservingFrames.js +11 -8
- package/dist/useNativeEditor.d.ts +1 -1
- package/dist/useNativeEditor.js +21 -11
- package/dist/useRichTextEditorCommands.d.ts +2 -2
- package/dist/useRichTextEditorCommands.js +59 -29
- package/dist/useRichTextEditorEvents.d.ts +3 -3
- package/dist/useRichTextEditorEvents.js +75 -32
- package/dist/useRichTextEditorMentions.d.ts +4 -4
- package/dist/useRichTextEditorMentions.js +48 -29
- package/dist/useRichTextEditorPresentation.d.ts +4 -4
- package/dist/useRichTextEditorPresentation.js +25 -23
- package/dist/useRichTextEditorState.d.ts +3 -3
- package/dist/useRichTextEditorState.js +20 -15
- package/dist/useRichTextEditorUpdates.d.ts +1 -1
- package/dist/useRichTextEditorUpdates.js +99 -41
- package/dist/useViewerAtoms.js +63 -36
- 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/package.json +11 -3
- package/react-native.config.js +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/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/src/AtomHost.tsx +58 -28
- package/src/EditorAddon.ts +18 -8
- package/src/EditorAddonNormalization.ts +40 -11
- package/src/EditorMentionThemeNormalization.ts +15 -4
- package/src/EditorMentionThemeValidation.ts +51 -14
- package/src/EditorStyleSheet.ts +1 -0
- package/src/EditorStyleSheetNormalization.ts +195 -64
- package/src/EditorTheme.ts +13 -5
- package/src/EditorToolbarComponent.tsx +2 -1
- package/src/EditorToolbarRegistry.tsx +23 -13
- package/src/EditorToolbarTypes.tsx +12 -12
- package/src/EditorToolbarVisuals.tsx +6 -3
- package/src/EditorUpdateRevision.ts +1 -0
- package/src/ExternalTextComposition.ts +259 -49
- package/src/ImageLoadingPolicy.ts +3 -1
- package/src/NativeEditorBoundaryError.ts +38 -11
- package/src/NativeEditorCollaborationTransport.ts +108 -27
- package/src/NativeEditorCreateConfig.ts +92 -19
- package/src/NativeEditorCreateJson.ts +163 -26
- package/src/NativeEditorCreateValidation.ts +33 -12
- package/src/NativeEditorDocumentBridge.ts +111 -19
- package/src/NativeEditorDocumentHandle.ts +9 -4
- package/src/NativeEditorLocalAwareness.ts +70 -15
- package/src/NativeEditorNativeModule.ts +13 -9
- package/src/NativeEditorRenderNormalization.ts +128 -29
- package/src/NativeEditorResultNormalization.ts +143 -32
- package/src/NativeEditorTypes.ts +12 -11
- package/src/NativeEditorV2Decimal.ts +12 -3
- package/src/NativeProseViewer.tsx +46 -16
- package/src/ResourceLimits.ts +15 -8
- package/src/RichTextEditor.tsx +3 -1
- package/src/RichTextEditorNativeTypes.tsx +1 -1
- package/src/RichTextEditorNativeView.tsx +2 -8
- package/src/RichTextEditorSerialization.tsx +70 -24
- package/src/RichTextEditorTypes.tsx +1 -1
- package/src/YjsCollaboration.ts +143 -47
- package/src/addons.ts +12 -8
- package/src/atomInstances.ts +25 -5
- package/src/atomUpdates.ts +23 -6
- package/src/atoms.ts +85 -18
- package/src/attributeValidation.ts +97 -33
- package/src/contentExpression.ts +218 -63
- package/src/listMarkers.ts +1 -1
- package/src/schemaDefinition.ts +82 -20
- package/src/schemaDocument.ts +15 -1
- package/src/schemaDocumentConstruction.ts +80 -16
- package/src/schemaNormalization.ts +155 -41
- package/src/schemaPresets.ts +37 -31
- package/src/schemaResolution.ts +126 -27
- package/src/specs/PreparedProseViewerNativeComponent.ts +4 -2
- package/src/useEditorToolbarInteractions.tsx +85 -36
- package/src/useEditorToolbarItems.tsx +50 -22
- package/src/useEditorToolbarPresentation.tsx +81 -58
- package/src/useEditorToolbarState.tsx +8 -6
- package/src/useFocusPreservingFrames.ts +43 -15
- package/src/useNativeEditor.ts +111 -51
- package/src/useRichTextEditorCommands.tsx +127 -54
- package/src/useRichTextEditorEvents.tsx +144 -42
- package/src/useRichTextEditorMentions.tsx +118 -37
- package/src/useRichTextEditorPresentation.tsx +128 -114
- package/src/useRichTextEditorState.tsx +76 -40
- package/src/useRichTextEditorUpdates.tsx +207 -56
- package/src/useViewerAtoms.tsx +162 -66
|
@@ -65,7 +65,12 @@ function RichTextViewer(props) {
|
|
|
65
65
|
const { contentJSON, contentHTML, schema, atoms, readOnly = true, atomsInteractive = true, atomViewport, onUpdateAtomAttrs, theme, allowBase64Images = false, imageLoadingPolicy, resourceLimits, collapseTrailingEmptyParagraphs = true, enableLinkTaps = true, renderImages = true, fontEnvironmentRevision = 0, addons, onPressLink, onError, ...viewProps } = props;
|
|
66
66
|
const { mentions, codeHighlighting } = (0, react_1.useMemo)(() => (0, addons_1.normalizeEditorAddons)(addons), [addons]);
|
|
67
67
|
const resolvedResourceLimits = (0, react_1.useMemo)(() => (resourceLimits ? (0, ResourceLimits_1.resolveEditorResourceLimits)(resourceLimits) : undefined), [resourceLimits]);
|
|
68
|
-
const configJson = (0, react_1.useMemo)(() => (0, ViewerConfiguration_1.serializePreparedProseViewerConfiguration)(resolveViewerConfiguration(schema, allowBase64Images, resolvedResourceLimits, mentions, atoms, codeHighlighting)), [allowBase64Images,
|
|
68
|
+
const configJson = (0, react_1.useMemo)(() => (0, ViewerConfiguration_1.serializePreparedProseViewerConfiguration)(resolveViewerConfiguration(schema, allowBase64Images, resolvedResourceLimits, mentions, atoms, codeHighlighting)), [allowBase64Images,
|
|
69
|
+
mentions,
|
|
70
|
+
resolvedResourceLimits,
|
|
71
|
+
schema,
|
|
72
|
+
atoms,
|
|
73
|
+
codeHighlighting]);
|
|
69
74
|
const themeJson = (0, react_1.useMemo)(() => (0, EditorTheme_1.serializeEditorTheme)(theme, mentions?.theme), [mentions?.theme, theme]);
|
|
70
75
|
const imagePolicyJson = (0, react_1.useMemo)(() => (0, ImageLoadingPolicy_1.serializeEditorImageLoadingPolicy)(imageLoadingPolicy), [imageLoadingPolicy]);
|
|
71
76
|
const sourceKind = contentJSON === undefined ? 'html' : 'json';
|
|
@@ -75,7 +80,9 @@ function RichTextViewer(props) {
|
|
|
75
80
|
}
|
|
76
81
|
return typeof contentJSON === 'string' ? contentJSON : stringifyCachedJson(contentJSON);
|
|
77
82
|
}, [contentHTML, contentJSON]);
|
|
78
|
-
const atomIdentity = (0, react_1.useMemo)(() => ({
|
|
83
|
+
const atomIdentity = (0, react_1.useMemo)(() => ({
|
|
84
|
+
sourceKind, source, configJson, themeJson, imagePolicyJson, renderImages, collapseTrailingEmptyParagraphs, fontEnvironmentRevision,
|
|
85
|
+
}), [
|
|
79
86
|
sourceKind,
|
|
80
87
|
source,
|
|
81
88
|
configJson,
|
|
@@ -122,8 +129,9 @@ function RichTextViewer(props) {
|
|
|
122
129
|
onError?.(event.nativeEvent);
|
|
123
130
|
}, [onError]);
|
|
124
131
|
const nativeViewer = ((0, jsx_runtime_1.jsx)(PreparedProseViewerNativeComponent_1.default, { ...(viewerAtoms.enabled ? { style: { alignSelf: 'stretch' } } : viewProps), sourceKind: sourceKind, source: source, configJson: configJson, themeJson: viewerAtoms.themeJson, onAtomLayout: viewerAtoms.enabled ? viewerAtoms.onAtomLayout : undefined, imagePolicyJson: imagePolicyJson, imagesEnabled: renderImages, collapsesWhenEmpty: collapseTrailingEmptyParagraphs, enableLinkTaps: enableLinkTaps && onPressLink != null, mentionInteractionsEnabled: mentions?.onPress != null, fontEnvironmentRevision: fontEnvironmentRevision, onPressLink: onPressLink ? handlePressLink : undefined, onPressMention: mentions?.onPress ? handlePressMention : undefined, onError: onError ? handleError : undefined }));
|
|
125
|
-
if (!viewerAtoms.enabled)
|
|
132
|
+
if (!viewerAtoms.enabled) {
|
|
126
133
|
return nativeViewer;
|
|
134
|
+
}
|
|
127
135
|
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { ...viewProps, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: { alignSelf: 'stretch' }, onLayout: viewerAtoms.onContainerLayout, children: [nativeViewer, viewerAtoms.children] }) }));
|
|
128
136
|
}
|
|
129
137
|
/** @deprecated Use RichTextViewer instead. */
|
package/dist/ResourceLimits.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Every field is optional; an omitted one uses
|
|
7
7
|
* {@link DEFAULT_EDITOR_RESOURCE_LIMITS}. Each value must be a positive
|
|
8
8
|
* integer no greater than the matching {@link HARD_EDITOR_RESOURCE_LIMITS}
|
|
9
|
-
* ceiling
|
|
9
|
+
* ceiling : anything else throws `NativeEditorBoundaryError`
|
|
10
10
|
* (`INVALID_RESOURCE_LIMIT`). Exceeding a limit at runtime raises
|
|
11
11
|
* `INPUT_LIMIT_EXCEEDED`, `DOCUMENT_LIMIT_EXCEEDED`, or `SCHEMA_INVALID`.
|
|
12
12
|
*/
|
|
@@ -23,7 +23,7 @@ export interface EditorResourceLimits {
|
|
|
23
23
|
maxSchemaExpressionBytes?: number;
|
|
24
24
|
/** Byte ceiling for one inbound collaboration message. */
|
|
25
25
|
maxCollaborationMessageBytes?: number;
|
|
26
|
-
/** Byte ceiling for an encoded Yjs state
|
|
26
|
+
/** Byte ceiling for an encoded Yjs state : imported room snapshots and exported ones. */
|
|
27
27
|
maxEncodedStateBytes?: number;
|
|
28
28
|
}
|
|
29
29
|
/**
|
package/dist/ResourceLimits.js
CHANGED
|
@@ -53,8 +53,9 @@ function resolveLimit(name, value) {
|
|
|
53
53
|
return resolved;
|
|
54
54
|
}
|
|
55
55
|
function validateOverrides(limits, ceilings) {
|
|
56
|
-
if (limits === undefined)
|
|
56
|
+
if (limits === undefined) {
|
|
57
57
|
return;
|
|
58
|
+
}
|
|
58
59
|
for (const name of Object.keys(ceilings)) {
|
|
59
60
|
const value = limits[name];
|
|
60
61
|
if (value !== undefined) {
|
|
@@ -76,5 +77,9 @@ function validateEditorCreateLimits(limits) {
|
|
|
76
77
|
* not a positive integer within its ceiling.
|
|
77
78
|
*/
|
|
78
79
|
function resolveEditorResourceLimits(limits = {}) {
|
|
79
|
-
|
|
80
|
+
const resolved = { ...exports.DEFAULT_EDITOR_RESOURCE_LIMITS };
|
|
81
|
+
for (const name of Object.keys(resolved)) {
|
|
82
|
+
resolved[name] = resolveLimit(name, limits[name]);
|
|
83
|
+
}
|
|
84
|
+
return resolved;
|
|
80
85
|
}
|
package/dist/RichTextEditor.js
CHANGED
|
@@ -8,7 +8,7 @@ const useRichTextEditorCommands_1 = require("./useRichTextEditorCommands");
|
|
|
8
8
|
const useRichTextEditorEvents_1 = require("./useRichTextEditorEvents");
|
|
9
9
|
const useRichTextEditorMentions_1 = require("./useRichTextEditorMentions");
|
|
10
10
|
const useRichTextEditorPresentation_1 = require("./useRichTextEditorPresentation");
|
|
11
|
-
exports.RichTextEditor = (0, react_1.forwardRef)(
|
|
11
|
+
exports.RichTextEditor = (0, react_1.forwardRef)((props, ref) => {
|
|
12
12
|
const state = (0, useRichTextEditorState_1.useRichTextEditorState)(props, ref);
|
|
13
13
|
const updates = (0, useRichTextEditorUpdates_1.useRichTextEditorUpdates)(state);
|
|
14
14
|
const commands = (0, useRichTextEditorCommands_1.useRichTextEditorCommands)({ ...state, ...updates });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExternalTextCompositionManager, type NativeExternalTextCompositionHandle } from './ExternalTextComposition';
|
|
1
|
+
import { type ExternalTextCompositionManager, type NativeExternalTextCompositionHandle } from './ExternalTextComposition';
|
|
2
2
|
import { type ReactNode } from 'react';
|
|
3
3
|
import { type NativeSyntheticEvent, type StyleProp, type ViewStyle } from 'react-native';
|
|
4
4
|
import { type DocumentJSON, type NativeEditorDocumentHandle } from './NativeEditorBridge';
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { type NativeEditorViewProps, type NativeEditorViewHandle } from './RichTextEditorNativeTypes';
|
|
3
|
-
export declare const NativeEditorView: React.ComponentType<NativeEditorViewProps & React.RefAttributes<NativeEditorViewHandle>>;
|
|
1
|
+
export declare const NativeEditorView: import("react").ComponentType<any>;
|
|
4
2
|
export declare const styles: {
|
|
5
3
|
container: {
|
|
6
4
|
position: "relative";
|
|
@@ -34,10 +34,12 @@ exports.LINK_TOOLBAR_ACTION_KEY = '__native-editor-link__';
|
|
|
34
34
|
exports.IMAGE_TOOLBAR_ACTION_KEY = '__native-editor-image__';
|
|
35
35
|
exports.nextNativeEditorToolbarFrameOwnerId = 1;
|
|
36
36
|
function mergeMentionSuggestionTheme(baseTheme, resolvedTheme) {
|
|
37
|
-
if (baseTheme == null)
|
|
37
|
+
if (baseTheme == null) {
|
|
38
38
|
return resolvedTheme;
|
|
39
|
-
|
|
39
|
+
}
|
|
40
|
+
if (resolvedTheme == null) {
|
|
40
41
|
return baseTheme;
|
|
42
|
+
}
|
|
41
43
|
return {
|
|
42
44
|
node: { ...baseTheme.node, ...resolvedTheme.node },
|
|
43
45
|
suggestions: {
|
|
@@ -62,11 +64,11 @@ exports.EMPTY_ACTIVE_STATE = {
|
|
|
62
64
|
};
|
|
63
65
|
function selectedAtomKeys(selection, instances) {
|
|
64
66
|
return new Set(instances
|
|
65
|
-
.filter(
|
|
67
|
+
.filter(instance => (0, atomInstances_1.atomSelected)(selection, instance.docPos))
|
|
66
68
|
.map(({ key }) => key));
|
|
67
69
|
}
|
|
68
70
|
function equalStringSets(left, right) {
|
|
69
|
-
return left.size === right.size && [...left].every(
|
|
71
|
+
return left.size === right.size && [...left].every(value => right.has(value));
|
|
70
72
|
}
|
|
71
73
|
function equalAtomInstances(left, right) {
|
|
72
74
|
return (left.length === right.length &&
|
|
@@ -79,10 +81,12 @@ function isRecord(value) {
|
|
|
79
81
|
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
80
82
|
}
|
|
81
83
|
function parseSelectionFromUpdate(value) {
|
|
82
|
-
if (!isRecord(value))
|
|
84
|
+
if (!isRecord(value)) {
|
|
83
85
|
return null;
|
|
84
|
-
|
|
86
|
+
}
|
|
87
|
+
if (value.type === 'all') {
|
|
85
88
|
return { type: 'all' };
|
|
89
|
+
}
|
|
86
90
|
if (value.type === 'node' && typeof value.pos === 'number') {
|
|
87
91
|
return { type: 'node', pos: value.pos };
|
|
88
92
|
}
|
|
@@ -99,18 +103,21 @@ function stringArray(value) {
|
|
|
99
103
|
: [];
|
|
100
104
|
}
|
|
101
105
|
function booleanMap(value) {
|
|
102
|
-
if (!isRecord(value))
|
|
106
|
+
if (!isRecord(value)) {
|
|
103
107
|
return {};
|
|
108
|
+
}
|
|
104
109
|
const result = {};
|
|
105
110
|
for (const key of Object.keys(value)) {
|
|
106
|
-
if (typeof value[key] === 'boolean')
|
|
111
|
+
if (typeof value[key] === 'boolean') {
|
|
107
112
|
result[key] = value[key];
|
|
113
|
+
}
|
|
108
114
|
}
|
|
109
115
|
return result;
|
|
110
116
|
}
|
|
111
117
|
function parseActiveStateFromUpdate(value) {
|
|
112
|
-
if (!isRecord(value))
|
|
118
|
+
if (!isRecord(value)) {
|
|
113
119
|
return null;
|
|
120
|
+
}
|
|
114
121
|
return {
|
|
115
122
|
marks: booleanMap(value.marks),
|
|
116
123
|
markAttrs: isRecord(value.markAttrs)
|
|
@@ -147,8 +154,9 @@ function acceptNativeCommitPayload(payload, boundEditorId, lastAcceptedRevision)
|
|
|
147
154
|
return null;
|
|
148
155
|
}
|
|
149
156
|
const snapshot = (0, NativeEditorBridge_1.normalizeNativeEditorV2RenderUpdateValue)(payload.updateJson);
|
|
150
|
-
if (snapshot == null || snapshot.documentVersion !== canonicalRevision)
|
|
157
|
+
if (snapshot == null || snapshot.documentVersion !== canonicalRevision) {
|
|
151
158
|
return null;
|
|
159
|
+
}
|
|
152
160
|
if (lastAcceptedRevision != null && BigInt(canonicalRevision) <= BigInt(lastAcceptedRevision)) {
|
|
153
161
|
return null;
|
|
154
162
|
}
|
|
@@ -184,11 +192,11 @@ function mapToolbarChildForNative(item, activeState, editable, onRequestLink, on
|
|
|
184
192
|
return item;
|
|
185
193
|
}
|
|
186
194
|
function mapToolbarItemsForNative(items, activeState, editable, onRequestLink, onRequestImage) {
|
|
187
|
-
return items.map(
|
|
195
|
+
return items.map(item => {
|
|
188
196
|
if (item.type === 'group') {
|
|
189
197
|
return {
|
|
190
198
|
...item,
|
|
191
|
-
items: item.items.map(
|
|
199
|
+
items: item.items.map(child => mapToolbarChildForNative(child, activeState, editable, onRequestLink, onRequestImage)),
|
|
192
200
|
};
|
|
193
201
|
}
|
|
194
202
|
if (item.type === 'separator') {
|
|
@@ -201,7 +209,7 @@ function serializeRemoteSelections(remoteSelections) {
|
|
|
201
209
|
if (!remoteSelections || remoteSelections.length === 0) {
|
|
202
210
|
return undefined;
|
|
203
211
|
}
|
|
204
|
-
const normalized = remoteSelections.map(
|
|
212
|
+
const normalized = remoteSelections.map(selection => {
|
|
205
213
|
const clientId = (0, NativeEditorBridge_1.normalizeNativeEditorV2DecimalId)(selection.clientId);
|
|
206
214
|
if (clientId == null) {
|
|
207
215
|
throw new Error('NativeRichTextEditor: remote clientId must be canonical decimal u64');
|
|
@@ -241,7 +249,9 @@ function parseCaretRectJson(raw) {
|
|
|
241
249
|
editorHeight == null) {
|
|
242
250
|
return null;
|
|
243
251
|
}
|
|
244
|
-
return {
|
|
252
|
+
return {
|
|
253
|
+
x, y, width, height, editorWidth, editorHeight,
|
|
254
|
+
};
|
|
245
255
|
}
|
|
246
256
|
catch {
|
|
247
257
|
return null;
|
|
@@ -167,7 +167,7 @@ export interface RichTextEditorProps {
|
|
|
167
167
|
/** Remote awareness selections rendered as native overlays. */
|
|
168
168
|
remoteSelections?: readonly RemoteSelectionDecoration[];
|
|
169
169
|
/**
|
|
170
|
-
* Shared v2 document session
|
|
170
|
+
* Shared v2 document session : the only construction path. The native
|
|
171
171
|
* view binds to the same session (its editorId is passed straight to the
|
|
172
172
|
* view), so typing, IME, selection, and toolbar commands flow through
|
|
173
173
|
* the native v2 adapters into the shared engine while this component
|
|
@@ -3,13 +3,13 @@ import type { RemoteSelectionDecoration } from './NativeRichTextEditor';
|
|
|
3
3
|
/**
|
|
4
4
|
* Transport lifecycle, projected from the Rust transport state.
|
|
5
5
|
*
|
|
6
|
-
* - `idle`
|
|
7
|
-
* - `disconnected`
|
|
8
|
-
* - `connecting`
|
|
9
|
-
* - `handshaking`
|
|
10
|
-
* - `synchronized`
|
|
11
|
-
* - `incompatible`
|
|
12
|
-
* - `destroyed`
|
|
6
|
+
* - `idle` : no transport configured; the handle is detached.
|
|
7
|
+
* - `disconnected` : configured but not currently connected.
|
|
8
|
+
* - `connecting` : opening the physical socket.
|
|
9
|
+
* - `handshaking` : socket open, Yjs sync in progress.
|
|
10
|
+
* - `synchronized` : sync complete; edits flow in both directions.
|
|
11
|
+
* - `incompatible` : the server's document cannot be reconciled with this one.
|
|
12
|
+
* - `destroyed` : the handle was destroyed.
|
|
13
13
|
*/
|
|
14
14
|
export type YjsTransportStatus = 'idle' | 'disconnected' | 'connecting' | 'handshaking' | 'synchronized' | 'incompatible' | 'destroyed';
|
|
15
15
|
/** Identity this client publishes to other peers through awareness. */
|
package/dist/YjsCollaboration.js
CHANGED
|
@@ -27,16 +27,19 @@ function asError(value, fallbackMessage) {
|
|
|
27
27
|
return value instanceof Error ? value : new Error(fallbackMessage);
|
|
28
28
|
}
|
|
29
29
|
function isStrictlyNewerSequence(candidate, current) {
|
|
30
|
-
if (current === null)
|
|
30
|
+
if (current === null) {
|
|
31
31
|
return true;
|
|
32
|
-
|
|
32
|
+
}
|
|
33
|
+
if (candidate.length !== current.length) {
|
|
33
34
|
return candidate.length > current.length;
|
|
35
|
+
}
|
|
34
36
|
return candidate > current;
|
|
35
37
|
}
|
|
36
38
|
function peersToRemoteSelections(peers) {
|
|
37
|
-
return peers.flatMap(
|
|
38
|
-
if (peer.isLocal || peer.cursor == null)
|
|
39
|
+
return peers.flatMap(peer => {
|
|
40
|
+
if (peer.isLocal || peer.cursor == null) {
|
|
39
41
|
return [];
|
|
42
|
+
}
|
|
40
43
|
const applicationState = peer.state && typeof peer.state.state === 'object' && peer.state.state !== null
|
|
41
44
|
? peer.state.state
|
|
42
45
|
: null;
|
|
@@ -64,14 +67,16 @@ function peersToRemoteSelections(peers) {
|
|
|
64
67
|
}
|
|
65
68
|
/**
|
|
66
69
|
* Narrow a caller selection to the text-only cursor awareness carries.
|
|
67
|
-
* Anything else
|
|
70
|
+
* Anything else : a node or all-document selection, or an absent one : is
|
|
68
71
|
* an explicit "no cursor", never a silently retained stale position.
|
|
69
72
|
*/
|
|
70
73
|
function normalizeAwarenessSelection(selection) {
|
|
71
|
-
if (selection === undefined || selection.type !== 'text')
|
|
74
|
+
if (selection === undefined || selection.type !== 'text') {
|
|
72
75
|
return undefined;
|
|
73
|
-
|
|
76
|
+
}
|
|
77
|
+
if (selection.anchor === undefined || selection.head === undefined) {
|
|
74
78
|
return undefined;
|
|
79
|
+
}
|
|
75
80
|
return (0, NativeEditorBridge_1.createNativeEditorLocalAwarenessSelection)(selection.anchor, selection.head);
|
|
76
81
|
}
|
|
77
82
|
/**
|
|
@@ -80,8 +85,9 @@ function normalizeAwarenessSelection(selection) {
|
|
|
80
85
|
* as the transport-less reads elsewhere in this module assume.
|
|
81
86
|
*/
|
|
82
87
|
function copyTransportConfig(config) {
|
|
83
|
-
if (config == null)
|
|
88
|
+
if (config == null) {
|
|
84
89
|
return null;
|
|
90
|
+
}
|
|
85
91
|
return {
|
|
86
92
|
url: config.url,
|
|
87
93
|
connect: config.connect,
|
|
@@ -126,7 +132,7 @@ function mergeAwarenessPartial(base, partial) {
|
|
|
126
132
|
};
|
|
127
133
|
// Selection is stated only when the caller states it. Rust holds the
|
|
128
134
|
// local cursor as a sticky index that already tracks every document
|
|
129
|
-
// change, so an omitted key means "retain it"
|
|
135
|
+
// change, so an omitted key means "retain it" : never "resend the last
|
|
130
136
|
// position I happened to see", which the document may have invalidated.
|
|
131
137
|
if ('selection' in partial) {
|
|
132
138
|
next.selection = normalizeAwarenessSelection(partial.selection) ?? null;
|
|
@@ -148,7 +154,7 @@ class YjsCollaborationControllerImpl {
|
|
|
148
154
|
this.callbacks = callbacks;
|
|
149
155
|
this.transport = copyTransportConfig(options.transport);
|
|
150
156
|
this._state = this.readEngineState(this.handle.bridge.getState());
|
|
151
|
-
this.removeTransportListener = this.handle.addCollaborationTransportListener(
|
|
157
|
+
this.removeTransportListener = this.handle.addCollaborationTransportListener(event => {
|
|
152
158
|
this.handleTransportEvent(event);
|
|
153
159
|
});
|
|
154
160
|
try {
|
|
@@ -177,15 +183,17 @@ class YjsCollaborationControllerImpl {
|
|
|
177
183
|
this.setConnectionEnabled(false);
|
|
178
184
|
}
|
|
179
185
|
reconnect() {
|
|
180
|
-
if (this.destroyed || this.transport === null)
|
|
186
|
+
if (this.destroyed || this.transport === null) {
|
|
181
187
|
return;
|
|
188
|
+
}
|
|
182
189
|
this.handle.configureCollaborationTransport({ ...this.transport, connect: false });
|
|
183
190
|
this.transport = { ...this.transport, connect: true };
|
|
184
191
|
this.handle.configureCollaborationTransport(this.transport);
|
|
185
192
|
}
|
|
186
193
|
destroy() {
|
|
187
|
-
if (this.destroyed)
|
|
194
|
+
if (this.destroyed) {
|
|
188
195
|
return;
|
|
196
|
+
}
|
|
189
197
|
this.destroyed = true;
|
|
190
198
|
this.removeTransportListener?.();
|
|
191
199
|
this.removeTransportListener = null;
|
|
@@ -194,13 +202,15 @@ class YjsCollaborationControllerImpl {
|
|
|
194
202
|
}
|
|
195
203
|
}
|
|
196
204
|
updateLocalAwareness(partial) {
|
|
197
|
-
if (this.destroyed)
|
|
205
|
+
if (this.destroyed) {
|
|
198
206
|
return;
|
|
207
|
+
}
|
|
199
208
|
// Presence exists only while there is a local user. Once awareness
|
|
200
209
|
// is withdrawn, focus and selection updates must not resurrect it
|
|
201
210
|
// as an anonymous entry; only a fresh user re-establishes it.
|
|
202
|
-
if (this.desiredAwareness === null && partial.user == null)
|
|
211
|
+
if (this.desiredAwareness === null && partial.user == null) {
|
|
203
212
|
return;
|
|
213
|
+
}
|
|
204
214
|
const base = this.desiredAwareness ?? { state: {}, focused: false };
|
|
205
215
|
this.publishAwareness(mergeAwarenessPartial(base, partial));
|
|
206
216
|
}
|
|
@@ -211,8 +221,9 @@ class YjsCollaborationControllerImpl {
|
|
|
211
221
|
this.updateLocalAwareness({ focused });
|
|
212
222
|
}
|
|
213
223
|
applyLocalAwarenessOption(user) {
|
|
214
|
-
if (this.destroyed)
|
|
224
|
+
if (this.destroyed) {
|
|
215
225
|
return;
|
|
226
|
+
}
|
|
216
227
|
if (user == null) {
|
|
217
228
|
// Always withdraw: a fresh controller inherits whatever presence
|
|
218
229
|
// a previous one left retained natively on this shared handle.
|
|
@@ -239,8 +250,9 @@ class YjsCollaborationControllerImpl {
|
|
|
239
250
|
});
|
|
240
251
|
}
|
|
241
252
|
setConnectionEnabled(connect) {
|
|
242
|
-
if (this.destroyed || this.transport === null)
|
|
253
|
+
if (this.destroyed || this.transport === null) {
|
|
243
254
|
return;
|
|
255
|
+
}
|
|
244
256
|
this.transport = { ...this.transport, connect };
|
|
245
257
|
this.handle.configureCollaborationTransport(this.transport);
|
|
246
258
|
}
|
|
@@ -251,7 +263,7 @@ class YjsCollaborationControllerImpl {
|
|
|
251
263
|
* `desiredAwareness` advances only after native acceptance.
|
|
252
264
|
*
|
|
253
265
|
* Presence is ambient UI state, not user data. A refusal is reported
|
|
254
|
-
* through `onError` and retried on the next change
|
|
266
|
+
* through `onError` and retried on the next change : it never escapes
|
|
255
267
|
* into a host focus, blur, or selection handler, and never fails an
|
|
256
268
|
* edit. The candidate is discarded so state stays consistent with Rust.
|
|
257
269
|
*/
|
|
@@ -261,8 +273,9 @@ class YjsCollaborationControllerImpl {
|
|
|
261
273
|
// Serializing caller-owned application state can itself fail on
|
|
262
274
|
// a cyclic or non-encodable value, so it stays inside the guard.
|
|
263
275
|
intentJson = JSON.stringify(intent);
|
|
264
|
-
if (intentJson === this.publishedAwarenessJson)
|
|
276
|
+
if (intentJson === this.publishedAwarenessJson) {
|
|
265
277
|
return;
|
|
278
|
+
}
|
|
266
279
|
this.handle.setLocalAwareness(intent);
|
|
267
280
|
}
|
|
268
281
|
catch (error) {
|
|
@@ -288,8 +301,9 @@ class YjsCollaborationControllerImpl {
|
|
|
288
301
|
this.callbacks.onError?.(error);
|
|
289
302
|
return;
|
|
290
303
|
}
|
|
291
|
-
if (event.kind !== 'state')
|
|
304
|
+
if (event.kind !== 'state') {
|
|
292
305
|
return;
|
|
306
|
+
}
|
|
293
307
|
this._peers = [...event.peers];
|
|
294
308
|
this.callbacks.onPeersChange?.(this._peers);
|
|
295
309
|
try {
|
|
@@ -383,8 +397,9 @@ function useYjsCollaboration(options) {
|
|
|
383
397
|
});
|
|
384
398
|
const stableTransportProtocolAdapter = (0, react_1.useMemo)(() => {
|
|
385
399
|
const current = transportProtocolAdapterRef.current;
|
|
386
|
-
if (current === null)
|
|
400
|
+
if (current === null) {
|
|
387
401
|
return null;
|
|
402
|
+
}
|
|
388
403
|
return {
|
|
389
404
|
protocols: [...current.protocols],
|
|
390
405
|
...(current.timeoutMillis === undefined
|
|
@@ -393,7 +408,7 @@ function useYjsCollaboration(options) {
|
|
|
393
408
|
...(current.terminalCloseCodes === undefined
|
|
394
409
|
? {}
|
|
395
410
|
: { terminalCloseCodes: [...current.terminalCloseCodes] }),
|
|
396
|
-
onOpen:
|
|
411
|
+
onOpen: context => transportProtocolAdapterRef.current?.onOpen(context) ?? {
|
|
397
412
|
action: 'reject',
|
|
398
413
|
},
|
|
399
414
|
onMessage: (context, frame) => transportProtocolAdapterRef.current?.onMessage(context, frame) ?? {
|
|
@@ -427,15 +442,15 @@ function useYjsCollaboration(options) {
|
|
|
427
442
|
: { protocolAdapter: stableTransportProtocolAdapter }),
|
|
428
443
|
},
|
|
429
444
|
}, {
|
|
430
|
-
onStateChange:
|
|
445
|
+
onStateChange: nextState => {
|
|
431
446
|
setState({ ...nextState });
|
|
432
447
|
callbacksRef.current.onStateChange?.(nextState);
|
|
433
448
|
},
|
|
434
|
-
onPeersChange:
|
|
449
|
+
onPeersChange: nextPeers => {
|
|
435
450
|
setPeers([...nextPeers]);
|
|
436
451
|
callbacksRef.current.onPeersChange?.(nextPeers);
|
|
437
452
|
},
|
|
438
|
-
onError:
|
|
453
|
+
onError: error => callbacksRef.current.onError?.(error),
|
|
439
454
|
});
|
|
440
455
|
controllerRef.current = controller;
|
|
441
456
|
setState({ ...controller.state });
|
|
@@ -468,8 +483,9 @@ function useYjsCollaboration(options) {
|
|
|
468
483
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
469
484
|
}, [localAwarenessKey]);
|
|
470
485
|
(0, react_1.useEffect)(() => {
|
|
471
|
-
if (transportUrl === null)
|
|
486
|
+
if (transportUrl === null) {
|
|
472
487
|
return;
|
|
488
|
+
}
|
|
473
489
|
if (transportConnect) {
|
|
474
490
|
controllerRef.current?.connect();
|
|
475
491
|
}
|
|
@@ -484,7 +500,7 @@ function useYjsCollaboration(options) {
|
|
|
484
500
|
connect: () => controllerRef.current?.connect(),
|
|
485
501
|
disconnect: () => controllerRef.current?.disconnect(),
|
|
486
502
|
reconnect: () => controllerRef.current?.reconnect(),
|
|
487
|
-
updateLocalAwareness:
|
|
503
|
+
updateLocalAwareness: partial => controllerRef.current?.updateLocalAwareness(partial),
|
|
488
504
|
editorBindings: {
|
|
489
505
|
documentHandle: options.handle,
|
|
490
506
|
documentRevision: state.documentRevision,
|
package/dist/addons.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface MentionQueryChangeEvent {
|
|
|
30
30
|
anchor: number;
|
|
31
31
|
head: number;
|
|
32
32
|
};
|
|
33
|
-
/** False once the query closed
|
|
33
|
+
/** False once the query closed : supply an empty suggestion list. */
|
|
34
34
|
isActive: boolean;
|
|
35
35
|
/** Engine document revision the query was computed against. */
|
|
36
36
|
documentVersion?: string;
|
|
@@ -52,7 +52,7 @@ export interface MentionSelectEvent {
|
|
|
52
52
|
export interface MentionSelectionAttrsEvent {
|
|
53
53
|
trigger: string;
|
|
54
54
|
suggestion: MentionSuggestion;
|
|
55
|
-
/** Attributes resolved so far
|
|
55
|
+
/** Attributes resolved so far : the suggestion's own, plus `label` and `mentionSuggestionChar`. */
|
|
56
56
|
attrs: Record<string, unknown>;
|
|
57
57
|
/** Mark attributes active at the insertion point. */
|
|
58
58
|
markAttrs: ReadonlyActiveState['markAttrs'];
|
|
@@ -69,7 +69,7 @@ export type MentionThemeResolveEvent = MentionSelectionAttrsEvent;
|
|
|
69
69
|
/**
|
|
70
70
|
* Mentions for `RichTextEditor`. The schema belongs to the document
|
|
71
71
|
* handle, so create the handle with {@link withMentionsSchema} applied to
|
|
72
|
-
* your schema
|
|
72
|
+
* your schema : this config alone does not add the `mention` node.
|
|
73
73
|
* (`RichTextViewer` adds it for you.)
|
|
74
74
|
*
|
|
75
75
|
* The host owns the suggestion list: react to `onQueryChange` by filtering
|
|
@@ -185,7 +185,7 @@ export interface MentionFragmentOptions {
|
|
|
185
185
|
/**
|
|
186
186
|
* Build a document fragment holding one mention node, ready for
|
|
187
187
|
* `insertContentJson`. Use it to insert a mention outside the suggestion
|
|
188
|
-
* flow
|
|
188
|
+
* flow : from a picker, say.
|
|
189
189
|
*
|
|
190
190
|
* @param attrs Attributes for the mention node, e.g. `{ label, id }`.
|
|
191
191
|
* @param descriptor Document node name to wrap the fragment in. Defaults to `doc`.
|
package/dist/addons.js
CHANGED
|
@@ -54,7 +54,7 @@ function mentionNodeSpec() {
|
|
|
54
54
|
* ```
|
|
55
55
|
*/
|
|
56
56
|
function withMentionsSchema(schema) {
|
|
57
|
-
const hasMentionNode = schema.nodes.some(
|
|
57
|
+
const hasMentionNode = schema.nodes.some(node => node.name === exports.MENTION_NODE_NAME);
|
|
58
58
|
if (hasMentionNode) {
|
|
59
59
|
return schema;
|
|
60
60
|
}
|
|
@@ -68,7 +68,7 @@ function normalizeNativeEditorAddons(addons) {
|
|
|
68
68
|
return addons?.codeHighlighting ? { codeHighlighting: addons.codeHighlighting } : undefined;
|
|
69
69
|
}
|
|
70
70
|
const trigger = addons.mentions.trigger?.trim() || DEFAULT_MENTION_TRIGGER;
|
|
71
|
-
const suggestions = (addons.mentions.suggestions ?? []).map(
|
|
71
|
+
const suggestions = (addons.mentions.suggestions ?? []).map(suggestion => {
|
|
72
72
|
const label = suggestion.label?.trim() || suggestion.title;
|
|
73
73
|
const attrs = {
|
|
74
74
|
label,
|
|
@@ -106,7 +106,7 @@ function serializeNormalizedEditorAddons(addons) {
|
|
|
106
106
|
/**
|
|
107
107
|
* Build a document fragment holding one mention node, ready for
|
|
108
108
|
* `insertContentJson`. Use it to insert a mention outside the suggestion
|
|
109
|
-
* flow
|
|
109
|
+
* flow : from a picker, say.
|
|
110
110
|
*
|
|
111
111
|
* @param attrs Attributes for the mention node, e.g. `{ label, id }`.
|
|
112
112
|
* @param descriptor Document node name to wrap the fragment in. Defaults to `doc`.
|
package/dist/atomInstances.js
CHANGED
|
@@ -67,13 +67,16 @@ function applyRenderPatch(previousBlocks, patch) {
|
|
|
67
67
|
];
|
|
68
68
|
}
|
|
69
69
|
function atomSelected(selection, docPos) {
|
|
70
|
-
if (selection.type === 'all')
|
|
70
|
+
if (selection.type === 'all') {
|
|
71
71
|
return true;
|
|
72
|
-
|
|
72
|
+
}
|
|
73
|
+
if (selection.type === 'node') {
|
|
73
74
|
return selection.pos === docPos;
|
|
75
|
+
}
|
|
74
76
|
const { anchor, head } = selection;
|
|
75
|
-
if (anchor == null || head == null)
|
|
77
|
+
if (anchor == null || head == null) {
|
|
76
78
|
return false;
|
|
79
|
+
}
|
|
77
80
|
const [from, to] = anchor <= head ? [anchor, head] : [head, anchor];
|
|
78
81
|
return from <= docPos && docPos + 1 <= to;
|
|
79
82
|
}
|
package/dist/atomUpdates.js
CHANGED
|
@@ -3,10 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.resolveAtomAttrsUpdate = resolveAtomAttrsUpdate;
|
|
4
4
|
const atomInstances_1 = require("./atomInstances");
|
|
5
5
|
function copyJson(value, ancestors = new Set()) {
|
|
6
|
-
if (value === null || typeof value === 'string' || typeof value === 'boolean')
|
|
6
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean') {
|
|
7
7
|
return value;
|
|
8
|
-
|
|
8
|
+
}
|
|
9
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
9
10
|
return value;
|
|
11
|
+
}
|
|
10
12
|
if (typeof value !== 'object' || ancestors.has(value) || ancestors.size > 128) {
|
|
11
13
|
throw new atomInstances_1.AtomUpdateAttrsError('not-applicable', 'Atom attributes must contain finite JSON values.');
|
|
12
14
|
}
|
|
@@ -17,7 +19,7 @@ function copyJson(value, ancestors = new Set()) {
|
|
|
17
19
|
}
|
|
18
20
|
ancestors.add(value);
|
|
19
21
|
const result = Array.isArray(value)
|
|
20
|
-
? Array.from(value,
|
|
22
|
+
? Array.from(value, entry => copyJson(entry, ancestors))
|
|
21
23
|
: Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, copyJson(entry, ancestors)]));
|
|
22
24
|
ancestors.delete(value);
|
|
23
25
|
return Object.freeze(result);
|
|
@@ -25,7 +27,8 @@ function copyJson(value, ancestors = new Set()) {
|
|
|
25
27
|
function resolveAtomAttrsUpdate(attrs, update) {
|
|
26
28
|
let current = copyJson(attrs);
|
|
27
29
|
let patch = {};
|
|
28
|
-
|
|
30
|
+
const updates = Array.isArray(update) ? update : [update];
|
|
31
|
+
for (const entry of updates) {
|
|
29
32
|
const value = typeof entry === 'function' ? entry(current) : entry;
|
|
30
33
|
if (value == null || typeof value !== 'object' || Array.isArray(value)) {
|
|
31
34
|
throw new atomInstances_1.AtomUpdateAttrsError('not-applicable', 'Atom updates must return an attribute object.');
|