@beyondwork/docx-react-component 1.0.33 → 1.0.35

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.
@@ -42,6 +42,8 @@ import {
42
42
  createCommandBridgePlugins,
43
43
  type CommandBridgeCallbacks,
44
44
  } from "./pm-command-bridge";
45
+ import { createCollabPlugins } from "./pm-collab-plugins";
46
+ import { prosemirrorToYXmlFragment } from "y-prosemirror";
45
47
  import { buildDecorations } from "./pm-decorations";
46
48
  import { createContextualInteractionPlugin } from "./pm-contextual-ui";
47
49
  import {
@@ -71,6 +73,8 @@ import type { MediaPreviewDescriptor } from "./pm-state-from-snapshot";
71
73
  */
72
74
  export interface TwProseMirrorSurfaceProps {
73
75
  currentUser: EditorUser;
76
+ ydoc?: import("yjs").Doc;
77
+ awareness?: import("y-protocols/awareness").Awareness;
74
78
  snapshot: RuntimeRenderSnapshot;
75
79
  canonicalDocument: CanonicalDocumentEnvelope;
76
80
  documentNavigation: DocumentNavigationSnapshot;
@@ -219,6 +223,7 @@ export const TwProseMirrorSurface = forwardRef<
219
223
  createSurfaceDecorationKey({
220
224
  markupDisplay,
221
225
  showTrackedChanges,
226
+ suggestionsEnabled,
222
227
  canEdit,
223
228
  activeCommentId: snapshot.comments.activeCommentId,
224
229
  activeRevisionId: props.activeRevisionId,
@@ -242,36 +247,51 @@ export const TwProseMirrorSurface = forwardRef<
242
247
  props.activeWorkflowScopeIds,
243
248
  props.workflowScopes,
244
249
  showTrackedChanges,
250
+ suggestionsEnabled,
245
251
  snapshot.comments.activeCommentId,
246
252
  ],
247
253
  );
248
254
 
255
+ const isCollabMode = Boolean(props.ydoc);
256
+
249
257
  // Create PM plugins (stable across renders — callbacks accessed via ref)
250
258
  const plugins = useMemo(() => {
259
+ const selectionCallbacks = {
260
+ onSelectionChange: (sel: SelectionSnapshot) => callbacksRef.current?.onSelectionChange(sel),
261
+ getPositionMap: () => callbacksRef.current?.getPositionMap() ?? null,
262
+ isSelectionSyncSuppressed: () =>
263
+ callbacksRef.current?.isSelectionSyncSuppressed?.() ?? false,
264
+ };
265
+
266
+ const corePlugins = props.ydoc
267
+ ? createCollabPlugins({
268
+ ydoc: props.ydoc,
269
+ awareness: props.awareness,
270
+ selectionCallbacks,
271
+ })
272
+ : createCommandBridgePlugins({
273
+ ...selectionCallbacks,
274
+ onInsertText: (text) => callbacksRef.current?.onInsertText(text),
275
+ onDeleteBackward: () => callbacksRef.current?.onDeleteBackward(),
276
+ onDeleteForward: () => callbacksRef.current?.onDeleteForward(),
277
+ onSplitParagraph: () => callbacksRef.current?.onSplitParagraph(),
278
+ onInsertHardBreak: () => callbacksRef.current?.onInsertHardBreak(),
279
+ onInsertTab: () => callbacksRef.current?.onInsertTab(),
280
+ onOutdentTab: () => callbacksRef.current?.onOutdentTab?.(),
281
+ onUndo: () => callbacksRef.current?.onUndo(),
282
+ onRedo: () => callbacksRef.current?.onRedo(),
283
+ onBlockedInput: (command, message) => callbacksRef.current?.onBlockedInput?.(command, message),
284
+ });
285
+
251
286
  return [
252
- ...createCommandBridgePlugins({
253
- onInsertText: (text) => callbacksRef.current?.onInsertText(text),
254
- onDeleteBackward: () => callbacksRef.current?.onDeleteBackward(),
255
- onDeleteForward: () => callbacksRef.current?.onDeleteForward(),
256
- onSplitParagraph: () => callbacksRef.current?.onSplitParagraph(),
257
- onInsertHardBreak: () => callbacksRef.current?.onInsertHardBreak(),
258
- onInsertTab: () => callbacksRef.current?.onInsertTab(),
259
- onOutdentTab: () => callbacksRef.current?.onOutdentTab?.(),
260
- onUndo: () => callbacksRef.current?.onUndo(),
261
- onRedo: () => callbacksRef.current?.onRedo(),
262
- onBlockedInput: (command, message) => callbacksRef.current?.onBlockedInput?.(command, message),
263
- onSelectionChange: (sel) => callbacksRef.current?.onSelectionChange(sel),
264
- getPositionMap: () => callbacksRef.current?.getPositionMap() ?? null,
265
- isSelectionSyncSuppressed: () =>
266
- callbacksRef.current?.isSelectionSyncSuppressed?.() ?? false,
267
- }),
287
+ ...corePlugins,
268
288
  createContextualInteractionPlugin({
269
289
  onCommentActivated: (commentId) => props.onCommentActivated?.(commentId),
270
290
  onRevisionActivated: (revisionId) => props.onRevisionActivated?.(revisionId),
271
291
  }),
272
292
  createSearchPlugin(),
273
293
  ];
274
- }, [props.onCommentActivated, props.onRevisionActivated]);
294
+ }, [props.onCommentActivated, props.onRevisionActivated, props.ydoc, props.awareness]);
275
295
 
