@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.
Files changed (161) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/android/build.gradle +3 -0
  4. package/app.plugin.js +42 -39
  5. package/dist/AtomHost.js +18 -11
  6. package/dist/EditorAddon.js +4 -2
  7. package/dist/EditorAddonNormalization.js +17 -9
  8. package/dist/EditorMentionThemeNormalization.js +6 -3
  9. package/dist/EditorMentionThemeValidation.js +28 -11
  10. package/dist/EditorStyleSheetNormalization.js +84 -37
  11. package/dist/EditorTheme.d.ts +4 -0
  12. package/dist/EditorTheme.js +5 -3
  13. package/dist/EditorToolbarComponent.d.ts +1 -1
  14. package/dist/EditorToolbarComponent.js +1 -1
  15. package/dist/EditorToolbarRegistry.js +5 -5
  16. package/dist/EditorToolbarTypes.d.ts +12 -12
  17. package/dist/EditorToolbarVisuals.js +1 -1
  18. package/dist/ExternalTextComposition.js +89 -43
  19. package/dist/ImageLoadingPolicy.d.ts +1 -1
  20. package/dist/NativeEditorBoundaryError.d.ts +7 -7
  21. package/dist/NativeEditorBoundaryError.js +9 -5
  22. package/dist/NativeEditorCollaborationTransport.d.ts +7 -7
  23. package/dist/NativeEditorCollaborationTransport.js +28 -15
  24. package/dist/NativeEditorCreateConfig.js +11 -6
  25. package/dist/NativeEditorCreateJson.js +51 -25
  26. package/dist/NativeEditorCreateValidation.js +11 -6
  27. package/dist/NativeEditorDocumentBridge.d.ts +1 -1
  28. package/dist/NativeEditorDocumentBridge.js +32 -18
  29. package/dist/NativeEditorDocumentHandle.d.ts +4 -4
  30. package/dist/NativeEditorDocumentHandle.js +1 -1
  31. package/dist/NativeEditorLocalAwareness.js +25 -13
  32. package/dist/NativeEditorNativeModule.d.ts +8 -1
  33. package/dist/NativeEditorNativeModule.js +7 -5
  34. package/dist/NativeEditorRenderNormalization.js +66 -25
  35. package/dist/NativeEditorResultNormalization.js +53 -29
  36. package/dist/NativeEditorTypes.d.ts +10 -10
  37. package/dist/NativeEditorV2Decimal.js +6 -3
  38. package/dist/NativeProseViewer.js +11 -3
  39. package/dist/ResourceLimits.d.ts +2 -2
  40. package/dist/ResourceLimits.js +7 -2
  41. package/dist/RichTextEditor.js +1 -1
  42. package/dist/RichTextEditorNativeTypes.d.ts +1 -1
  43. package/dist/RichTextEditorNativeView.d.ts +1 -3
  44. package/dist/RichTextEditorSerialization.js +24 -14
  45. package/dist/RichTextEditorTypes.d.ts +1 -1
  46. package/dist/YjsCollaboration.d.ts +7 -7
  47. package/dist/YjsCollaboration.js +42 -26
  48. package/dist/addons.d.ts +4 -4
  49. package/dist/addons.js +3 -3
  50. package/dist/atomInstances.js +6 -3
  51. package/dist/atomUpdates.js +7 -4
  52. package/dist/atoms.js +20 -12
  53. package/dist/attributeValidation.js +55 -24
  54. package/dist/contentExpression.js +80 -42
  55. package/dist/listMarkers.d.ts +1 -1
  56. package/dist/listMarkers.js +1 -1
  57. package/dist/schemaDefinition.d.ts +1 -1
  58. package/dist/schemaDefinition.js +23 -14
  59. package/dist/schemaDocument.js +1 -1
  60. package/dist/schemaDocumentConstruction.js +25 -14
  61. package/dist/schemaNormalization.js +51 -29
  62. package/dist/schemaPresets.d.ts +1 -1
  63. package/dist/schemaPresets.js +9 -4
  64. package/dist/schemaResolution.js +46 -25
  65. package/dist/specs/PreparedProseViewerNativeComponent.d.ts +4 -0
  66. package/dist/useEditorToolbarInteractions.d.ts +2 -2
  67. package/dist/useEditorToolbarInteractions.js +45 -23
  68. package/dist/useEditorToolbarItems.d.ts +1 -1
  69. package/dist/useEditorToolbarItems.js +4 -3
  70. package/dist/useEditorToolbarPresentation.d.ts +3 -3
  71. package/dist/useEditorToolbarPresentation.js +4 -4
  72. package/dist/useEditorToolbarState.d.ts +1 -1
  73. package/dist/useEditorToolbarState.js +1 -1
  74. package/dist/useFocusPreservingFrames.js +11 -8
  75. package/dist/useNativeEditor.d.ts +1 -1
  76. package/dist/useNativeEditor.js +21 -11
  77. package/dist/useRichTextEditorCommands.d.ts +2 -2
  78. package/dist/useRichTextEditorCommands.js +59 -29
  79. package/dist/useRichTextEditorEvents.d.ts +3 -3
  80. package/dist/useRichTextEditorEvents.js +75 -32
  81. package/dist/useRichTextEditorMentions.d.ts +4 -4
  82. package/dist/useRichTextEditorMentions.js +48 -29
  83. package/dist/useRichTextEditorPresentation.d.ts +4 -4
  84. package/dist/useRichTextEditorPresentation.js +25 -23
  85. package/dist/useRichTextEditorState.d.ts +3 -3
  86. package/dist/useRichTextEditorState.js +20 -15
  87. package/dist/useRichTextEditorUpdates.d.ts +1 -1
  88. package/dist/useRichTextEditorUpdates.js +99 -41
  89. package/dist/useViewerAtoms.js +63 -36
  90. package/ios/EditorCore.xcframework/Info.plist +5 -5
  91. package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
  92. package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
  93. package/package.json +11 -3
  94. package/react-native.config.js +1 -1
  95. package/rust/android/arm64-v8a/libeditor_core.so +0 -0
  96. package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
  97. package/rust/android/x86/libeditor_core.so +0 -0
  98. package/rust/android/x86_64/libeditor_core.so +0 -0
  99. package/src/AtomHost.tsx +58 -28
  100. package/src/EditorAddon.ts +18 -8
  101. package/src/EditorAddonNormalization.ts +40 -11
  102. package/src/EditorMentionThemeNormalization.ts +15 -4
  103. package/src/EditorMentionThemeValidation.ts +51 -14
  104. package/src/EditorStyleSheet.ts +1 -0
  105. package/src/EditorStyleSheetNormalization.ts +195 -64
  106. package/src/EditorTheme.ts +13 -5
  107. package/src/EditorToolbarComponent.tsx +2 -1
  108. package/src/EditorToolbarRegistry.tsx +23 -13
  109. package/src/EditorToolbarTypes.tsx +12 -12
  110. package/src/EditorToolbarVisuals.tsx +6 -3
  111. package/src/EditorUpdateRevision.ts +1 -0
  112. package/src/ExternalTextComposition.ts +259 -49
  113. package/src/ImageLoadingPolicy.ts +3 -1
  114. package/src/NativeEditorBoundaryError.ts +38 -11
  115. package/src/NativeEditorCollaborationTransport.ts +108 -27
  116. package/src/NativeEditorCreateConfig.ts +92 -19
  117. package/src/NativeEditorCreateJson.ts +163 -26
  118. package/src/NativeEditorCreateValidation.ts +33 -12
  119. package/src/NativeEditorDocumentBridge.ts +111 -19
  120. package/src/NativeEditorDocumentHandle.ts +9 -4
  121. package/src/NativeEditorLocalAwareness.ts +70 -15
  122. package/src/NativeEditorNativeModule.ts +13 -9
  123. package/src/NativeEditorRenderNormalization.ts +128 -29
  124. package/src/NativeEditorResultNormalization.ts +143 -32
  125. package/src/NativeEditorTypes.ts +12 -11
  126. package/src/NativeEditorV2Decimal.ts +12 -3
  127. package/src/NativeProseViewer.tsx +46 -16
  128. package/src/ResourceLimits.ts +15 -8
  129. package/src/RichTextEditor.tsx +3 -1
  130. package/src/RichTextEditorNativeTypes.tsx +1 -1
  131. package/src/RichTextEditorNativeView.tsx +2 -8
  132. package/src/RichTextEditorSerialization.tsx +70 -24
  133. package/src/RichTextEditorTypes.tsx +1 -1
  134. package/src/YjsCollaboration.ts +143 -47
  135. package/src/addons.ts +12 -8
  136. package/src/atomInstances.ts +25 -5
  137. package/src/atomUpdates.ts +23 -6
  138. package/src/atoms.ts +85 -18
  139. package/src/attributeValidation.ts +97 -33
  140. package/src/contentExpression.ts +218 -63
  141. package/src/listMarkers.ts +1 -1
  142. package/src/schemaDefinition.ts +82 -20
  143. package/src/schemaDocument.ts +15 -1
  144. package/src/schemaDocumentConstruction.ts +80 -16
  145. package/src/schemaNormalization.ts +155 -41
  146. package/src/schemaPresets.ts +37 -31
  147. package/src/schemaResolution.ts +126 -27
  148. package/src/specs/PreparedProseViewerNativeComponent.ts +4 -2
  149. package/src/useEditorToolbarInteractions.tsx +85 -36
  150. package/src/useEditorToolbarItems.tsx +50 -22
  151. package/src/useEditorToolbarPresentation.tsx +81 -58
  152. package/src/useEditorToolbarState.tsx +8 -6
  153. package/src/useFocusPreservingFrames.ts +43 -15
  154. package/src/useNativeEditor.ts +111 -51
  155. package/src/useRichTextEditorCommands.tsx +127 -54
  156. package/src/useRichTextEditorEvents.tsx +144 -42
  157. package/src/useRichTextEditorMentions.tsx +118 -37
  158. package/src/useRichTextEditorPresentation.tsx +128 -114
  159. package/src/useRichTextEditorState.tsx +76 -40
  160. package/src/useRichTextEditorUpdates.tsx +207 -56
  161. package/src/useViewerAtoms.tsx +162 -66
