@beyondwork/docx-react-component 1.0.31 → 1.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/package.json +1 -1
- package/src/api/public-types.ts +16 -1
- package/src/api/session-state.ts +2 -0
- package/src/io/docx-session.ts +16 -3
- package/src/io/ooxml/parse-footnotes.ts +23 -33
- package/src/io/ooxml/parse-headers-footers.ts +20 -21
- package/src/io/ooxml/workflow-payload.ts +311 -8
- package/src/model/snapshot.ts +113 -1
- package/src/runtime/document-runtime.ts +207 -33
- package/src/runtime/surface-projection.ts +156 -7
- package/src/ui/WordReviewEditor.tsx +13 -5
- package/src/ui/editor-surface-controller.tsx +2 -0
- package/src/ui/headless/selection-tool-resolver.ts +4 -1
- package/src/ui/headless/selection-tool-types.ts +1 -2
- package/src/ui/workflow-surface-blocked-rails.ts +19 -1
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +1 -2
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +1 -2
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +136 -24
- package/src/ui-tailwind/editor-surface/pm-schema.ts +29 -0
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +12 -0
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -0
- package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +20 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +31 -0
- package/src/ui-tailwind/theme/editor-theme.css +8 -0
|
@@ -3,7 +3,6 @@ import type {
|
|
|
3
3
|
CommentSidebarThreadSnapshot,
|
|
4
4
|
FormattingStateSnapshot,
|
|
5
5
|
InteractionGuardSnapshot,
|
|
6
|
-
StyleCatalogSnapshot,
|
|
7
6
|
TableStructureContextSnapshot,
|
|
8
7
|
SuggestionsSnapshot,
|
|
9
8
|
TrackedChangeEntrySnapshot,
|
|
@@ -79,7 +78,7 @@ export type StructureContextKind = "table" | "image" | "object" | "list";
|
|
|
79
78
|
export interface StructureContextSelectionToolModel extends BaseSelectionToolModel {
|
|
80
79
|
kind: "structure-context";
|
|
81
80
|
structureKind: StructureContextKind;
|
|
82
|
-
tableStyles?:
|
|
81
|
+
tableStyles?: Array<{ styleId: string; displayName: string }>;
|
|
83
82
|
activeTable?: TableStructureContextSnapshot | null;
|
|
84
83
|
activeImage?: ActiveImageContext;
|
|
85
84
|
activeObject?: ActiveObjectContext;
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
RuntimeRenderSnapshot,
|
|
4
4
|
SurfaceBlockSnapshot,
|
|
5
5
|
WorkflowBlockedCommandReason,
|
|
6
|
+
WorkflowLockedZone,
|
|
6
7
|
WorkflowMarkupSnapshot,
|
|
7
8
|
} from "../api/public-types";
|
|
8
9
|
|
|
@@ -10,6 +11,21 @@ export function deriveVisibleWorkflowBlockedRails(
|
|
|
10
11
|
surface: RuntimeRenderSnapshot["surface"] | undefined,
|
|
11
12
|
markupSnapshot: WorkflowMarkupSnapshot | null,
|
|
12
13
|
): WorkflowBlockedCommandReason[] {
|
|
14
|
+
return deriveVisibleWorkflowLockedZones(surface, markupSnapshot).map((zone) => ({
|
|
15
|
+
code: zone.code,
|
|
16
|
+
message: zone.detail,
|
|
17
|
+
fragmentId: zone.fragmentId,
|
|
18
|
+
label: zone.label,
|
|
19
|
+
detail: zone.detail,
|
|
20
|
+
anchor: zone.anchor,
|
|
21
|
+
storyTarget: zone.storyTarget,
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function deriveVisibleWorkflowLockedZones(
|
|
26
|
+
surface: RuntimeRenderSnapshot["surface"] | undefined,
|
|
27
|
+
markupSnapshot: WorkflowMarkupSnapshot | null,
|
|
28
|
+
): WorkflowLockedZone[] {
|
|
13
29
|
if (!surface || !markupSnapshot) {
|
|
14
30
|
return [];
|
|
15
31
|
}
|
|
@@ -22,8 +38,10 @@ export function deriveVisibleWorkflowBlockedRails(
|
|
|
22
38
|
),
|
|
23
39
|
)
|
|
24
40
|
.map((fragment) => ({
|
|
41
|
+
fragmentId: fragment.fragmentId,
|
|
25
42
|
code: fragment.blockedReasonCode,
|
|
26
|
-
|
|
43
|
+
label: fragment.label,
|
|
44
|
+
detail: fragment.detail,
|
|
27
45
|
anchor: fragment.anchor,
|
|
28
46
|
storyTarget: fragment.storyTarget,
|
|
29
47
|
}));
|
|
@@ -52,8 +52,7 @@ export function TwSelectionToolStructure(props: TwSelectionToolStructureProps) {
|
|
|
52
52
|
return (
|
|
53
53
|
<TwTableContextToolbar
|
|
54
54
|
disabled={!props.model.canMutate}
|
|
55
|
-
tableContext={props.model.activeTable ?? null}
|
|
56
|
-
tableStyles={props.model.tableStyles ?? []}
|
|
55
|
+
tableContext={props.model.activeTable ?? null} tableStyles={props.model.tableStyles ?? []}
|
|
57
56
|
onSetTableStyle={props.onSetTableStyle}
|
|
58
57
|
onAddRowBefore={props.onAddRowBefore}
|
|
59
58
|
onAddRowAfter={props.onAddRowAfter}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
-
StyleCatalogSnapshot,
|
|
5
4
|
TableOperationCapabilitySnapshot,
|
|
6
5
|
TableStructureContextSnapshot,
|
|
7
6
|
} from "../../api/public-types";
|
|
@@ -10,7 +9,7 @@ import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-edi
|
|
|
10
9
|
export interface TwTableContextToolbarProps {
|
|
11
10
|
disabled: boolean;
|
|
12
11
|
tableContext: TableStructureContextSnapshot | null;
|
|
13
|
-
tableStyles:
|
|
12
|
+
tableStyles: Array<{ styleId: string; displayName: string }>;
|
|
14
13
|
onSetTableStyle?: (styleId: string) => void;
|
|
15
14
|
onAddRowBefore?: () => void;
|
|
16
15
|
onAddRowAfter?: () => void;
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
EditorStoryTarget,
|
|
10
10
|
WorkflowBlockedCommandReason,
|
|
11
11
|
WorkflowCandidateRange,
|
|
12
|
+
WorkflowLockedZone,
|
|
12
13
|
WorkflowMetadataMarkup,
|
|
13
14
|
WorkflowScope,
|
|
14
15
|
} from "../../api/public-types";
|
|
@@ -81,16 +82,16 @@ function getWorkflowMetadataInlineClass(): string {
|
|
|
81
82
|
|
|
82
83
|
function getWorkflowBlockedInlineClass(reason: WorkflowBlockedCommandReason): string {
|
|
83
84
|
if (reason.code === "workflow_blocked_import") {
|
|
84
|
-
return "wre-workflow-inline wre-workflow-inline-blocked-import";
|
|
85
|
+
return "wre-workflow-inline wre-workflow-inline-locked-zone wre-workflow-inline-blocked-import";
|
|
85
86
|
}
|
|
86
|
-
return "wre-workflow-inline wre-workflow-inline-preserve-only";
|
|
87
|
+
return "wre-workflow-inline wre-workflow-inline-locked-zone wre-workflow-inline-preserve-only";
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
function getWorkflowBlockedRailClass(reason: WorkflowBlockedCommandReason): string {
|
|
90
91
|
if (reason.code === "workflow_blocked_import") {
|
|
91
|
-
return "wre-workflow-rail wre-workflow-rail-blocked-import";
|
|
92
|
+
return "wre-workflow-rail wre-workflow-rail-locked-zone wre-workflow-rail-blocked-import";
|
|
92
93
|
}
|
|
93
|
-
return "wre-workflow-rail wre-workflow-rail-preserve-only";
|
|
94
|
+
return "wre-workflow-rail wre-workflow-rail-locked-zone wre-workflow-rail-preserve-only";
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
function hasBlockChildren(node: PMNode): boolean {
|
|
@@ -172,6 +173,56 @@ function buildAnchorPmRange(
|
|
|
172
173
|
};
|
|
173
174
|
}
|
|
174
175
|
|
|
176
|
+
function collectLockedPmRanges(
|
|
177
|
+
lockedZones: readonly WorkflowLockedZone[] | undefined,
|
|
178
|
+
activeStory: EditorStoryTarget,
|
|
179
|
+
positionMap: PositionMap,
|
|
180
|
+
): Array<{ from: number; to: number; zone: WorkflowLockedZone }> {
|
|
181
|
+
if (!lockedZones || lockedZones.length === 0) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
const ranges: Array<{ from: number; to: number; zone: WorkflowLockedZone }> = [];
|
|
185
|
+
for (const zone of lockedZones) {
|
|
186
|
+
const zoneStoryTarget = zone.storyTarget ?? MAIN_STORY_TARGET;
|
|
187
|
+
if (!storyTargetsEqual(zoneStoryTarget, activeStory)) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const pmRange = buildAnchorPmRange(zone.anchor, positionMap);
|
|
191
|
+
if (!pmRange || !pmRange.allowInline || pmRange.from >= pmRange.to) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
ranges.push({ from: pmRange.from, to: pmRange.to, zone });
|
|
195
|
+
}
|
|
196
|
+
return ranges;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function subtractInlineOverlaps(
|
|
200
|
+
baseRange: { from: number; to: number },
|
|
201
|
+
blockedRanges: Array<{ from: number; to: number }>,
|
|
202
|
+
): Array<{ from: number; to: number }> {
|
|
203
|
+
let segments = [baseRange];
|
|
204
|
+
for (const blockedRange of blockedRanges) {
|
|
205
|
+
const nextSegments: Array<{ from: number; to: number }> = [];
|
|
206
|
+
for (const segment of segments) {
|
|
207
|
+
if (blockedRange.to <= segment.from || blockedRange.from >= segment.to) {
|
|
208
|
+
nextSegments.push(segment);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (blockedRange.from > segment.from) {
|
|
212
|
+
nextSegments.push({ from: segment.from, to: blockedRange.from });
|
|
213
|
+
}
|
|
214
|
+
if (blockedRange.to < segment.to) {
|
|
215
|
+
nextSegments.push({ from: blockedRange.to, to: segment.to });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
segments = nextSegments;
|
|
219
|
+
if (segments.length === 0) {
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return segments.filter((segment) => segment.from < segment.to);
|
|
224
|
+
}
|
|
225
|
+
|
|
175
226
|
function pushRailDecorations(
|
|
176
227
|
decorations: Decoration[],
|
|
177
228
|
doc: PMNode,
|
|
@@ -210,10 +261,12 @@ export function buildDecorations(
|
|
|
210
261
|
revisionModel: RevisionDecorationModel | undefined,
|
|
211
262
|
markupDisplay: MarkupDisplay,
|
|
212
263
|
showTrackedChanges = true,
|
|
264
|
+
suggestionsEnabled = false,
|
|
213
265
|
workflowScopes?: readonly WorkflowScope[],
|
|
214
266
|
activeStory: EditorStoryTarget = MAIN_STORY_TARGET,
|
|
215
267
|
workflowCandidates?: readonly WorkflowCandidateRange[],
|
|
216
268
|
workflowBlockedReasons?: readonly WorkflowBlockedCommandReason[],
|
|
269
|
+
workflowLockedZones?: readonly WorkflowLockedZone[],
|
|
217
270
|
activeWorkflowWorkItemId?: string | null,
|
|
218
271
|
activeWorkflowScopeIds?: readonly string[],
|
|
219
272
|
workflowMetadata?: readonly WorkflowMetadataMarkup[],
|
|
@@ -221,6 +274,7 @@ export function buildDecorations(
|
|
|
221
274
|
const decorations: Decoration[] = [];
|
|
222
275
|
const railRangeCache = new Map<string, Array<{ from: number; to: number }>>();
|
|
223
276
|
const activeScopeIds = new Set(activeWorkflowScopeIds ?? []);
|
|
277
|
+
const lockedPmRanges = collectLockedPmRanges(workflowLockedZones, activeStory, positionMap);
|
|
224
278
|
|
|
225
279
|
// Walk comment threads and create inline decorations
|
|
226
280
|
if (commentModel) {
|
|
@@ -271,6 +325,47 @@ export function buildDecorations(
|
|
|
271
325
|
// Skip visual styling when tracked changes display is off
|
|
272
326
|
if (!showTrackedChanges) continue;
|
|
273
327
|
|
|
328
|
+
const pmFrom = positionMap.runtimeToPm(rev.from);
|
|
329
|
+
const pmTo = positionMap.runtimeToPm(rev.to);
|
|
330
|
+
if (pmFrom >= pmTo) continue;
|
|
331
|
+
|
|
332
|
+
if (suggestionsEnabled) {
|
|
333
|
+
if (rev.kind === "insertion") {
|
|
334
|
+
decorations.push(
|
|
335
|
+
Decoration.inline(pmFrom, pmTo, {
|
|
336
|
+
class: "text-insert",
|
|
337
|
+
"data-revision-id": rev.revisionId,
|
|
338
|
+
}),
|
|
339
|
+
);
|
|
340
|
+
decorations.push(
|
|
341
|
+
Decoration.widget(pmFrom, () => {
|
|
342
|
+
const el = document.createElement("span");
|
|
343
|
+
el.textContent = "[";
|
|
344
|
+
el.className = "text-insert";
|
|
345
|
+
el.setAttribute("contenteditable", "false");
|
|
346
|
+
return el;
|
|
347
|
+
}, { side: -1, key: `${rev.revisionId}-open` }),
|
|
348
|
+
);
|
|
349
|
+
decorations.push(
|
|
350
|
+
Decoration.widget(pmTo, () => {
|
|
351
|
+
const el = document.createElement("span");
|
|
352
|
+
el.textContent = "]";
|
|
353
|
+
el.className = "text-insert";
|
|
354
|
+
el.setAttribute("contenteditable", "false");
|
|
355
|
+
return el;
|
|
356
|
+
}, { side: 1, key: `${rev.revisionId}-close` }),
|
|
357
|
+
);
|
|
358
|
+
} else if (rev.kind === "deletion") {
|
|
359
|
+
decorations.push(
|
|
360
|
+
Decoration.inline(pmFrom, pmTo, {
|
|
361
|
+
class: "text-danger line-through decoration-danger/80 decoration-1",
|
|
362
|
+
"data-revision-id": rev.revisionId,
|
|
363
|
+
}),
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
|
|
274
369
|
const cls = getRevisionHighlightClass(
|
|
275
370
|
revisionModel,
|
|
276
371
|
rev.from,
|
|
@@ -279,16 +374,12 @@ export function buildDecorations(
|
|
|
279
374
|
);
|
|
280
375
|
if (!cls) continue;
|
|
281
376
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
"data-revision-id": rev.revisionId,
|
|
289
|
-
}),
|
|
290
|
-
);
|
|
291
|
-
}
|
|
377
|
+
decorations.push(
|
|
378
|
+
Decoration.inline(pmFrom, pmTo, {
|
|
379
|
+
class: cls,
|
|
380
|
+
"data-revision-id": rev.revisionId,
|
|
381
|
+
}),
|
|
382
|
+
);
|
|
292
383
|
}
|
|
293
384
|
}
|
|
294
385
|
|
|
@@ -307,15 +398,21 @@ export function buildDecorations(
|
|
|
307
398
|
);
|
|
308
399
|
|
|
309
400
|
if (pmRange.allowInline && pmRange.from < pmRange.to) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
"data-workflow-scope-id": scope.scopeId,
|
|
314
|
-
"data-workflow-scope-mode": scope.mode,
|
|
315
|
-
"data-workflow-active": isActiveWorkItem ? "true" : "false",
|
|
316
|
-
...(isSelectionZone ? { "data-workflow-zone": "selection" } : {}),
|
|
317
|
-
}),
|
|
401
|
+
const visibleScopeSegments = subtractInlineOverlaps(
|
|
402
|
+
{ from: pmRange.from, to: pmRange.to },
|
|
403
|
+
lockedPmRanges.filter((range) => range.to > pmRange.from && range.from < pmRange.to),
|
|
318
404
|
);
|
|
405
|
+
for (const visibleSegment of visibleScopeSegments) {
|
|
406
|
+
decorations.push(
|
|
407
|
+
Decoration.inline(visibleSegment.from, visibleSegment.to, {
|
|
408
|
+
class: getWorkflowInlineClass(scope, isActiveWorkItem, isSelectionZone),
|
|
409
|
+
"data-workflow-scope-id": scope.scopeId,
|
|
410
|
+
"data-workflow-scope-mode": scope.mode,
|
|
411
|
+
"data-workflow-active": isActiveWorkItem ? "true" : "false",
|
|
412
|
+
...(isSelectionZone ? { "data-workflow-zone": "selection" } : {}),
|
|
413
|
+
}),
|
|
414
|
+
);
|
|
415
|
+
}
|
|
319
416
|
}
|
|
320
417
|
|
|
321
418
|
pushRailDecorations(decorations, doc, pmRange.from, pmRange.to, {
|
|
@@ -375,8 +472,19 @@ export function buildDecorations(
|
|
|
375
472
|
}
|
|
376
473
|
}
|
|
377
474
|
|
|
378
|
-
if (workflowBlockedReasons) {
|
|
379
|
-
|
|
475
|
+
if ((workflowLockedZones && workflowLockedZones.length > 0) || workflowBlockedReasons) {
|
|
476
|
+
const blockedReasonsToRender = workflowLockedZones && workflowLockedZones.length > 0
|
|
477
|
+
? workflowLockedZones.map((zone) => ({
|
|
478
|
+
code: zone.code,
|
|
479
|
+
message: zone.detail,
|
|
480
|
+
anchor: zone.anchor,
|
|
481
|
+
storyTarget: zone.storyTarget,
|
|
482
|
+
fragmentId: zone.fragmentId,
|
|
483
|
+
label: zone.label,
|
|
484
|
+
detail: zone.detail,
|
|
485
|
+
}))
|
|
486
|
+
: workflowBlockedReasons ?? [];
|
|
487
|
+
for (const reason of blockedReasonsToRender) {
|
|
380
488
|
if (
|
|
381
489
|
reason.code !== "workflow_preserve_only" &&
|
|
382
490
|
reason.code !== "workflow_blocked_import"
|
|
@@ -393,6 +501,8 @@ export function buildDecorations(
|
|
|
393
501
|
Decoration.inline(pmRange.from, pmRange.to, {
|
|
394
502
|
class: getWorkflowBlockedInlineClass(reason),
|
|
395
503
|
"data-workflow-blocked-code": reason.code,
|
|
504
|
+
...(reason.fragmentId ? { "data-workflow-fragment-id": reason.fragmentId } : {}),
|
|
505
|
+
"data-workflow-zone": "locked",
|
|
396
506
|
}),
|
|
397
507
|
);
|
|
398
508
|
}
|
|
@@ -402,6 +512,8 @@ export function buildDecorations(
|
|
|
402
512
|
className: getWorkflowBlockedRailClass(reason),
|
|
403
513
|
attrs: {
|
|
404
514
|
"data-workflow-blocked-code": reason.code,
|
|
515
|
+
...(reason.fragmentId ? { "data-workflow-fragment-id": reason.fragmentId } : {}),
|
|
516
|
+
"data-workflow-zone": "locked",
|
|
405
517
|
},
|
|
406
518
|
}, railRangeCache);
|
|
407
519
|
}
|
|
@@ -489,6 +489,7 @@ export const editorSchema = new Schema({
|
|
|
489
489
|
label: { default: "Locked" },
|
|
490
490
|
detail: { default: "" },
|
|
491
491
|
presentation: { default: "inline-chip" },
|
|
492
|
+
displayText: { default: null },
|
|
492
493
|
},
|
|
493
494
|
toDOM(node) {
|
|
494
495
|
const presentation = node.attrs.presentation as string;
|
|
@@ -505,6 +506,34 @@ export const editorSchema = new Schema({
|
|
|
505
506
|
},
|
|
506
507
|
];
|
|
507
508
|
}
|
|
509
|
+
if (presentation === "text-box") {
|
|
510
|
+
return [
|
|
511
|
+
"span",
|
|
512
|
+
{
|
|
513
|
+
class: "mx-0.5 inline-flex max-w-full whitespace-pre-wrap rounded border border-slate-300 bg-slate-50 px-2 py-1 align-top text-sm leading-snug text-slate-700 shadow-sm",
|
|
514
|
+
"data-node-type": "opaque_inline",
|
|
515
|
+
"data-inline-presentation": "text-box",
|
|
516
|
+
contenteditable: "false",
|
|
517
|
+
title: node.attrs.detail as string,
|
|
518
|
+
"aria-label": node.attrs.label as string,
|
|
519
|
+
},
|
|
520
|
+
(node.attrs.displayText as string | null) ?? (node.attrs.label as string),
|
|
521
|
+
];
|
|
522
|
+
}
|
|
523
|
+
if (presentation === "checkbox") {
|
|
524
|
+
return [
|
|
525
|
+
"span",
|
|
526
|
+
{
|
|
527
|
+
class: "mx-0.5 inline-flex h-5 min-w-[1.25rem] items-center justify-center rounded border border-slate-300 bg-white px-1 align-text-bottom text-sm leading-none text-slate-700",
|
|
528
|
+
"data-node-type": "opaque_inline",
|
|
529
|
+
"data-inline-presentation": "checkbox",
|
|
530
|
+
contenteditable: "false",
|
|
531
|
+
title: node.attrs.detail as string,
|
|
532
|
+
"aria-label": node.attrs.label as string,
|
|
533
|
+
},
|
|
534
|
+
(node.attrs.displayText as string | null) ?? "☐",
|
|
535
|
+
];
|
|
536
|
+
}
|
|
508
537
|
return [
|
|
509
538
|
"span",
|
|
510
539
|
{
|
|
@@ -508,6 +508,17 @@ function buildOpaqueInlineOrComplexAtom(
|
|
|
508
508
|
const label = segment.label;
|
|
509
509
|
const detail = segment.detail;
|
|
510
510
|
|
|
511
|
+
if (segment.presentation === "text-box" || segment.presentation === "checkbox") {
|
|
512
|
+
return editorSchema.nodes.opaque_inline.create({
|
|
513
|
+
fragmentId: segment.fragmentId,
|
|
514
|
+
warningId: segment.warningId,
|
|
515
|
+
label,
|
|
516
|
+
detail,
|
|
517
|
+
presentation: segment.presentation,
|
|
518
|
+
displayText: segment.displayText ?? null,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
511
522
|
if (showUnsupportedObjectPreviews && label === "Embedded chart") {
|
|
512
523
|
return editorSchema.nodes.chart_atom.create({ detail });
|
|
513
524
|
}
|
|
@@ -548,6 +559,7 @@ function buildOpaqueInlineOrComplexAtom(
|
|
|
548
559
|
label,
|
|
549
560
|
detail,
|
|
550
561
|
presentation: segment.presentation ?? "inline-chip",
|
|
562
|
+
displayText: segment.displayText ?? null,
|
|
551
563
|
});
|
|
552
564
|
}
|
|
553
565
|
|
|
@@ -43,6 +43,7 @@ export function createSurfaceDecorationKey(input: {
|
|
|
43
43
|
workflowScopeSignature?: string;
|
|
44
44
|
workflowCandidateSignature?: string;
|
|
45
45
|
workflowBlockedSignature?: string;
|
|
46
|
+
workflowLockedZoneSignature?: string;
|
|
46
47
|
workflowMetadataSignature?: string;
|
|
47
48
|
activeWorkflowWorkItemId?: string | null;
|
|
48
49
|
activeWorkflowScopeIds?: readonly string[];
|
|
@@ -56,6 +57,7 @@ export function createSurfaceDecorationKey(input: {
|
|
|
56
57
|
workflowScopeSignature: input.workflowScopeSignature ?? null,
|
|
57
58
|
workflowCandidateSignature: input.workflowCandidateSignature ?? null,
|
|
58
59
|
workflowBlockedSignature: input.workflowBlockedSignature ?? null,
|
|
60
|
+
workflowLockedZoneSignature: input.workflowLockedZoneSignature ?? null,
|
|
59
61
|
workflowMetadataSignature: input.workflowMetadataSignature ?? null,
|
|
60
62
|
activeWorkflowWorkItemId: input.activeWorkflowWorkItemId ?? null,
|
|
61
63
|
activeWorkflowScopeIds: input.activeWorkflowScopeIds ?? [],
|
|
@@ -106,6 +106,26 @@ export function TwInlineToken(props: TwInlineTokenProps) {
|
|
|
106
106
|
);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
if (segment.presentation === "checkbox") {
|
|
110
|
+
return (
|
|
111
|
+
<button
|
|
112
|
+
type="button"
|
|
113
|
+
tabIndex={-1}
|
|
114
|
+
onMouseDown={(e) => {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
props.onSelectionChange?.(createSelectionSnapshot(segment.from, segment.to));
|
|
117
|
+
}}
|
|
118
|
+
className={`inline-flex h-5 min-w-[1.25rem] items-center justify-center rounded border border-slate-300 bg-white px-1 text-sm leading-none text-slate-700 ${commentClass} ${selected ? "ring-1 ring-accent/30" : ""} ${focusRingClass}`}
|
|
119
|
+
title={segment.detail}
|
|
120
|
+
data-inline-presentation="checkbox"
|
|
121
|
+
>
|
|
122
|
+
{renderTwCaret(selection, segment.from)}
|
|
123
|
+
{segment.displayText ?? "☐"}
|
|
124
|
+
{renderTwCaret(selection, segment.to)}
|
|
125
|
+
</button>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
109
129
|
return (
|
|
110
130
|
<button
|
|
111
131
|
type="button"
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
SelectionSnapshot,
|
|
19
19
|
WorkflowBlockedCommandReason,
|
|
20
20
|
WorkflowCandidateRange,
|
|
21
|
+
WorkflowLockedZone,
|
|
21
22
|
WorkflowMetadataMarkup,
|
|
22
23
|
WorkflowScope,
|
|
23
24
|
} from "../../api/public-types";
|
|
@@ -79,6 +80,7 @@ export interface TwProseMirrorSurfaceProps {
|
|
|
79
80
|
activeRevisionId?: string;
|
|
80
81
|
activeSelectionToolKind?: ActiveSelectionToolModel["kind"] | null;
|
|
81
82
|
showTrackedChanges?: boolean;
|
|
83
|
+
suggestionsEnabled?: boolean;
|
|
82
84
|
/** When true, the surface renders inside the page workspace (vs canvas). */
|
|
83
85
|
isPageWorkspace?: boolean;
|
|
84
86
|
onFocus: FocusEventHandler<HTMLDivElement>;
|
|
@@ -101,6 +103,7 @@ export interface TwProseMirrorSurfaceProps {
|
|
|
101
103
|
workflowScopes?: readonly WorkflowScope[];
|
|
102
104
|
workflowCandidates?: readonly WorkflowCandidateRange[];
|
|
103
105
|
workflowBlockedReasons?: readonly WorkflowBlockedCommandReason[];
|
|
106
|
+
workflowLockedZones?: readonly WorkflowLockedZone[];
|
|
104
107
|
activeWorkflowWorkItemId?: string | null;
|
|
105
108
|
activeWorkflowScopeIds?: readonly string[];
|
|
106
109
|
workflowMetadata?: readonly WorkflowMetadataMarkup[];
|
|
@@ -194,6 +197,7 @@ export const TwProseMirrorSurface = forwardRef<
|
|
|
194
197
|
[snapshot.comments],
|
|
195
198
|
);
|
|
196
199
|
const showTrackedChanges = props.showTrackedChanges !== false;
|
|
200
|
+
const suggestionsEnabled = props.suggestionsEnabled ?? false;
|
|
197
201
|
// Always create the revision model — needed for deletion hiding in clean mode
|
|
198
202
|
// even when the tracked changes display toggle is off.
|
|
199
203
|
const revisionModel = useMemo(
|
|
@@ -221,6 +225,7 @@ export const TwProseMirrorSurface = forwardRef<
|
|
|
221
225
|
workflowScopeSignature: createWorkflowScopeSignature(props.workflowScopes),
|
|
222
226
|
workflowCandidateSignature: createWorkflowCandidateSignature(props.workflowCandidates),
|
|
223
227
|
workflowBlockedSignature: createWorkflowBlockedSignature(props.workflowBlockedReasons),
|
|
228
|
+
workflowLockedZoneSignature: createWorkflowLockedZoneSignature(props.workflowLockedZones),
|
|
224
229
|
workflowMetadataSignature: createWorkflowMetadataSignature(props.workflowMetadata),
|
|
225
230
|
activeWorkflowWorkItemId: props.activeWorkflowWorkItemId ?? null,
|
|
226
231
|
activeWorkflowScopeIds: props.activeWorkflowScopeIds ?? [],
|
|
@@ -231,6 +236,7 @@ export const TwProseMirrorSurface = forwardRef<
|
|
|
231
236
|
props.activeRevisionId,
|
|
232
237
|
props.workflowCandidates,
|
|
233
238
|
props.workflowBlockedReasons,
|
|
239
|
+
props.workflowLockedZones,
|
|
234
240
|
props.workflowMetadata,
|
|
235
241
|
props.activeWorkflowWorkItemId,
|
|
236
242
|
props.activeWorkflowScopeIds,
|
|
@@ -276,10 +282,12 @@ export const TwProseMirrorSurface = forwardRef<
|
|
|
276
282
|
revisionModel,
|
|
277
283
|
markupDisplay,
|
|
278
284
|
showTrackedChanges,
|
|
285
|
+
suggestionsEnabled,
|
|
279
286
|
props.workflowScopes,
|
|
280
287
|
snapshot.activeStory,
|
|
281
288
|
props.workflowCandidates,
|
|
282
289
|
props.workflowBlockedReasons,
|
|
290
|
+
props.workflowLockedZones,
|
|
283
291
|
props.activeWorkflowWorkItemId,
|
|
284
292
|
props.activeWorkflowScopeIds,
|
|
285
293
|
props.workflowMetadata,
|
|
@@ -300,11 +308,13 @@ export const TwProseMirrorSurface = forwardRef<
|
|
|
300
308
|
props.activeWorkflowScopeIds,
|
|
301
309
|
props.activeWorkflowWorkItemId,
|
|
302
310
|
props.workflowBlockedReasons,
|
|
311
|
+
props.workflowLockedZones,
|
|
303
312
|
props.workflowMetadata,
|
|
304
313
|
props.workflowCandidates,
|
|
305
314
|
props.workflowScopes,
|
|
306
315
|
revisionModel,
|
|
307
316
|
showTrackedChanges,
|
|
317
|
+
suggestionsEnabled,
|
|
308
318
|
],
|
|
309
319
|
);
|
|
310
320
|
|
|
@@ -331,10 +341,12 @@ export const TwProseMirrorSurface = forwardRef<
|
|
|
331
341
|
revisionModel,
|
|
332
342
|
markupDisplay,
|
|
333
343
|
showTrackedChanges,
|
|
344
|
+
suggestionsEnabled,
|
|
334
345
|
props.workflowScopes,
|
|
335
346
|
snapshot.activeStory,
|
|
336
347
|
props.workflowCandidates,
|
|
337
348
|
props.workflowBlockedReasons,
|
|
349
|
+
props.workflowLockedZones,
|
|
338
350
|
props.activeWorkflowWorkItemId,
|
|
339
351
|
props.activeWorkflowScopeIds,
|
|
340
352
|
props.workflowMetadata,
|
|
@@ -898,6 +910,24 @@ function createWorkflowBlockedSignature(
|
|
|
898
910
|
).join("|");
|
|
899
911
|
}
|
|
900
912
|
|
|
913
|
+
function createWorkflowLockedZoneSignature(
|
|
914
|
+
lockedZones: readonly WorkflowLockedZone[] | undefined,
|
|
915
|
+
): string {
|
|
916
|
+
if (!lockedZones || lockedZones.length === 0) {
|
|
917
|
+
return "";
|
|
918
|
+
}
|
|
919
|
+
return lockedZones.map((zone) =>
|
|
920
|
+
[
|
|
921
|
+
zone.fragmentId,
|
|
922
|
+
zone.code,
|
|
923
|
+
zone.label,
|
|
924
|
+
zone.detail,
|
|
925
|
+
serializeAnchorSignature(zone.anchor),
|
|
926
|
+
serializeStoryTargetSignature(zone.storyTarget),
|
|
927
|
+
].join(":")
|
|
928
|
+
).join("|");
|
|
929
|
+
}
|
|
930
|
+
|
|
901
931
|
function createWorkflowMetadataSignature(
|
|
902
932
|
metadata: readonly WorkflowMetadataMarkup[] | undefined,
|
|
903
933
|
): string {
|
|
@@ -922,6 +952,7 @@ function serializeAnchorSignature(
|
|
|
922
952
|
| WorkflowScope["anchor"]
|
|
923
953
|
| WorkflowCandidateRange["anchor"]
|
|
924
954
|
| WorkflowBlockedCommandReason["anchor"]
|
|
955
|
+
| WorkflowLockedZone["anchor"]
|
|
925
956
|
| WorkflowMetadataMarkup["anchor"]
|
|
926
957
|
| undefined,
|
|
927
958
|
): string {
|
|
@@ -326,6 +326,10 @@
|
|
|
326
326
|
text-underline-offset: 0.18em;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
+
.prosemirror-surface .ProseMirror .wre-workflow-inline-locked-zone {
|
|
330
|
+
border-radius: 0.35rem;
|
|
331
|
+
}
|
|
332
|
+
|
|
329
333
|
.prosemirror-surface .ProseMirror .wre-workflow-rail {
|
|
330
334
|
position: relative;
|
|
331
335
|
padding-left: 0.875rem;
|
|
@@ -421,6 +425,10 @@
|
|
|
421
425
|
);
|
|
422
426
|
}
|
|
423
427
|
|
|
428
|
+
.prosemirror-surface .ProseMirror .wre-workflow-rail-locked-zone {
|
|
429
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--wre-workflow-rail-color, var(--color-danger)) 10%, transparent);
|
|
430
|
+
}
|
|
431
|
+
|
|
424
432
|
.prosemirror-surface .ProseMirror .wre-workflow-rail-selection-zone {
|
|
425
433
|
background: transparent;
|
|
426
434
|
}
|