@beyondwork/docx-react-component 1.0.42 → 1.0.45
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 +17 -0
- package/package.json +5 -4
- package/src/api/editor-state-types.ts +110 -0
- package/src/api/public-types.ts +333 -4
- package/src/core/commands/formatting-commands.ts +7 -1
- package/src/core/commands/index.ts +60 -10
- package/src/core/commands/text-commands.ts +59 -0
- package/src/core/search/search-text.ts +15 -2
- package/src/core/selection/review-anchors.ts +131 -21
- package/src/index.ts +29 -1
- package/src/io/chart-preview-resolver.ts +281 -0
- package/src/io/docx-session.ts +692 -2
- package/src/io/export/build-app-properties-xml.ts +1 -1
- package/src/io/export/serialize-comments.ts +38 -9
- package/src/io/export/twip.ts +1 -1
- package/src/io/load-scheduler.ts +230 -0
- package/src/io/normalize/normalize-text.ts +116 -0
- package/src/io/ooxml/parse-comments.ts +0 -33
- package/src/io/ooxml/parse-complex-content.ts +14 -0
- package/src/io/ooxml/parse-main-document.ts +4 -0
- package/src/io/ooxml/workflow-payload-validator.ts +97 -1
- package/src/io/ooxml/workflow-payload.ts +172 -1
- package/src/preservation/opaque-region.ts +5 -0
- package/src/review/store/comment-remapping.ts +2 -2
- package/src/runtime/collab-session.ts +1 -1
- package/src/runtime/document-runtime.ts +661 -42
- package/src/runtime/edit-dispatch/dispatch-text-command.ts +98 -0
- package/src/runtime/edit-dispatch/index.ts +2 -0
- package/src/runtime/edit-dispatch/list-aware-dispatch.ts +125 -0
- package/src/runtime/editor-state-channel.ts +544 -0
- package/src/runtime/editor-state-integration.ts +217 -0
- package/src/runtime/editor-surface/capabilities.ts +411 -0
- package/src/runtime/layout/index.ts +2 -0
- package/src/runtime/layout/inert-layout-facet.ts +4 -0
- package/src/runtime/layout/layout-engine-instance.ts +63 -2
- package/src/runtime/layout/layout-engine-version.ts +41 -0
- package/src/runtime/layout/paginated-layout-engine.ts +211 -14
- package/src/runtime/layout/public-facet.ts +430 -1
- package/src/runtime/perf-counters.ts +28 -0
- package/src/runtime/prerender/cache-envelope.ts +29 -0
- package/src/runtime/prerender/cache-key.ts +66 -0
- package/src/runtime/prerender/font-fingerprint.ts +17 -0
- package/src/runtime/prerender/graph-canonicalize.ts +121 -0
- package/src/runtime/prerender/indexeddb-cache.ts +184 -0
- package/src/runtime/prerender/prerender-document.ts +145 -0
- package/src/runtime/render/block-fragment-projection.ts +2 -0
- package/src/runtime/render/render-frame-types.ts +17 -0
- package/src/runtime/render/render-kernel.ts +172 -29
- package/src/runtime/selection/post-edit-validator.ts +77 -0
- package/src/runtime/surface-projection.ts +45 -7
- package/src/runtime/workflow-markup.ts +71 -16
- package/src/ui/WordReviewEditor.tsx +142 -237
- package/src/ui/editor-command-bag.ts +14 -0
- package/src/ui/editor-runtime-boundary.ts +115 -12
- package/src/ui/editor-shell-view.tsx +10 -0
- package/src/ui/editor-surface-controller.tsx +5 -0
- package/src/ui/headless/selection-helpers.ts +10 -0
- package/src/ui/runtime-shortcut-dispatch.ts +28 -68
- package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +62 -2
- package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +281 -0
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +48 -0
- package/src/ui-tailwind/editor-surface/paste-plain-text.ts +72 -0
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +118 -8
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +76 -165
- package/src/ui-tailwind/editor-surface/pm-schema.ts +170 -4
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +58 -7
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -0
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +265 -0
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +8 -255
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +47 -0
- package/src/ui-tailwind/index.ts +5 -1
- package/src/ui-tailwind/page-stack/tw-endnote-area.tsx +57 -0
- package/src/ui-tailwind/page-stack/tw-footnote-area.tsx +71 -0
- package/src/ui-tailwind/page-stack/tw-page-footer-band.tsx +73 -0
- package/src/ui-tailwind/page-stack/tw-page-header-band.tsx +74 -0
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +477 -0
- package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +374 -0
- package/src/ui-tailwind/page-stack/use-visible-block-range.ts +157 -0
- package/src/ui-tailwind/review/comment-markdown-renderer.tsx +155 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +77 -16
- package/src/ui-tailwind/theme/editor-theme.css +47 -14
- package/src/ui-tailwind/tw-review-workspace.tsx +303 -123
package/README.md
CHANGED
|
@@ -216,9 +216,26 @@ Current integration honesty:
|
|
|
216
216
|
|
|
217
217
|
- [`docs/maintainers/README.md`](docs/maintainers/README.md)
|
|
218
218
|
- [`docs/ccep-contract-templates/README.md`](docs/ccep-contract-templates/README.md)
|
|
219
|
+
- [`docs/plans/README.md`](docs/plans/README.md) — active development lanes + open-issues register
|
|
219
220
|
|
|
220
221
|
The CCEP corpus is kept on `main` as a maintainer-safe smoke-doc source set for agreement-heavy validation and wrapper or agent benchmarking.
|
|
221
222
|
|
|
223
|
+
### Active Development Lanes
|
|
224
|
+
|
|
225
|
+
Engineering work is organized into 9 lanes (5 active now + 2 later polish + 2 final) — full plan index, status, and worktree map at [`docs/plans/README.md`](docs/plans/README.md). Each lane has a single self-contained doc at `docs/plans/lane-N-<name>.md`:
|
|
226
|
+
|
|
227
|
+
| Lane | Mission | Status |
|
|
228
|
+
|---|---|---|
|
|
229
|
+
| 1. [**Editing Foundation**](docs/plans/lane-1-editing-foundation.md) | Close I1–I6 + ship the SelectionLayer / EditLayer / StructureLayer / SurfaceLayer split | active |
|
|
230
|
+
| 2. [**Render Performance**](docs/plans/lane-2-render-performance.md) | <32 ms typing P50 + <1 s cold-open on 200-page CCEP; never stuck on loading | active |
|
|
231
|
+
| 3. [**Layout Engine + OOXML Fidelity**](docs/plans/lane-3-layout-engine-ooxml-fidelity.md) | Land P9 anchor-index + P10 incremental relayout + drain O2/O3/O4 round-trip + tables/TOC/page numbers/front pages/image structures | active |
|
|
232
|
+
| 4. [**Collab + CLM/Vallor Integration**](docs/plans/lane-4-collab-clm-vallor.md) | Promote collab plumbing → first-class user mode; wire Sunday CLM × React E2E demo | active |
|
|
233
|
+
| 5. [**Charts (independent)**](docs/plans/lane-5-charts.md) | Move charts from preserve-only opaque preview to Word-accurate native rendering (Stages 1–7) | active |
|
|
234
|
+
| 6. [**Visual Chrome / Layout Polish**](docs/plans/lane-6-visual-chrome-layout-polish.md) | True-to-Word visuals: discrete paper cards, native page chrome, float-wrap, validation bar | LATER |
|
|
235
|
+
| 7. [**Bugs / Gaps / Cross-cutting**](docs/plans/lane-7-bugs-gaps-cross-cutting.md) | Drain V#/O#/X# register; trigger-gated work; infrastructure hardening | LATER |
|
|
236
|
+
| 8. [**API Ergonomics / Errors / Backwards Compatibility**](docs/plans/lane-8-api-ergonomics.md) | Refactor `docs/reference/public-api.md` end-to-end with groups + per-code error catalog + ergonomics fixes; maintain backwards compat | LATER (Tracks A+C shipped 2026-04-19) |
|
|
237
|
+
| 9. [**Shipping**](docs/plans/lane-9-shipping.md) | Production-readiness: API freeze, semver discipline, changelog, telemetry, customer migration guides, doc completeness audit | FINAL |
|
|
238
|
+
|
|
222
239
|
### Technical Wiki
|
|
223
240
|
|
|
224
241
|
- [`docs/wiki/`](docs/wiki/) — Feature-by-feature technical documentation (25+ topics covering OOXML, ProseMirror, runtime, and platform)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beyondwork/docx-react-component",
|
|
3
3
|
"publisher": "beyondwork",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.45",
|
|
5
5
|
"description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
|
|
6
6
|
"packageManager": "pnpm@10.30.3",
|
|
7
7
|
"type": "module",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"scripts": {
|
|
97
97
|
"build": "tsup",
|
|
98
98
|
"test": "bash scripts/run-workspace-tests.sh",
|
|
99
|
-
"test:repo": "node scripts/run-repo-tests.mjs core",
|
|
99
|
+
"test:repo": "node scripts/ci-check-layout-engine-version.mjs && node scripts/run-repo-tests.mjs core",
|
|
100
100
|
"test:repo:all": "node scripts/run-repo-tests.mjs all",
|
|
101
101
|
"test:repo:optional": "node scripts/run-repo-tests.mjs optional",
|
|
102
102
|
"test:repo:browser-ui": "node scripts/run-repo-tests.mjs browser-ui",
|
|
@@ -165,9 +165,9 @@
|
|
|
165
165
|
"react": "^19.2.0",
|
|
166
166
|
"react-dom": "^19.2.0",
|
|
167
167
|
"tailwindcss": "^4.2.2",
|
|
168
|
-
"yjs": "^13.6.0",
|
|
169
168
|
"y-prosemirror": "^1.2.0",
|
|
170
|
-
"y-protocols": "^1.0.0"
|
|
169
|
+
"y-protocols": "^1.0.0",
|
|
170
|
+
"yjs": "^13.6.0"
|
|
171
171
|
},
|
|
172
172
|
"peerDependenciesMeta": {
|
|
173
173
|
"yjs": {
|
|
@@ -186,6 +186,7 @@
|
|
|
186
186
|
"@types/react": "19.2.14",
|
|
187
187
|
"@types/react-dom": "19.2.3",
|
|
188
188
|
"@typescript/native-preview": "7.0.0-dev.20260409.1",
|
|
189
|
+
"fake-indexeddb": "^6.2.5",
|
|
189
190
|
"jsdom": "^29.0.1",
|
|
190
191
|
"pixelmatch": "^7.1.0",
|
|
191
192
|
"pngjs": "^7.0.0",
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editor-State Persistence Channel (schema 1.2).
|
|
3
|
+
*
|
|
4
|
+
* Makes the .docx act as a load/save API for editor-level overlay
|
|
5
|
+
* state (host annotations, workflow overlay, workflow metadata, work
|
|
6
|
+
* items, and future subsystems). Storage policy per subsystem is
|
|
7
|
+
* host-controlled across three locations:
|
|
8
|
+
*
|
|
9
|
+
* - `"in-document"` — full blob serialized into /customXml/item1.xml
|
|
10
|
+
* - `"rowstore"` — doc carries a key reference; host persists the blob
|
|
11
|
+
* - `"key-only"` — doc carries only a correlation key; host rehydrates
|
|
12
|
+
*
|
|
13
|
+
* See docs/plans/editor-state-persistence.md for the full design. 1.2
|
|
14
|
+
* extends schema 1.1 additively (unknown namespaces are preserved
|
|
15
|
+
* opaquely per existing forward-compat rules).
|
|
16
|
+
*/
|
|
17
|
+
export type EditorStateNamespace =
|
|
18
|
+
| "hostAnnotations"
|
|
19
|
+
| "workflowOverlay"
|
|
20
|
+
| "workflowMetadata"
|
|
21
|
+
| "workItems";
|
|
22
|
+
|
|
23
|
+
export type EditorStateLocation = "in-document" | "rowstore" | "key-only";
|
|
24
|
+
|
|
25
|
+
export type EditorStateResolveErrorMode = "block" | "empty" | "retain-last-known";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Host-configured per-namespace persistence policy. Default (when
|
|
29
|
+
* absent) is {location: "in-document", onResolveError: "block",
|
|
30
|
+
* debounceMs: 250}, which preserves current behavior.
|
|
31
|
+
*/
|
|
32
|
+
export interface EditorStatePolicyEntry {
|
|
33
|
+
location: EditorStateLocation;
|
|
34
|
+
/** Host-provided correlation key (e.g. review_session_id). Editor generates a UUID if absent. */
|
|
35
|
+
key?: string;
|
|
36
|
+
onResolveError?: EditorStateResolveErrorMode;
|
|
37
|
+
debounceMs?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type EditorStatePolicy = Partial<
|
|
41
|
+
Record<EditorStateNamespace, EditorStatePolicyEntry>
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Reference stored in the docx when the subsystem is backed by a
|
|
46
|
+
* host-owned store. The editor never interprets entryKey — it round-
|
|
47
|
+
* trips verbatim via resolver + persister callbacks.
|
|
48
|
+
*/
|
|
49
|
+
export interface EditorStateStorageRef {
|
|
50
|
+
namespace: EditorStateNamespace;
|
|
51
|
+
location: Exclude<EditorStateLocation, "in-document">;
|
|
52
|
+
entryKey: string;
|
|
53
|
+
schemaVersion: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Typed blob the resolver returns and the persister accepts.
|
|
58
|
+
* `data` is the subsystem-specific payload (e.g. a HostAnnotationOverlay
|
|
59
|
+
* for the "hostAnnotations" namespace). Use `schemaVersion` to pin the
|
|
60
|
+
* blob shape so older hosts don't silently load newer blobs.
|
|
61
|
+
*/
|
|
62
|
+
export interface EditorStateBlob {
|
|
63
|
+
namespace: EditorStateNamespace;
|
|
64
|
+
schemaVersion: string;
|
|
65
|
+
data: unknown;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Host-supplied pull-resolver. Returns `null` when the entry key is
|
|
70
|
+
* unknown or revoked. Matches the ScopeMetadataResolver undefined
|
|
71
|
+
* convention but allows null explicitly per the design doc §5 for
|
|
72
|
+
* symmetry with Promise<T | null> idioms. Either is acceptable as
|
|
73
|
+
* "not found"; internal code normalizes.
|
|
74
|
+
*/
|
|
75
|
+
export type EditorStateResolver = (
|
|
76
|
+
namespace: EditorStateNamespace,
|
|
77
|
+
entryKey: string,
|
|
78
|
+
) => Promise<EditorStateBlob | null>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Host-supplied persister. Called after the editor's debounce window
|
|
82
|
+
* when a subsystem's state has changed under `rowstore` or `key-only`
|
|
83
|
+
* policy. Must be idempotent within the debounce window.
|
|
84
|
+
*/
|
|
85
|
+
export type EditorStatePersister = (
|
|
86
|
+
namespace: EditorStateNamespace,
|
|
87
|
+
entryKey: string,
|
|
88
|
+
blob: EditorStateBlob,
|
|
89
|
+
) => Promise<void>;
|
|
90
|
+
|
|
91
|
+
export interface EditorStatePolicyMigration {
|
|
92
|
+
namespace: EditorStateNamespace;
|
|
93
|
+
from: EditorStateLocation;
|
|
94
|
+
to: EditorStateLocation;
|
|
95
|
+
key?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface EditorStatePartLoadFailure {
|
|
99
|
+
namespace: EditorStateNamespace;
|
|
100
|
+
entryKey?: string;
|
|
101
|
+
error: Error;
|
|
102
|
+
fallback: "empty" | "retain-last-known";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface EditorStatePartPersistFailure {
|
|
106
|
+
namespace: EditorStateNamespace;
|
|
107
|
+
entryKey: string;
|
|
108
|
+
error: Error;
|
|
109
|
+
attemptCount: number;
|
|
110
|
+
}
|
package/src/api/public-types.ts
CHANGED
|
@@ -9,6 +9,20 @@ import type {
|
|
|
9
9
|
ScopeMetadataResolver,
|
|
10
10
|
ScopeMetadataStorageRef,
|
|
11
11
|
} from "./scope-metadata-resolver-types.ts";
|
|
12
|
+
import type {
|
|
13
|
+
EditorStateNamespace,
|
|
14
|
+
EditorStateLocation,
|
|
15
|
+
EditorStateResolveErrorMode,
|
|
16
|
+
EditorStatePolicyEntry,
|
|
17
|
+
EditorStatePolicy,
|
|
18
|
+
EditorStateStorageRef,
|
|
19
|
+
EditorStateBlob,
|
|
20
|
+
EditorStateResolver,
|
|
21
|
+
EditorStatePersister,
|
|
22
|
+
EditorStatePolicyMigration,
|
|
23
|
+
EditorStatePartLoadFailure,
|
|
24
|
+
EditorStatePartPersistFailure,
|
|
25
|
+
} from "./editor-state-types.ts";
|
|
12
26
|
|
|
13
27
|
export type {
|
|
14
28
|
MetadataPersistenceMode,
|
|
@@ -16,6 +30,20 @@ export type {
|
|
|
16
30
|
ScopeMetadataResolver,
|
|
17
31
|
ScopeMetadataStorageRef,
|
|
18
32
|
};
|
|
33
|
+
export type {
|
|
34
|
+
EditorStateNamespace,
|
|
35
|
+
EditorStateLocation,
|
|
36
|
+
EditorStateResolveErrorMode,
|
|
37
|
+
EditorStatePolicyEntry,
|
|
38
|
+
EditorStatePolicy,
|
|
39
|
+
EditorStateStorageRef,
|
|
40
|
+
EditorStateBlob,
|
|
41
|
+
EditorStateResolver,
|
|
42
|
+
EditorStatePersister,
|
|
43
|
+
EditorStatePolicyMigration,
|
|
44
|
+
EditorStatePartLoadFailure,
|
|
45
|
+
EditorStatePartPersistFailure,
|
|
46
|
+
};
|
|
19
47
|
|
|
20
48
|
export type { CanonicalParagraphFormatting, CanonicalRunFormatting };
|
|
21
49
|
|
|
@@ -34,6 +62,8 @@ export type {
|
|
|
34
62
|
PublicPageRegion,
|
|
35
63
|
PublicPageRegions,
|
|
36
64
|
PublicPageSpan,
|
|
65
|
+
PublicRegionBlock,
|
|
66
|
+
PublicRegionKind,
|
|
37
67
|
PublicResolvedPageStories,
|
|
38
68
|
PublicResolvedParagraphFormatting,
|
|
39
69
|
PublicResolvedRunFormatting,
|
|
@@ -753,7 +783,7 @@ export type SurfaceInlineSegment =
|
|
|
753
783
|
textColor?: string;
|
|
754
784
|
};
|
|
755
785
|
hyperlinkHref?: string;
|
|
756
|
-
/** Cascaded run formatting for this text segment (docDefaults → paragraph style chain → character style → direct).
|
|
786
|
+
/** Cascaded run formatting for this text segment (docDefaults → paragraph style chain → character style → direct). Populated in `src/runtime/surface-projection.ts` via `resolveEffectiveRunFormatting`. Present only when the resolved cascade has at least one field. */
|
|
757
787
|
resolvedRunFormatting?: CanonicalRunFormatting;
|
|
758
788
|
}
|
|
759
789
|
| {
|
|
@@ -787,6 +817,14 @@ export type SurfaceInlineSegment =
|
|
|
787
817
|
blockedReasonCode?: "workflow_preserve_only" | "workflow_blocked_import";
|
|
788
818
|
presentation?: "inline-chip" | "quiet-marker" | "text-box" | "checkbox";
|
|
789
819
|
displayText?: string;
|
|
820
|
+
/**
|
|
821
|
+
* For chart/SmartArt/WordArt/VML opaque inlines, the media-catalog id
|
|
822
|
+
* of the preview bitmap extracted from `mc:Fallback` (when Word cached
|
|
823
|
+
* one). Resolved by the surface into a blob URL and rendered by the
|
|
824
|
+
* corresponding PM atom so reviewers see the chart picture instead of
|
|
825
|
+
* a text badge. Absent when the object has no cached fallback image.
|
|
826
|
+
*/
|
|
827
|
+
previewMediaId?: string;
|
|
790
828
|
state: "locked-preserve-only";
|
|
791
829
|
}
|
|
792
830
|
| {
|
|
@@ -943,7 +981,16 @@ export type SurfaceBlockSnapshot =
|
|
|
943
981
|
detail: string;
|
|
944
982
|
featureKey?: string;
|
|
945
983
|
blockedReasonCode?: "workflow_preserve_only" | "workflow_blocked_import";
|
|
946
|
-
|
|
984
|
+
/**
|
|
985
|
+
* When set, this opaque_block is a size-preserving placeholder generated
|
|
986
|
+
* by viewport culling, NOT a real preserved fragment. The PM schema uses
|
|
987
|
+
* this to claim the original block's position span without rendering
|
|
988
|
+
* content.
|
|
989
|
+
*
|
|
990
|
+
* See docs/plans/lane-2-render-performance.md.
|
|
991
|
+
*/
|
|
992
|
+
placeholderSize?: number;
|
|
993
|
+
state: "locked-preserve-only" | "placeholder-culled";
|
|
947
994
|
};
|
|
948
995
|
|
|
949
996
|
export interface SecondaryStorySurface {
|
|
@@ -959,6 +1006,15 @@ export interface EditorSurfaceSnapshot {
|
|
|
959
1006
|
blocks: SurfaceBlockSnapshot[];
|
|
960
1007
|
lockedFragmentIds: string[];
|
|
961
1008
|
secondaryStories: SecondaryStorySurface[];
|
|
1009
|
+
/**
|
|
1010
|
+
* Block index range rendered as real (non-placeholder) in this snapshot.
|
|
1011
|
+
* Blocks outside this range are placeholder opaque_blocks carrying the
|
|
1012
|
+
* original position range but no content. `null` (default) = all blocks
|
|
1013
|
+
* are real (legacy behavior).
|
|
1014
|
+
*
|
|
1015
|
+
* See docs/plans/lane-2-render-performance.md.
|
|
1016
|
+
*/
|
|
1017
|
+
viewportBlockRange: { start: number; end: number } | null;
|
|
962
1018
|
}
|
|
963
1019
|
|
|
964
1020
|
export type EditorWarningCode =
|
|
@@ -1631,6 +1687,16 @@ export interface AddCommentParams {
|
|
|
1631
1687
|
authorId?: string;
|
|
1632
1688
|
}
|
|
1633
1689
|
|
|
1690
|
+
export interface AddCommentResult {
|
|
1691
|
+
commentId: string;
|
|
1692
|
+
anchor: EditorAnchorProjection;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
export interface AddCommentReplyResult {
|
|
1696
|
+
commentId: string;
|
|
1697
|
+
entryId: string;
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1634
1700
|
export interface ExportDocxOptions {
|
|
1635
1701
|
fileName?: string;
|
|
1636
1702
|
reason?: string;
|
|
@@ -2299,6 +2365,25 @@ export interface AutosaveConfig {
|
|
|
2299
2365
|
debounceMs?: number;
|
|
2300
2366
|
}
|
|
2301
2367
|
|
|
2368
|
+
/**
|
|
2369
|
+
* Fired after a plain-text paste or drop has been dispatched through
|
|
2370
|
+
* the runtime-owned input callbacks. Rich-text paste (HTML, Office
|
|
2371
|
+
* clipboard) does NOT produce this event — those payloads still fire
|
|
2372
|
+
* `onBlockedInput` with a distinguishing message.
|
|
2373
|
+
*
|
|
2374
|
+
* See `docs/plans/editor-paste-drop.md` for the full semantics.
|
|
2375
|
+
*/
|
|
2376
|
+
export interface WordReviewEditorPasteEvent {
|
|
2377
|
+
type: "paste_applied";
|
|
2378
|
+
documentId: string;
|
|
2379
|
+
/** Count of segments dispatched across text + split + hard_break. */
|
|
2380
|
+
segmentCount: number;
|
|
2381
|
+
/** Total character count across all text segments in the payload. */
|
|
2382
|
+
charCount: number;
|
|
2383
|
+
/** Whether the event originated from a paste or a drop. */
|
|
2384
|
+
source: "paste" | "drop";
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2302
2387
|
export type WordReviewEditorEvent =
|
|
2303
2388
|
| {
|
|
2304
2389
|
type: "ready";
|
|
@@ -2442,6 +2527,7 @@ export type WordReviewEditorEvent =
|
|
|
2442
2527
|
command: string;
|
|
2443
2528
|
reasons: WorkflowBlockedCommandReason[];
|
|
2444
2529
|
}
|
|
2530
|
+
| WordReviewEditorPasteEvent
|
|
2445
2531
|
| {
|
|
2446
2532
|
/**
|
|
2447
2533
|
* Scope card mode selector fired a mode change. Host relays to the
|
|
@@ -2520,8 +2606,82 @@ export type WordReviewEditorEvent =
|
|
|
2520
2606
|
version?: number;
|
|
2521
2607
|
} | null;
|
|
2522
2608
|
defaultPolicy: MetadataConflictPolicy;
|
|
2609
|
+
}
|
|
2610
|
+
| {
|
|
2611
|
+
/**
|
|
2612
|
+
* Schema 1.2 — emitted when a keyed namespace fails to resolve
|
|
2613
|
+
* during load. Subsystem fallback behavior is per `onResolveError`
|
|
2614
|
+
* policy.
|
|
2615
|
+
*/
|
|
2616
|
+
type: "editor_state_part_load_failed";
|
|
2617
|
+
documentId: string;
|
|
2618
|
+
failure: EditorStatePartLoadFailure;
|
|
2619
|
+
}
|
|
2620
|
+
| {
|
|
2621
|
+
/**
|
|
2622
|
+
* Schema 1.2 — emitted when a debounced persist call fails.
|
|
2623
|
+
* The editor retains the dirty snapshot for retry.
|
|
2624
|
+
*/
|
|
2625
|
+
type: "editor_state_part_persist_failed";
|
|
2626
|
+
documentId: string;
|
|
2627
|
+
failure: EditorStatePartPersistFailure;
|
|
2628
|
+
}
|
|
2629
|
+
| {
|
|
2630
|
+
/**
|
|
2631
|
+
* Schema 1.2 — emitted when the saved location in a docx differs
|
|
2632
|
+
* from the current host policy; the current policy wins and the
|
|
2633
|
+
* next serialize honors it.
|
|
2634
|
+
*/
|
|
2635
|
+
type: "editor_state_policy_migrated";
|
|
2636
|
+
documentId: string;
|
|
2637
|
+
migration: EditorStatePolicyMigration;
|
|
2638
|
+
}
|
|
2639
|
+
| {
|
|
2640
|
+
/**
|
|
2641
|
+
* Schema 1.2 — emitted when an unknown namespace is encountered
|
|
2642
|
+
* in the payload (forward-compat warning; preserved opaquely).
|
|
2643
|
+
*/
|
|
2644
|
+
type: "editor_state_unknown_namespace";
|
|
2645
|
+
documentId: string;
|
|
2646
|
+
namespace: string;
|
|
2647
|
+
}
|
|
2648
|
+
| {
|
|
2649
|
+
/**
|
|
2650
|
+
* Emitted once per load-pipeline stage when the staged loader is
|
|
2651
|
+
* active. Purely observational — hosts can render a progress chip
|
|
2652
|
+
* or ignore entirely. See `docs/plans/fastload.md` for the stage
|
|
2653
|
+
* ordering contract.
|
|
2654
|
+
*/
|
|
2655
|
+
type: "load-stage";
|
|
2656
|
+
documentId: string;
|
|
2657
|
+
stage: LoadStage;
|
|
2658
|
+
durationMs: number;
|
|
2659
|
+
}
|
|
2660
|
+
| {
|
|
2661
|
+
/**
|
|
2662
|
+
* Emitted once sub-parts (headers, footers, footnotes, endnotes,
|
|
2663
|
+
* theme, settings) have finished their post-ready idle hydration.
|
|
2664
|
+
* Before this event fires, `canonicalDocument.subParts.*` may be
|
|
2665
|
+
* empty arrays / defaults; after it fires, they carry the parsed
|
|
2666
|
+
* values. Calling `ensureSubPartsHydrated` eagerly forces
|
|
2667
|
+
* hydration synchronously.
|
|
2668
|
+
*/
|
|
2669
|
+
type: "subparts-hydrated";
|
|
2670
|
+
documentId: string;
|
|
2523
2671
|
};
|
|
2524
2672
|
|
|
2673
|
+
/**
|
|
2674
|
+
* Ordered list of stages emitted by the staged load pipeline. The
|
|
2675
|
+
* skeleton-ready stage corresponds to the `ready` event firing.
|
|
2676
|
+
*/
|
|
2677
|
+
export type LoadStage =
|
|
2678
|
+
| "opc"
|
|
2679
|
+
| "body"
|
|
2680
|
+
| "styles-numbering-comments"
|
|
2681
|
+
| "skeleton-ready"
|
|
2682
|
+
| "subparts"
|
|
2683
|
+
| "compatibility";
|
|
2684
|
+
|
|
2525
2685
|
export interface LoadResult {
|
|
2526
2686
|
source?: ExternalDocumentSource;
|
|
2527
2687
|
}
|
|
@@ -2561,11 +2721,50 @@ export interface EditorTelemetryEvent {
|
|
|
2561
2721
|
detail?: Record<string, unknown>;
|
|
2562
2722
|
}
|
|
2563
2723
|
|
|
2724
|
+
/**
|
|
2725
|
+
* Stage 0B.1 — parameters passed to `EditorHostAdapter.renderChartPreview`
|
|
2726
|
+
* when the importer encounters a `c:chartSpace` that has no cached
|
|
2727
|
+
* `mc:Fallback` bitmap. Hosts receive the raw chart-part XML plus the
|
|
2728
|
+
* theme XML and the intended display size, and return either preview
|
|
2729
|
+
* bytes (typically PNG or SVG) or `null` to fall back to the typed
|
|
2730
|
+
* badge.
|
|
2731
|
+
*
|
|
2732
|
+
* Field stability: only additive changes; no field is ever renamed or
|
|
2733
|
+
* removed. Host implementations should use structural narrowing to
|
|
2734
|
+
* ignore unknown fields in future versions.
|
|
2735
|
+
*/
|
|
2736
|
+
export interface ChartPreviewResolveParams {
|
|
2737
|
+
/** Chart part body (`word/charts/chartN.xml`). UTF-8 string. */
|
|
2738
|
+
chartXml: string;
|
|
2739
|
+
/** Absolute package path of the chart part (e.g. `/word/charts/chart1.xml`). Useful as a cache key. */
|
|
2740
|
+
chartPartPath: string;
|
|
2741
|
+
/** Body of `theme1.xml` when the package ships one; `undefined` otherwise. */
|
|
2742
|
+
themeXml: string | undefined;
|
|
2743
|
+
/** Intended display width in EMU (extracted from the drawing's `wp:extent`). */
|
|
2744
|
+
widthEmu: number;
|
|
2745
|
+
/** Intended display height in EMU. */
|
|
2746
|
+
heightEmu: number;
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2564
2749
|
export interface EditorHostAdapter {
|
|
2565
2750
|
load?(params: LoadRequest): Promise<LoadResult>;
|
|
2566
2751
|
saveSession?(params: SaveSessionParams): Promise<SaveSessionResult>;
|
|
2567
2752
|
saveExport?(params: SaveExportParams): Promise<SaveExportResult>;
|
|
2568
2753
|
logEvent?(event: EditorTelemetryEvent): void;
|
|
2754
|
+
/**
|
|
2755
|
+
* Stage 0B.1: render a chart to bitmap/SVG preview bytes. Called at
|
|
2756
|
+
* import time for every `c:chartSpace` that ships without a cached
|
|
2757
|
+
* `mc:Fallback` blip. Return `null` to fall back to the typed badge
|
|
2758
|
+
* (Stage 0 behavior). Return a `Uint8Array` to inject the bytes as
|
|
2759
|
+
* a synthetic `MediaItem` and render the preview through the
|
|
2760
|
+
* existing `chart_atom` path.
|
|
2761
|
+
*
|
|
2762
|
+
* Content type is inferred from the first few bytes: `image/png`
|
|
2763
|
+
* for a PNG magic number, `image/svg+xml` otherwise. Hosts that
|
|
2764
|
+
* need a different content type should extend this contract in a
|
|
2765
|
+
* follow-up.
|
|
2766
|
+
*/
|
|
2767
|
+
renderChartPreview?(params: ChartPreviewResolveParams): Promise<Uint8Array | null>;
|
|
2569
2768
|
}
|
|
2570
2769
|
|
|
2571
2770
|
export interface EditorDatastoreAdapter {
|
|
@@ -2580,11 +2779,11 @@ export interface WordReviewEditorRef {
|
|
|
2580
2779
|
blur(): void;
|
|
2581
2780
|
undo(): void;
|
|
2582
2781
|
redo(): void;
|
|
2583
|
-
addComment(params: AddCommentParams):
|
|
2782
|
+
addComment(params: AddCommentParams): AddCommentResult;
|
|
2584
2783
|
openComment(commentId: string): void;
|
|
2585
2784
|
resolveComment(commentId: string): void;
|
|
2586
2785
|
reopenComment(commentId: string): void;
|
|
2587
|
-
addCommentReply(commentId: string, body: string):
|
|
2786
|
+
addCommentReply(commentId: string, body: string): AddCommentReplyResult;
|
|
2588
2787
|
editCommentBody(commentId: string, body: string): void;
|
|
2589
2788
|
deleteComment(commentId: string): void;
|
|
2590
2789
|
acceptChange(changeId: string): void;
|
|
@@ -2767,6 +2966,35 @@ export interface WordReviewEditorRef {
|
|
|
2767
2966
|
* registered.
|
|
2768
2967
|
*/
|
|
2769
2968
|
convertScopesToExternal(scopeIds: string[]): Promise<void>;
|
|
2969
|
+
/**
|
|
2970
|
+
* Schema 1.2 — editor-state persistence channel. Configures which
|
|
2971
|
+
* subsystems persist in-document / rowstore / key-only. Each call
|
|
2972
|
+
* merges into the current policy map. Mismatched locations with an
|
|
2973
|
+
* already-loaded docx fire `editor_state_policy_migrated` and
|
|
2974
|
+
* schedule the next serialize to honor the new location.
|
|
2975
|
+
*/
|
|
2976
|
+
configureEditorStatePolicy(policy: EditorStatePolicy): void;
|
|
2977
|
+
/**
|
|
2978
|
+
* Register the host's pull-resolver. Required before the editor
|
|
2979
|
+
* loads a 1.2 docx whose policy includes any keyed location.
|
|
2980
|
+
*/
|
|
2981
|
+
registerEditorStateResolver(resolver: EditorStateResolver): void;
|
|
2982
|
+
/**
|
|
2983
|
+
* Register the host's persister. Required before any subsystem
|
|
2984
|
+
* mutation under keyed policy fires a debounced persist.
|
|
2985
|
+
*/
|
|
2986
|
+
registerEditorStatePersister(persister: EditorStatePersister): void;
|
|
2987
|
+
/**
|
|
2988
|
+
* Return the entry key currently associated with a namespace under
|
|
2989
|
+
* keyed policy, or undefined when the namespace is in-document /
|
|
2990
|
+
* key not yet assigned.
|
|
2991
|
+
*/
|
|
2992
|
+
getEditorStateKey(namespace: EditorStateNamespace): string | undefined;
|
|
2993
|
+
/**
|
|
2994
|
+
* Re-attempt any persists that failed since the last successful
|
|
2995
|
+
* write. Pass a namespace to scope the retry; omit to retry all.
|
|
2996
|
+
*/
|
|
2997
|
+
retryPendingPersist(namespace?: EditorStateNamespace): Promise<void>;
|
|
2770
2998
|
setHostAnnotationOverlay(overlay: HostAnnotationOverlay): void;
|
|
2771
2999
|
clearHostAnnotationOverlay(): void;
|
|
2772
3000
|
getHostAnnotationSnapshot(): HostAnnotationSnapshot;
|
|
@@ -2866,6 +3094,38 @@ export interface WordReviewEditorProps {
|
|
|
2866
3094
|
currentUser: EditorUser;
|
|
2867
3095
|
ydoc?: import('yjs').Doc;
|
|
2868
3096
|
awareness?: import("y-protocols/awareness").Awareness;
|
|
3097
|
+
/**
|
|
3098
|
+
* Optional collab session built via `createCollabSession(...)`. When
|
|
3099
|
+
* set, the `"collab"` chrome preset mounts the top nav (presence
|
|
3100
|
+
* strip, role + audience chips, tamper banner, negotiation action
|
|
3101
|
+
* bar, send-to-supplier button). P9a–f components are pure
|
|
3102
|
+
* presentational; this prop is what wires them to live state. Pass
|
|
3103
|
+
* `undefined` to keep the chrome in non-collab mode even when
|
|
3104
|
+
* `chromePreset === "collab"`.
|
|
3105
|
+
*/
|
|
3106
|
+
collabSession?: import("../runtime/collab-session.ts").CollabSession;
|
|
3107
|
+
/**
|
|
3108
|
+
* Transport status signal for the presence strip + role chip (P9b /
|
|
3109
|
+
* P9c). Optional — defaults to "offline" when omitted.
|
|
3110
|
+
*/
|
|
3111
|
+
collabTransportStatus?: "connected" | "syncing" | "offline";
|
|
3112
|
+
/**
|
|
3113
|
+
* Identifier for the currently-focused comment, used by the collab
|
|
3114
|
+
* top nav's audience chip + negotiation action bar (P9c / P9e). The
|
|
3115
|
+
* host derives this from its own selection / rail-focus signal.
|
|
3116
|
+
*/
|
|
3117
|
+
activeCommentId?: string;
|
|
3118
|
+
/**
|
|
3119
|
+
* Baseline metadata for the send-to-supplier flow (P9f). Required
|
|
3120
|
+
* to enable the button; when omitted, the button stays present but
|
|
3121
|
+
* the confirmation modal becomes a no-op pass-through.
|
|
3122
|
+
*/
|
|
3123
|
+
collabSendBaseline?: {
|
|
3124
|
+
originDocumentId: string;
|
|
3125
|
+
originPayloadId: string;
|
|
3126
|
+
originContentHash: string;
|
|
3127
|
+
payloadXml: string;
|
|
3128
|
+
};
|
|
2869
3129
|
initialDocx?: Uint8Array | ArrayBuffer;
|
|
2870
3130
|
initialSessionState?: EditorSessionState;
|
|
2871
3131
|
initialSnapshot?: PersistedEditorSnapshot;
|
|
@@ -2880,6 +3140,20 @@ export interface WordReviewEditorProps {
|
|
|
2880
3140
|
chromePreset?: WordReviewEditorChromePreset;
|
|
2881
3141
|
chromeOptions?: Partial<WordReviewEditorChromeOptions>;
|
|
2882
3142
|
markupDisplay?: "clean" | "simple" | "all";
|
|
3143
|
+
/**
|
|
3144
|
+
* Harness/debug-only preview toggle for preserve-only features
|
|
3145
|
+
* (charts, SmartArt, shapes, WordArt, VML rendered as typed atoms;
|
|
3146
|
+
* the "N preserve-only features detected" banner; lock-callouts).
|
|
3147
|
+
*
|
|
3148
|
+
* MUST default to `false`. Consumer hosts MUST NOT hard-code `true` —
|
|
3149
|
+
* this is a dev opt-in gated by the harness dev drawer's
|
|
3150
|
+
* `debugMode && showUnsupportedObjectPreviews` AND-gate. Flipping the
|
|
3151
|
+
* default or dropping the AND-gate is a regression that has landed
|
|
3152
|
+
* three times (PRs #124, #131, #160); the invariant is locked by
|
|
3153
|
+
* `test/ui/unsupported-previews-invariant.test.ts`.
|
|
3154
|
+
*
|
|
3155
|
+
* @default false
|
|
3156
|
+
*/
|
|
2883
3157
|
showUnsupportedObjectPreviews?: boolean;
|
|
2884
3158
|
showReviewPanel?: boolean;
|
|
2885
3159
|
chromeVisibility?: Partial<WordReviewEditorChromeVisibility>;
|
|
@@ -2904,6 +3178,61 @@ export interface WordReviewEditorProps {
|
|
|
2904
3178
|
* inline "Comments panel" icon appears only when a callback is wired.
|
|
2905
3179
|
*/
|
|
2906
3180
|
onReviewSidebarComments?: () => void;
|
|
3181
|
+
/**
|
|
3182
|
+
* Optional: fires when the user invokes the Find shortcut
|
|
3183
|
+
* (Ctrl/Cmd+F) with the editor focused. When a host wires this
|
|
3184
|
+
* callback, the editor treats the shortcut as host-delegated and
|
|
3185
|
+
* suppresses the browser's native Find flow; when omitted, the
|
|
3186
|
+
* shortcut falls through to the browser default. The callback
|
|
3187
|
+
* receives the current selection so the host can pre-populate its
|
|
3188
|
+
* Find panel with the selected text.
|
|
3189
|
+
*
|
|
3190
|
+
* Capability id: `shortcut.find`. See
|
|
3191
|
+
* `src/runtime/editor-surface/capabilities.ts` for the full
|
|
3192
|
+
* capability contract.
|
|
3193
|
+
*/
|
|
3194
|
+
onFindRequested?: (context: ShortcutDelegationContext) => void;
|
|
3195
|
+
/**
|
|
3196
|
+
* Optional: fires when the user invokes Print (Ctrl/Cmd+P) with
|
|
3197
|
+
* the editor focused. When wired, the editor calls this callback
|
|
3198
|
+
* and suppresses the browser's native print dialog — the host is
|
|
3199
|
+
* expected to open its own print flow (e.g. a PDF export pipeline
|
|
3200
|
+
* that preserves review chrome). When omitted, Ctrl/Cmd+P falls
|
|
3201
|
+
* through to the browser's print dialog.
|
|
3202
|
+
*
|
|
3203
|
+
* Capability id: `shortcut.print`.
|
|
3204
|
+
*/
|
|
3205
|
+
onPrintRequested?: () => void;
|
|
3206
|
+
/**
|
|
3207
|
+
* Optional: fires when the user invokes a zoom shortcut
|
|
3208
|
+
* (Ctrl/Cmd+Plus, Ctrl/Cmd+Minus, Ctrl/Cmd+0). When wired, the
|
|
3209
|
+
* editor suppresses the browser's native zoom and delegates the
|
|
3210
|
+
* direction to the host. When omitted, the browser handles zoom
|
|
3211
|
+
* as usual.
|
|
3212
|
+
*
|
|
3213
|
+
* Capability ids: `shortcut.zoom-in`, `shortcut.zoom-out`,
|
|
3214
|
+
* `shortcut.zoom-reset`.
|
|
3215
|
+
*/
|
|
3216
|
+
onZoomRequested?: (direction: "in" | "out" | "reset") => void;
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
/**
|
|
3220
|
+
* Selection context handed to host-delegated shortcut callbacks
|
|
3221
|
+
* (`onFindRequested`, future `onReplaceRequested`, etc.) so the host
|
|
3222
|
+
* can pre-populate its own UI with the user's current selection.
|
|
3223
|
+
*
|
|
3224
|
+
* - `selectionText` is truncated to the first 500 characters —
|
|
3225
|
+
* Find / Replace panels typically only need a snippet, and unbounded
|
|
3226
|
+
* text would be wasteful for large selections.
|
|
3227
|
+
* - `selectionRange` is the same shape exposed via the
|
|
3228
|
+
* `selection_changed` editor event, so hosts can reuse selection
|
|
3229
|
+
* plumbing.
|
|
3230
|
+
*/
|
|
3231
|
+
export interface ShortcutDelegationContext {
|
|
3232
|
+
/** The user-visible text of the selection, truncated to 500 chars. Empty string when collapsed. */
|
|
3233
|
+
selectionText: string;
|
|
3234
|
+
/** The selection range as a SelectionSnapshot. */
|
|
3235
|
+
selectionRange: SelectionSnapshot;
|
|
2907
3236
|
}
|
|
2908
3237
|
|
|
2909
3238
|
export interface WordReviewEditorChromeVisibility {
|
|
@@ -624,7 +624,13 @@ function applyAlignment(
|
|
|
624
624
|
return true;
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
-
|
|
627
|
+
/**
|
|
628
|
+
* Adjusts the paragraph's indentation (or list level if the paragraph carries
|
|
629
|
+
* `numbering`) by ±INDENT_STEP_TWIPS. Mutates `paragraph` in place — caller
|
|
630
|
+
* must clone first if the source is shared. Returns `false` when no change
|
|
631
|
+
* occurred (already at the 0 / 8 bound, or no-op).
|
|
632
|
+
*/
|
|
633
|
+
export function applyIndentation(paragraph: ParagraphNode, delta: -1 | 1): boolean {
|
|
628
634
|
if (paragraph.numbering) {
|
|
629
635
|
const nextLevel = clamp(paragraph.numbering.level + delta, 0, 8);
|
|
630
636
|
if (nextLevel === paragraph.numbering.level) {
|