@@ -20,13 +20,13 @@ import type { RemoteSelectionDecoration } from './NativeRichTextEditor';
20
20
  /**
21
21
  * Transport lifecycle, projected from the Rust transport state.
22
22
  *
23
- * - `idle` no transport configured; the handle is detached.
24
- * - `disconnected` configured but not currently connected.
25
- * - `connecting` opening the physical socket.
26
- * - `handshaking` socket open, Yjs sync in progress.
27
- * - `synchronized` sync complete; edits flow in both directions.
28
- * - `incompatible` the server's document cannot be reconciled with this one.
29
- * - `destroyed` the handle was destroyed.
23
+ * - `idle` : no transport configured; the handle is detached.
24
+ * - `disconnected` : configured but not currently connected.
25
+ * - `connecting` : opening the physical socket.
26
+ * - `handshaking` : socket open, Yjs sync in progress.
27
+ * - `synchronized` : sync complete; edits flow in both directions.
28
+ * - `incompatible` : the server's document cannot be reconciled with this one.
29
+ * - `destroyed` : the handle was destroyed.
30
30
  */
31
31
  export type YjsTransportStatus =
32
32
  | 'idle'
@@ -178,26 +178,37 @@ function asError(value: unknown, fallbackMessage: string): Error {
178
178
  }
