@apollohg/react-native-rich-text-editor 2.0.0 → 2.0.2

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 (180) hide show
  1. package/README.md +8 -116
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/android/build.gradle +3 -0
  4. package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +25 -0
  5. package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +1 -0
  6. package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +101 -33
  7. package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +15 -3
  8. package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +14 -0
  9. package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1 -1
  10. package/app.plugin.js +42 -39
  11. package/dist/AtomHost.js +18 -11
  12. package/dist/EditorAddon.js +4 -2
  13. package/dist/EditorAddonNormalization.js +17 -9
  14. package/dist/EditorMentionThemeNormalization.js +6 -3
  15. package/dist/EditorMentionThemeValidation.js +28 -11
  16. package/dist/EditorStyleSheetNormalization.js +85 -38
  17. package/dist/EditorStyleSheetTypes.d.ts +1 -1
  18. package/dist/EditorTheme.d.ts +4 -0
  19. package/dist/EditorTheme.js +5 -3
  20. package/dist/EditorToolbarComponent.d.ts +1 -1
  21. package/dist/EditorToolbarComponent.js +1 -1
  22. package/dist/EditorToolbarRegistry.js +5 -5
  23. package/dist/EditorToolbarTypes.d.ts +12 -12
  24. package/dist/EditorToolbarVisuals.js +1 -1
  25. package/dist/ExternalTextComposition.js +89 -43
  26. package/dist/ImageLoadingPolicy.d.ts +1 -1
  27. package/dist/NativeEditorBoundaryError.d.ts +7 -7
  28. package/dist/NativeEditorBoundaryError.js +9 -5
  29. package/dist/NativeEditorCollaborationTransport.d.ts +7 -7
  30. package/dist/NativeEditorCollaborationTransport.js +28 -15
  31. package/dist/NativeEditorCreateConfig.js +11 -6
  32. package/dist/NativeEditorCreateJson.js +51 -25
  33. package/dist/NativeEditorCreateValidation.js +11 -6
  34. package/dist/NativeEditorDocumentBridge.d.ts +1 -1
  35. package/dist/NativeEditorDocumentBridge.js +32 -18
  36. package/dist/NativeEditorDocumentHandle.d.ts +4 -4
  37. package/dist/NativeEditorDocumentHandle.js +1 -1
  38. package/dist/NativeEditorLocalAwareness.js +25 -13
  39. package/dist/NativeEditorNativeModule.d.ts +8 -1
  40. package/dist/NativeEditorNativeModule.js +7 -5
  41. package/dist/NativeEditorRenderNormalization.js +66 -25
  42. package/dist/NativeEditorResultNormalization.js +53 -29
  43. package/dist/NativeEditorTypes.d.ts +10 -10
  44. package/dist/NativeEditorV2Decimal.js +6 -3
  45. package/dist/NativeProseViewer.js +11 -3
  46. package/dist/ResourceLimits.d.ts +2 -2
  47. package/dist/ResourceLimits.js +7 -2
  48. package/dist/RichTextEditor.js +1 -1
  49. package/dist/RichTextEditorNativeTypes.d.ts +1 -1
  50. package/dist/RichTextEditorNativeView.d.ts +1 -3
  51. package/dist/RichTextEditorSerialization.js +24 -14
  52. package/dist/RichTextEditorTypes.d.ts +1 -1
  53. package/dist/YjsCollaboration.d.ts +7 -7
  54. package/dist/YjsCollaboration.js +42 -26
  55. package/dist/addons.d.ts +4 -4
  56. package/dist/addons.js +3 -3
  57. package/dist/atomInstances.js +6 -3
  58. package/dist/atomUpdates.js +7 -4
  59. package/dist/atoms.js +20 -12
  60. package/dist/attributeValidation.js +55 -24
  61. package/dist/contentExpression.js +80 -42
  62. package/dist/listMarkers.d.ts +1 -1
  63. package/dist/listMarkers.js +1 -1
  64. package/dist/schemaDefinition.d.ts +1 -1
  65. package/dist/schemaDefinition.js +23 -14
  66. package/dist/schemaDocument.js +1 -1
  67. package/dist/schemaDocumentConstruction.js +25 -14
  68. package/dist/schemaNormalization.js +51 -29
  69. package/dist/schemaPresets.d.ts +1 -1
  70. package/dist/schemaPresets.js +9 -4
  71. package/dist/schemaResolution.js +46 -25
  72. package/dist/specs/PreparedProseViewerNativeComponent.d.ts +4 -0
  73. package/dist/useEditorToolbarInteractions.d.ts +2 -2
  74. package/dist/useEditorToolbarInteractions.js +45 -23
  75. package/dist/useEditorToolbarItems.d.ts +1 -1
  76. package/dist/useEditorToolbarItems.js +4 -3
  77. package/dist/useEditorToolbarPresentation.d.ts +3 -3
  78. package/dist/useEditorToolbarPresentation.js +4 -4
  79. package/dist/useEditorToolbarState.d.ts +1 -1
  80. package/dist/useEditorToolbarState.js +1 -1
  81. package/dist/useFocusPreservingFrames.js +11 -8
  82. package/dist/useNativeEditor.d.ts +1 -1
  83. package/dist/useNativeEditor.js +21 -11
  84. package/dist/useRichTextEditorCommands.d.ts +2 -2
  85. package/dist/useRichTextEditorCommands.js +59 -29
  86. package/dist/useRichTextEditorEvents.d.ts +3 -3
  87. package/dist/useRichTextEditorEvents.js +75 -32
  88. package/dist/useRichTextEditorMentions.d.ts +4 -4
  89. package/dist/useRichTextEditorMentions.js +48 -29
  90. package/dist/useRichTextEditorPresentation.d.ts +4 -4
  91. package/dist/useRichTextEditorPresentation.js +25 -23
  92. package/dist/useRichTextEditorState.d.ts +3 -3
  93. package/dist/useRichTextEditorState.js +20 -15
  94. package/dist/useRichTextEditorUpdates.d.ts +1 -1
  95. package/dist/useRichTextEditorUpdates.js +99 -41
  96. package/dist/useViewerAtoms.js +63 -36
  97. package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
  98. package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
  99. package/ios/EditorInteractionOverlays.swift +114 -122
  100. package/ios/EditorLayoutManager.swift +13 -3
  101. package/ios/EditorStyleSheet.swift +12 -1
  102. package/ios/EditorTextView+Images.swift +32 -10
  103. package/ios/EditorTextView.swift +1 -1
  104. package/ios/EditorV2Adapter+RenderValidation.swift +2 -2
  105. package/ios/NativeToolbar+ButtonStyle.swift +17 -5
  106. package/ios/NativeToolbar+Diagnostics.swift +15 -0
  107. package/ios/RenderBridge+Atoms.swift +0 -1
  108. package/ios/RenderBridge+Attachments.swift +0 -29
  109. package/ios/RichTextEditorView.swift +4 -12
  110. package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +3 -2
  111. package/package.json +11 -3
  112. package/react-native.config.js +1 -1
  113. package/rust/android/arm64-v8a/libeditor_core.so +0 -0
  114. package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
  115. package/rust/android/x86/libeditor_core.so +0 -0
  116. package/rust/android/x86_64/libeditor_core.so +0 -0
  117. package/src/AtomHost.tsx +58 -28
  118. package/src/EditorAddon.ts +18 -8
  119. package/src/EditorAddonNormalization.ts +40 -11
  120. package/src/EditorMentionThemeNormalization.ts +15 -4
  121. package/src/EditorMentionThemeValidation.ts +51 -14
  122. package/src/EditorStyleSheet.ts +1 -0
  123. package/src/EditorStyleSheetNormalization.ts +195 -64
  124. package/src/EditorStyleSheetTypes.ts +1 -1
  125. package/src/EditorTheme.ts +13 -5
  126. package/src/EditorToolbarComponent.tsx +2 -1
  127. package/src/EditorToolbarRegistry.tsx +23 -13
  128. package/src/EditorToolbarTypes.tsx +12 -12
  129. package/src/EditorToolbarVisuals.tsx +6 -3
  130. package/src/EditorUpdateRevision.ts +1 -0
  131. package/src/ExternalTextComposition.ts +259 -49
  132. package/src/ImageLoadingPolicy.ts +3 -1
  133. package/src/NativeEditorBoundaryError.ts +38 -11
  134. package/src/NativeEditorCollaborationTransport.ts +108 -27
  135. package/src/NativeEditorCreateConfig.ts +92 -19
  136. package/src/NativeEditorCreateJson.ts +163 -26
  137. package/src/NativeEditorCreateValidation.ts +33 -12
  138. package/src/NativeEditorDocumentBridge.ts +111 -19
  139. package/src/NativeEditorDocumentHandle.ts +9 -4
  140. package/src/NativeEditorLocalAwareness.ts +70 -15
  141. package/src/NativeEditorNativeModule.ts +13 -9
  142. package/src/NativeEditorRenderNormalization.ts +128 -29
  143. package/src/NativeEditorResultNormalization.ts +143 -32
  144. package/src/NativeEditorTypes.ts +12 -11
  145. package/src/NativeEditorV2Decimal.ts +12 -3
  146. package/src/NativeProseViewer.tsx +46 -16
  147. package/src/ResourceLimits.ts +15 -8
  148. package/src/RichTextEditor.tsx +3 -1
  149. package/src/RichTextEditorNativeTypes.tsx +1 -1
  150. package/src/RichTextEditorNativeView.tsx +2 -8
  151. package/src/RichTextEditorSerialization.tsx +70 -24
  152. package/src/RichTextEditorTypes.tsx +1 -1
  153. package/src/YjsCollaboration.ts +143 -47
  154. package/src/addons.ts +12 -8
  155. package/src/atomInstances.ts +25 -5
  156. package/src/atomUpdates.ts +23 -6
  157. package/src/atoms.ts +85 -18
  158. package/src/attributeValidation.ts +97 -33
  159. package/src/contentExpression.ts +218 -63
  160. package/src/listMarkers.ts +1 -1
  161. package/src/schemaDefinition.ts +82 -20
  162. package/src/schemaDocument.ts +15 -1
  163. package/src/schemaDocumentConstruction.ts +80 -16
  164. package/src/schemaNormalization.ts +155 -41
  165. package/src/schemaPresets.ts +37 -31
  166. package/src/schemaResolution.ts +126 -27
  167. package/src/specs/PreparedProseViewerNativeComponent.ts +4 -2
  168. package/src/useEditorToolbarInteractions.tsx +85 -36
  169. package/src/useEditorToolbarItems.tsx +50 -22
  170. package/src/useEditorToolbarPresentation.tsx +81 -58
  171. package/src/useEditorToolbarState.tsx +8 -6
  172. package/src/useFocusPreservingFrames.ts +43 -15
  173. package/src/useNativeEditor.ts +111 -51
  174. package/src/useRichTextEditorCommands.tsx +127 -54
  175. package/src/useRichTextEditorEvents.tsx +144 -42
  176. package/src/useRichTextEditorMentions.tsx +118 -37
  177. package/src/useRichTextEditorPresentation.tsx +128 -114
  178. package/src/useRichTextEditorState.tsx +76 -40
  179. package/src/useRichTextEditorUpdates.tsx +207 -56
  180. package/src/useViewerAtoms.tsx +162 -66
