@beyondwork/docx-react-component 1.0.28 → 1.0.30
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 +26 -37
- package/src/api/public-types.ts +531 -0
- package/src/api/session-state.ts +2 -0
- package/src/core/commands/index.ts +201 -79
- package/src/core/commands/table-structure-commands.ts +138 -5
- package/src/core/state/text-transaction.ts +370 -3
- package/src/index.ts +41 -0
- package/src/io/docx-session.ts +318 -25
- package/src/io/export/serialize-footnotes.ts +41 -46
- package/src/io/export/serialize-headers-footers.ts +36 -40
- package/src/io/export/serialize-main-document.ts +55 -89
- package/src/io/export/serialize-numbering.ts +104 -4
- package/src/io/export/serialize-runtime-revisions.ts +196 -2
- package/src/io/export/split-story-blocks-for-runtime-revisions.ts +252 -0
- package/src/io/export/table-properties-xml.ts +318 -0
- package/src/io/normalize/normalize-text.ts +34 -3
- package/src/io/ooxml/parse-comments.ts +6 -0
- package/src/io/ooxml/parse-footnotes.ts +69 -13
- package/src/io/ooxml/parse-headers-footers.ts +54 -11
- package/src/io/ooxml/parse-main-document.ts +112 -42
- package/src/io/ooxml/parse-numbering.ts +341 -26
- package/src/io/ooxml/parse-revisions.ts +118 -4
- package/src/io/ooxml/parse-styles.ts +176 -0
- package/src/io/ooxml/parse-tables.ts +34 -25
- package/src/io/ooxml/revision-boundaries.ts +127 -3
- package/src/io/ooxml/workflow-payload.ts +544 -0
- package/src/model/canonical-document.ts +91 -1
- package/src/model/snapshot.ts +112 -1
- package/src/preservation/store.ts +73 -3
- package/src/review/store/comment-store.ts +19 -1
- package/src/review/store/revision-actions.ts +29 -0
- package/src/review/store/revision-store.ts +12 -1
- package/src/review/store/revision-types.ts +11 -0
- package/src/runtime/context-analytics.ts +824 -0
- package/src/runtime/document-locations.ts +521 -0
- package/src/runtime/document-navigation.ts +14 -1
- package/src/runtime/document-outline.ts +440 -0
- package/src/runtime/document-runtime.ts +941 -45
- package/src/runtime/event-refresh-hints.ts +137 -0
- package/src/runtime/numbering-prefix.ts +67 -39
- package/src/runtime/page-layout-estimation.ts +100 -7
- package/src/runtime/resolved-numbering-geometry.ts +293 -0
- package/src/runtime/session-capabilities.ts +2 -2
- package/src/runtime/suggestions-snapshot.ts +137 -0
- package/src/runtime/surface-projection.ts +223 -27
- package/src/runtime/table-style-resolver.ts +409 -0
- package/src/runtime/view-state.ts +17 -1
- package/src/runtime/workflow-markup.ts +54 -14
- package/src/ui/WordReviewEditor.tsx +1269 -87
- package/src/ui/editor-command-bag.ts +7 -0
- package/src/ui/editor-runtime-boundary.ts +111 -10
- package/src/ui/editor-shell-view.tsx +17 -15
- package/src/ui/editor-surface-controller.tsx +5 -0
- package/src/ui/headless/selection-tool-context.ts +19 -0
- package/src/ui/headless/selection-tool-resolver.ts +752 -0
- package/src/ui/headless/selection-tool-types.ts +129 -0
- package/src/ui/headless/selection-toolbar-model.ts +10 -33
- package/src/ui/runtime-shortcut-dispatch.ts +365 -0
- package/src/ui-tailwind/chrome/chrome-preset-model.ts +107 -0
- package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +15 -0
- package/src/ui-tailwind/chrome/review-queue-bar.tsx +97 -0
- package/src/ui-tailwind/chrome/tw-context-analytics-summary.tsx +122 -0
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +1 -9
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +1 -5
- package/src/ui-tailwind/chrome/tw-page-ruler.tsx +8 -29
- package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +23 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +35 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-formatting.tsx +37 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +298 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +116 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-suggestion.tsx +29 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +27 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +3 -3
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +3 -3
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +86 -14
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +57 -52
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +36 -52
- package/src/ui-tailwind/editor-surface/pm-schema.ts +56 -5
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +87 -24
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +4 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +135 -32
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +74 -7
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +17 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +19 -17
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +10 -10
- package/src/ui-tailwind/status/tw-status-bar.tsx +10 -6
- package/src/ui-tailwind/theme/editor-theme.css +58 -40
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -4
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +250 -181
- package/src/ui-tailwind/tw-review-workspace.tsx +323 -280
- package/src/validation/compatibility-engine.ts +246 -2
- package/src/validation/docx-comment-proof.ts +24 -11
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
StyleCatalogSnapshot,
|
|
5
|
+
TableOperationCapabilitySnapshot,
|
|
6
|
+
TableStructureContextSnapshot,
|
|
7
|
+
} from "../../api/public-types";
|
|
4
8
|
import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-editor-selection";
|
|
5
9
|
|
|
6
10
|
export interface TwTableContextToolbarProps {
|
|
7
11
|
disabled: boolean;
|
|
12
|
+
tableContext: TableStructureContextSnapshot | null;
|
|
8
13
|
tableStyles: StyleCatalogSnapshot["tables"];
|
|
9
14
|
onSetTableStyle?: (styleId: string) => void;
|
|
10
15
|
onAddRowBefore?: () => void;
|
|
@@ -38,13 +43,25 @@ export function TwTableContextToolbar(props: TwTableContextToolbarProps) {
|
|
|
38
43
|
Table
|
|
39
44
|
</span>
|
|
40
45
|
|
|
46
|
+
{props.tableContext?.currentCell.isHeader ? (
|
|
47
|
+
<span className="rounded bg-accent-soft px-2 py-1 text-[10px] font-medium text-accent">
|
|
48
|
+
Header row
|
|
49
|
+
</span>
|
|
50
|
+
) : null}
|
|
51
|
+
|
|
41
52
|
<select
|
|
42
53
|
aria-label="Table style"
|
|
43
54
|
className="h-8 rounded-md border border-border bg-canvas px-2 text-xs text-primary disabled:opacity-40"
|
|
44
|
-
disabled={
|
|
55
|
+
disabled={
|
|
56
|
+
props.disabled ||
|
|
57
|
+
props.tableStyles.length === 0 ||
|
|
58
|
+
!props.onSetTableStyle ||
|
|
59
|
+
!props.tableContext?.operations.setTableStyle.enabled
|
|
60
|
+
}
|
|
45
61
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
46
62
|
onChange={(event) => props.onSetTableStyle?.(event.target.value)}
|
|
47
|
-
|
|
63
|
+
value={props.tableContext?.currentStyleId ?? ""}
|
|
64
|
+
title={props.tableContext?.operations.setTableStyle.reason}
|
|
48
65
|
>
|
|
49
66
|
<option value="" disabled>Table style</option>
|
|
50
67
|
{props.tableStyles.map((style) => (
|
|
@@ -54,28 +71,68 @@ export function TwTableContextToolbar(props: TwTableContextToolbarProps) {
|
|
|
54
71
|
))}
|
|
55
72
|
</select>
|
|
56
73
|
|
|
57
|
-
<ToolbarButton
|
|
74
|
+
<ToolbarButton
|
|
75
|
+
ariaLabel="Add row above"
|
|
76
|
+
capability={props.tableContext?.operations.addRowBefore}
|
|
77
|
+
disabled={props.disabled}
|
|
78
|
+
onClick={props.onAddRowBefore}
|
|
79
|
+
>
|
|
58
80
|
Row above
|
|
59
81
|
</ToolbarButton>
|
|
60
|
-
<ToolbarButton
|
|
82
|
+
<ToolbarButton
|
|
83
|
+
ariaLabel="Add row below"
|
|
84
|
+
capability={props.tableContext?.operations.addRowAfter}
|
|
85
|
+
disabled={props.disabled}
|
|
86
|
+
onClick={props.onAddRowAfter}
|
|
87
|
+
>
|
|
61
88
|
Row below
|
|
62
89
|
</ToolbarButton>
|
|
63
|
-
<ToolbarButton
|
|
90
|
+
<ToolbarButton
|
|
91
|
+
ariaLabel="Delete row"
|
|
92
|
+
capability={props.tableContext?.operations.deleteRow}
|
|
93
|
+
disabled={props.disabled}
|
|
94
|
+
onClick={props.onDeleteRow}
|
|
95
|
+
>
|
|
64
96
|
Delete row
|
|
65
97
|
</ToolbarButton>
|
|
66
|
-
<ToolbarButton
|
|
98
|
+
<ToolbarButton
|
|
99
|
+
ariaLabel="Add column left"
|
|
100
|
+
capability={props.tableContext?.operations.addColumnBefore}
|
|
101
|
+
disabled={props.disabled}
|
|
102
|
+
onClick={props.onAddColumnBefore}
|
|
103
|
+
>
|
|
67
104
|
Column left
|
|
68
105
|
</ToolbarButton>
|
|
69
|
-
<ToolbarButton
|
|
106
|
+
<ToolbarButton
|
|
107
|
+
ariaLabel="Add column right"
|
|
108
|
+
capability={props.tableContext?.operations.addColumnAfter}
|
|
109
|
+
disabled={props.disabled}
|
|
110
|
+
onClick={props.onAddColumnAfter}
|
|
111
|
+
>
|
|
70
112
|
Column right
|
|
71
113
|
</ToolbarButton>
|
|
72
|
-
<ToolbarButton
|
|
114
|
+
<ToolbarButton
|
|
115
|
+
ariaLabel="Delete column"
|
|
116
|
+
capability={props.tableContext?.operations.deleteColumn}
|
|
117
|
+
disabled={props.disabled}
|
|
118
|
+
onClick={props.onDeleteColumn}
|
|
119
|
+
>
|
|
73
120
|
Delete column
|
|
74
121
|
</ToolbarButton>
|
|
75
|
-
<ToolbarButton
|
|
122
|
+
<ToolbarButton
|
|
123
|
+
ariaLabel="Merge cells"
|
|
124
|
+
capability={props.tableContext?.operations.mergeCells}
|
|
125
|
+
disabled={props.disabled}
|
|
126
|
+
onClick={props.onMergeCells}
|
|
127
|
+
>
|
|
76
128
|
Merge
|
|
77
129
|
</ToolbarButton>
|
|
78
|
-
<ToolbarButton
|
|
130
|
+
<ToolbarButton
|
|
131
|
+
ariaLabel="Split cell"
|
|
132
|
+
capability={props.tableContext?.operations.splitCell}
|
|
133
|
+
disabled={props.disabled}
|
|
134
|
+
onClick={props.onSplitCell}
|
|
135
|
+
>
|
|
79
136
|
Split
|
|
80
137
|
</ToolbarButton>
|
|
81
138
|
|
|
@@ -86,16 +143,27 @@ export function TwTableContextToolbar(props: TwTableContextToolbarProps) {
|
|
|
86
143
|
key={color}
|
|
87
144
|
type="button"
|
|
88
145
|
aria-label={`Set cell fill ${color}`}
|
|
89
|
-
disabled={
|
|
146
|
+
disabled={
|
|
147
|
+
props.disabled ||
|
|
148
|
+
!props.onSetCellBackground ||
|
|
149
|
+
!props.tableContext?.operations.setCellBackground.enabled
|
|
150
|
+
}
|
|
90
151
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
91
152
|
onClick={() => props.onSetCellBackground?.(color)}
|
|
92
153
|
className="h-6 w-6 rounded border border-border disabled:opacity-40"
|
|
93
154
|
style={{ backgroundColor: color }}
|
|
155
|
+
title={props.tableContext?.operations.setCellBackground.reason}
|
|
94
156
|
/>
|
|
95
157
|
))}
|
|
96
158
|
</div>
|
|
97
159
|
|
|
98
|
-
<ToolbarButton
|
|
160
|
+
<ToolbarButton
|
|
161
|
+
ariaLabel="Delete table"
|
|
162
|
+
capability={props.tableContext?.operations.deleteTable}
|
|
163
|
+
danger
|
|
164
|
+
disabled={props.disabled}
|
|
165
|
+
onClick={props.onDeleteTable}
|
|
166
|
+
>
|
|
99
167
|
Delete table
|
|
100
168
|
</ToolbarButton>
|
|
101
169
|
</div>
|
|
@@ -104,18 +172,22 @@ export function TwTableContextToolbar(props: TwTableContextToolbarProps) {
|
|
|
104
172
|
|
|
105
173
|
function ToolbarButton(props: {
|
|
106
174
|
ariaLabel: string;
|
|
175
|
+
capability?: TableOperationCapabilitySnapshot;
|
|
107
176
|
children: React.ReactNode;
|
|
108
177
|
danger?: boolean;
|
|
109
178
|
disabled: boolean;
|
|
110
179
|
onClick?: () => void;
|
|
111
180
|
}) {
|
|
181
|
+
const capabilityEnabled = props.capability?.enabled ?? true;
|
|
182
|
+
const title = !capabilityEnabled ? props.capability?.reason : undefined;
|
|
112
183
|
return (
|
|
113
184
|
<button
|
|
114
185
|
type="button"
|
|
115
186
|
aria-label={props.ariaLabel}
|
|
116
|
-
disabled={props.disabled || !props.onClick}
|
|
187
|
+
disabled={props.disabled || !props.onClick || !capabilityEnabled}
|
|
117
188
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
118
189
|
onClick={props.onClick}
|
|
190
|
+
title={title}
|
|
119
191
|
className={`inline-flex h-8 items-center rounded-md px-2 text-xs font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${
|
|
120
192
|
props.danger
|
|
121
193
|
? "text-danger hover:bg-danger/10"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { NodeSelection, Plugin, PluginKey } from "prosemirror-state";
|
|
2
|
-
import { keymap } from "prosemirror-keymap";
|
|
3
2
|
import { columnResizing, goToNextCell, isInTable, tableEditing } from "prosemirror-tables";
|
|
4
3
|
|
|
5
4
|
import type { SelectionSnapshot } from "../../api/public-types";
|
|
@@ -7,6 +6,7 @@ import {
|
|
|
7
6
|
createNodeSelectionSnapshot,
|
|
8
7
|
createSelectionSnapshot,
|
|
9
8
|
} from "../../ui/headless/selection-helpers";
|
|
9
|
+
import { resolveSurfaceShortcut } from "../../ui/runtime-shortcut-dispatch";
|
|
10
10
|
import type { PositionMap } from "./pm-position-map";
|
|
11
11
|
|
|
12
12
|
export interface CommandBridgeCallbacks {
|
|
@@ -74,8 +74,10 @@ export function createCommandBridgePlugins(
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
const { anchor, head } = view.state.selection;
|
|
77
|
+
const runtimeAnchor = posMap.pmToRuntime(anchor);
|
|
78
|
+
const runtimeHead = posMap.pmToRuntime(head);
|
|
77
79
|
callbacks.onSelectionChange(
|
|
78
|
-
createSelectionSnapshot(
|
|
80
|
+
createSelectionSnapshot(runtimeAnchor, runtimeHead),
|
|
79
81
|
);
|
|
80
82
|
}
|
|
81
83
|
},
|
|
@@ -122,55 +124,58 @@ export function createCommandBridgePlugins(
|
|
|
122
124
|
},
|
|
123
125
|
});
|
|
124
126
|
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
127
|
+
// Keydown bridge: normalize mounted editor keys through the shared shortcut
|
|
128
|
+
// dispatcher so the PM layer and shell layer follow the same routing model.
|
|
129
|
+
const keydownPlugin = new Plugin({
|
|
130
|
+
props: {
|
|
131
|
+
handleKeyDown(view, event) {
|
|
132
|
+
if (isComposing) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const resolution = resolveSurfaceShortcut(
|
|
137
|
+
{
|
|
138
|
+
key: event.key,
|
|
139
|
+
ctrlKey: event.ctrlKey,
|
|
140
|
+
metaKey: event.metaKey,
|
|
141
|
+
altKey: event.altKey,
|
|
142
|
+
shiftKey: event.shiftKey,
|
|
143
|
+
},
|
|
144
|
+
{ inTable: isInTable(view.state) },
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
switch (resolution.kind) {
|
|
148
|
+
case "none":
|
|
149
|
+
return false;
|
|
150
|
+
case "delete-backward":
|
|
151
|
+
callbacks.onDeleteBackward();
|
|
152
|
+
return true;
|
|
153
|
+
case "delete-forward":
|
|
154
|
+
callbacks.onDeleteForward();
|
|
155
|
+
return true;
|
|
156
|
+
case "split-paragraph":
|
|
157
|
+
callbacks.onSplitParagraph();
|
|
158
|
+
return true;
|
|
159
|
+
case "insert-hard-break":
|
|
160
|
+
callbacks.onInsertHardBreak();
|
|
161
|
+
return true;
|
|
162
|
+
case "insert-tab":
|
|
163
|
+
callbacks.onInsertTab();
|
|
164
|
+
return true;
|
|
165
|
+
case "outdent-tab":
|
|
166
|
+
callbacks.onOutdentTab?.();
|
|
167
|
+
return true;
|
|
168
|
+
case "navigate-table-cell":
|
|
169
|
+
return goToNextCell(resolution.direction)(view.state, view.dispatch, view);
|
|
170
|
+
case "history":
|
|
171
|
+
if (resolution.history === "undo") {
|
|
172
|
+
callbacks.onUndo();
|
|
173
|
+
} else {
|
|
174
|
+
callbacks.onRedo();
|
|
175
|
+
}
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
},
|
|
174
179
|
},
|
|
175
180
|
});
|
|
176
181
|
|
|
@@ -180,5 +185,5 @@ export function createCommandBridgePlugins(
|
|
|
180
185
|
const tablePlugin = tableEditing();
|
|
181
186
|
const columnResizingPlugin = columnResizing();
|
|
182
187
|
|
|
183
|
-
return [filterPlugin, selectionPlugin, inputPlugin,
|
|
188
|
+
return [filterPlugin, selectionPlugin, inputPlugin, keydownPlugin, tablePlugin, columnResizingPlugin];
|
|
184
189
|
}
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
EditorStoryTarget,
|
|
10
10
|
WorkflowBlockedCommandReason,
|
|
11
11
|
WorkflowCandidateRange,
|
|
12
|
+
WorkflowMetadataMarkup,
|
|
12
13
|
WorkflowScope,
|
|
13
14
|
} from "../../api/public-types";
|
|
14
15
|
import { MAIN_STORY_TARGET, storyTargetsEqual } from "../../core/selection/mapping.ts";
|
|
@@ -74,6 +75,10 @@ function getWorkflowCandidateRailClass(): string {
|
|
|
74
75
|
return "wre-workflow-rail wre-workflow-rail-candidate";
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
function getWorkflowMetadataInlineClass(): string {
|
|
79
|
+
return "wre-workflow-inline wre-workflow-inline-metadata";
|
|
80
|
+
}
|
|
81
|
+
|
|
77
82
|
function getWorkflowBlockedInlineClass(reason: WorkflowBlockedCommandReason): string {
|
|
78
83
|
if (reason.code === "workflow_blocked_import") {
|
|
79
84
|
return "wre-workflow-inline wre-workflow-inline-blocked-import";
|
|
@@ -211,13 +216,11 @@ export function buildDecorations(
|
|
|
211
216
|
workflowBlockedReasons?: readonly WorkflowBlockedCommandReason[],
|
|
212
217
|
activeWorkflowWorkItemId?: string | null,
|
|
213
218
|
activeWorkflowScopeIds?: readonly string[],
|
|
214
|
-
|
|
219
|
+
workflowMetadata?: readonly WorkflowMetadataMarkup[],
|
|
215
220
|
): DecorationSet {
|
|
216
221
|
const decorations: Decoration[] = [];
|
|
217
222
|
const railRangeCache = new Map<string, Array<{ from: number; to: number }>>();
|
|
218
223
|
const activeScopeIds = new Set(activeWorkflowScopeIds ?? []);
|
|
219
|
-
// In suggestions mode, tracked changes are always shown regardless of the toggle.
|
|
220
|
-
const effectiveShowTracked = suggestionsEnabled ? true : showTrackedChanges;
|
|
221
224
|
|
|
222
225
|
// Walk comment threads and create inline decorations
|
|
223
226
|
if (commentModel) {
|
|
@@ -251,7 +254,7 @@ export function buildDecorations(
|
|
|
251
254
|
// Always hide deletions in clean mode (final-text semantics).
|
|
252
255
|
// This is the critical behavior: "hide tracked changes" must show
|
|
253
256
|
// the document as if accepted, not show deleted text as kept text.
|
|
254
|
-
if (markupDisplay === "clean" && rev.kind === "deletion"
|
|
257
|
+
if (markupDisplay === "clean" && rev.kind === "deletion") {
|
|
255
258
|
const pmFrom = positionMap.runtimeToPm(rev.from);
|
|
256
259
|
const pmTo = positionMap.runtimeToPm(rev.to);
|
|
257
260
|
if (pmFrom < pmTo) {
|
|
@@ -266,48 +269,7 @@ export function buildDecorations(
|
|
|
266
269
|
}
|
|
267
270
|
|
|
268
271
|
// Skip visual styling when tracked changes display is off
|
|
269
|
-
if (!
|
|
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
|
-
}
|
|
272
|
+
if (!showTrackedChanges) continue;
|
|
311
273
|
|
|
312
274
|
const cls = getRevisionHighlightClass(
|
|
313
275
|
revisionModel,
|
|
@@ -317,12 +279,16 @@ export function buildDecorations(
|
|
|
317
279
|
);
|
|
318
280
|
if (!cls) continue;
|
|
319
281
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
282
|
+
const pmFrom = positionMap.runtimeToPm(rev.from);
|
|
283
|
+
const pmTo = positionMap.runtimeToPm(rev.to);
|
|
284
|
+
if (pmFrom < pmTo) {
|
|
285
|
+
decorations.push(
|
|
286
|
+
Decoration.inline(pmFrom, pmTo, {
|
|
287
|
+
class: cls,
|
|
288
|
+
"data-revision-id": rev.revisionId,
|
|
289
|
+
}),
|
|
290
|
+
);
|
|
291
|
+
}
|
|
326
292
|
}
|
|
327
293
|
}
|
|
328
294
|
|
|
@@ -365,6 +331,24 @@ export function buildDecorations(
|
|
|
365
331
|
}
|
|
366
332
|
}
|
|
367
333
|
|
|
334
|
+
if (workflowMetadata) {
|
|
335
|
+
for (const metadata of workflowMetadata) {
|
|
336
|
+
const metadataStoryTarget = metadata.storyTarget ?? MAIN_STORY_TARGET;
|
|
337
|
+
if (!storyTargetsEqual(metadataStoryTarget, activeStory)) continue;
|
|
338
|
+
const pmRange = buildAnchorPmRange(metadata.anchor, positionMap);
|
|
339
|
+
if (!pmRange || !pmRange.allowInline || pmRange.from >= pmRange.to) continue;
|
|
340
|
+
|
|
341
|
+
decorations.push(
|
|
342
|
+
Decoration.inline(pmRange.from, pmRange.to, {
|
|
343
|
+
class: getWorkflowMetadataInlineClass(),
|
|
344
|
+
...(metadata.color ? { style: `--wre-workflow-metadata-color: ${metadata.color};` } : {}),
|
|
345
|
+
"data-workflow-entry-id": metadata.entryId,
|
|
346
|
+
"data-workflow-metadata-id": metadata.metadataId,
|
|
347
|
+
}),
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
368
352
|
if (workflowCandidates) {
|
|
369
353
|
for (const candidate of workflowCandidates) {
|
|
370
354
|
const candidateStoryTarget = candidate.storyTarget ?? MAIN_STORY_TARGET;
|
|
@@ -99,6 +99,20 @@ function sanitizeLinkHref(raw: string | null | undefined): string | null {
|
|
|
99
99
|
return null;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
function resolveMarkerJustificationCss(raw: string | null): string {
|
|
103
|
+
switch (raw) {
|
|
104
|
+
case "left":
|
|
105
|
+
return "flex-start";
|
|
106
|
+
case "center":
|
|
107
|
+
return "center";
|
|
108
|
+
case "right":
|
|
109
|
+
case "both":
|
|
110
|
+
case "distribute":
|
|
111
|
+
default:
|
|
112
|
+
return "flex-end";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
102
116
|
/**
|
|
103
117
|
* ProseMirror schema for the supported live surface slice.
|
|
104
118
|
*
|
|
@@ -121,6 +135,8 @@ export const editorSchema = new Schema({
|
|
|
121
135
|
numberingLevel: { default: null },
|
|
122
136
|
numberingPrefix: { default: null },
|
|
123
137
|
numberingSuffix: { default: null },
|
|
138
|
+
numberingMarkerWidth: { default: null },
|
|
139
|
+
numberingMarkerJustification: { default: null },
|
|
124
140
|
alignment: { default: null },
|
|
125
141
|
spacingBefore: { default: null },
|
|
126
142
|
spacingAfter: { default: null },
|
|
@@ -142,6 +158,7 @@ export const editorSchema = new Schema({
|
|
|
142
158
|
outlineLevel: { default: null },
|
|
143
159
|
bidi: { default: null },
|
|
144
160
|
pageBreakBefore: { default: null },
|
|
161
|
+
hiddenTextOnly: { default: null },
|
|
145
162
|
},
|
|
146
163
|
parseDOM: [{ tag: "p" }],
|
|
147
164
|
toDOM(node) {
|
|
@@ -191,6 +208,12 @@ export const editorSchema = new Schema({
|
|
|
191
208
|
}
|
|
192
209
|
const pageBreak = node.attrs.pageBreakBefore as boolean | null;
|
|
193
210
|
if (pageBreak) styles.push("border-top: 2px dashed rgba(0,0,0,0.1); padding-top: 8px; margin-top: 16px");
|
|
211
|
+
const hiddenTextOnly = node.attrs.hiddenTextOnly as boolean | null;
|
|
212
|
+
if (hiddenTextOnly) {
|
|
213
|
+
attrs["data-hidden-paragraph"] = "true";
|
|
214
|
+
attrs["aria-hidden"] = "true";
|
|
215
|
+
styles.push("display: none");
|
|
216
|
+
}
|
|
194
217
|
const bidi = node.attrs.bidi as boolean | null;
|
|
195
218
|
if (bidi) attrs.dir = "rtl";
|
|
196
219
|
if (headingLevel) {
|
|
@@ -218,6 +241,8 @@ export const editorSchema = new Schema({
|
|
|
218
241
|
const numberingPrefix = node.attrs.numberingPrefix as string | null;
|
|
219
242
|
const numberingLevel = node.attrs.numberingLevel as number | null;
|
|
220
243
|
const numberingSuffix = node.attrs.numberingSuffix as "tab" | "space" | "nothing" | null;
|
|
244
|
+
const numberingMarkerWidth = node.attrs.numberingMarkerWidth as number | null;
|
|
245
|
+
const numberingMarkerJustification = node.attrs.numberingMarkerJustification as string | null;
|
|
221
246
|
const children: Array<string | number | readonly unknown[]> = [];
|
|
222
247
|
if (pageBreak) {
|
|
223
248
|
children.push([
|
|
@@ -232,20 +257,46 @@ export const editorSchema = new Schema({
|
|
|
232
257
|
]);
|
|
233
258
|
}
|
|
234
259
|
if (numberingPrefix) {
|
|
235
|
-
const
|
|
236
|
-
|
|
260
|
+
const hasResolvedMarkerWidth =
|
|
261
|
+
typeof numberingMarkerWidth === "number" && numberingMarkerWidth > 0;
|
|
262
|
+
const fallbackMinWidth = Math.min(Math.max(numberingPrefix.length + 1, 4), 14);
|
|
263
|
+
const fallbackMarginRight =
|
|
264
|
+
numberingSuffix === "nothing"
|
|
265
|
+
? "0.25rem"
|
|
266
|
+
: numberingSuffix === "space"
|
|
267
|
+
? "0.5rem"
|
|
268
|
+
: "0.75rem";
|
|
269
|
+
const prefixStyles = [
|
|
270
|
+
`font-variant-numeric: tabular-nums`,
|
|
271
|
+
`justify-content: ${resolveMarkerJustificationCss(numberingMarkerJustification)}`,
|
|
272
|
+
];
|
|
273
|
+
if (hasResolvedMarkerWidth) {
|
|
274
|
+
const markerWidthPx = Math.max(1, Math.round(numberingMarkerWidth / 20));
|
|
275
|
+
prefixStyles.push(
|
|
276
|
+
`width: ${markerWidthPx}px`,
|
|
277
|
+
`min-width: ${markerWidthPx}px`,
|
|
278
|
+
`flex-basis: ${markerWidthPx}px`,
|
|
279
|
+
`margin-right: 0`,
|
|
280
|
+
`overflow: visible`,
|
|
281
|
+
);
|
|
282
|
+
} else {
|
|
283
|
+
prefixStyles.push(
|
|
284
|
+
`min-width: ${fallbackMinWidth}ch`,
|
|
285
|
+
`margin-right: ${fallbackMarginRight}`,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
237
288
|
children.push([
|
|
238
289
|
"span",
|
|
239
290
|
{
|
|
240
291
|
class:
|
|
241
|
-
"inline-flex select-none items-center
|
|
292
|
+
"inline-flex select-none items-center text-tertiary font-[family-name:var(--font-legal-sans)]",
|
|
242
293
|
contenteditable: "false",
|
|
243
294
|
"data-numbering-prefix": numberingPrefix,
|
|
244
295
|
...(typeof numberingLevel === "number"
|
|
245
296
|
? { "data-numbering-level": String(numberingLevel) }
|
|
246
297
|
: {}),
|
|
247
298
|
...(numberingSuffix ? { "data-numbering-suffix": numberingSuffix } : {}),
|
|
248
|
-
style:
|
|
299
|
+
style: prefixStyles.join("; "),
|
|
249
300
|
},
|
|
250
301
|
numberingPrefix,
|
|
251
302
|
]);
|
|
@@ -569,7 +620,7 @@ export const editorSchema = new Schema({
|
|
|
569
620
|
return [
|
|
570
621
|
"span",
|
|
571
622
|
{
|
|
572
|
-
class: "inline-flex items-center gap-1 mx-0.5 px-1.5 py-0.5
|
|
623
|
+
class: "inline-flex items-center gap-1 mx-0.5 rounded border border-success/25 bg-success-soft px-1.5 py-0.5 text-xs text-success",
|
|
573
624
|
"data-node-type": "shape_atom",
|
|
574
625
|
contenteditable: "false",
|
|
575
626
|
title: (node.attrs.detail as string) ?? label,
|