@beyondwork/docx-react-component 1.0.24 → 1.0.26-rc2
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/package.json +1 -1
- package/src/api/public-types.ts +1 -0
- package/src/core/commands/index.ts +64 -13
- package/src/core/selection/review-anchors.ts +1 -6
- package/src/io/export/serialize-comments.ts +10 -4
- package/src/review/store/comment-remapping.ts +2 -12
- package/src/runtime/document-runtime.ts +2 -2
- package/src/runtime/session-capabilities.ts +1 -1
- package/src/ui/WordReviewEditor.tsx +6 -0
- package/src/ui/editor-surface-controller.tsx +1 -0
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +52 -12
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +5 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beyondwork/docx-react-component",
|
|
3
3
|
"publisher": "beyondwork",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.26rc2",
|
|
5
5
|
"description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": [
|
package/src/api/public-types.ts
CHANGED
|
@@ -1398,6 +1398,7 @@ export interface WordReviewEditorProps {
|
|
|
1398
1398
|
loadSourcePolicy?: LoadSourcePolicy;
|
|
1399
1399
|
readOnly?: boolean;
|
|
1400
1400
|
reviewMode?: "editing" | "review";
|
|
1401
|
+
suggestionsEnabled?: boolean;
|
|
1401
1402
|
markupDisplay?: "clean" | "simple" | "all";
|
|
1402
1403
|
showReviewPanel?: boolean;
|
|
1403
1404
|
chromeVisibility?: Partial<WordReviewEditorChromeVisibility>;
|
|
@@ -1309,7 +1309,7 @@ function applySuggestingInsert(
|
|
|
1309
1309
|
}
|
|
1310
1310
|
|
|
1311
1311
|
if (isCollapsed) {
|
|
1312
|
-
// Pure insertion at cursor: apply normally, then create insertion revision
|
|
1312
|
+
// Pure insertion at cursor: apply normally, then create or extend an insertion revision.
|
|
1313
1313
|
const result = insertText(state.document, selection, text, { timestamp: context.timestamp });
|
|
1314
1314
|
const insertedFrom = from;
|
|
1315
1315
|
const insertedTo = from + Array.from(text).length;
|
|
@@ -1321,25 +1321,53 @@ function applySuggestingInsert(
|
|
|
1321
1321
|
result.mapping,
|
|
1322
1322
|
);
|
|
1323
1323
|
|
|
1324
|
-
//
|
|
1325
|
-
//
|
|
1326
|
-
|
|
1324
|
+
// If there is an authored insertion revision that ends exactly where this
|
|
1325
|
+
// text was inserted (i.e. the cursor was at the end of a prior insertion),
|
|
1326
|
+
// extend that revision to cover the new text rather than creating a new one.
|
|
1327
|
+
// This groups consecutive keystrokes into a single tracked change.
|
|
1328
|
+
const adjacentInsertion = findAdjacentAuthoredInsertion(
|
|
1327
1329
|
reviewState.document.review.revisions,
|
|
1328
|
-
"insertion",
|
|
1329
1330
|
insertedFrom,
|
|
1330
|
-
insertedTo,
|
|
1331
|
-
authorId,
|
|
1332
|
-
context.timestamp,
|
|
1333
1331
|
);
|
|
1334
1332
|
|
|
1333
|
+
let finalRevisionId: string;
|
|
1334
|
+
let finalRevisions: Record<string, CanonicalRevisionRecord>;
|
|
1335
|
+
|
|
1336
|
+
if (adjacentInsertion && adjacentInsertion.anchor.kind === "range") {
|
|
1337
|
+
const extended: CanonicalRevisionRecord = {
|
|
1338
|
+
...adjacentInsertion,
|
|
1339
|
+
anchor: {
|
|
1340
|
+
kind: "range",
|
|
1341
|
+
range: { from: adjacentInsertion.anchor.range.from, to: insertedTo },
|
|
1342
|
+
assoc: { start: 1, end: -1 },
|
|
1343
|
+
},
|
|
1344
|
+
};
|
|
1345
|
+
finalRevisionId = extended.changeId;
|
|
1346
|
+
finalRevisions = {
|
|
1347
|
+
...reviewState.document.review.revisions,
|
|
1348
|
+
[extended.changeId]: extended,
|
|
1349
|
+
};
|
|
1350
|
+
} else {
|
|
1351
|
+
const revision = createAuthoredRevision(
|
|
1352
|
+
reviewState.document.review.revisions,
|
|
1353
|
+
"insertion",
|
|
1354
|
+
insertedFrom,
|
|
1355
|
+
insertedTo,
|
|
1356
|
+
authorId,
|
|
1357
|
+
context.timestamp,
|
|
1358
|
+
);
|
|
1359
|
+
finalRevisionId = revision.changeId;
|
|
1360
|
+
finalRevisions = {
|
|
1361
|
+
...reviewState.document.review.revisions,
|
|
1362
|
+
[revision.changeId]: revision,
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1335
1366
|
const finalDocument: CanonicalDocumentEnvelope = {
|
|
1336
1367
|
...reviewState.document,
|
|
1337
1368
|
review: {
|
|
1338
1369
|
...reviewState.document.review,
|
|
1339
|
-
revisions:
|
|
1340
|
-
...reviewState.document.review.revisions,
|
|
1341
|
-
[revision.changeId]: revision,
|
|
1342
|
-
},
|
|
1370
|
+
revisions: finalRevisions,
|
|
1343
1371
|
},
|
|
1344
1372
|
};
|
|
1345
1373
|
|
|
@@ -1360,7 +1388,7 @@ function applySuggestingInsert(
|
|
|
1360
1388
|
mapping: result.mapping,
|
|
1361
1389
|
effects: {
|
|
1362
1390
|
...reviewState.effects,
|
|
1363
|
-
revisionAuthored: { changeId:
|
|
1391
|
+
revisionAuthored: { changeId: finalRevisionId, kind: "insertion" },
|
|
1364
1392
|
},
|
|
1365
1393
|
},
|
|
1366
1394
|
);
|
|
@@ -1704,3 +1732,26 @@ function findOverlappingAuthoredDeletion(
|
|
|
1704
1732
|
}
|
|
1705
1733
|
return undefined;
|
|
1706
1734
|
}
|
|
1735
|
+
|
|
1736
|
+
/**
|
|
1737
|
+
* Find an open authored insertion revision whose end position equals `cursorAt`.
|
|
1738
|
+
* Used to extend an existing insertion when the user keeps typing at the same
|
|
1739
|
+
* position rather than creating a new revision for every character.
|
|
1740
|
+
*/
|
|
1741
|
+
function findAdjacentAuthoredInsertion(
|
|
1742
|
+
revisions: Record<string, CanonicalRevisionRecord>,
|
|
1743
|
+
cursorAt: number,
|
|
1744
|
+
): CanonicalRevisionRecord | undefined {
|
|
1745
|
+
for (const revision of Object.values(revisions)) {
|
|
1746
|
+
if (
|
|
1747
|
+
revision.kind === "insertion" &&
|
|
1748
|
+
revision.status === "open" &&
|
|
1749
|
+
revision.metadata?.source === "runtime" &&
|
|
1750
|
+
revision.anchor.kind === "range" &&
|
|
1751
|
+
revision.anchor.range.to === cursorAt
|
|
1752
|
+
) {
|
|
1753
|
+
return revision;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return undefined;
|
|
1757
|
+
}
|
|
@@ -104,7 +104,6 @@ export function rangeStaysWithinSingleParagraph(
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
export function canCreateDocxCommentAnchor(
|
|
107
|
-
content: unknown,
|
|
108
107
|
anchor: ReviewAnchor,
|
|
109
108
|
): boolean {
|
|
110
109
|
if (anchor.kind !== "range") {
|
|
@@ -112,11 +111,7 @@ export function canCreateDocxCommentAnchor(
|
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
const normalized = normalizeRange(anchor.range);
|
|
115
|
-
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return rangeStaysWithinSingleParagraph(content, normalized);
|
|
114
|
+
return normalized.from !== normalized.to;
|
|
120
115
|
}
|
|
121
116
|
|
|
122
117
|
function readSurfaceBlocks(
|
|
@@ -261,19 +261,25 @@ export function serializeCommentAnchorsIntoDocumentXml(
|
|
|
261
261
|
continue;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
const
|
|
264
|
+
const startParagraph = paragraphs.find(
|
|
265
265
|
(candidate) =>
|
|
266
266
|
anchor.range.from >= candidate.start &&
|
|
267
|
+
anchor.range.from <= candidate.end,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const endParagraph = paragraphs.find(
|
|
271
|
+
(candidate) =>
|
|
272
|
+
anchor.range.to >= candidate.start &&
|
|
267
273
|
anchor.range.to <= candidate.end,
|
|
268
274
|
);
|
|
269
275
|
|
|
270
|
-
if (!
|
|
276
|
+
if (!startParagraph || !endParagraph) {
|
|
271
277
|
skippedCommentIds.push(thread.commentId);
|
|
272
278
|
continue;
|
|
273
279
|
}
|
|
274
280
|
|
|
275
|
-
const startIndex =
|
|
276
|
-
const endIndex =
|
|
281
|
+
const startIndex = startParagraph.boundaries.get(anchor.range.from);
|
|
282
|
+
const endIndex = endParagraph.boundaries.get(anchor.range.to);
|
|
277
283
|
|
|
278
284
|
if (startIndex === undefined || endIndex === undefined) {
|
|
279
285
|
skippedCommentIds.push(thread.commentId);
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
getAnchorRange,
|
|
5
5
|
mapReviewAnchor,
|
|
6
6
|
mappingTouchesAnchorContent,
|
|
7
|
-
rangeStaysWithinSingleParagraph,
|
|
8
7
|
type ReviewAnchor,
|
|
9
8
|
} from "../../core/selection/review-anchors.ts";
|
|
10
9
|
import type { TransactionMapping } from "../../core/selection/mapping.ts";
|
|
@@ -28,7 +27,7 @@ export function remapCommentThreads(
|
|
|
28
27
|
const comments = Object.fromEntries(
|
|
29
28
|
Object.entries(options.comments).map(([commentId, comment]) => [
|
|
30
29
|
commentId,
|
|
31
|
-
remapCommentThread(comment, options.mapping
|
|
30
|
+
remapCommentThread(comment, options.mapping),
|
|
32
31
|
]),
|
|
33
32
|
);
|
|
34
33
|
const detachedCommentIds = Object.values(comments)
|
|
@@ -45,14 +44,13 @@ export function remapCommentThreads(
|
|
|
45
44
|
export function remapCommentThread(
|
|
46
45
|
comment: CommentThreadRecord,
|
|
47
46
|
mapping: TransactionMapping,
|
|
48
|
-
nextContent: unknown,
|
|
49
47
|
): CommentThreadRecord {
|
|
50
48
|
if (comment.anchor.kind === "detached") {
|
|
51
49
|
return comment;
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
const mappedAnchor = mapReviewAnchor(comment.anchor, mapping);
|
|
55
|
-
const anchor = normalizeCommentAnchor(comment.anchor, mappedAnchor, mapping
|
|
53
|
+
const anchor = normalizeCommentAnchor(comment.anchor, mappedAnchor, mapping);
|
|
56
54
|
|
|
57
55
|
return {
|
|
58
56
|
...comment,
|
|
@@ -64,7 +62,6 @@ function normalizeCommentAnchor(
|
|
|
64
62
|
previousAnchor: ReviewAnchor,
|
|
65
63
|
mappedAnchor: ReviewAnchor,
|
|
66
64
|
mapping: TransactionMapping,
|
|
67
|
-
nextContent: unknown,
|
|
68
65
|
): ReviewAnchor {
|
|
69
66
|
if (mappedAnchor.kind === "detached") {
|
|
70
67
|
return mappedAnchor;
|
|
@@ -83,13 +80,6 @@ function normalizeCommentAnchor(
|
|
|
83
80
|
return detachReviewAnchor(previousRange, detachReason(mapping));
|
|
84
81
|
}
|
|
85
82
|
|
|
86
|
-
if (
|
|
87
|
-
mappedAnchor.kind === "range" &&
|
|
88
|
-
!rangeStaysWithinSingleParagraph(nextContent, mappedAnchor.range)
|
|
89
|
-
) {
|
|
90
|
-
return detachReviewAnchor(previousRange, "invalidatedByStructureChange");
|
|
91
|
-
}
|
|
92
|
-
|
|
93
83
|
return mappedAnchor;
|
|
94
84
|
}
|
|
95
85
|
|
|
@@ -1160,9 +1160,9 @@ export function createDocumentRuntime(
|
|
|
1160
1160
|
const selection = params.anchor
|
|
1161
1161
|
? createSelectionFromPublicAnchor(params.anchor)
|
|
1162
1162
|
: state.selection;
|
|
1163
|
-
if (!canCreateDocxCommentAnchor(
|
|
1163
|
+
if (!canCreateDocxCommentAnchor(anchor)) {
|
|
1164
1164
|
const message =
|
|
1165
|
-
"DOCX comments must use a non-empty range
|
|
1165
|
+
"DOCX comments must use a non-empty range.";
|
|
1166
1166
|
emitError({
|
|
1167
1167
|
errorId: createSessionId("comment-anchor", clock()),
|
|
1168
1168
|
code: "validation_failed",
|
|
@@ -110,7 +110,7 @@ export function deriveCapabilities(
|
|
|
110
110
|
activeStory.kind === "main" &&
|
|
111
111
|
!snapshot.selection.isCollapsed &&
|
|
112
112
|
Boolean(snapshot.surface) &&
|
|
113
|
-
canCreateDocxCommentAnchor(
|
|
113
|
+
canCreateDocxCommentAnchor(toRuntimeAnchor(snapshot.selection.activeRange));
|
|
114
114
|
const canExport = isReady && !exportBlocked && !hasFatalError;
|
|
115
115
|
|
|
116
116
|
// Revision capabilities
|
|
@@ -520,6 +520,7 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
520
520
|
onWarning,
|
|
521
521
|
readOnly = false,
|
|
522
522
|
reviewMode = "review",
|
|
523
|
+
suggestionsEnabled = false,
|
|
523
524
|
showReviewPanel = true,
|
|
524
525
|
chromeVisibility,
|
|
525
526
|
} = props;
|
|
@@ -693,6 +694,10 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
693
694
|
activeRuntime.setViewMode(effectiveViewMode);
|
|
694
695
|
}, [activeRuntime, effectiveViewMode]);
|
|
695
696
|
|
|
697
|
+
useEffect(() => {
|
|
698
|
+
activeRuntime.setDocumentMode(suggestionsEnabled ? "suggesting" : "editing");
|
|
699
|
+
}, [activeRuntime, suggestionsEnabled]);
|
|
700
|
+
|
|
696
701
|
useEffect(() => {
|
|
697
702
|
runtimeViewStateSeedRef.current = {
|
|
698
703
|
workspaceMode: viewState.workspaceMode,
|
|
@@ -1707,6 +1712,7 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
1707
1712
|
markupDisplay={liveMarkupDisplay}
|
|
1708
1713
|
activeRevisionId={activeRevisionId}
|
|
1709
1714
|
showTrackedChanges={showTrackedChanges}
|
|
1715
|
+
suggestionsEnabled={suggestionsEnabled}
|
|
1710
1716
|
mediaPreviews={mediaPreviews}
|
|
1711
1717
|
isPageWorkspace={isPageWorkspace}
|
|
1712
1718
|
workflowScopes={workflowScopeSnapshot?.scopes}
|
|
@@ -27,6 +27,7 @@ export interface EditorSurfaceControllerProps {
|
|
|
27
27
|
markupDisplay: MarkupDisplay;
|
|
28
28
|
activeRevisionId?: string;
|
|
29
29
|
showTrackedChanges?: boolean;
|
|
30
|
+
suggestionsEnabled?: boolean;
|
|
30
31
|
mediaPreviews?: Record<string, MediaPreviewDescriptor>;
|
|
31
32
|
isPageWorkspace?: boolean;
|
|
32
33
|
onSelectionToolbarAnchorChange?: (anchor: SelectionToolbarAnchor | null) => void;
|
|
@@ -211,10 +211,13 @@ export function buildDecorations(
|
|
|
211
211
|
workflowBlockedReasons?: readonly WorkflowBlockedCommandReason[],
|
|
212
212
|
activeWorkflowWorkItemId?: string | null,
|
|
213
213
|
activeWorkflowScopeIds?: readonly string[],
|
|
214
|
+
suggestionsEnabled = false,
|
|
214
215
|
): DecorationSet {
|
|
215
216
|
const decorations: Decoration[] = [];
|
|
216
217
|
const railRangeCache = new Map<string, Array<{ from: number; to: number }>>();
|
|
217
218
|
const activeScopeIds = new Set(activeWorkflowScopeIds ?? []);
|
|
219
|
+
// In suggestions mode, tracked changes are always shown regardless of the toggle.
|
|
220
|
+
const effectiveShowTracked = suggestionsEnabled ? true : showTrackedChanges;
|
|
218
221
|
|
|
219
222
|
// Walk comment threads and create inline decorations
|
|
220
223
|
if (commentModel) {
|
|
@@ -248,7 +251,7 @@ export function buildDecorations(
|
|
|
248
251
|
// Always hide deletions in clean mode (final-text semantics).
|
|
249
252
|
// This is the critical behavior: "hide tracked changes" must show
|
|
250
253
|
// the document as if accepted, not show deleted text as kept text.
|
|
251
|
-
if (markupDisplay === "clean" && rev.kind === "deletion") {
|
|
254
|
+
if (markupDisplay === "clean" && rev.kind === "deletion" && !suggestionsEnabled) {
|
|
252
255
|
const pmFrom = positionMap.runtimeToPm(rev.from);
|
|
253
256
|
const pmTo = positionMap.runtimeToPm(rev.to);
|
|
254
257
|
if (pmFrom < pmTo) {
|
|
@@ -263,7 +266,48 @@ export function buildDecorations(
|
|
|
263
266
|
}
|
|
264
267
|
|
|
265
268
|
// Skip visual styling when tracked changes display is off
|
|
266
|
-
if (!
|
|
269
|
+
if (!effectiveShowTracked) continue;
|
|
270
|
+
|
|
271
|
+
const pmFrom = positionMap.runtimeToPm(rev.from);
|
|
272
|
+
const pmTo = positionMap.runtimeToPm(rev.to);
|
|
273
|
+
if (pmFrom >= pmTo) continue;
|
|
274
|
+
|
|
275
|
+
if (suggestionsEnabled) {
|
|
276
|
+
if (rev.kind === "insertion") {
|
|
277
|
+
decorations.push(
|
|
278
|
+
Decoration.inline(pmFrom, pmTo, {
|
|
279
|
+
class: "text-insert",
|
|
280
|
+
"data-revision-id": rev.revisionId,
|
|
281
|
+
}),
|
|
282
|
+
);
|
|
283
|
+
decorations.push(
|
|
284
|
+
Decoration.widget(pmFrom, () => {
|
|
285
|
+
const el = document.createElement("span");
|
|
286
|
+
el.textContent = "[";
|
|
287
|
+
el.className = "text-insert";
|
|
288
|
+
el.setAttribute("contenteditable", "false");
|
|
289
|
+
return el;
|
|
290
|
+
}, { side: -1, key: `${rev.revisionId}-open` }),
|
|
291
|
+
);
|
|
292
|
+
decorations.push(
|
|
293
|
+
Decoration.widget(pmTo, () => {
|
|
294
|
+
const el = document.createElement("span");
|
|
295
|
+
el.textContent = "]";
|
|
296
|
+
el.className = "text-insert";
|
|
297
|
+
el.setAttribute("contenteditable", "false");
|
|
298
|
+
return el;
|
|
299
|
+
}, { side: 1, key: `${rev.revisionId}-close` }),
|
|
300
|
+
);
|
|
301
|
+
} else if (rev.kind === "deletion") {
|
|
302
|
+
decorations.push(
|
|
303
|
+
Decoration.inline(pmFrom, pmTo, {
|
|
304
|
+
class: "text-danger line-through decoration-danger/80 decoration-1",
|
|
305
|
+
"data-revision-id": rev.revisionId,
|
|
306
|
+
}),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
267
311
|
|
|
268
312
|
const cls = getRevisionHighlightClass(
|
|
269
313
|
revisionModel,
|
|
@@ -273,16 +317,12 @@ export function buildDecorations(
|
|
|
273
317
|
);
|
|
274
318
|
if (!cls) continue;
|
|
275
319
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
"data-revision-id": rev.revisionId,
|
|
283
|
-
}),
|
|
284
|
-
);
|
|
285
|
-
}
|
|
320
|
+
decorations.push(
|
|
321
|
+
Decoration.inline(pmFrom, pmTo, {
|
|
322
|
+
class: cls,
|
|
323
|
+
"data-revision-id": rev.revisionId,
|
|
324
|
+
}),
|
|
325
|
+
);
|
|
286
326
|
}
|
|
287
327
|
}
|
|
288
328
|
|
|
@@ -43,6 +43,7 @@ export function createSurfaceDecorationKey(input: {
|
|
|
43
43
|
workflowBlockedSignature?: string;
|
|
44
44
|
activeWorkflowWorkItemId?: string | null;
|
|
45
45
|
activeWorkflowScopeIds?: readonly string[];
|
|
46
|
+
suggestionsEnabled?: boolean;
|
|
46
47
|
}): string {
|
|
47
48
|
return JSON.stringify({
|
|
48
49
|
markupDisplay: input.markupDisplay,
|
|
@@ -55,5 +56,6 @@ export function createSurfaceDecorationKey(input: {
|
|
|
55
56
|
workflowBlockedSignature: input.workflowBlockedSignature ?? null,
|
|
56
57
|
activeWorkflowWorkItemId: input.activeWorkflowWorkItemId ?? null,
|
|
57
58
|
activeWorkflowScopeIds: input.activeWorkflowScopeIds ?? [],
|
|
59
|
+
suggestionsEnabled: input.suggestionsEnabled ?? false,
|
|
58
60
|
});
|
|
59
61
|
}
|
|
@@ -75,6 +75,7 @@ export interface TwProseMirrorSurfaceProps {
|
|
|
75
75
|
markupDisplay: MarkupDisplay;
|
|
76
76
|
activeRevisionId?: string;
|
|
77
77
|
showTrackedChanges?: boolean;
|
|
78
|
+
suggestionsEnabled?: boolean;
|
|
78
79
|
/** When true, the surface renders inside the page workspace (vs canvas). */
|
|
79
80
|
isPageWorkspace?: boolean;
|
|
80
81
|
onFocus: FocusEventHandler<HTMLDivElement>;
|
|
@@ -204,6 +205,7 @@ export const TwProseMirrorSurface = forwardRef<
|
|
|
204
205
|
}),
|
|
205
206
|
[mediaPreviewKey, snapshot.activeStory, surface],
|
|
206
207
|
);
|
|
208
|
+
const suggestionsEnabled = props.suggestionsEnabled === true;
|
|
207
209
|
const decorationBuildKey = useMemo(
|
|
208
210
|
() =>
|
|
209
211
|
createSurfaceDecorationKey({
|
|
@@ -217,6 +219,7 @@ export const TwProseMirrorSurface = forwardRef<
|
|
|
217
219
|
workflowBlockedSignature: createWorkflowBlockedSignature(props.workflowBlockedReasons),
|
|
218
220
|
activeWorkflowWorkItemId: props.activeWorkflowWorkItemId ?? null,
|
|
219
221
|
activeWorkflowScopeIds: props.activeWorkflowScopeIds ?? [],
|
|
222
|
+
suggestionsEnabled,
|
|
220
223
|
}),
|
|
221
224
|
[
|
|
222
225
|
canEdit,
|
|
@@ -229,6 +232,7 @@ export const TwProseMirrorSurface = forwardRef<
|
|
|
229
232
|
props.workflowScopes,
|
|
230
233
|
showTrackedChanges,
|
|
231
234
|
snapshot.comments.activeCommentId,
|
|
235
|
+
suggestionsEnabled,
|
|
232
236
|
],
|
|
233
237
|
);
|
|
234
238
|
|
|
@@ -274,6 +278,7 @@ export const TwProseMirrorSurface = forwardRef<
|
|
|
274
278
|
props.workflowBlockedReasons,
|
|
275
279
|
props.activeWorkflowWorkItemId,
|
|
276
280
|
props.activeWorkflowScopeIds,
|
|
281
|
+
suggestionsEnabled,
|
|
277
282
|
);
|
|
278
283
|
view.setProps({
|
|
279
284
|
editable: () => canEdit,
|