179
179
 
180
180
  function isStrictlyNewerSequence(candidate: string, current: string | null): boolean {
181
- if (current === null) return true;
182
- if (candidate.length !== current.length) return candidate.length > current.length;
181
+ if (current === null) {
182
+ return true;
183
+ }
184
+
185
+ if (candidate.length !== current.length) {
186
+ return candidate.length > current.length;
187
+ }
188
+
183
189
  return candidate > current;
184
190
  }
185
191
 
186
192
  function peersToRemoteSelections(
187
193
  peers: readonly NativeEditorPeerInfo[]
188
194
  ): RemoteSelectionDecoration[] {
189
- return peers.flatMap((peer) => {
190
- if (peer.isLocal || peer.cursor == null) return [];
195
+ return peers.flatMap(peer => {
196
+ if (peer.isLocal || peer.cursor == null) {
197
+ return [];
198
+ }
199
+
191
200
  const applicationState =
192
201
  peer.state && typeof peer.state.state === 'object' && peer.state.state !== null
193
202
  ? (peer.state.state as Record<string, unknown>)
194
203
  : null;
204
+
195
205
  const user =
196
206
  applicationState &&
197
207
  typeof applicationState.user === 'object' &&
198
208
  applicationState.user !== null
199
209
  ? (applicationState.user as Record<string, unknown>)
200
210
  : null;
211
+
201
212
  return [
202
213
  {
203
214
  clientId: peer.clientId,
@@ -221,14 +232,20 @@ function peersToRemoteSelections(
221
232
 
222
233
  /**
223
234
  * Narrow a caller selection to the text-only cursor awareness carries.
224
- * Anything else a node or all-document selection, or an absent one is
235
+ * Anything else : a node or all-document selection, or an absent one : is
225
236
  * an explicit "no cursor", never a silently retained stale position.
226
237
  */
227
238
  function normalizeAwarenessSelection(
228
239
  selection: Selection | undefined
229
240
  ): NativeEditorLocalAwarenessSelection | undefined {
230
- if (selection === undefined || selection.type !== 'text') return undefined;
231
- if (selection.anchor === undefined || selection.head === undefined) return undefined;
241
+ if (selection === undefined || selection.type !== 'text') {
242
+ return undefined;
243
+ }
244
+
245
+ if (selection.anchor === undefined || selection.head === undefined) {
246
+ return undefined;
247
+ }
248
+
232
249
  return createNativeEditorLocalAwarenessSelection(selection.anchor, selection.head);
233
250
  }
234
251
 
@@ -240,7 +257,10 @@ function normalizeAwarenessSelection(
240
257
  function copyTransportConfig(
241
258
  config: NativeCollaborationTransportConfig | null | undefined
242
259
  ): NativeCollaborationTransportConfig | null {
243
- if (config == null) return null;
260
+ if (config == null) {
261
+ return null;
262
+ }
263
+
244
264
  return {
245
265
  url: config.url,
246
266
  connect: config.connect,
@@ -273,16 +293,19 @@ function mergeAwarenessPartial(
273
293
  partial: Partial<LocalAwarenessState>
274
294
  ): NativeEditorLocalAwarenessIntent {
275
295
  const state: Record<string, unknown> = { ...base.state };
296
+
276
297
  if (partial.user != null) {
277
298
  const baseUser =
278
299
  state.user != null && typeof state.user === 'object'
279
300
  ? (state.user as Record<string, unknown>)
280
301
  : {};
302
+
281
303
  state.user = copyLocalAwarenessUser({
282
304
  ...baseUser,
283
305
  ...partial.user,
284
- } as unknown as LocalAwarenessUser);
306
+ });
285
307
  }
308
+
286
309
  const next: NativeEditorLocalAwarenessIntent = {
287
310
  state,
288
311
  focused:
@@ -290,28 +313,40 @@ function mergeAwarenessPartial(
290
313
  ? partial.focused
291
314
  : base.focused,
292
315
  };
316
+
293
317
  // Selection is stated only when the caller states it. Rust holds the
294
318
  // local cursor as a sticky index that already tracks every document
295
- // change, so an omitted key means "retain it" never "resend the last
319
+ // change, so an omitted key means "retain it" : never "resend the last
296
320
  // position I happened to see", which the document may have invalidated.
297
321
  if ('selection' in partial) {
298
322
  next.selection = normalizeAwarenessSelection(partial.selection) ?? null;
299
323
  }
324
+
300
325
  return next;
301
326
  }
302
327
 
303
328
  class YjsCollaborationControllerImpl implements YjsCollaborationController {
304
329
  private readonly handle: NativeEditorDocumentHandle;
330
+
305
331
  private readonly documentId: string;
332
+
306
333
  private readonly callbacks: MutableCallbacks;
334
+
307
335
  private transport: NativeCollaborationTransportConfig | null;
336
+
308
337
  private removeTransportListener: (() => void) | null = null;
338
+
309
339
  private desiredAwareness: NativeEditorLocalAwarenessIntent | null = null;
340
+
310
341
  /** Serialized form of the last intent Rust accepted, for dedup. */
311
342
  private publishedAwarenessJson: string | null = null;
343
+
312
344
  private lastEventSequence: string | null = null;
345
+
313
346
  private destroyed = false;
347
+
314
348
  private _state: YjsCollaborationState;
349
+
315
350
  private _peers: NativeEditorPeerInfo[] = [];
316
351
 
317
352
  constructor(options: YjsCollaborationOptions, callbacks: MutableCallbacks = {}) {
@@ -321,9 +356,11 @@ class YjsCollaborationControllerImpl implements YjsCollaborationController {
321
356
  this.callbacks = callbacks;
322
357
  this.transport = copyTransportConfig(options.transport);
323
358
  this._state = this.readEngineState(this.handle.bridge.getState());
324
- this.removeTransportListener = this.handle.addCollaborationTransportListener((event) => {
359
+
360
+ this.removeTransportListener = this.handle.addCollaborationTransportListener(event => {
325
361
  this.handleTransportEvent(event);
326
362
  });
363
+
327
364
  try {
328
365
  this.applyLocalAwarenessOption(options.localAwareness);
329
366
  this.handle.configureCollaborationTransport(this.transport);
@@ -355,28 +392,41 @@ class YjsCollaborationControllerImpl implements YjsCollaborationController {
355
392
  }
356
393
 
357
394
  reconnect(): void {
358
- if (this.destroyed || this.transport === null) return;
395
+ if (this.destroyed || this.transport === null) {
396
+ return;
397
+ }
398
+
359
399
  this.handle.configureCollaborationTransport({ ...this.transport, connect: false });
360
400
  this.transport = { ...this.transport, connect: true };
361
401
  this.handle.configureCollaborationTransport(this.transport);
362
402
  }
363
403
 
364
404
  destroy(): void {
365
- if (this.destroyed) return;
405
+ if (this.destroyed) {
406
+ return;
407
+ }
408
+
366
409
  this.destroyed = true;
367
410
  this.removeTransportListener?.();
368
411
  this.removeTransportListener = null;
412
+
369
413
  if (!this.handle.isDestroyed) {
370
414
  this.handle.configureCollaborationTransport(null);
371
415
  }
372
416
  }
373
417
 
374
418
  updateLocalAwareness(partial: Partial<LocalAwarenessState>): void {
375
- if (this.destroyed) return;
419
+ if (this.destroyed) {
420
+ return;
421
+ }
422
+
376
423
  // Presence exists only while there is a local user. Once awareness
377
424
  // is withdrawn, focus and selection updates must not resurrect it
378
425
  // as an anonymous entry; only a fresh user re-establishes it.
379
- if (this.desiredAwareness === null && partial.user == null) return;
426
+ if (this.desiredAwareness === null && partial.user == null) {
427
+ return;
428
+ }
429
+
380
430
  const base = this.desiredAwareness ?? { state: {}, focused: false };
381
431
  this.publishAwareness(mergeAwarenessPartial(base, partial));
382
432
  }
@@ -390,7 +440,10 @@ class YjsCollaborationControllerImpl implements YjsCollaborationController {
390
440
  }
391
441
 
392
442
  applyLocalAwarenessOption(user?: LocalAwarenessUser): void {
393
- if (this.destroyed) return;
443
+ if (this.destroyed) {
444
+ return;
445
+ }
446
+
394
447
  if (user == null) {
395
448
  // Always withdraw: a fresh controller inherits whatever presence
396
449
  // a previous one left retained natively on this shared handle.
@@ -398,13 +451,18 @@ class YjsCollaborationControllerImpl implements YjsCollaborationController {
398
451
  this.handle.setLocalAwareness(null);
399
452
  } catch (error) {
400
453
  this.reportError(asError(error, 'Local awareness withdrawal failed'));
454
+
401
455
  return;
402
456
  }
457
+
403
458
  this.desiredAwareness = null;
404
459
  this.publishedAwarenessJson = null;
460
+
405
461
  return;
406
462
  }
463
+
407
464
  const current = this.desiredAwareness;
465
+
408
466
  // No `selection` key: the Rust-owned cursor carries across a user
409
467
  // change untouched.
410
468
  this.publishAwareness({
@@ -417,7 +475,10 @@ class YjsCollaborationControllerImpl implements YjsCollaborationController {
417
475
  }
418
476
 
419
477
  private setConnectionEnabled(connect: boolean): void {
420
- if (this.destroyed || this.transport === null) return;
478
+ if (this.destroyed || this.transport === null) {
479
+ return;
480
+ }
481
+
421
482
  this.transport = { ...this.transport, connect };
422
483
  this.handle.configureCollaborationTransport(this.transport);
423
484
  }
@@ -429,22 +490,29 @@ class YjsCollaborationControllerImpl implements YjsCollaborationController {
429
490
  * `desiredAwareness` advances only after native acceptance.
430
491
  *
431
492
  * Presence is ambient UI state, not user data. A refusal is reported
432
- * through `onError` and retried on the next change it never escapes
493
+ * through `onError` and retried on the next change : it never escapes
433
494
  * into a host focus, blur, or selection handler, and never fails an
434
495
  * edit. The candidate is discarded so state stays consistent with Rust.
435
496
  */
436
497
  private publishAwareness(intent: NativeEditorLocalAwarenessIntent): void {
437
498
  let intentJson: string;
499
+
438
500
  try {
439
501
  // Serializing caller-owned application state can itself fail on
440
502
  // a cyclic or non-encodable value, so it stays inside the guard.
441
503
  intentJson = JSON.stringify(intent);
442
- if (intentJson === this.publishedAwarenessJson) return;
504
+
505
+ if (intentJson === this.publishedAwarenessJson) {
506
+ return;
507
+ }
508
+
443
509
  this.handle.setLocalAwareness(intent);
444
510
  } catch (error) {
445
511
  this.reportError(asError(error, 'Local awareness publication failed'));
512
+
446
513
  return;
447
514
  }
515
+
448
516
  this.desiredAwareness = intent;
449
517
  this.publishedAwarenessJson = intentJson;
450
518
  }
@@ -461,16 +529,24 @@ class YjsCollaborationControllerImpl implements YjsCollaborationController {
461
529
  ) {
462
530
  return;
463
531
  }
532
+
464
533
  this.lastEventSequence = event.eventSequence;
534
+
465
535
  if (event.kind === 'error') {
466
536
  const error = event.error ?? new Error('Native collaboration transport failed');
467
537
  this.setState({ lastError: error });
468
538
  this.callbacks.onError?.(error);
539
+
469
540
  return;
470
541
  }
471
- if (event.kind !== 'state') return;
472
- this._peers = [...event.peers];
542
+
543
+ if (event.kind !== 'state') {
544
+ return;
545
+ }
546
+
547
+ this._peers = [ ...event.peers ];
473
548
  this.callbacks.onPeersChange?.(this._peers);
549
+
474
550
  try {
475
551
  this.setState({
476
552
  ...this.readEngineState(event.state, this._state),
@@ -488,14 +564,16 @@ class YjsCollaborationControllerImpl implements YjsCollaborationController {
488
564
  previous?: YjsCollaborationState
489
565
  ): YjsCollaborationState {
490
566
  const awaitingRemote = engineState.documentState === 'AwaitRemote';
567
+
491
568
  const documentJson =
492
569
  !awaitingRemote &&
493
570
  previous?.documentRevision === engineState.documentRevision &&
494
571
  previous.documentJson !== null
495
572
  ? previous.documentJson
496
573
  : awaitingRemote
497
- ? null
498
- : this.handle.bridge.getDocumentJson();
574
+ ? null
575
+ : this.handle.bridge.getDocumentJson();
576
+
499
577
  return {
500
578
  documentId: this.documentId,
501
579
  status: mapTransportState(engineState.transportState),
@@ -551,17 +629,20 @@ export function createYjsCollaborationController(
551
629
  export function useYjsCollaboration(options: YjsCollaborationOptions): UseYjsCollaborationResult {
552
630
  _assertNativeEditorDocumentHandle(options.handle);
553
631
  const callbacksRef = useRef<MutableCallbacks>({});
632
+
554
633
  callbacksRef.current = {
555
634
  onPeersChange: options.onPeersChange,
556
635
  onStateChange: options.onStateChange,
557
636
  onError: options.onError,
558
637
  };
638
+
559
639
  const controllerRef = useRef<YjsCollaborationControllerImpl | null>(null);
560
640
  const localAwarenessKey = localAwarenessDependencyKey(options.localAwareness);
561
641
  const transportUrl = options.transport?.url ?? null;
562
642
  const transportConnect = options.transport?.connect ?? false;
563
643
  const transportProtocolAdapterRef = useRef<NativeCollaborationProtocolAdapter | null>(null);
564
644
  transportProtocolAdapterRef.current = options.transport?.protocolAdapter ?? null;
645
+
565
646
  const transportProtocolAdapterMetadata = JSON.stringify(
566
647
  options.transport?.protocolAdapter == null
567
648
  ? null
@@ -571,18 +652,23 @@ export function useYjsCollaboration(options: YjsCollaborationOptions): UseYjsCol
571
652
  terminalCloseCodes: options.transport.protocolAdapter.terminalCloseCodes,
572
653
  }
573
654
  );
655
+
574
656
  const stableTransportProtocolAdapter = useMemo<NativeCollaborationProtocolAdapter | null>(() => {
575
657
  const current = transportProtocolAdapterRef.current;
576
- if (current === null) return null;
658
+
659
+ if (current === null) {
660
+ return null;
661
+ }
662
+
577
663
  return {
578
- protocols: [...current.protocols],
664
+ protocols: [ ...current.protocols ],
579
665
  ...(current.timeoutMillis === undefined
580
666
  ? {}
581
667
  : { timeoutMillis: current.timeoutMillis }),
582
668
  ...(current.terminalCloseCodes === undefined
583
669
  ? {}
584
- : { terminalCloseCodes: [...current.terminalCloseCodes] }),
585
- onOpen: (context) =>
670
+ : { terminalCloseCodes: [ ...current.terminalCloseCodes ] }),
671
+ onOpen: context =>
586
672
  transportProtocolAdapterRef.current?.onOpen(context) ?? {
587
673
  action: 'reject',
588
674
  },
@@ -594,18 +680,21 @@ export function useYjsCollaboration(options: YjsCollaborationOptions): UseYjsCol
594
680
  // Static metadata changes require a new native socket descriptor.
595
681
  // Callback identity alone is read through the ref and never reconnects.
596
682
  // eslint-disable-next-line react-hooks/exhaustive-deps
597
- }, [transportProtocolAdapterMetadata]);
598
- const [state, setState] = useState<YjsCollaborationState>({
683
+ }, [ transportProtocolAdapterMetadata ]);
684
+
685
+ const [ state, setState ] = useState<YjsCollaborationState>({
599
686
  documentId: options.documentId,
600
687
  status: 'idle',
601
688
  isConnected: false,
602
689
  documentJson: null,
603
690
  documentRevision: null,
604
691
  });
605
- const [peers, setPeers] = useState<NativeEditorPeerInfo[]>([]);
692
+
693
+ const [ peers, setPeers ] = useState<NativeEditorPeerInfo[]>([]);
606
694
 
607
695
  useEffect(() => {
608
696
  let controller: YjsCollaborationControllerImpl;
697
+
609
698
  try {
610
699
  controller = new YjsCollaborationControllerImpl(
611
700
  {
@@ -622,23 +711,25 @@ export function useYjsCollaboration(options: YjsCollaborationOptions): UseYjsCol
622
711
  },
623
712
  },
624
713
  {
625
- onStateChange: (nextState) => {
714
+ onStateChange: nextState => {
626
715
  setState({ ...nextState });
627
716
  callbacksRef.current.onStateChange?.(nextState);
628
717
  },
629
- onPeersChange: (nextPeers) => {
630
- setPeers([...nextPeers]);
718
+ onPeersChange: nextPeers => {
719
+ setPeers([ ...nextPeers ]);
631
720
  callbacksRef.current.onPeersChange?.(nextPeers);
632
721
  },
633
- onError: (error) => callbacksRef.current.onError?.(error),
722
+ onError: error => callbacksRef.current.onError?.(error),
634
723
  }
635
724
  );
725
+
636
726
  controllerRef.current = controller;
637
727
  setState({ ...controller.state });
638
- setPeers([...controller.peers]);
728
+ setPeers([ ...controller.peers ]);
639
729
  } catch (error) {
640
730
  const nextError = asError(error, 'Native collaboration initialization failed');
641
731
  controllerRef.current = null;
732
+
642
733
  setState({
643
734
  documentId: options.documentId,
644
735
  status: 'idle',
@@ -647,9 +738,11 @@ export function useYjsCollaboration(options: YjsCollaborationOptions): UseYjsCol
647
738
  documentRevision: null,
648
739
  lastError: nextError,
649
740
  });
741
+
650
742
  setPeers([]);
651
743
  callbacksRef.current.onError?.(nextError);
652
744
  }
745
+
653
746
  return () => {
654
747
  controllerRef.current?.destroy();
655
748
  controllerRef.current = null;
@@ -657,21 +750,24 @@ export function useYjsCollaboration(options: YjsCollaborationOptions): UseYjsCol
657
750
  // The controller owns one URL/handle binding. Connection intent is
658
751
  // updated independently below without recreating the observer.
659
752
  // eslint-disable-next-line react-hooks/exhaustive-deps
660
- }, [options.documentId, options.handle, stableTransportProtocolAdapter, transportUrl]);
753
+ }, [ options.documentId, options.handle, stableTransportProtocolAdapter, transportUrl ]);
661
754
 
662
755
  useEffect(() => {
663
756
  controllerRef.current?.applyLocalAwarenessOption(options.localAwareness);
664
757
  // eslint-disable-next-line react-hooks/exhaustive-deps
665
- }, [localAwarenessKey]);
758
+ }, [ localAwarenessKey ]);
666
759
 
667
760
  useEffect(() => {
668
- if (transportUrl === null) return;
761
+ if (transportUrl === null) {
762
+ return;
763
+ }
764
+
669
765
  if (transportConnect) {
670
766
  controllerRef.current?.connect();
671
767
  } else {
672
768
  controllerRef.current?.disconnect();
673
769
  }
674
- }, [transportConnect, stableTransportProtocolAdapter, transportUrl]);
770
+ }, [ transportConnect, stableTransportProtocolAdapter, transportUrl ]);
675
771
 
676
772
  return {
677
773
  state,
@@ -680,7 +776,7 @@ export function useYjsCollaboration(options: YjsCollaborationOptions): UseYjsCol
680
776
  connect: () => controllerRef.current?.connect(),
681
777
  disconnect: () => controllerRef.current?.disconnect(),
682
778
  reconnect: () => controllerRef.current?.reconnect(),
683
- updateLocalAwareness: (partial) => controllerRef.current?.updateLocalAwareness(partial),
779
+ updateLocalAwareness: partial => controllerRef.current?.updateLocalAwareness(partial),
684
780
  editorBindings: {
685
781
  documentHandle: options.handle,
686
782
  documentRevision: state.documentRevision,
package/src/addons.ts CHANGED
@@ -40,7 +40,7 @@ export interface MentionQueryChangeEvent {
40
40
  anchor: number;
41
41
  head: number;
42
42
  };
43
- /** False once the query closed supply an empty suggestion list. */
43
+ /** False once the query closed : supply an empty suggestion list. */
44
44
  isActive: boolean;
45
45
  /** Engine document revision the query was computed against. */
46
46
  documentVersion?: string;
@@ -64,7 +64,7 @@ export interface MentionSelectEvent {
64
64
  export interface MentionSelectionAttrsEvent {
65
65
  trigger: string;
66
66
  suggestion: MentionSuggestion;
67
- /** Attributes resolved so far the suggestion's own, plus `label` and `mentionSuggestionChar`. */
67
+ /** Attributes resolved so far : the suggestion's own, plus `label` and `mentionSuggestionChar`. */
68
68
  attrs: Record<string, unknown>;
69
69
  /** Mark attributes active at the insertion point. */
70
70
  markAttrs: ReadonlyActiveState['markAttrs'];
@@ -83,7 +83,7 @@ export type MentionThemeResolveEvent = MentionSelectionAttrsEvent;
83
83
  /**
84
84
  * Mentions for `RichTextEditor`. The schema belongs to the document
85
85
  * handle, so create the handle with {@link withMentionsSchema} applied to
86
- * your schema this config alone does not add the `mention` node.
86
+ * your schema : this config alone does not add the `mention` node.
87
87
  * (`RichTextViewer` adds it for you.)
88
88
  *
89
89
  * The host owns the suggestion list: react to `onQueryChange` by filtering
@@ -218,14 +218,15 @@ export function mentionNodeSpec(): NodeSpec {
218
218
  * ```
219
219
  */
220
220
  export function withMentionsSchema(schema: SchemaDefinition): SchemaDefinition {
221
- const hasMentionNode = schema.nodes.some((node) => node.name === MENTION_NODE_NAME);
221
+ const hasMentionNode = schema.nodes.some(node => node.name === MENTION_NODE_NAME);
222
+
222
223
  if (hasMentionNode) {
223
224
  return schema;
224
225
  }
225
226
 
226
227
  return {
227
228
  ...schema,
228
- nodes: [...schema.nodes, mentionNodeSpec()],
229
+ nodes: [ ...schema.nodes, mentionNodeSpec() ],
229
230
  };
230
231
  }
231
232
 
@@ -237,8 +238,10 @@ export function normalizeNativeEditorAddons(
237
238
  }
238
239
 
239
240
  const trigger = addons.mentions.trigger?.trim() || DEFAULT_MENTION_TRIGGER;
240
- const suggestions = (addons.mentions.suggestions ?? []).map((suggestion) => {
241
+
242
+ const suggestions = (addons.mentions.suggestions ?? []).map(suggestion => {
241
243
  const label = suggestion.label?.trim() || suggestion.title;
244
+
242
245
  const attrs = {
243
246
  label,
244
247
  mentionSuggestionChar: trigger,
@@ -276,6 +279,7 @@ export function serializeNormalizedEditorAddons(
276
279
  addons?: NormalizedEditorAddons
277
280
  ): string | undefined {
278
281
  const normalized = normalizeNativeEditorAddons(addons);
282
+
279
283
  return normalized ? JSON.stringify(normalized) : undefined;
280
284
  }
281
285
 
@@ -288,7 +292,7 @@ export interface MentionFragmentOptions {
288
292
  /**
289
293
  * Build a document fragment holding one mention node, ready for
290
294
  * `insertContentJson`. Use it to insert a mention outside the suggestion
291
- * flow from a picker, say.
295
+ * flow : from a picker, say.
292
296
  *
293
297
  * @param attrs Attributes for the mention node, e.g. `{ label, id }`.
294
298
  * @param descriptor Document node name to wrap the fragment in. Defaults to `doc`.
@@ -304,7 +308,7 @@ export function buildMentionFragmentJson(
304
308
  type: MENTION_NODE_NAME,
305
309
  attrs,
306
310
  },
307
- ...(options?.trailingSpace ? [{ type: 'text', text: ' ' }] : []),
311
+ ...(options?.trailingSpace ? [ { type: 'text', text: ' ' } ] : []),
308
312
  ],
309
313
  descriptor
310
314
  );
@@ -56,8 +56,10 @@ export function collectAtomInstanceBlocks(
56
56
  const instanceBlocks: AtomInstance[][] = [];
57
57
  const instances: AtomInstance[] = [];
58
58
  let hasOnlyStableKeys = true;
59
+
59
60
  for (const block of renderBlocks) {
60
61
  const blockInstances: AtomInstance[] = [];
62
+
61
63
  for (const element of block) {
62
64
  if (
63
65
  element.type !== 'voidBlock' ||
@@ -66,28 +68,35 @@ export function collectAtomInstanceBlocks(
66
68
  ) {
67
69
  continue;
68
70
  }
71
+
69
72
  const occurrence = occurrences.get(element.nodeType) ?? 0;
70
73
  occurrences.set(element.nodeType, occurrence + 1);
74
+
71
75
  if (
72
76
  !registeredTypes.has(element.nodeType) &&
73
77
  NATIVE_VOID_BLOCK_TYPES.has(element.nodeType)
74
78
  ) {
75
79
  continue;
76
80
  }
81
+
77
82
  const hasStableKey = typeof element.atomId === 'string';
83
+
78
84
  const instance = {
79
- key: hasStableKey ? element.atomId! : `${element.nodeType}:${occurrence}`,
85
+ key: hasStableKey ? element.atomId : `${element.nodeType}:${occurrence}`,
80
86
  hasStableKey,
81
87
  nodeType: element.nodeType,
82
88
  attrs: element.attrs ?? EMPTY_ATOM_ATTRS,
83
89
  docPos: element.docPos,
84
90
  };
91
+
85
92
  hasOnlyStableKeys &&= hasStableKey;
86
93
  blockInstances.push(instance);
87
94
  instances.push(instance);
88
95
  }
96
+
89
97
  instanceBlocks.push(blockInstances);
90
98
  }
99
+
91
100
  return { instanceBlocks, instances, hasOnlyStableKeys };
92
101
  }
93
102
 
@@ -114,10 +123,21 @@ export function applyRenderPatch<Element>(
114
123
  }
115
124
 
116
125
  export function atomSelected(selection: Selection, docPos: number): boolean {
117
- if (selection.type === 'all') return true;
118
- if (selection.type === 'node') return selection.pos === docPos;
126
+ if (selection.type === 'all') {
127
+ return true;
128
+ }
129
+
130
+ if (selection.type === 'node') {
131
+ return selection.pos === docPos;
132
+ }
133
+
119
134
  const { anchor, head } = selection;
120
- if (anchor == null || head == null) return false;
121
- const [from, to] = anchor <= head ? [anchor, head] : [head, anchor];
135
+
136
+ if (anchor == null || head == null) {
137
+ return false;
138
+ }
139
+
140
+ const [ from, to ] = anchor <= head ? [ anchor, head ] : [ head, anchor ];
141
+
122
142
  return from <= docPos && docPos + 1 <= to;
123
143
  }