276
296
  const applyDecorationProps = useCallback(
277
297
  (view: EditorView, positionMap: PositionMap): void => {
@@ -318,10 +338,14 @@ export const TwProseMirrorSurface = forwardRef<
318
338
  ],
319
339
  );
320
340
 
321
- // Create or update the PM document only when the structural key changes.
322
341
  useEffect(() => {
323
342
  if (!mountRef.current || !surface) return;
324
343
 
344
+ // Collab mode: y-prosemirror owns the doc after initial mount
345
+ if (isCollabMode && viewRef.current) {
346
+ return;
347
+ }
348
+
325
349
  if (viewRef.current && documentBuildKeyRef.current === documentBuildKey) {
326
350
  return;
327
351
  }
@@ -355,19 +379,23 @@ export const TwProseMirrorSurface = forwardRef<
355
379
  incrementInvalidationCounter("pm.laneA.rebuilds");
356
380
 
357
381
  if (!viewRef.current) {
358
- // First time surface is available — create the EditorView
359
382
  const view = new EditorView(mountRef.current, {
360
383
  state,
361
384
  nodeViews: tableNodeViews,
362
385
  editable: () => canEdit,
363
386
  decorations: () => decorations,
364
- dispatchTransaction(tr) {
365
- const newState = view.state.apply(tr);
366
- view.updateState(newState);
367
- },
368
387
  });
369
388
  viewRef.current = view;
370
389
  recordPerfSample("pm.mount");
390
+
391
+ if (isCollabMode && props.ydoc) {
392
+ const yXmlFragment = props.ydoc.getXmlFragment("prosemirror");
393
+ if (yXmlFragment.length === 0) {
394
+ props.ydoc.transact(() => {
395
+ prosemirrorToYXmlFragment(view.state.doc, yXmlFragment);
396
+ });
397
+ }
398
+ }
371
399
  } else {
372
400
  suppressSelectionEchoRef.current = true;
373
401
  viewRef.current.updateState(state);
@@ -391,10 +419,12 @@ export const TwProseMirrorSurface = forwardRef<
391
419
  }, [
392
420
  applyDecorationProps,
393
421
  documentBuildKey,
422
+ isCollabMode,
394
423
  surface,
395
424
  snapshot.selection,
396
425
  plugins,
397
426
  props.mediaPreviews,
427
+ props.ydoc,
398
428
  ]);
399
429
 
400
430
  // Update decorations and editability without rebuilding the PM document.
@@ -427,6 +457,7 @@ export const TwProseMirrorSurface = forwardRef<
427
457
  ]);
428
458
 
429
459
  useEffect(() => {
460
+ if (isCollabMode) return;
430
461
  const view = viewRef.current;
431
462
  const positionMap = positionMapRef.current;
432
463
  if (!view || !surface || !positionMap) {
@@ -448,7 +479,7 @@ export const TwProseMirrorSurface = forwardRef<
448
479
  queueMicrotask(() => {
449
480
  suppressSelectionEchoRef.current = false;
450
481
  });
451
- }, [snapshot.selection, surface]);
482
+ }, [isCollabMode, snapshot.selection, surface]);
452
483
 
453
484
  useEffect(() => {
454
485
  if (!pendingSelectionProbeRef.current) {