@beyondwork/docx-react-component 1.0.19 → 1.0.21
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 +44 -25
- package/src/api/public-types.ts +336 -0
- package/src/api/session-state.ts +2 -0
- package/src/core/commands/formatting-commands.ts +1 -1
- package/src/core/commands/index.ts +14 -2
- package/src/core/search/search-text.ts +28 -0
- package/src/core/state/editor-state.ts +3 -0
- package/src/index.ts +21 -0
- package/src/io/docx-session.ts +363 -17
- package/src/io/export/serialize-comments.ts +104 -34
- package/src/io/export/serialize-footnotes.ts +198 -1
- package/src/io/export/serialize-headers-footers.ts +203 -10
- package/src/io/export/serialize-main-document.ts +83 -3
- package/src/io/export/split-review-boundaries.ts +181 -19
- package/src/io/normalize/normalize-text.ts +82 -8
- package/src/io/ooxml/highlight-colors.ts +39 -0
- package/src/io/ooxml/parse-comments.ts +85 -19
- package/src/io/ooxml/parse-fields.ts +396 -0
- package/src/io/ooxml/parse-footnotes.ts +240 -2
- package/src/io/ooxml/parse-headers-footers.ts +431 -7
- package/src/io/ooxml/parse-inline-media.ts +15 -1
- package/src/io/ooxml/parse-main-document.ts +396 -14
- package/src/io/ooxml/parse-revisions.ts +317 -38
- package/src/legal/bookmarks.ts +44 -0
- package/src/legal/cross-references.ts +59 -1
- package/src/model/canonical-document.ts +117 -1
- package/src/model/snapshot.ts +85 -1
- package/src/review/store/revision-store.ts +6 -0
- package/src/review/store/revision-types.ts +1 -0
- package/src/runtime/document-navigation.ts +52 -13
- package/src/runtime/document-runtime.ts +1521 -75
- package/src/runtime/read-only-diagnostics-runtime.ts +8 -0
- package/src/runtime/session-capabilities.ts +33 -3
- package/src/runtime/surface-projection.ts +86 -25
- package/src/runtime/table-schema.ts +2 -2
- package/src/runtime/view-state.ts +24 -6
- package/src/runtime/workflow-markup.ts +349 -0
- package/src/ui/WordReviewEditor.tsx +915 -1314
- package/src/ui/editor-command-bag.ts +120 -0
- package/src/ui/editor-runtime-boundary.ts +1448 -0
- package/src/ui/editor-shell-view.tsx +134 -0
- package/src/ui/editor-surface-controller.tsx +55 -0
- package/src/ui/headless/revision-decoration-model.ts +4 -4
- package/src/ui/runtime-snapshot-selectors.ts +197 -0
- package/src/ui/workflow-surface-blocked-rails.ts +94 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +18 -2
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +129 -0
- package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +34 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +27 -2
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +128 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +86 -14
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +2 -2
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +237 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-schema.ts +139 -8
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +98 -48
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +55 -0
- package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +7 -1
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +190 -48
- package/src/ui-tailwind/page-chrome-model.ts +27 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +7 -7
- package/src/ui-tailwind/review/tw-health-panel.tsx +31 -2
- package/src/ui-tailwind/review/tw-review-rail.tsx +3 -3
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +15 -15
- package/src/ui-tailwind/theme/editor-theme.css +130 -0
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +543 -5
- package/src/ui-tailwind/tw-review-workspace.tsx +316 -19
- package/src/validation/compatibility-engine.ts +27 -4
- package/src/validation/compatibility-report.ts +1 -0
- package/src/validation/docx-comment-proof.ts +220 -0
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beyondwork/docx-react-component",
|
|
3
3
|
"publisher": "beyondwork",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.21",
|
|
5
5
|
"description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
|
|
6
|
+
"packageManager": "pnpm@10.30.3",
|
|
6
7
|
"type": "module",
|
|
7
8
|
"sideEffects": [
|
|
8
9
|
"**/*.css"
|
|
@@ -73,6 +74,14 @@
|
|
|
73
74
|
"types": "./src/core/commands/table-structure-commands.ts",
|
|
74
75
|
"import": "./src/core/commands/table-structure-commands.ts"
|
|
75
76
|
},
|
|
77
|
+
"./core/commands/style-commands": {
|
|
78
|
+
"types": "./src/core/commands/style-commands.ts",
|
|
79
|
+
"import": "./src/core/commands/style-commands.ts"
|
|
80
|
+
},
|
|
81
|
+
"./core/commands/section-layout-commands": {
|
|
82
|
+
"types": "./src/core/commands/section-layout-commands.ts",
|
|
83
|
+
"import": "./src/core/commands/section-layout-commands.ts"
|
|
84
|
+
},
|
|
76
85
|
"./core/state/editor-state": {
|
|
77
86
|
"types": "./src/core/state/editor-state.ts",
|
|
78
87
|
"import": "./src/core/state/editor-state.ts"
|
|
@@ -80,6 +89,30 @@
|
|
|
80
89
|
"./ui-tailwind/theme/editor-theme.css": "./src/ui-tailwind/theme/editor-theme.css"
|
|
81
90
|
},
|
|
82
91
|
"types": "./src/index.ts",
|
|
92
|
+
"scripts": {
|
|
93
|
+
"build": "tsup",
|
|
94
|
+
"test": "bash scripts/run-workspace-tests.sh",
|
|
95
|
+
"test:repo": "node scripts/run-repo-tests.mjs core",
|
|
96
|
+
"test:repo:all": "node scripts/run-repo-tests.mjs all",
|
|
97
|
+
"test:repo:optional": "node scripts/run-repo-tests.mjs optional",
|
|
98
|
+
"test:wcag-audit": "node scripts/run-repo-tests.mjs wcag-audit",
|
|
99
|
+
"test:harness": "pnpm --filter @docx-react-component/react-word-editor-harness test",
|
|
100
|
+
"lint": "pnpm run lint:no-authored-js && pnpm run lint:docs-contracts && pnpm run lint:tsgo && pnpm run lint:tsgo:harness",
|
|
101
|
+
"lint:docs-contracts": "bash scripts/check-reference-load-contract.sh",
|
|
102
|
+
"lint:no-authored-js": "bash scripts/check-no-authored-js.sh",
|
|
103
|
+
"lint:tsgo": "tsgo --noEmit -p tsconfig.build.json",
|
|
104
|
+
"lint:tsgo:harness": "pnpm --filter @docx-react-component/react-word-editor-harness lint:tsgo",
|
|
105
|
+
"context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
|
|
106
|
+
"wave:doctor": "bash scripts/context7-export-env.sh run pnpm exec wave doctor --json",
|
|
107
|
+
"wave:dry-run": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main --dry-run --no-dashboard",
|
|
108
|
+
"wave:launch": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main",
|
|
109
|
+
"wave:launch:managed": "bash scripts/wave-launch.sh",
|
|
110
|
+
"wave:status": "bash scripts/wave-status.sh",
|
|
111
|
+
"wave:watch": "bash scripts/wave-watch.sh --follow",
|
|
112
|
+
"wave:dashboard:current": "bash scripts/wave-dashboard-attach.sh current",
|
|
113
|
+
"wave:dashboard:global": "bash scripts/wave-dashboard-attach.sh global",
|
|
114
|
+
"harness:dev": "pnpm --filter @docx-react-component/react-word-editor-harness dev"
|
|
115
|
+
},
|
|
83
116
|
"keywords": [
|
|
84
117
|
"docx",
|
|
85
118
|
"word",
|
|
@@ -142,28 +175,14 @@
|
|
|
142
175
|
"tsup": "^8.3.0",
|
|
143
176
|
"tsx": "^4.21.0"
|
|
144
177
|
},
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
"
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
"lint:docs-contracts": "bash scripts/check-reference-load-contract.sh",
|
|
155
|
-
"lint:no-authored-js": "bash scripts/check-no-authored-js.sh",
|
|
156
|
-
"lint:tsgo": "tsgo --noEmit -p tsconfig.build.json",
|
|
157
|
-
"lint:tsgo:harness": "pnpm --filter @docx-react-component/react-word-editor-harness lint:tsgo",
|
|
158
|
-
"context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
|
|
159
|
-
"wave:doctor": "bash scripts/context7-export-env.sh run pnpm exec wave doctor --json",
|
|
160
|
-
"wave:dry-run": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main --dry-run --no-dashboard",
|
|
161
|
-
"wave:launch": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main",
|
|
162
|
-
"wave:launch:managed": "bash scripts/wave-launch.sh",
|
|
163
|
-
"wave:status": "bash scripts/wave-status.sh",
|
|
164
|
-
"wave:watch": "bash scripts/wave-watch.sh --follow",
|
|
165
|
-
"wave:dashboard:current": "bash scripts/wave-dashboard-attach.sh current",
|
|
166
|
-
"wave:dashboard:global": "bash scripts/wave-dashboard-attach.sh global",
|
|
167
|
-
"harness:dev": "pnpm --filter @docx-react-component/react-word-editor-harness dev"
|
|
178
|
+
"pnpm": {
|
|
179
|
+
"onlyBuiltDependencies": [
|
|
180
|
+
"esbuild",
|
|
181
|
+
"sharp"
|
|
182
|
+
],
|
|
183
|
+
"overrides": {
|
|
184
|
+
"react": "19.2.4",
|
|
185
|
+
"react-dom": "19.2.4"
|
|
186
|
+
}
|
|
168
187
|
}
|
|
169
|
-
}
|
|
188
|
+
}
|
package/src/api/public-types.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { PersistedEditorSnapshot as RuntimePersistedEditorSnapshot } from "../core/state/editor-state.ts";
|
|
2
|
+
import type {
|
|
3
|
+
FieldFamily as FieldFamilyType,
|
|
4
|
+
FieldRefreshStatus as FieldRefreshStatusType,
|
|
5
|
+
SupportedFieldFamily as SupportedFieldFamilyType,
|
|
6
|
+
} from "../model/canonical-document.ts";
|
|
7
|
+
|
|
8
|
+
export type FieldFamily = FieldFamilyType;
|
|
9
|
+
export type FieldRefreshStatus = FieldRefreshStatusType;
|
|
10
|
+
export type SupportedFieldFamily = SupportedFieldFamilyType;
|
|
2
11
|
|
|
3
12
|
export type ExternalDocumentSource =
|
|
4
13
|
| {
|
|
@@ -349,6 +358,70 @@ export interface DocumentHeadingSnapshot {
|
|
|
349
358
|
sectionIndex: number;
|
|
350
359
|
}
|
|
351
360
|
|
|
361
|
+
/** A single field entry in the runtime field snapshot. */
|
|
362
|
+
export interface FieldEntrySnapshot {
|
|
363
|
+
/** Stable index of the field in document order. */
|
|
364
|
+
index: number;
|
|
365
|
+
/** Classified field family. */
|
|
366
|
+
fieldFamily: FieldFamily;
|
|
367
|
+
/** Whether this family is in the supported refresh slice. */
|
|
368
|
+
supported: boolean;
|
|
369
|
+
/** Raw field instruction string. */
|
|
370
|
+
instruction: string;
|
|
371
|
+
/** Target bookmark for REF/PAGEREF/NOTEREF fields. */
|
|
372
|
+
fieldTarget?: string;
|
|
373
|
+
/** Current refresh status. */
|
|
374
|
+
refreshStatus: FieldRefreshStatus;
|
|
375
|
+
/** Resolved display text (from field children). */
|
|
376
|
+
displayText: string;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** Runtime field snapshot — read-only view of all fields in the document. */
|
|
380
|
+
export interface FieldSnapshot {
|
|
381
|
+
/** Total number of fields in the document. */
|
|
382
|
+
totalCount: number;
|
|
383
|
+
/** Number of fields in the supported refresh slice. */
|
|
384
|
+
supportedCount: number;
|
|
385
|
+
/** Number of fields that are preserve-only. */
|
|
386
|
+
preserveOnlyCount: number;
|
|
387
|
+
/** Ordered field entries. */
|
|
388
|
+
fields: FieldEntrySnapshot[];
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** Options for runtime-backed field refresh. */
|
|
392
|
+
export interface UpdateFieldsOptions {
|
|
393
|
+
/** When true, only refresh fields in the supported slice. Default true. */
|
|
394
|
+
supportedOnly?: boolean;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Result of a runtime-backed field refresh operation. */
|
|
398
|
+
export interface UpdateFieldsResult {
|
|
399
|
+
/** Total number of fields in the document. */
|
|
400
|
+
totalCount: number;
|
|
401
|
+
/** Number of fields that were refreshed. */
|
|
402
|
+
updatedCount: number;
|
|
403
|
+
/** Number of preserve-only fields left unchanged. */
|
|
404
|
+
preserveOnlyCount: number;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** Options for runtime-backed TOC refresh. */
|
|
408
|
+
export interface TocRefreshOptions {
|
|
409
|
+
/** Maximum heading outline level to include (1–9, default 3). */
|
|
410
|
+
maxLevel?: number;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** Result of a TOC refresh operation. */
|
|
414
|
+
export interface TocRefreshResult {
|
|
415
|
+
/** Number of TOC entries generated from heading structure. */
|
|
416
|
+
entryCount: number;
|
|
417
|
+
/** Heading entries that populated the TOC. */
|
|
418
|
+
entries: Array<{
|
|
419
|
+
level: number;
|
|
420
|
+
text: string;
|
|
421
|
+
pageIndex: number;
|
|
422
|
+
}>;
|
|
423
|
+
}
|
|
424
|
+
|
|
352
425
|
export interface StyleCatalogEntrySnapshot {
|
|
353
426
|
styleId: string;
|
|
354
427
|
displayName: string;
|
|
@@ -450,6 +523,17 @@ export type SurfaceInlineSegment =
|
|
|
450
523
|
noteKind: "footnote" | "endnote";
|
|
451
524
|
noteId: string;
|
|
452
525
|
label: string;
|
|
526
|
+
}
|
|
527
|
+
| {
|
|
528
|
+
segmentId: string;
|
|
529
|
+
kind: "field_ref";
|
|
530
|
+
from: number;
|
|
531
|
+
to: number;
|
|
532
|
+
fieldFamily: SupportedFieldFamily;
|
|
533
|
+
fieldTarget?: string;
|
|
534
|
+
instruction: string;
|
|
535
|
+
refreshStatus: FieldRefreshStatus;
|
|
536
|
+
label: string;
|
|
453
537
|
};
|
|
454
538
|
|
|
455
539
|
export interface SurfaceTableCellSnapshot {
|
|
@@ -488,6 +572,7 @@ export type SurfaceBlockSnapshot =
|
|
|
488
572
|
numberingSuffix?: "tab" | "space" | "nothing";
|
|
489
573
|
alignment?: "left" | "center" | "right" | "both" | "distribute";
|
|
490
574
|
spacing?: { before?: number; after?: number; line?: number; lineRule?: string };
|
|
575
|
+
contextualSpacing?: boolean;
|
|
491
576
|
indentation?: { left?: number; right?: number; firstLine?: number; hanging?: number };
|
|
492
577
|
borders?: { top?: unknown; left?: unknown; bottom?: unknown; right?: unknown; bar?: unknown; between?: unknown };
|
|
493
578
|
shading?: { fill?: string; color?: string; val?: string };
|
|
@@ -639,6 +724,61 @@ export interface CompatibilityPanelSnapshot {
|
|
|
639
724
|
|
|
640
725
|
export type ViewMode = "editing" | "review" | "view";
|
|
641
726
|
|
|
727
|
+
/**
|
|
728
|
+
* Runtime document-mode — controls whether editing is unrestricted, restricted
|
|
729
|
+
* to suggestions, or fully read-only at the document level.
|
|
730
|
+
*
|
|
731
|
+
* Distinct from `ViewMode` (editor posture) and `WorkspaceMode` (shell layout).
|
|
732
|
+
* `DocumentMode` reflects document-level editing authority, not presentation.
|
|
733
|
+
*/
|
|
734
|
+
export type DocumentMode = "editing" | "suggesting" | "viewing";
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* A single permission range parsed from `w:permStart` / `w:permEnd` in the
|
|
738
|
+
* source package. Ranges carry stable ids, edit-rights metadata, and an
|
|
739
|
+
* enforcement reason that the UI can display.
|
|
740
|
+
*/
|
|
741
|
+
export interface ProtectionRange {
|
|
742
|
+
/** Stable range id from `w:id` on the originating `w:permStart`. */
|
|
743
|
+
rangeId: string;
|
|
744
|
+
/** Start offset of the editable range when the runtime can map it safely. */
|
|
745
|
+
start?: number;
|
|
746
|
+
/** End offset of the editable range when the runtime can map it safely. */
|
|
747
|
+
end?: number;
|
|
748
|
+
/** Editor group allowed to edit this range (`w:edGrp`), if specified. */
|
|
749
|
+
editorGroup?: string;
|
|
750
|
+
/** Specific editor identity (`w:ed`), if specified. */
|
|
751
|
+
editor?: string;
|
|
752
|
+
/** Whether this range is currently enforced by the runtime. */
|
|
753
|
+
enforced: boolean;
|
|
754
|
+
/** Human-readable reason for the enforcement state. */
|
|
755
|
+
enforcementReason: string;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Snapshot of the document's protection posture. Descriptive and bounded —
|
|
760
|
+
* no public bypass around protected ranges, export blockers, or preserve-only
|
|
761
|
+
* regions.
|
|
762
|
+
*/
|
|
763
|
+
export interface ProtectionSnapshot {
|
|
764
|
+
/** Whether document-level protection is declared in the source package. */
|
|
765
|
+
hasDocumentProtection: boolean;
|
|
766
|
+
/**
|
|
767
|
+
* The declared protection edit type from `w:documentProtection` in
|
|
768
|
+
* `settings.xml` (e.g. `"readOnly"`, `"comments"`, `"trackedChanges"`,
|
|
769
|
+
* `"forms"`, `"none"`). `undefined` when no protection element exists.
|
|
770
|
+
*/
|
|
771
|
+
editType?: string;
|
|
772
|
+
/** Whether the declared protection is enforced (`w:enforcement="1"`). */
|
|
773
|
+
enforcementActive: boolean;
|
|
774
|
+
/** All parsed permission ranges from the source package. */
|
|
775
|
+
ranges: ProtectionRange[];
|
|
776
|
+
/** Count of permission ranges that are currently enforced. */
|
|
777
|
+
enforcedRangeCount: number;
|
|
778
|
+
/** Count of permission ranges preserved but not live-enforced. */
|
|
779
|
+
preservedRangeCount: number;
|
|
780
|
+
}
|
|
781
|
+
|
|
642
782
|
export type CaretAffinity = "forward" | "backward" | "none";
|
|
643
783
|
|
|
644
784
|
export interface ActiveListContext {
|
|
@@ -670,6 +810,7 @@ export interface LayoutMeasurement {
|
|
|
670
810
|
|
|
671
811
|
export interface EditorViewStateSnapshot {
|
|
672
812
|
viewMode: ViewMode;
|
|
813
|
+
documentMode: DocumentMode;
|
|
673
814
|
workspaceMode: WorkspaceMode;
|
|
674
815
|
zoomLevel: ZoomLevel;
|
|
675
816
|
activeStory: EditorStoryTarget;
|
|
@@ -697,6 +838,7 @@ export interface RuntimeRenderSnapshot {
|
|
|
697
838
|
isReady: boolean;
|
|
698
839
|
isDirty: boolean;
|
|
699
840
|
readOnly: boolean;
|
|
841
|
+
documentMode: DocumentMode;
|
|
700
842
|
selection: SelectionSnapshot;
|
|
701
843
|
activeStory: EditorStoryTarget;
|
|
702
844
|
pageLayout?: PageLayoutSnapshot;
|
|
@@ -708,6 +850,7 @@ export interface RuntimeRenderSnapshot {
|
|
|
708
850
|
fatalError?: EditorError;
|
|
709
851
|
commandState: CommandStateSnapshot;
|
|
710
852
|
surface?: EditorSurfaceSnapshot;
|
|
853
|
+
protectionSnapshot: ProtectionSnapshot;
|
|
711
854
|
}
|
|
712
855
|
|
|
713
856
|
export interface EditorSessionState {
|
|
@@ -721,6 +864,7 @@ export interface EditorSessionState {
|
|
|
721
864
|
canonicalDocument: RuntimePersistedEditorSnapshot["canonicalDocument"];
|
|
722
865
|
compatibility: CompatibilityReport;
|
|
723
866
|
warningLog: EditorWarning[];
|
|
867
|
+
protectionSnapshot?: ProtectionSnapshot;
|
|
724
868
|
sourcePackage?: RuntimePersistedEditorSnapshot["sourcePackage"];
|
|
725
869
|
}
|
|
726
870
|
|
|
@@ -736,6 +880,7 @@ export interface PersistedEditorSnapshot {
|
|
|
736
880
|
canonicalDocument: RuntimePersistedEditorSnapshot["canonicalDocument"];
|
|
737
881
|
compatibility: CompatibilityReport;
|
|
738
882
|
warningLog: EditorWarning[];
|
|
883
|
+
protectionSnapshot?: ProtectionSnapshot;
|
|
739
884
|
sourcePackage?: RuntimePersistedEditorSnapshot["sourcePackage"];
|
|
740
885
|
}
|
|
741
886
|
|
|
@@ -762,6 +907,169 @@ export interface ExportResult {
|
|
|
762
907
|
delivery: ExportDelivery;
|
|
763
908
|
}
|
|
764
909
|
|
|
910
|
+
export type WorkflowScopeMode = "edit" | "suggest" | "comment" | "view";
|
|
911
|
+
|
|
912
|
+
export interface WorkflowCandidateRange {
|
|
913
|
+
candidateId: string;
|
|
914
|
+
storyTarget?: EditorStoryTarget;
|
|
915
|
+
anchor: EditorAnchorProjection;
|
|
916
|
+
label?: string;
|
|
917
|
+
source?: "search" | "playbook" | "host" | "ai";
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
export interface WorkflowScope {
|
|
921
|
+
scopeId: string;
|
|
922
|
+
mode: WorkflowScopeMode;
|
|
923
|
+
anchor: EditorAnchorProjection;
|
|
924
|
+
storyTarget?: EditorStoryTarget;
|
|
925
|
+
workItemId?: string;
|
|
926
|
+
label?: string;
|
|
927
|
+
domain?: "legal" | "commercial" | "finance" | "other";
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
export interface WorkflowWorkItem {
|
|
931
|
+
workItemId: string;
|
|
932
|
+
title: string;
|
|
933
|
+
description?: string;
|
|
934
|
+
domain?: "legal" | "commercial" | "finance" | "other";
|
|
935
|
+
status?: "pending" | "active" | "done" | "blocked";
|
|
936
|
+
scopeIds: string[];
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
export interface WorkflowOverlay {
|
|
940
|
+
overlayVersion: "workflow-overlay/1";
|
|
941
|
+
candidates?: WorkflowCandidateRange[];
|
|
942
|
+
scopes: WorkflowScope[];
|
|
943
|
+
workItems?: WorkflowWorkItem[];
|
|
944
|
+
activeWorkItemId?: string | null;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
export interface WorkflowBlockedCommandReason {
|
|
948
|
+
code:
|
|
949
|
+
| "outside_workflow_scope"
|
|
950
|
+
| "workflow_comment_only"
|
|
951
|
+
| "workflow_view_only"
|
|
952
|
+
| "workflow_preserve_only"
|
|
953
|
+
| "workflow_blocked_import"
|
|
954
|
+
| "document_read_only"
|
|
955
|
+
| "document_viewing_mode"
|
|
956
|
+
| "protected_range"
|
|
957
|
+
| "unsupported_surface";
|
|
958
|
+
message: string;
|
|
959
|
+
scopeId?: string;
|
|
960
|
+
workItemId?: string;
|
|
961
|
+
anchor?: EditorAnchorProjection;
|
|
962
|
+
storyTarget?: EditorStoryTarget;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
export interface WorkflowScopeSnapshot {
|
|
966
|
+
overlayPresent: boolean;
|
|
967
|
+
activeWorkItemId?: string | null;
|
|
968
|
+
activeWorkItem?: WorkflowWorkItem;
|
|
969
|
+
scopes: WorkflowScope[];
|
|
970
|
+
candidates: WorkflowCandidateRange[];
|
|
971
|
+
blockedReasons: WorkflowBlockedCommandReason[];
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
export interface InteractionGuardSnapshot {
|
|
975
|
+
blockedReasons: WorkflowBlockedCommandReason[];
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
export type WorkflowMarkupKind =
|
|
979
|
+
| "highlight"
|
|
980
|
+
| "comment"
|
|
981
|
+
| "revision"
|
|
982
|
+
| "field"
|
|
983
|
+
| "protected_range"
|
|
984
|
+
| "opaque_fragment";
|
|
985
|
+
|
|
986
|
+
export interface WorkflowMarkupBase {
|
|
987
|
+
markupId: string;
|
|
988
|
+
kind: WorkflowMarkupKind;
|
|
989
|
+
anchor: EditorAnchorProjection;
|
|
990
|
+
storyTarget?: EditorStoryTarget;
|
|
991
|
+
label: string;
|
|
992
|
+
excerpt?: string;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
export interface WorkflowHighlightMarkup extends WorkflowMarkupBase {
|
|
996
|
+
kind: "highlight";
|
|
997
|
+
color: string;
|
|
998
|
+
text: string;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
export interface WorkflowCommentMarkup extends WorkflowMarkupBase {
|
|
1002
|
+
kind: "comment";
|
|
1003
|
+
commentId: string;
|
|
1004
|
+
status: CommentSidebarThreadSnapshot["status"];
|
|
1005
|
+
warningCount: number;
|
|
1006
|
+
entryCount: number;
|
|
1007
|
+
createdAt: string;
|
|
1008
|
+
createdBy: string;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
export interface WorkflowRevisionMarkup extends WorkflowMarkupBase {
|
|
1012
|
+
kind: "revision";
|
|
1013
|
+
revisionId: string;
|
|
1014
|
+
revisionKind: TrackedChangeEntrySnapshot["kind"];
|
|
1015
|
+
status: TrackedChangeEntrySnapshot["status"];
|
|
1016
|
+
actionability: TrackedChangeEntrySnapshot["actionability"];
|
|
1017
|
+
authorId: string;
|
|
1018
|
+
createdAt: string;
|
|
1019
|
+
preserveOnlyReason?: string;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
export interface WorkflowFieldMarkup extends WorkflowMarkupBase {
|
|
1023
|
+
kind: "field";
|
|
1024
|
+
fieldIndex: number;
|
|
1025
|
+
fieldFamily: FieldFamily;
|
|
1026
|
+
fieldTarget?: string;
|
|
1027
|
+
refreshStatus: FieldRefreshStatus;
|
|
1028
|
+
displayText: string;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
export interface WorkflowProtectedRangeMarkup extends WorkflowMarkupBase {
|
|
1032
|
+
kind: "protected_range";
|
|
1033
|
+
rangeId: string;
|
|
1034
|
+
enforced: boolean;
|
|
1035
|
+
enforcementReason: string;
|
|
1036
|
+
editorGroup?: string;
|
|
1037
|
+
editor?: string;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
export interface WorkflowOpaqueFragmentMarkup extends WorkflowMarkupBase {
|
|
1041
|
+
kind: "opaque_fragment";
|
|
1042
|
+
fragmentId: string;
|
|
1043
|
+
warningId: string;
|
|
1044
|
+
detail: string;
|
|
1045
|
+
blockedReasonCode: "workflow_preserve_only" | "workflow_blocked_import";
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
export type WorkflowMarkupItem =
|
|
1049
|
+
| WorkflowHighlightMarkup
|
|
1050
|
+
| WorkflowCommentMarkup
|
|
1051
|
+
| WorkflowRevisionMarkup
|
|
1052
|
+
| WorkflowFieldMarkup
|
|
1053
|
+
| WorkflowProtectedRangeMarkup
|
|
1054
|
+
| WorkflowOpaqueFragmentMarkup;
|
|
1055
|
+
|
|
1056
|
+
export interface WorkflowMarkupSnapshot {
|
|
1057
|
+
totalCount: number;
|
|
1058
|
+
items: WorkflowMarkupItem[];
|
|
1059
|
+
highlights: WorkflowHighlightMarkup[];
|
|
1060
|
+
comments: WorkflowCommentMarkup[];
|
|
1061
|
+
revisions: WorkflowRevisionMarkup[];
|
|
1062
|
+
fields: WorkflowFieldMarkup[];
|
|
1063
|
+
protectedRanges: WorkflowProtectedRangeMarkup[];
|
|
1064
|
+
opaqueFragments: WorkflowOpaqueFragmentMarkup[];
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
export interface WorkflowCandidateRangeOptions {
|
|
1068
|
+
kinds?: WorkflowMarkupKind[];
|
|
1069
|
+
includeDetached?: boolean;
|
|
1070
|
+
source?: WorkflowCandidateRange["source"];
|
|
1071
|
+
}
|
|
1072
|
+
|
|
765
1073
|
export type AutosaveState =
|
|
766
1074
|
| { status: "idle" }
|
|
767
1075
|
| { status: "saving" }
|
|
@@ -858,6 +1166,22 @@ export type WordReviewEditorEvent =
|
|
|
858
1166
|
type: "export_completed";
|
|
859
1167
|
documentId: string;
|
|
860
1168
|
result: ExportResult;
|
|
1169
|
+
}
|
|
1170
|
+
| {
|
|
1171
|
+
type: "workflow_overlay_changed";
|
|
1172
|
+
documentId: string;
|
|
1173
|
+
snapshot: WorkflowScopeSnapshot;
|
|
1174
|
+
}
|
|
1175
|
+
| {
|
|
1176
|
+
type: "workflow_active_work_item_changed";
|
|
1177
|
+
documentId: string;
|
|
1178
|
+
activeWorkItemId: string | null;
|
|
1179
|
+
}
|
|
1180
|
+
| {
|
|
1181
|
+
type: "command_blocked";
|
|
1182
|
+
documentId: string;
|
|
1183
|
+
command: string;
|
|
1184
|
+
reasons: WorkflowBlockedCommandReason[];
|
|
861
1185
|
};
|
|
862
1186
|
|
|
863
1187
|
export interface LoadResult {
|
|
@@ -980,7 +1304,12 @@ export interface WordReviewEditorRef {
|
|
|
980
1304
|
closeStory(): void;
|
|
981
1305
|
getPageLayoutSnapshot(): PageLayoutSnapshot | null;
|
|
982
1306
|
getDocumentNavigationSnapshot(): DocumentNavigationSnapshot;
|
|
1307
|
+
getFieldSnapshot(): FieldSnapshot;
|
|
1308
|
+
updateFields(options?: UpdateFieldsOptions): UpdateFieldsResult;
|
|
1309
|
+
updateTableOfContents(options?: TocRefreshOptions): TocRefreshResult;
|
|
983
1310
|
getViewState(): EditorViewStateSnapshot;
|
|
1311
|
+
setDocumentMode(mode: DocumentMode): void;
|
|
1312
|
+
getProtectionSnapshot(): ProtectionSnapshot;
|
|
984
1313
|
setWorkspaceMode(mode: WorkspaceMode): void;
|
|
985
1314
|
setZoom(level: ZoomLevel): void;
|
|
986
1315
|
insertSectionBreak(type: SectionBreakType, options?: { afterSectionIndex?: number }): void;
|
|
@@ -993,6 +1322,13 @@ export interface WordReviewEditorRef {
|
|
|
993
1322
|
setHeaderFooterLink(sectionIndex: number, params: HeaderFooterLinkPatch): void;
|
|
994
1323
|
setImageLayout(mediaId: string, dimensions: { widthEmu: number; heightEmu: number }): void;
|
|
995
1324
|
setImageFrame(mediaId: string, offsets: { horizontalOffsetEmu?: number; verticalOffsetEmu?: number }): void;
|
|
1325
|
+
setWorkflowOverlay(overlay: WorkflowOverlay): void;
|
|
1326
|
+
clearWorkflowOverlay(): void;
|
|
1327
|
+
getWorkflowScopeSnapshot(): WorkflowScopeSnapshot | null;
|
|
1328
|
+
getInteractionGuardSnapshot(): InteractionGuardSnapshot;
|
|
1329
|
+
getWorkflowMarkupSnapshot(): WorkflowMarkupSnapshot;
|
|
1330
|
+
getWorkflowCandidateRanges(options?: WorkflowCandidateRangeOptions): WorkflowCandidateRange[];
|
|
1331
|
+
replaceWorkflowMarkupText(markupId: string, text: string): void;
|
|
996
1332
|
}
|
|
997
1333
|
|
|
998
1334
|
export interface WordReviewEditorProps {
|
package/src/api/session-state.ts
CHANGED
|
@@ -31,6 +31,7 @@ export function editorSessionStateFromPersistedSnapshot(
|
|
|
31
31
|
canonicalDocument: snapshot.canonicalDocument,
|
|
32
32
|
compatibility: snapshot.compatibility,
|
|
33
33
|
warningLog: snapshot.warningLog,
|
|
34
|
+
protectionSnapshot: snapshot.protectionSnapshot,
|
|
34
35
|
sourcePackage: snapshot.sourcePackage,
|
|
35
36
|
});
|
|
36
37
|
}
|
|
@@ -53,6 +54,7 @@ export function persistedSnapshotFromEditorSessionState(
|
|
|
53
54
|
canonicalDocument: sessionState.canonicalDocument,
|
|
54
55
|
compatibility: sessionState.compatibility,
|
|
55
56
|
warningLog: sessionState.warningLog,
|
|
57
|
+
protectionSnapshot: sessionState.protectionSnapshot,
|
|
56
58
|
sourcePackage: sessionState.sourcePackage,
|
|
57
59
|
});
|
|
58
60
|
}
|
|
@@ -973,7 +973,7 @@ function setMarkValue(
|
|
|
973
973
|
): TextMark[] | undefined {
|
|
974
974
|
const nextMarks = cloneMarks(marks).filter((candidate) => {
|
|
975
975
|
if (markType === "backgroundColor") {
|
|
976
|
-
return candidate.type !== "backgroundColor";
|
|
976
|
+
return candidate.type !== "backgroundColor" && candidate.type !== "highlight";
|
|
977
977
|
}
|
|
978
978
|
return candidate.type !== markType;
|
|
979
979
|
});
|
|
@@ -60,6 +60,7 @@ export type EditorCommand =
|
|
|
60
60
|
document: CanonicalDocumentEnvelope;
|
|
61
61
|
mapping?: TransactionMapping;
|
|
62
62
|
selection?: SelectionSnapshot;
|
|
63
|
+
protectionSelection?: SelectionSnapshot;
|
|
63
64
|
origin?: CommandOrigin;
|
|
64
65
|
}
|
|
65
66
|
| {
|
|
@@ -110,6 +111,7 @@ export type EditorCommand =
|
|
|
110
111
|
| {
|
|
111
112
|
type: "comment.add";
|
|
112
113
|
comment: CommentThreadRecord;
|
|
114
|
+
selection?: SelectionSnapshot;
|
|
113
115
|
origin?: CommandOrigin;
|
|
114
116
|
}
|
|
115
117
|
| {
|
|
@@ -615,7 +617,12 @@ export function remapSelection(
|
|
|
615
617
|
}
|
|
616
618
|
|
|
617
619
|
if (activeRange.kind === "node") {
|
|
618
|
-
return
|
|
620
|
+
return {
|
|
621
|
+
anchor: activeRange.at,
|
|
622
|
+
head: activeRange.at,
|
|
623
|
+
isCollapsed: true,
|
|
624
|
+
activeRange,
|
|
625
|
+
};
|
|
619
626
|
}
|
|
620
627
|
|
|
621
628
|
return {
|
|
@@ -680,7 +687,12 @@ function normalizeSelection(selection: SelectionSnapshot): SelectionSnapshot {
|
|
|
680
687
|
}
|
|
681
688
|
|
|
682
689
|
if (activeRange.kind === "node") {
|
|
683
|
-
return
|
|
690
|
+
return {
|
|
691
|
+
anchor: activeRange.at,
|
|
692
|
+
head: activeRange.at,
|
|
693
|
+
isCollapsed: true,
|
|
694
|
+
activeRange,
|
|
695
|
+
};
|
|
684
696
|
}
|
|
685
697
|
|
|
686
698
|
return {
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
SearchOptions as PublicSearchOptions,
|
|
4
4
|
SecondaryStorySurface,
|
|
5
5
|
SurfaceBlockSnapshot,
|
|
6
|
+
WorkflowCandidateRange,
|
|
6
7
|
} from "../../api/public-types";
|
|
7
8
|
|
|
8
9
|
export interface SearchTextOptions extends PublicSearchOptions {
|
|
@@ -156,6 +157,33 @@ export function searchSecondaryStories(
|
|
|
156
157
|
return results;
|
|
157
158
|
}
|
|
158
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Derive workflow candidate ranges from search results.
|
|
162
|
+
*
|
|
163
|
+
* Maps search hits into `WorkflowCandidateRange` values suitable for
|
|
164
|
+
* inclusion in a host-fed `WorkflowOverlay`. The runtime search results
|
|
165
|
+
* supply runtime-mapped `from`/`to` positions that align with the
|
|
166
|
+
* canonical document model.
|
|
167
|
+
*/
|
|
168
|
+
export function deriveCandidateRangesFromSearch(
|
|
169
|
+
results: readonly SurfaceSearchResult[],
|
|
170
|
+
storyTarget?: EditorStoryTarget,
|
|
171
|
+
source: WorkflowCandidateRange["source"] = "search",
|
|
172
|
+
): WorkflowCandidateRange[] {
|
|
173
|
+
return results.map((result, index) => ({
|
|
174
|
+
candidateId: `search-candidate-${index}`,
|
|
175
|
+
storyTarget,
|
|
176
|
+
anchor: {
|
|
177
|
+
kind: "range" as const,
|
|
178
|
+
from: result.from,
|
|
179
|
+
to: result.to,
|
|
180
|
+
assoc: { start: 1 as const, end: -1 as const },
|
|
181
|
+
},
|
|
182
|
+
label: result.excerpt,
|
|
183
|
+
source,
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
|
|
159
187
|
export function projectSurfaceText(
|
|
160
188
|
blocks: readonly SurfaceBlockSnapshot[],
|
|
161
189
|
): ProjectedSurfaceText {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
assertPersistedEditorSnapshot as assertModelPersistedEditorSnapshot,
|
|
11
11
|
createPersistedEditorSnapshot as createModelPersistedEditorSnapshot,
|
|
12
12
|
validatePersistedEditorSnapshot as validateModelPersistedEditorSnapshot,
|
|
13
|
+
type ProtectionSnapshotRecord as ModelProtectionSnapshotRecord,
|
|
13
14
|
type PersistedEditorSnapshot as ModelPersistedEditorSnapshot,
|
|
14
15
|
} from "../../model/snapshot.ts";
|
|
15
16
|
import {
|
|
@@ -638,6 +639,7 @@ export function createPersistedEditorSnapshot(
|
|
|
638
639
|
editorBuild: string;
|
|
639
640
|
savedAt: string;
|
|
640
641
|
compatibility?: CompatibilityReport;
|
|
642
|
+
protectionSnapshot?: ModelProtectionSnapshotRecord;
|
|
641
643
|
},
|
|
642
644
|
): PersistedEditorSnapshot {
|
|
643
645
|
const snapshot = createModelPersistedEditorSnapshot({
|
|
@@ -647,6 +649,7 @@ export function createPersistedEditorSnapshot(
|
|
|
647
649
|
canonicalDocument: state.document,
|
|
648
650
|
compatibility: (options.compatibility ?? state.compatibility) as never,
|
|
649
651
|
warningLog: state.warnings as never,
|
|
652
|
+
protectionSnapshot: options.protectionSnapshot,
|
|
650
653
|
sourcePackage: state.sourcePackage,
|
|
651
654
|
});
|
|
652
655
|
return snapshot as PersistedEditorSnapshot;
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,8 @@ export {
|
|
|
6
6
|
EDITOR_SESSION_STATE_VERSION,
|
|
7
7
|
} from "./api/session-state.ts";
|
|
8
8
|
export type {
|
|
9
|
+
LoadRequest,
|
|
10
|
+
LoadSourcePolicy,
|
|
9
11
|
EditorSessionState,
|
|
10
12
|
EditorHostAdapter,
|
|
11
13
|
WordReviewEditorProps,
|
|
@@ -72,4 +74,23 @@ export type {
|
|
|
72
74
|
SurfaceTextMark,
|
|
73
75
|
EditorSurfaceSnapshot,
|
|
74
76
|
CommandStateSnapshot,
|
|
77
|
+
WorkflowScopeMode,
|
|
78
|
+
WorkflowCandidateRange,
|
|
79
|
+
WorkflowScope,
|
|
80
|
+
WorkflowWorkItem,
|
|
81
|
+
WorkflowOverlay,
|
|
82
|
+
WorkflowBlockedCommandReason,
|
|
83
|
+
WorkflowScopeSnapshot,
|
|
84
|
+
InteractionGuardSnapshot,
|
|
85
|
+
WorkflowMarkupKind,
|
|
86
|
+
WorkflowMarkupBase,
|
|
87
|
+
WorkflowHighlightMarkup,
|
|
88
|
+
WorkflowCommentMarkup,
|
|
89
|
+
WorkflowRevisionMarkup,
|
|
90
|
+
WorkflowFieldMarkup,
|
|
91
|
+
WorkflowProtectedRangeMarkup,
|
|
92
|
+
WorkflowOpaqueFragmentMarkup,
|
|
93
|
+
WorkflowMarkupItem,
|
|
94
|
+
WorkflowMarkupSnapshot,
|
|
95
|
+
WorkflowCandidateRangeOptions,
|
|
75
96
|
} from "./api/public-types.ts";
|