@@ -77,6 +77,7 @@ let nextExternalCompositionId = 1n;
77
77
  function allocateExternalCompositionId(): string {
78
78
  const id = nextExternalCompositionId.toString(10);
79
79
  nextExternalCompositionId += 1n;
80
+
80
81
  return id;
81
82
  }
82
83
 
@@ -122,7 +123,8 @@ function isRecord(value: unknown): value is Record<string, unknown> {
122
123
 
123
124
  function hasExactKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
124
125
  const actual = Object.keys(value);
125
- return actual.length === keys.length && actual.every((key) => keys.includes(key));
126
+
127
+ return actual.length === keys.length && actual.every(key => keys.includes(key));
126
128
  }
127
129
 
128
130
  const ERROR_KEYS = [
@@ -137,42 +139,63 @@ const ERROR_KEYS = [
137
139
  ] as const;
138
140
 
139
141
  function parseError(value: unknown): NativeEditorError | null {
140
- if (!isRecord(value) || !hasExactKeys(value, ERROR_KEYS)) return null;
142
+ if (!isRecord(value) || !hasExactKeys(value, ERROR_KEYS)) {
143
+ return null;
144
+ }
145
+
141
146
  return normalizeNativeEditorV2Error({ error: value });
142
147
  }
143
148
 
144
149
  function parseNativeCompositionResult(resultJson: string): NativeCompositionResult {
145
150
  let value: unknown;
151
+
146
152
  try {
147
153
  value = JSON.parse(resultJson);
148
154
  } catch {
149
155
  throw invalidResultError();
150
156
  }
157
+
151
158
  if (!isRecord(value) || value.version !== 1 || typeof value.type !== 'string') {
152
159
  throw invalidResultError();
153
160
  }
154
161
 
155
162
  if (value.type === 'active') {
156
163
  if (
157
- !hasExactKeys(value, ['version', 'type', 'sessionId']) ||
164
+ !hasExactKeys(value, [ 'version', 'type', 'sessionId' ]) ||
158
165
  typeof value.sessionId !== 'string'
159
166
  ) {
160
167
  throw invalidResultError();
161
168
  }
169
+
162
170
  return { version: 1, type: 'active', sessionId: value.sessionId };
163
171
  }
164
172
 
165
173
  if (value.type === 'ended') {
166
174
  const keys = Object.prototype.hasOwnProperty.call(value, 'error')
167
- ? ['version', 'type', 'sessionId', 'outcome', 'cause', 'text', 'error']
168
- : ['version', 'type', 'sessionId', 'outcome', 'cause', 'text'];
175
+ ? [ 'version',
176
+ 'type',
177
+ 'sessionId',
178
+ 'outcome',
179
+ 'cause',
180
+ 'text',
181
+ 'error' ]
182
+ : [ 'version',
183
+ 'type',
184
+ 'sessionId',
185
+ 'outcome',
186
+ 'cause',
187
+ 'text' ];
188
+
169
189
  const validOutcome = value.outcome === 'committed' || value.outcome === 'cancelled';
190
+
170
191
  const validCause =
171
192
  value.cause === 'consumer' ||
172
193
  value.cause === 'interaction' ||
173
194
  value.cause === 'documentChange' ||
174
195
  value.cause === 'lifecycle';
196
+
175
197
  const error = keys.includes('error') ? parseError(value.error) : undefined;
198
+
176
199
  if (
177
200
  !hasExactKeys(value, keys) ||
178
201
  typeof value.sessionId !== 'string' ||
@@ -183,6 +206,7 @@ function parseNativeCompositionResult(resultJson: string): NativeCompositionResu
183
206
  ) {
184
207
  throw invalidResultError();
185
208
  }
209
+
186
210
  return {
187
211
  version: 1,
188
212
  type: 'ended',
@@ -196,13 +220,15 @@ function parseNativeCompositionResult(resultJson: string): NativeCompositionResu
196
220
 
197
221
  if (value.type === 'error') {
198
222
  const error = parseError(value.error);
223
+
199
224
  if (
200
- !hasExactKeys(value, ['version', 'type', 'sessionId', 'error']) ||
225
+ !hasExactKeys(value, [ 'version', 'type', 'sessionId', 'error' ]) ||
201
226
  (value.sessionId !== null && typeof value.sessionId !== 'string') ||
202
227
  error == null
203
228
  ) {
204
229
  throw invalidResultError();
205
230
  }
231
+
206
232
  return { version: 1, type: 'error', sessionId: value.sessionId, error };
207
233
  }
208
234
 
@@ -214,8 +240,10 @@ function requireActiveResult(result: NativeCompositionResult, sessionId: string)
214
240
  if (result.sessionId !== null && result.sessionId !== sessionId) {
215
241
  throw invalidResultError();
216
242
  }
243
+
217
244
  throw nativeEditorV2ErrorToException(result.error);
218
245
  }
246
+
219
247
  if (result.type !== 'active' || result.sessionId !== sessionId) {
220
248
  throw invalidResultError();
221
249
  }
@@ -229,141 +257,227 @@ function requireEndResult(
229
257
  if (result.sessionId !== null && result.sessionId !== sessionId) {
230
258
  throw invalidResultError();
231
259
  }
260
+
232
261
  throw nativeEditorV2ErrorToException(result.error);
233
262
  }
263
+
234
264
  if (result.type !== 'ended' || result.sessionId !== sessionId) {
235
265
  throw invalidResultError();
236
266
  }
267
+
237
268
  return result;
238
269
  }
239
270
 
240
271
  function settleWaiters(waiters: Waiter[], error?: unknown): void {
241
272
  for (const waiter of waiters) {
242
- if (error === undefined) waiter.resolve();
243
- else waiter.reject(error);
273
+ if (error === undefined) {
274
+ waiter.resolve();
275
+ } else {
276
+ waiter.reject(error);
277
+ }
244
278
  }
245
279
  }
246
280
 
247
281
  class ManagedExternalTextCompositionSession implements ExternalTextCompositionSession {
248
282
  latestText = '';
283
+
249
284
  private terminal = false;
285
+
250
286
  private terminalResult: NativeCompositionEndResult | null = null;
287
+
251
288
  private terminalError: NativeEditorErrorBase | null = null;
289
+
252
290
  private resolveTerminal!: (result: NativeCompositionEndResult) => void;
253
- private readonly terminalCompletion = new Promise<NativeCompositionEndResult>((resolve) => {
291
+
292
+ private readonly terminalCompletion = new Promise<NativeCompositionEndResult>(resolve => {
254
293
  this.resolveTerminal = resolve;
255
294
  });
295
+
256
296
  private closing: 'commit' | 'cancel' | null = null;
297
+
257
298
  private updateInFlight: Promise<void> | null = null;
299
+
258
300
  private inFlightWaiters: Waiter[] = [];
301
+
259
302
  private pendingUpdate: PendingUpdate | null = null;
303
+
260
304
  private cancelPromise: Promise<void> | null = null;
261
305
 
262
306
  constructor(
263
307
  readonly id: string,
264
308
  private readonly host: ManagedExternalTextCompositionHost,
265
309
  private readonly options: ExternalTextCompositionOptions
266
- ) {}
310
+ ) {
311
+ }
267
312
 
268
313
  update(text: string): Promise<void> {
269
- if (!this.canMutate()) return Promise.reject(endedLifecycleError());
314
+ if (!this.canMutate()) {
315
+ return Promise.reject(endedLifecycleError());
316
+ }
317
+
270
318
  this.latestText = text;
319
+
271
320
  return new Promise<void>((resolve, reject) => {
272
321
  const waiter = { resolve, reject };
322
+
273
323
  if (this.updateInFlight != null) {
274
324
  if (this.pendingUpdate == null) {
275
- this.pendingUpdate = { text, waiters: [waiter] };
325
+ this.pendingUpdate = { text, waiters: [ waiter ] };
276
326
  } else {
277
327
  this.pendingUpdate.text = text;
278
328
  this.pendingUpdate.waiters.push(waiter);
279
329
  }
330
+
280
331
  return;
281
332
  }
282
- this.startUpdate(text, [waiter]);
333
+
334
+ this.startUpdate(text, [ waiter ]);
283
335
  });
284
336
  }
285
337
 
286
338
  async commit(finalText: string): Promise<void> {
287
- if (!this.canMutate()) throw endedLifecycleError();
339
+ if (!this.canMutate()) {
340
+ throw endedLifecycleError();
341
+ }
342
+
288
343
  this.closing = 'commit';
289
344
  this.latestText = finalText;
345
+
290
346
  if (this.updateInFlight != null) {
291
347
  const update = await this.raceWithTerminal(this.updateInFlight);
348
+
292
349
  if (update.type === 'terminal') {
293
350
  this.requireSuccessfulConsumerCommit(update.result, finalText);
351
+
294
352
  return;
295
353
  }
296
- if (update.type === 'failed') throw update.error;
354
+
355
+ if (update.type === 'failed') {
356
+ throw update.error;
357
+ }
297
358
  }
359
+
298
360
  if (this.terminalResult != null) {
299
361
  this.requireSuccessfulConsumerCommit(this.terminalResult, finalText);
362
+
300
363
  return;
301
364
  }
302
- if (!this.canFinish()) throw endedLifecycleError();
365
+
366
+ if (!this.canFinish()) {
367
+ throw endedLifecycleError();
368
+ }
303
369
 
304
370
  const foldedWaiters = this.takePendingWaiters();
305
371
  const commit = await this.raceWithTerminal(this.host.commit(this.id, finalText));
372
+
306
373
  if (commit.type === 'terminal') {
307
374
  const error = this.explicitCommitError(commit.result, finalText);
308
375
  settleWaiters(foldedWaiters, error);
309
- if (error) throw error;
376
+
377
+ if (error) {
378
+ throw error;
379
+ }
380
+
310
381
  return;
311
382
  }
383
+
312
384
  if (commit.type === 'failed') {
313
385
  settleWaiters(foldedWaiters, commit.error);
314
- if (!this.terminal && this.host.owns(this)) this.closing = null;
386
+
387
+ if (!this.terminal && this.host.owns(this)) {
388
+ this.closing = null;
389
+ }
390
+
315
391
  throw commit.error;
316
392
  }
393
+
317
394
  try {
318
395
  this.requireOwnership();
319
396
  const endResult = requireEndResult(commit.value, this.id);
320
397
  const endError = this.explicitCommitError(endResult, finalText);
321
398
  this.finish(endResult);
322
399
  settleWaiters(foldedWaiters, endError);
323
- if (endError) throw endError;
400
+
401
+ if (endError) {
402
+ throw endError;
403
+ }
324
404
  } catch (error) {
325
405
  settleWaiters(foldedWaiters, error);
326
- if (!this.terminal && this.host.owns(this)) this.closing = null;
406
+
407
+ if (!this.terminal && this.host.owns(this)) {
408
+ this.closing = null;
409
+ }
410
+
327
411
  throw error;
328
412
  }
329
413
  }
330
414
 
331
415
  cancel(): Promise<void> {
332
- if (this.terminal) return Promise.resolve();
333
- if (this.cancelPromise != null) return this.cancelPromise;
334
- if (!this.canMutate()) return Promise.reject(endedLifecycleError());
416
+ if (this.terminal) {
417
+ return Promise.resolve();
418
+ }
419
+
420
+ if (this.cancelPromise != null) {
421
+ return this.cancelPromise;
422
+ }
423
+
424
+ if (!this.canMutate()) {
425
+ return Promise.reject(endedLifecycleError());
426
+ }
427
+
335
428
  this.cancelPromise = this.cancelWithCause('consumer');
429
+
336
430
  return this.cancelPromise;
337
431
  }
338
432
 
339
433
  cancelForDocumentChange(): Promise<void> {
340
- if (this.terminal) return Promise.resolve();
341
- if (this.cancelPromise != null) return this.cancelPromise;
342
- if (!this.canMutate()) return Promise.reject(endedLifecycleError());
434
+ if (this.terminal) {
435
+ return Promise.resolve();
436
+ }
437
+
438
+ if (this.cancelPromise != null) {
439
+ return this.cancelPromise;
440
+ }
441
+
442
+ if (!this.canMutate()) {
443
+ return Promise.reject(endedLifecycleError());
444
+ }
445
+
343
446
  this.cancelPromise = this.cancelWithCause('documentChange');
447
+
344
448
  return this.cancelPromise;
345
449
  }
346
450
 
347
451
  finish(result: NativeCompositionEndResult): void {
348
- if (this.terminal) return;
452
+ if (this.terminal) {
453
+ return;
454
+ }
455
+
349
456
  this.terminal = true;
350
457
  this.terminalResult = result;
458
+
351
459
  const error = result.error
352
460
  ? nativeEditorV2ErrorToException(result.error)
353
461
  : endedLifecycleError();
462
+
354
463
  this.terminalError = error;
464
+
355
465
  const waiterError =
356
466
  this.closing === 'commit' && this.explicitCommitError(result, this.latestText) == null
357
467
  ? undefined
358
468
  : error;
469
+
359
470
  settleWaiters(this.inFlightWaiters, waiterError);
360
471
  this.inFlightWaiters = [];
472
+
361
473
  if (this.pendingUpdate != null) {
362
474
  settleWaiters(this.pendingUpdate.waiters, waiterError);
363
475
  this.pendingUpdate = null;
364
476
  }
477
+
365
478
  this.host.release(this);
366
479
  this.resolveTerminal(result);
480
+
367
481
  this.options.onEnd?.({
368
482
  outcome: result.outcome,
369
483
  cause: result.cause,
@@ -394,8 +508,12 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
394
508
  } catch (error) {
395
509
  settleWaiters(waiters, error);
396
510
  } finally {
397
- if (this.inFlightWaiters === waiters) this.inFlightWaiters = [];
511
+ if (this.inFlightWaiters === waiters) {
512
+ this.inFlightWaiters = [];
513
+ }
514
+
398
515
  this.updateInFlight = null;
516
+
399
517
  if (!this.terminal && this.closing == null && this.pendingUpdate != null) {
400
518
  const pending = this.pendingUpdate;
401
519
  this.pendingUpdate = null;
@@ -407,20 +525,23 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
407
525
  private takePendingWaiters(): Waiter[] {
408
526
  const waiters = this.pendingUpdate?.waiters ?? [];
409
527
  this.pendingUpdate = null;
528
+
410
529
  return waiters;
411
530
  }
412
531
 
413
532
  private requireOwnership(): void {
414
- if (!this.host.owns(this)) throw endedLifecycleError();
533
+ if (!this.host.owns(this)) {
534
+ throw endedLifecycleError();
535
+ }
415
536
  }
416
537
 
417
538
  private raceWithTerminal<T>(work: Promise<T>): Promise<TerminalRace<T>> {
418
539
  return Promise.race([
419
540
  work.then<TerminalRace<T>, TerminalRace<T>>(
420
- (value) => ({ type: 'completed', value }),
541
+ value => ({ type: 'completed', value }),
421
542
  (error: unknown) => ({ type: 'failed', error })
422
543
  ),
423
- this.terminalCompletion.then<TerminalRace<T>>((result) => ({
544
+ this.terminalCompletion.then<TerminalRace<T>>(result => ({
424
545
  type: 'terminal',
425
546
  result,
426
547
  })),
@@ -439,6 +560,7 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
439
560
  ) {
440
561
  return undefined;
441
562
  }
563
+
442
564
  return result.error ? nativeEditorV2ErrorToException(result.error) : endedLifecycleError();
443
565
  }
444
566
 
@@ -447,7 +569,10 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
447
569
  finalText: string
448
570
  ): void {
449
571
  const error = this.explicitCommitError(result, finalText);
450
- if (error) throw error;
572
+
573
+ if (error) {
574
+ throw error;
575
+ }
451
576
  }
452
577
 
453
578
  private settleTerminalWaiters(waiters: Waiter[]): void {
@@ -456,41 +581,65 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
456
581
 
457
582
  private async cancelWithCause(cause: 'consumer' | 'documentChange'): Promise<void> {
458
583
  this.closing = 'cancel';
584
+
459
585
  if (this.updateInFlight != null) {
460
586
  const update = await this.raceWithTerminal(this.updateInFlight);
461
- if (update.type === 'terminal') return;
462
- if (update.type === 'failed') throw update.error;
587
+
588
+ if (update.type === 'terminal') {
589
+ return;
590
+ }
591
+
592
+ if (update.type === 'failed') {
593
+ throw update.error;
594
+ }
463
595
  }
464
- if (!this.canFinish()) return;
596
+
597
+ if (!this.canFinish()) {
598
+ return;
599
+ }
600
+
465
601
  const pendingWaiters = this.takePendingWaiters();
466
602
  const cancellation = await this.raceWithTerminal(this.host.cancel(this.id, cause));
603
+
467
604
  if (cancellation.type === 'terminal') {
468
605
  this.settleTerminalWaiters(pendingWaiters);
606
+
469
607
  return;
470
608
  }
609
+
471
610
  if (cancellation.type === 'failed') {
472
611
  settleWaiters(pendingWaiters, cancellation.error);
612
+
473
613
  if (this.host.owns(this)) {
474
614
  this.closing = null;
475
615
  this.cancelPromise = null;
476
616
  }
617
+
477
618
  throw cancellation.error;
478
619
  }
620
+
479
621
  try {
480
622
  this.requireOwnership();
481
623
  const endResult = requireEndResult(cancellation.value, this.id);
624
+
482
625
  const endError = endResult.error
483
626
  ? nativeEditorV2ErrorToException(endResult.error)
484
627
  : undefined;
628
+
485
629
  this.finish(endResult);
486
630
  settleWaiters(pendingWaiters, endError);
487
- if (endError) throw endError;
631
+
632
+ if (endError) {
633
+ throw endError;
634
+ }
488
635
  } catch (error) {
489
636
  settleWaiters(pendingWaiters, error);
637
+
490
638
  if (!this.terminal && this.host.owns(this)) {
491
639
  this.closing = null;
492
640
  this.cancelPromise = null;
493
641
  }
642
+
494
643
  throw error;
495
644
  }
496
645
  }
@@ -498,14 +647,21 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
498
647
 
499
648
  export class ExternalTextCompositionManager {
500
649
  private active: ManagedExternalTextCompositionSession | null = null;
650
+
501
651
  private pendingBegin: ManagedExternalTextCompositionSession | null = null;
652
+
502
653
  private pendingEnd: NativeCompositionEndResult | null = null;
654
+
503
655
  private disposed = false;
656
+
504
657
  private beginTail: Promise<void> = Promise.resolve();
658
+
505
659
  private readonly sessionHost: ManagedExternalTextCompositionHost = {
506
- owns: (session) => !this.disposed && this.active === session,
507
- release: (session) => {
508
- if (this.active === session) this.active = null;
660
+ owns: session => !this.disposed && this.active === session,
661
+ release: session => {
662
+ if (this.active === session) {
663
+ this.active = null;
664
+ }
509
665
  },
510
666
  update: (sessionId, text) => this.invokeUpdate(sessionId, text),
511
667
  commit: (sessionId, text) => this.invokeCommit(sessionId, text),
@@ -515,10 +671,12 @@ export class ExternalTextCompositionManager {
515
671
  constructor(
516
672
  private readonly editorId: string,
517
673
  private readonly getNativeHandle: () => NativeExternalTextCompositionHandle | null
518
- ) {}
674
+ ) {
675
+ }
519
676
 
520
677
  supports(): boolean {
521
678
  const handle = this.getNativeHandle();
679
+
522
680
  return (
523
681
  typeof handle?.beginExternalTextComposition === 'function' &&
524
682
  typeof handle.updateExternalTextComposition === 'function' &&
@@ -529,21 +687,32 @@ export class ExternalTextCompositionManager {
529
687
 
530
688
  begin(options: ExternalTextCompositionOptions = {}): Promise<ExternalTextCompositionSession> {
531
689
  const pending = this.beginTail.then(() => this.beginSerialized(options));
690
+
532
691
  this.beginTail = pending.then(
533
692
  () => undefined,
534
693
  () => undefined
535
694
  );
695
+
536
696
  return pending;
537
697
  }
538
698
 
539
699
  handleNativeEnd(editorId: string, resultJson: string): void {
540
- if (editorId !== this.editorId) return;
700
+ if (editorId !== this.editorId) {
701
+ return;
702
+ }
703
+
541
704
  const result = parseNativeCompositionResult(resultJson);
542
- if (result.type !== 'ended') return;
705
+
706
+ if (result.type !== 'ended') {
707
+ return;
708
+ }
709
+
543
710
  if (result.sessionId === this.active?.id) {
544
711
  this.active.finish(result);
712
+
545
713
  return;
546
714
  }
715
+
547
716
  if (result.sessionId === this.pendingBegin?.id && this.pendingEnd == null) {
548
717
  this.pendingEnd = result;
549
718
  this.pendingBegin.finish(result);
@@ -555,10 +724,17 @@ export class ExternalTextCompositionManager {
555
724
  }
556
725
 
557
726
  dispose(): void {
558
- if (this.disposed) return;
727
+ if (this.disposed) {
728
+ return;
729
+ }
730
+
559
731
  this.disposed = true;
560
732
  const active = this.active;
561
- if (!active) return;
733
+
734
+ if (!active) {
735
+ return;
736
+ }
737
+
562
738
  active.finish({
563
739
  version: 1,
564
740
  type: 'ended',
@@ -567,6 +743,7 @@ export class ExternalTextCompositionManager {
567
743
  cause: 'lifecycle',
568
744
  text: active.latestText,
569
745
  });
746
+
570
747
  void this.invokeCancel(active.id, 'lifecycle').catch(() => undefined);
571
748
  }
572
749
 
@@ -580,30 +757,39 @@ export class ExternalTextCompositionManager {
580
757
  private async invokeBegin(sessionId: string): Promise<NativeCompositionResult> {
581
758
  const handle = this.getNativeHandle();
582
759
  const method = handle?.beginExternalTextComposition;
760
+
583
761
  if (typeof method !== 'function') {
584
762
  throw this.unsupportedError();
585
763
  }
764
+
586
765
  const resultJson = await method.call(handle, sessionId);
766
+
587
767
  return parseNativeCompositionResult(resultJson);
588
768
  }
589
769
 
590
770
  private async invokeUpdate(sessionId: string, text: string): Promise<NativeCompositionResult> {
591
771
  const handle = this.getNativeHandle();
592
772
  const method = handle?.updateExternalTextComposition;
773
+
593
774
  if (typeof method !== 'function') {
594
775
  throw this.unsupportedError();
595
776
  }
777
+
596
778
  const resultJson = await method.call(handle, sessionId, text);
779
+
597
780
  return parseNativeCompositionResult(resultJson);
598
781
  }
599
782
 
600
783
  private async invokeCommit(sessionId: string, text: string): Promise<NativeCompositionResult> {
601
784
  const handle = this.getNativeHandle();
602
785
  const method = handle?.commitExternalTextComposition;
786
+
603
787
  if (typeof method !== 'function') {
604
788
  throw this.unsupportedError();
605
789
  }
790
+
606
791
  const resultJson = await method.call(handle, sessionId, text);
792
+
607
793
  return parseNativeCompositionResult(resultJson);
608
794
  }
609
795
 
@@ -613,40 +799,64 @@ export class ExternalTextCompositionManager {
613
799
  ): Promise<NativeCompositionResult> {
614
800
  const handle = this.getNativeHandle();
615
801
  const method = handle?.cancelExternalTextComposition;
802
+
616
803
  if (typeof method !== 'function') {
617
804
  throw this.unsupportedError();
618
805
  }
806
+
619
807
  const resultJson = await method.call(handle, sessionId, cause);
808
+
620
809
  return parseNativeCompositionResult(resultJson);
621
810
  }
622
811
 
623
812
  private async beginSerialized(
624
813
  options: ExternalTextCompositionOptions
625
814
  ): Promise<ExternalTextCompositionSession> {
626
- if (this.disposed) throw endedLifecycleError();
815
+ if (this.disposed) {
816
+ throw endedLifecycleError();
817
+ }
818
+
627
819
  if (!this.supports()) {
628
820
  throw this.unsupportedError();
629
821
  }
630
- if (this.active) await this.active.commit(this.active.latestText);
631
- if (this.disposed) throw endedLifecycleError();
822
+
823
+ if (this.active) {
824
+ await this.active.commit(this.active.latestText);
825
+ }
826
+
827
+ if (this.disposed) {
828
+ throw endedLifecycleError();
829
+ }
632
830
 
633
831
  const session = new ManagedExternalTextCompositionSession(
634
832
  allocateExternalCompositionId(),
635
833
  this.sessionHost,
636
834
  options
637
835
  );
836
+
638
837
  this.pendingBegin = session;
639
838
  this.pendingEnd = null;
839
+
640
840
  try {
641
841
  const result = await this.invokeBegin(session.id);
842
+
642
843
  if (this.disposed) {
643
844
  void this.invokeCancel(session.id, 'lifecycle').catch(() => undefined);
644
845
  throw endedLifecycleError();
645
846
  }
646
- if (this.pendingBegin !== session) throw endedLifecycleError();
847
+
848
+ if (this.pendingBegin !== session) {
849
+ throw endedLifecycleError();
850
+ }
851
+
647
852
  requireActiveResult(result, session.id);
648
- if (this.pendingEnd != null) throw endedLifecycleError();
853
+
854
+ if (this.pendingEnd != null) {
855
+ throw endedLifecycleError();
856
+ }
857
+
649
858
  this.active = session;
859
+
650
860
  return session;
651
861
  } finally {
652
862
  if (this.pendingBegin === session) {