@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
|
@@ -90,11 +90,17 @@ export interface ParagraphStyleDefinition {
|
|
|
90
90
|
basedOn?: string;
|
|
91
91
|
nextStyle?: string;
|
|
92
92
|
outlineLevel?: number;
|
|
93
|
+
numbering?: ParagraphStyleNumberingReference;
|
|
93
94
|
displayName: string;
|
|
94
95
|
kind: "paragraph";
|
|
95
96
|
isDefault: boolean;
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
export interface ParagraphStyleNumberingReference {
|
|
100
|
+
numberingInstanceId: string;
|
|
101
|
+
level?: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
98
104
|
export interface CharacterStyleDefinition {
|
|
99
105
|
styleId: string;
|
|
100
106
|
basedOn?: string;
|
|
@@ -109,6 +115,8 @@ export interface TableStyleDefinition {
|
|
|
109
115
|
displayName: string;
|
|
110
116
|
kind: "table";
|
|
111
117
|
isDefault: boolean;
|
|
118
|
+
formatting?: TableStyleFormatting;
|
|
119
|
+
conditionalFormatting?: Partial<Record<TableStyleConditionalRegion, TableStyleFormatting>>;
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
export interface LatentStyleDefinition {
|
|
@@ -130,6 +138,13 @@ export interface AbstractNumberingDefinition {
|
|
|
130
138
|
levels: NumberingLevelDefinition[];
|
|
131
139
|
}
|
|
132
140
|
|
|
141
|
+
export interface NumberingLevelParagraphGeometry {
|
|
142
|
+
justification?: "left" | "center" | "right" | "both" | "distribute";
|
|
143
|
+
spacing?: ParagraphSpacing;
|
|
144
|
+
indentation?: ParagraphIndentation;
|
|
145
|
+
tabStops?: TabStop[];
|
|
146
|
+
}
|
|
147
|
+
|
|
133
148
|
export interface NumberingLevelDefinition {
|
|
134
149
|
level: number;
|
|
135
150
|
format: string;
|
|
@@ -138,6 +153,18 @@ export interface NumberingLevelDefinition {
|
|
|
138
153
|
paragraphStyleId?: string;
|
|
139
154
|
isLegalNumbering?: boolean;
|
|
140
155
|
suffix?: "tab" | "space" | "nothing";
|
|
156
|
+
paragraphGeometry?: NumberingLevelParagraphGeometry;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface NumberingLevelOverrideDefinition {
|
|
160
|
+
level: number;
|
|
161
|
+
format?: string;
|
|
162
|
+
text?: string;
|
|
163
|
+
startAt?: number;
|
|
164
|
+
paragraphStyleId?: string;
|
|
165
|
+
isLegalNumbering?: boolean;
|
|
166
|
+
suffix?: "tab" | "space" | "nothing";
|
|
167
|
+
paragraphGeometry?: NumberingLevelParagraphGeometry;
|
|
141
168
|
}
|
|
142
169
|
|
|
143
170
|
export interface NumberingInstance {
|
|
@@ -149,6 +176,7 @@ export interface NumberingInstance {
|
|
|
149
176
|
export interface NumberingLevelOverride {
|
|
150
177
|
level: number;
|
|
151
178
|
startAt?: number;
|
|
179
|
+
levelDefinition?: NumberingLevelOverrideDefinition;
|
|
152
180
|
}
|
|
153
181
|
|
|
154
182
|
export interface MediaCatalog {
|
|
@@ -303,7 +331,7 @@ export interface ParagraphIndentation {
|
|
|
303
331
|
|
|
304
332
|
export interface TabStop {
|
|
305
333
|
position: number;
|
|
306
|
-
align: "left" | "center" | "right" | "decimal" | "bar" | "clear";
|
|
334
|
+
align: "left" | "center" | "right" | "decimal" | "num" | "bar" | "clear";
|
|
307
335
|
leader?: "none" | "dot" | "hyphen" | "underscore" | "heavy" | "middleDot";
|
|
308
336
|
}
|
|
309
337
|
|
|
@@ -400,6 +428,37 @@ export interface TableLook {
|
|
|
400
428
|
noVBand?: boolean;
|
|
401
429
|
}
|
|
402
430
|
|
|
431
|
+
export type TableStyleConditionalRegion =
|
|
432
|
+
| "firstRow"
|
|
433
|
+
| "lastRow"
|
|
434
|
+
| "firstColumn"
|
|
435
|
+
| "lastColumn"
|
|
436
|
+
| "band1Horz"
|
|
437
|
+
| "band2Horz"
|
|
438
|
+
| "band1Vert"
|
|
439
|
+
| "band2Vert";
|
|
440
|
+
|
|
441
|
+
export interface TableStyleFormatting {
|
|
442
|
+
table?: {
|
|
443
|
+
width?: TableWidth;
|
|
444
|
+
alignment?: "left" | "center" | "right";
|
|
445
|
+
borders?: TableBorders;
|
|
446
|
+
cellMargins?: TableCellMargins;
|
|
447
|
+
tblLook?: TableLook;
|
|
448
|
+
};
|
|
449
|
+
row?: {
|
|
450
|
+
height?: number;
|
|
451
|
+
heightRule?: "auto" | "atLeast" | "exact";
|
|
452
|
+
isHeader?: boolean;
|
|
453
|
+
};
|
|
454
|
+
cell?: {
|
|
455
|
+
width?: TableWidth;
|
|
456
|
+
borders?: TableCellBorders;
|
|
457
|
+
shading?: CellShading;
|
|
458
|
+
verticalAlign?: "top" | "center" | "bottom";
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
403
462
|
export interface TableNode {
|
|
404
463
|
type: "table";
|
|
405
464
|
styleId?: string;
|
|
@@ -476,6 +535,7 @@ export interface CustomXmlNode {
|
|
|
476
535
|
type: "custom_xml";
|
|
477
536
|
uri?: string;
|
|
478
537
|
element?: string;
|
|
538
|
+
rawXml?: string;
|
|
479
539
|
children: BlockNode[];
|
|
480
540
|
}
|
|
481
541
|
|
|
@@ -935,6 +995,8 @@ export interface CommentThreadMetadata {
|
|
|
935
995
|
source?: "runtime" | "import";
|
|
936
996
|
rootOoxmlCommentId?: string;
|
|
937
997
|
rootParaId?: string;
|
|
998
|
+
detachedReason?: "incomplete-markers" | "multi-paragraph" | "opaque-region" | "revision-overlap";
|
|
999
|
+
actionabilityNote?: string;
|
|
938
1000
|
}
|
|
939
1001
|
|
|
940
1002
|
export interface RevisionPropertyChangeData {
|
|
@@ -945,6 +1007,7 @@ export interface RevisionPropertyChangeData {
|
|
|
945
1007
|
export interface RevisionMoveData {
|
|
946
1008
|
moveId: string;
|
|
947
1009
|
direction: "from" | "to";
|
|
1010
|
+
linkedRevisionId?: string;
|
|
948
1011
|
}
|
|
949
1012
|
|
|
950
1013
|
export type RevisionStoryTargetRecord =
|
|
@@ -979,6 +1042,17 @@ export interface RevisionMetadataRecord {
|
|
|
979
1042
|
source?: "runtime" | "import";
|
|
980
1043
|
storyTarget?: RevisionStoryTargetRecord;
|
|
981
1044
|
preserveOnlyReason?: string;
|
|
1045
|
+
suggestionId?: string;
|
|
1046
|
+
semanticKind?:
|
|
1047
|
+
| "insertion"
|
|
1048
|
+
| "deletion"
|
|
1049
|
+
| "replacement"
|
|
1050
|
+
| "formatting-change"
|
|
1051
|
+
| "paragraph-property-change"
|
|
1052
|
+
| "structural-change"
|
|
1053
|
+
| "object-change";
|
|
1054
|
+
linkedRevisionIds?: string[];
|
|
1055
|
+
predecessorSuggestionId?: string;
|
|
982
1056
|
importedRevisionForm?:
|
|
983
1057
|
| "run-insertion"
|
|
984
1058
|
| "run-deletion"
|
|
@@ -1909,6 +1983,9 @@ function validateRevisionMetadata(
|
|
|
1909
1983
|
}
|
|
1910
1984
|
for (const field of [
|
|
1911
1985
|
"preserveOnlyReason",
|
|
1986
|
+
"suggestionId",
|
|
1987
|
+
"semanticKind",
|
|
1988
|
+
"predecessorSuggestionId",
|
|
1912
1989
|
"importedRevisionForm",
|
|
1913
1990
|
"originalRevisionType",
|
|
1914
1991
|
"ooxmlRevisionId",
|
|
@@ -1918,6 +1995,19 @@ function validateRevisionMetadata(
|
|
|
1918
1995
|
}
|
|
1919
1996
|
}
|
|
1920
1997
|
|
|
1998
|
+
if (record.linkedRevisionIds !== undefined) {
|
|
1999
|
+
if (!Array.isArray(record.linkedRevisionIds)) {
|
|
2000
|
+
issues.push({
|
|
2001
|
+
path: `${path}.linkedRevisionIds`,
|
|
2002
|
+
message: "linkedRevisionIds must be an array of strings.",
|
|
2003
|
+
});
|
|
2004
|
+
} else {
|
|
2005
|
+
for (const [index, value] of record.linkedRevisionIds.entries()) {
|
|
2006
|
+
expectString(value, `${path}.linkedRevisionIds.${index}`, issues);
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
|
|
1921
2011
|
if (record.propertyChangeData !== undefined) {
|
|
1922
2012
|
const pcd = asPlainObject(record.propertyChangeData, `${path}.propertyChangeData`, issues);
|
|
1923
2013
|
if (pcd) {
|
package/src/model/snapshot.ts
CHANGED
|
@@ -98,6 +98,30 @@ export interface PersistedSourcePackage {
|
|
|
98
98
|
bytesBase64: Base64;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
export interface PersistedWorkflowMetadataDefinition {
|
|
102
|
+
metadataId: string;
|
|
103
|
+
kind: string;
|
|
104
|
+
label: string;
|
|
105
|
+
color?: string;
|
|
106
|
+
icon?: string;
|
|
107
|
+
persistence: "runtime-only" | "session" | "document-metadata";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface PersistedWorkflowMetadataEntry {
|
|
111
|
+
entryId: string;
|
|
112
|
+
metadataId: string;
|
|
113
|
+
anchor: Record<string, unknown>;
|
|
114
|
+
storyTarget?: Record<string, unknown>;
|
|
115
|
+
value?: Record<string, unknown>;
|
|
116
|
+
scopeId?: string;
|
|
117
|
+
workItemId?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface PersistedWorkflowMetadataSnapshot {
|
|
121
|
+
definitions: PersistedWorkflowMetadataDefinition[];
|
|
122
|
+
entries: PersistedWorkflowMetadataEntry[];
|
|
123
|
+
}
|
|
124
|
+
|
|
101
125
|
export interface PersistedEditorSnapshot {
|
|
102
126
|
snapshotVersion: PersistedEditorSnapshotVersion;
|
|
103
127
|
schemaVersion: typeof CDS_SCHEMA_VERSION;
|
|
@@ -112,6 +136,7 @@ export interface PersistedEditorSnapshot {
|
|
|
112
136
|
warningLog: EditorWarning[];
|
|
113
137
|
protectionSnapshot?: ProtectionSnapshotRecord;
|
|
114
138
|
sourcePackage?: PersistedSourcePackage;
|
|
139
|
+
workflowMetadata?: PersistedWorkflowMetadataSnapshot;
|
|
115
140
|
}
|
|
116
141
|
|
|
117
142
|
const SNAPSHOT_REQUIRED_TOP_LEVEL_KEYS = [
|
|
@@ -128,7 +153,7 @@ const SNAPSHOT_REQUIRED_TOP_LEVEL_KEYS = [
|
|
|
128
153
|
"warningLog",
|
|
129
154
|
] as const;
|
|
130
155
|
|
|
131
|
-
const SNAPSHOT_OPTIONAL_TOP_LEVEL_KEYS = ["sourcePackage", "protectionSnapshot"] as const;
|
|
156
|
+
const SNAPSHOT_OPTIONAL_TOP_LEVEL_KEYS = ["sourcePackage", "protectionSnapshot", "workflowMetadata"] as const;
|
|
132
157
|
|
|
133
158
|
export interface ProtectionRangeRecord {
|
|
134
159
|
rangeId: string;
|
|
@@ -283,6 +308,9 @@ export function validatePersistedEditorSnapshot(
|
|
|
283
308
|
if (record.protectionSnapshot !== undefined) {
|
|
284
309
|
validateProtectionSnapshot(record.protectionSnapshot, "$.protectionSnapshot", issues);
|
|
285
310
|
}
|
|
311
|
+
if (record.workflowMetadata !== undefined) {
|
|
312
|
+
validateWorkflowMetadataSnapshot(record.workflowMetadata, "$.workflowMetadata", issues);
|
|
313
|
+
}
|
|
286
314
|
|
|
287
315
|
return issues;
|
|
288
316
|
}
|
|
@@ -614,6 +642,89 @@ function validateProtectionSnapshot(
|
|
|
614
642
|
}
|
|
615
643
|
}
|
|
616
644
|
|
|
645
|
+
function validateWorkflowMetadataSnapshot(
|
|
646
|
+
value: unknown,
|
|
647
|
+
path: string,
|
|
648
|
+
issues: ModelValidationIssue[],
|
|
649
|
+
): void {
|
|
650
|
+
const record = asPlainObject(value, path, issues);
|
|
651
|
+
if (!record) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
if (!Array.isArray(record.definitions)) {
|
|
655
|
+
issues.push({ path: `${path}.definitions`, message: "definitions must be an array." });
|
|
656
|
+
} else {
|
|
657
|
+
record.definitions.forEach((definition, index) =>
|
|
658
|
+
validateWorkflowMetadataDefinition(definition, `${path}.definitions[${index}]`, issues),
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
if (!Array.isArray(record.entries)) {
|
|
662
|
+
issues.push({ path: `${path}.entries`, message: "entries must be an array." });
|
|
663
|
+
} else {
|
|
664
|
+
record.entries.forEach((entry, index) =>
|
|
665
|
+
validateWorkflowMetadataEntry(entry, `${path}.entries[${index}]`, issues),
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function validateWorkflowMetadataDefinition(
|
|
671
|
+
value: unknown,
|
|
672
|
+
path: string,
|
|
673
|
+
issues: ModelValidationIssue[],
|
|
674
|
+
): void {
|
|
675
|
+
const record = asPlainObject(value, path, issues);
|
|
676
|
+
if (!record) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
expectString(record.metadataId, `${path}.metadataId`, issues);
|
|
680
|
+
expectString(record.kind, `${path}.kind`, issues);
|
|
681
|
+
expectString(record.label, `${path}.label`, issues);
|
|
682
|
+
if (record.color !== undefined) {
|
|
683
|
+
expectString(record.color, `${path}.color`, issues);
|
|
684
|
+
}
|
|
685
|
+
if (record.icon !== undefined) {
|
|
686
|
+
expectString(record.icon, `${path}.icon`, issues);
|
|
687
|
+
}
|
|
688
|
+
if (
|
|
689
|
+
record.persistence !== "runtime-only" &&
|
|
690
|
+
record.persistence !== "session" &&
|
|
691
|
+
record.persistence !== "document-metadata"
|
|
692
|
+
) {
|
|
693
|
+
issues.push({
|
|
694
|
+
path: `${path}.persistence`,
|
|
695
|
+
message: "persistence must be runtime-only, session, or document-metadata.",
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function validateWorkflowMetadataEntry(
|
|
701
|
+
value: unknown,
|
|
702
|
+
path: string,
|
|
703
|
+
issues: ModelValidationIssue[],
|
|
704
|
+
): void {
|
|
705
|
+
const record = asPlainObject(value, path, issues);
|
|
706
|
+
if (!record) {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
expectString(record.entryId, `${path}.entryId`, issues);
|
|
710
|
+
expectString(record.metadataId, `${path}.metadataId`, issues);
|
|
711
|
+
if (!asPlainObject(record.anchor, `${path}.anchor`, issues)) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (record.storyTarget !== undefined) {
|
|
715
|
+
asPlainObject(record.storyTarget, `${path}.storyTarget`, issues);
|
|
716
|
+
}
|
|
717
|
+
if (record.value !== undefined) {
|
|
718
|
+
asPlainObject(record.value, `${path}.value`, issues);
|
|
719
|
+
}
|
|
720
|
+
if (record.scopeId !== undefined) {
|
|
721
|
+
expectString(record.scopeId, `${path}.scopeId`, issues);
|
|
722
|
+
}
|
|
723
|
+
if (record.workItemId !== undefined) {
|
|
724
|
+
expectString(record.workItemId, `${path}.workItemId`, issues);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
617
728
|
function validateProtectionRange(
|
|
618
729
|
value: unknown,
|
|
619
730
|
path: string,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
BlockNode,
|
|
2
3
|
DocRange,
|
|
3
4
|
OpaqueFragmentRecord,
|
|
4
5
|
PreservationStore,
|
|
@@ -21,6 +22,11 @@ export interface OpaqueFragmentDescriptor {
|
|
|
21
22
|
detail: string;
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
const BLOCKED_IMPORT_FEATURE_KEYS = new Set<OpaqueFragmentDescriptor["featureKey"]>([
|
|
26
|
+
"alt-chunk",
|
|
27
|
+
"custom-xml",
|
|
28
|
+
]);
|
|
29
|
+
|
|
24
30
|
export function createPreservationStore(
|
|
25
31
|
seed?: Partial<PreservationStore>,
|
|
26
32
|
): PreservationStore {
|
|
@@ -122,16 +128,18 @@ export function describeOpaqueFragment(
|
|
|
122
128
|
if (/\b(?:w:)?altChunk\b/u.test(xml)) {
|
|
123
129
|
return {
|
|
124
130
|
featureKey: "alt-chunk",
|
|
125
|
-
label: "
|
|
131
|
+
label: "AltChunk import",
|
|
126
132
|
detail,
|
|
127
133
|
};
|
|
128
134
|
}
|
|
129
135
|
|
|
130
136
|
if (/\b(?:w:)?object\b|\b(?:o:)?OLEObject\b/u.test(xml)) {
|
|
137
|
+
const isOleObject = /\b(?:o:)?OLEObject\b/u.test(xml);
|
|
138
|
+
const label = isOleObject ? "OLE embedded object" : "Embedded object";
|
|
131
139
|
return {
|
|
132
140
|
featureKey: "embedded-objects",
|
|
133
|
-
label
|
|
134
|
-
detail
|
|
141
|
+
label,
|
|
142
|
+
detail: `${detail} Preserve-only embedded object retained for export and Word reopen.`,
|
|
135
143
|
};
|
|
136
144
|
}
|
|
137
145
|
|
|
@@ -150,6 +158,54 @@ export function describeOpaqueFragment(
|
|
|
150
158
|
};
|
|
151
159
|
}
|
|
152
160
|
|
|
161
|
+
export function describeStructuredWrapperBlock(
|
|
162
|
+
block: BlockNode,
|
|
163
|
+
): OpaqueFragmentDescriptor | null {
|
|
164
|
+
if (block.type === "sdt") {
|
|
165
|
+
if (isTocContentControl(block)) {
|
|
166
|
+
return {
|
|
167
|
+
featureKey: "content-controls",
|
|
168
|
+
label: "TOC content control",
|
|
169
|
+
detail: "TOC content control remains a wrapper-heavy template structure and read-only.",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (block.properties.sdtType === "docPartObj") {
|
|
173
|
+
return {
|
|
174
|
+
featureKey: "content-controls",
|
|
175
|
+
label: "Template content control",
|
|
176
|
+
detail: "Template content control remains a wrapper-heavy document part and read-only.",
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (block.type === "custom_xml") {
|
|
182
|
+
const descriptor = [block.element, block.uri].filter(Boolean).join(" ");
|
|
183
|
+
return {
|
|
184
|
+
featureKey: "custom-xml",
|
|
185
|
+
label: "Custom XML wrapper",
|
|
186
|
+
detail: descriptor.length > 0
|
|
187
|
+
? `Custom XML wrapper ${descriptor} remains package-backed and read-only.`
|
|
188
|
+
: "Custom XML wrapper remains package-backed and read-only.",
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (block.type === "alt_chunk") {
|
|
193
|
+
return {
|
|
194
|
+
featureKey: "alt-chunk",
|
|
195
|
+
label: "AltChunk import",
|
|
196
|
+
detail: `AltChunk import remains package-backed and blocked through relationship ${block.relationshipId}.`,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function isBlockedImportFeatureKey(
|
|
204
|
+
featureKey: OpaqueFragmentDescriptor["featureKey"],
|
|
205
|
+
): boolean {
|
|
206
|
+
return BLOCKED_IMPORT_FEATURE_KEYS.has(featureKey);
|
|
207
|
+
}
|
|
208
|
+
|
|
153
209
|
function createDetail(fragment: OpaqueFragmentRecord): string {
|
|
154
210
|
const detail = [
|
|
155
211
|
`Preserved whole-unit from ${fragment.lastKnownRange.from}-${fragment.lastKnownRange.to}.`,
|
|
@@ -164,6 +220,20 @@ function createDetail(fragment: OpaqueFragmentRecord): string {
|
|
|
164
220
|
: "Preserved whole-unit to keep unsupported OOXML intact.";
|
|
165
221
|
}
|
|
166
222
|
|
|
223
|
+
function isTocContentControl(block: Extract<BlockNode, { type: "sdt" }>): boolean {
|
|
224
|
+
const searchText = [
|
|
225
|
+
block.properties.alias,
|
|
226
|
+
block.properties.tag,
|
|
227
|
+
block.properties.sdtType,
|
|
228
|
+
block.properties.propertiesXml,
|
|
229
|
+
]
|
|
230
|
+
.filter(Boolean)
|
|
231
|
+
.join(" ")
|
|
232
|
+
.toLowerCase();
|
|
233
|
+
|
|
234
|
+
return searchText.includes("table of contents") || /\btoc\b/u.test(searchText);
|
|
235
|
+
}
|
|
236
|
+
|
|
167
237
|
function rangesIntersect(left: DocRange, right: DocRange): boolean {
|
|
168
238
|
return left.from < right.to && right.from < left.to;
|
|
169
239
|
}
|
|
@@ -45,6 +45,8 @@ export interface CommentThreadMetadata {
|
|
|
45
45
|
source?: "runtime" | "import";
|
|
46
46
|
rootOoxmlCommentId?: string;
|
|
47
47
|
rootParaId?: string;
|
|
48
|
+
detachedReason?: "incomplete-markers" | "multi-paragraph" | "opaque-region" | "revision-overlap";
|
|
49
|
+
actionabilityNote?: string;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
export interface CommentStore {
|
|
@@ -261,6 +263,7 @@ function toSidebarThread(
|
|
|
261
263
|
): CommentSidebarThread {
|
|
262
264
|
const firstEntry = thread.entries[0];
|
|
263
265
|
const anchorSummary = summarizeCommentAnchor(thread.anchor);
|
|
266
|
+
const detachedReason = thread.metadata?.detachedReason;
|
|
264
267
|
|
|
265
268
|
return {
|
|
266
269
|
commentId: thread.commentId,
|
|
@@ -272,7 +275,9 @@ function toSidebarThread(
|
|
|
272
275
|
warningCount: thread.warningIds.length,
|
|
273
276
|
anchorLabel:
|
|
274
277
|
anchorSummary.state === "detached"
|
|
275
|
-
?
|
|
278
|
+
? detachedReason
|
|
279
|
+
? `Detached (${humanizeDetachedReason(detachedReason)}) ${anchorSummary.range.from}-${anchorSummary.range.to}`
|
|
280
|
+
: `Detached ${anchorSummary.range.from}-${anchorSummary.range.to}`
|
|
276
281
|
: `Range ${anchorSummary.range.from}-${anchorSummary.range.to}`,
|
|
277
282
|
isActive: activeCommentId === thread.commentId,
|
|
278
283
|
resolvedAt: thread.resolution?.resolvedAt,
|
|
@@ -310,6 +315,19 @@ function compareSidebarThreads(
|
|
|
310
315
|
return left.createdAt.localeCompare(right.createdAt);
|
|
311
316
|
}
|
|
312
317
|
|
|
318
|
+
function humanizeDetachedReason(reason: NonNullable<CommentThreadMetadata["detachedReason"]>): string {
|
|
319
|
+
switch (reason) {
|
|
320
|
+
case "incomplete-markers":
|
|
321
|
+
return "incomplete markers";
|
|
322
|
+
case "multi-paragraph":
|
|
323
|
+
return "multi-paragraph";
|
|
324
|
+
case "opaque-region":
|
|
325
|
+
return "opaque region";
|
|
326
|
+
case "revision-overlap":
|
|
327
|
+
return "revision overlap";
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
313
331
|
function priorityForStatus(status: CommentThreadStatus): number {
|
|
314
332
|
switch (status) {
|
|
315
333
|
case "open":
|
|
@@ -98,6 +98,23 @@ export function applyRevisionAction(
|
|
|
98
98
|
);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
if (canResolveImportedDeletionWithoutContentMutation(revision, options.intent)) {
|
|
102
|
+
const resultingStatus = toResultingStatus(options.intent);
|
|
103
|
+
return {
|
|
104
|
+
document: options.document,
|
|
105
|
+
store: setRevisionStatus(options.store, revision.revisionId, resultingStatus),
|
|
106
|
+
mapping: createEmptyMapping(),
|
|
107
|
+
outcome: {
|
|
108
|
+
kind: "applied",
|
|
109
|
+
revisionId: revision.revisionId,
|
|
110
|
+
intent: options.intent,
|
|
111
|
+
resultingStatus,
|
|
112
|
+
contentChanged: false,
|
|
113
|
+
},
|
|
114
|
+
detachedRevisionIds: [],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
101
118
|
const story = parseTextStory(options.document.content);
|
|
102
119
|
const range = normalizeRange(
|
|
103
120
|
revision.anchor.range.from,
|
|
@@ -270,6 +287,18 @@ function requiresContentDeletion(
|
|
|
270
287
|
);
|
|
271
288
|
}
|
|
272
289
|
|
|
290
|
+
function canResolveImportedDeletionWithoutContentMutation(
|
|
291
|
+
revision: RevisionRecord,
|
|
292
|
+
intent: RevisionActionIntent,
|
|
293
|
+
): boolean {
|
|
294
|
+
return (
|
|
295
|
+
revision.kind === "deletion" &&
|
|
296
|
+
intent === "accept" &&
|
|
297
|
+
revision.metadata.source === "import" &&
|
|
298
|
+
revision.metadata.originalRevisionType !== "paragraph-del"
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
273
302
|
function requiresParagraphBoundaryDeletion(
|
|
274
303
|
revision: RevisionRecord,
|
|
275
304
|
intent: RevisionActionIntent,
|
|
@@ -87,6 +87,10 @@ export function createRevisionRecord(
|
|
|
87
87
|
(getRevisionActionability(params.kind) === "preserve-only"
|
|
88
88
|
? "Imported preserve-only revision."
|
|
89
89
|
: undefined),
|
|
90
|
+
suggestionId: params.metadata?.suggestionId,
|
|
91
|
+
semanticKind: params.metadata?.semanticKind,
|
|
92
|
+
linkedRevisionIds: params.metadata?.linkedRevisionIds,
|
|
93
|
+
predecessorSuggestionId: params.metadata?.predecessorSuggestionId,
|
|
90
94
|
importedRevisionForm: params.metadata?.importedRevisionForm,
|
|
91
95
|
originalRevisionType: params.metadata?.originalRevisionType,
|
|
92
96
|
ooxmlRevisionId: params.metadata?.ooxmlRevisionId,
|
|
@@ -220,7 +224,14 @@ function toSidebarEntry(revision: RevisionRecord): RevisionSidebarEntry {
|
|
|
220
224
|
return {
|
|
221
225
|
revisionId: revision.revisionId,
|
|
222
226
|
kind: revision.kind,
|
|
223
|
-
label:
|
|
227
|
+
label:
|
|
228
|
+
revision.kind === "move"
|
|
229
|
+
? moveData?.direction === "from"
|
|
230
|
+
? "Move from"
|
|
231
|
+
: moveData?.direction === "to"
|
|
232
|
+
? "Move to"
|
|
233
|
+
: describeRevisionKind(revision.kind)
|
|
234
|
+
: describeRevisionKind(revision.kind),
|
|
224
235
|
status: revision.status,
|
|
225
236
|
actionability,
|
|
226
237
|
anchorLabel:
|
|
@@ -36,6 +36,17 @@ export interface RevisionMetadataEnvelope {
|
|
|
36
36
|
source: "runtime" | "import";
|
|
37
37
|
storyTarget?: RevisionStoryTargetRecord;
|
|
38
38
|
preserveOnlyReason?: string;
|
|
39
|
+
suggestionId?: string;
|
|
40
|
+
semanticKind?:
|
|
41
|
+
| "insertion"
|
|
42
|
+
| "deletion"
|
|
43
|
+
| "replacement"
|
|
44
|
+
| "formatting-change"
|
|
45
|
+
| "paragraph-property-change"
|
|
46
|
+
| "structural-change"
|
|
47
|
+
| "object-change";
|
|
48
|
+
linkedRevisionIds?: string[];
|
|
49
|
+
predecessorSuggestionId?: string;
|
|
39
50
|
importedRevisionForm?:
|
|
40
51
|
| "run-insertion"
|
|
41
52
|
| "run-deletion"
|