@beyondwork/docx-react-component 1.0.42 → 1.0.43
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 +30 -41
- package/src/api/editor-state-types.ts +110 -0
- package/src/api/public-types.ts +194 -1
- package/src/core/commands/index.ts +33 -8
- package/src/core/search/search-text.ts +15 -2
- package/src/index.ts +13 -0
- package/src/io/docx-session.ts +672 -2
- package/src/io/load-scheduler.ts +230 -0
- package/src/io/normalize/normalize-text.ts +83 -0
- package/src/io/ooxml/workflow-payload-validator.ts +97 -1
- package/src/io/ooxml/workflow-payload.ts +172 -1
- package/src/runtime/collab-session.ts +1 -1
- package/src/runtime/document-runtime.ts +364 -36
- package/src/runtime/editor-state-channel.ts +544 -0
- package/src/runtime/editor-state-integration.ts +217 -0
- package/src/runtime/layout/index.ts +2 -0
- package/src/runtime/layout/inert-layout-facet.ts +2 -0
- package/src/runtime/layout/layout-engine-instance.ts +17 -2
- package/src/runtime/layout/paginated-layout-engine.ts +211 -14
- package/src/runtime/layout/public-facet.ts +400 -1
- package/src/runtime/perf-counters.ts +28 -0
- package/src/runtime/render/render-frame-types.ts +17 -0
- package/src/runtime/render/render-kernel.ts +172 -29
- package/src/runtime/surface-projection.ts +10 -5
- package/src/runtime/workflow-markup.ts +71 -16
- package/src/ui/WordReviewEditor.tsx +67 -45
- package/src/ui/editor-command-bag.ts +14 -0
- package/src/ui/editor-runtime-boundary.ts +110 -11
- 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-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-schema.ts +152 -4
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +35 -7
- 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 +9 -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/review/comment-markdown-renderer.tsx +155 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +77 -16
- package/src/ui-tailwind/tw-review-workspace.tsx +172 -94
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beyondwork/docx-react-component",
|
|
3
3
|
"publisher": "beyondwork",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.43",
|
|
5
5
|
"description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
|
|
6
|
-
"packageManager": "pnpm@10.30.3",
|
|
7
6
|
"type": "module",
|
|
8
7
|
"sideEffects": [
|
|
9
8
|
"**/*.css"
|
|
@@ -93,35 +92,6 @@
|
|
|
93
92
|
"./ui-tailwind/theme/editor-theme.css": "./src/ui-tailwind/theme/editor-theme.css"
|
|
94
93
|
},
|
|
95
94
|
"types": "./src/index.ts",
|
|
96
|
-
"scripts": {
|
|
97
|
-
"build": "tsup",
|
|
98
|
-
"test": "bash scripts/run-workspace-tests.sh",
|
|
99
|
-
"test:repo": "node scripts/run-repo-tests.mjs core",
|
|
100
|
-
"test:repo:all": "node scripts/run-repo-tests.mjs all",
|
|
101
|
-
"test:repo:optional": "node scripts/run-repo-tests.mjs optional",
|
|
102
|
-
"test:repo:browser-ui": "node scripts/run-repo-tests.mjs browser-ui",
|
|
103
|
-
"test:wcag-audit": "node scripts/run-repo-tests.mjs wcag-audit",
|
|
104
|
-
"test:harness": "pnpm --filter @docx-react-component/react-word-editor-harness test",
|
|
105
|
-
"test:visual": "VISUAL_SMOKE_PROFILE=bare pnpm exec playwright test --project=chromium",
|
|
106
|
-
"test:visual:chrome": "VISUAL_SMOKE_PROFILE=chrome-cycle pnpm exec playwright test --project=chromium",
|
|
107
|
-
"visual:list-runs": "node scripts/visual-smoke-list-runs.mjs",
|
|
108
|
-
"mcp:visual-smoke": "node scripts/visual-smoke-mcp.mjs",
|
|
109
|
-
"lint": "pnpm run lint:no-authored-js && pnpm run lint:docs-contracts && pnpm run lint:tsgo && pnpm run lint:tsgo:harness",
|
|
110
|
-
"lint:docs-contracts": "bash scripts/check-reference-load-contract.sh",
|
|
111
|
-
"lint:no-authored-js": "bash scripts/check-no-authored-js.sh",
|
|
112
|
-
"lint:tsgo": "tsgo --noEmit -p tsconfig.build.json",
|
|
113
|
-
"lint:tsgo:harness": "pnpm --filter @docx-react-component/react-word-editor-harness lint:tsgo",
|
|
114
|
-
"context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
|
|
115
|
-
"wave:doctor": "bash scripts/context7-export-env.sh run pnpm exec wave doctor --json",
|
|
116
|
-
"wave:dry-run": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main --dry-run --no-dashboard",
|
|
117
|
-
"wave:launch": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main",
|
|
118
|
-
"wave:launch:managed": "bash scripts/wave-launch.sh",
|
|
119
|
-
"wave:status": "bash scripts/wave-status.sh",
|
|
120
|
-
"wave:watch": "bash scripts/wave-watch.sh --follow",
|
|
121
|
-
"wave:dashboard:current": "bash scripts/wave-dashboard-attach.sh current",
|
|
122
|
-
"wave:dashboard:global": "bash scripts/wave-dashboard-attach.sh global",
|
|
123
|
-
"harness:dev": "pnpm --filter @docx-react-component/react-word-editor-harness dev"
|
|
124
|
-
},
|
|
125
95
|
"keywords": [
|
|
126
96
|
"docx",
|
|
127
97
|
"word",
|
|
@@ -204,14 +174,33 @@
|
|
|
204
174
|
"y-protocols": "^1.0.7",
|
|
205
175
|
"yjs": "^13.6.30"
|
|
206
176
|
},
|
|
207
|
-
"
|
|
208
|
-
"
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
"
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
177
|
+
"scripts": {
|
|
178
|
+
"build": "tsup",
|
|
179
|
+
"test": "bash scripts/run-workspace-tests.sh",
|
|
180
|
+
"test:repo": "node scripts/run-repo-tests.mjs core",
|
|
181
|
+
"test:repo:all": "node scripts/run-repo-tests.mjs all",
|
|
182
|
+
"test:repo:optional": "node scripts/run-repo-tests.mjs optional",
|
|
183
|
+
"test:repo:browser-ui": "node scripts/run-repo-tests.mjs browser-ui",
|
|
184
|
+
"test:wcag-audit": "node scripts/run-repo-tests.mjs wcag-audit",
|
|
185
|
+
"test:harness": "pnpm --filter @docx-react-component/react-word-editor-harness test",
|
|
186
|
+
"test:visual": "VISUAL_SMOKE_PROFILE=bare pnpm exec playwright test --project=chromium",
|
|
187
|
+
"test:visual:chrome": "VISUAL_SMOKE_PROFILE=chrome-cycle pnpm exec playwright test --project=chromium",
|
|
188
|
+
"visual:list-runs": "node scripts/visual-smoke-list-runs.mjs",
|
|
189
|
+
"mcp:visual-smoke": "node scripts/visual-smoke-mcp.mjs",
|
|
190
|
+
"lint": "pnpm run lint:no-authored-js && pnpm run lint:docs-contracts && pnpm run lint:tsgo && pnpm run lint:tsgo:harness",
|
|
191
|
+
"lint:docs-contracts": "bash scripts/check-reference-load-contract.sh",
|
|
192
|
+
"lint:no-authored-js": "bash scripts/check-no-authored-js.sh",
|
|
193
|
+
"lint:tsgo": "tsgo --noEmit -p tsconfig.build.json",
|
|
194
|
+
"lint:tsgo:harness": "pnpm --filter @docx-react-component/react-word-editor-harness lint:tsgo",
|
|
195
|
+
"context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
|
|
196
|
+
"wave:doctor": "bash scripts/context7-export-env.sh run pnpm exec wave doctor --json",
|
|
197
|
+
"wave:dry-run": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main --dry-run --no-dashboard",
|
|
198
|
+
"wave:launch": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main",
|
|
199
|
+
"wave:launch:managed": "bash scripts/wave-launch.sh",
|
|
200
|
+
"wave:status": "bash scripts/wave-status.sh",
|
|
201
|
+
"wave:watch": "bash scripts/wave-watch.sh --follow",
|
|
202
|
+
"wave:dashboard:current": "bash scripts/wave-dashboard-attach.sh current",
|
|
203
|
+
"wave:dashboard:global": "bash scripts/wave-dashboard-attach.sh global",
|
|
204
|
+
"harness:dev": "pnpm --filter @docx-react-component/react-word-editor-harness dev"
|
|
216
205
|
}
|
|
217
|
-
}
|
|
206
|
+
}
|
|
@@ -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
|
| {
|
|
@@ -2299,6 +2337,25 @@ export interface AutosaveConfig {
|
|
|
2299
2337
|
debounceMs?: number;
|
|
2300
2338
|
}
|
|
2301
2339
|
|
|
2340
|
+
/**
|
|
2341
|
+
* Fired after a plain-text paste or drop has been dispatched through
|
|
2342
|
+
* the runtime-owned input callbacks. Rich-text paste (HTML, Office
|
|
2343
|
+
* clipboard) does NOT produce this event — those payloads still fire
|
|
2344
|
+
* `onBlockedInput` with a distinguishing message.
|
|
2345
|
+
*
|
|
2346
|
+
* See `docs/plans/editor-paste-drop.md` for the full semantics.
|
|
2347
|
+
*/
|
|
2348
|
+
export interface WordReviewEditorPasteEvent {
|
|
2349
|
+
type: "paste_applied";
|
|
2350
|
+
documentId: string;
|
|
2351
|
+
/** Count of segments dispatched across text + split + hard_break. */
|
|
2352
|
+
segmentCount: number;
|
|
2353
|
+
/** Total character count across all text segments in the payload. */
|
|
2354
|
+
charCount: number;
|
|
2355
|
+
/** Whether the event originated from a paste or a drop. */
|
|
2356
|
+
source: "paste" | "drop";
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2302
2359
|
export type WordReviewEditorEvent =
|
|
2303
2360
|
| {
|
|
2304
2361
|
type: "ready";
|
|
@@ -2442,6 +2499,7 @@ export type WordReviewEditorEvent =
|
|
|
2442
2499
|
command: string;
|
|
2443
2500
|
reasons: WorkflowBlockedCommandReason[];
|
|
2444
2501
|
}
|
|
2502
|
+
| WordReviewEditorPasteEvent
|
|
2445
2503
|
| {
|
|
2446
2504
|
/**
|
|
2447
2505
|
* Scope card mode selector fired a mode change. Host relays to the
|
|
@@ -2520,8 +2578,82 @@ export type WordReviewEditorEvent =
|
|
|
2520
2578
|
version?: number;
|
|
2521
2579
|
} | null;
|
|
2522
2580
|
defaultPolicy: MetadataConflictPolicy;
|
|
2581
|
+
}
|
|
2582
|
+
| {
|
|
2583
|
+
/**
|
|
2584
|
+
* Schema 1.2 — emitted when a keyed namespace fails to resolve
|
|
2585
|
+
* during load. Subsystem fallback behavior is per `onResolveError`
|
|
2586
|
+
* policy.
|
|
2587
|
+
*/
|
|
2588
|
+
type: "editor_state_part_load_failed";
|
|
2589
|
+
documentId: string;
|
|
2590
|
+
failure: EditorStatePartLoadFailure;
|
|
2591
|
+
}
|
|
2592
|
+
| {
|
|
2593
|
+
/**
|
|
2594
|
+
* Schema 1.2 — emitted when a debounced persist call fails.
|
|
2595
|
+
* The editor retains the dirty snapshot for retry.
|
|
2596
|
+
*/
|
|
2597
|
+
type: "editor_state_part_persist_failed";
|
|
2598
|
+
documentId: string;
|
|
2599
|
+
failure: EditorStatePartPersistFailure;
|
|
2600
|
+
}
|
|
2601
|
+
| {
|
|
2602
|
+
/**
|
|
2603
|
+
* Schema 1.2 — emitted when the saved location in a docx differs
|
|
2604
|
+
* from the current host policy; the current policy wins and the
|
|
2605
|
+
* next serialize honors it.
|
|
2606
|
+
*/
|
|
2607
|
+
type: "editor_state_policy_migrated";
|
|
2608
|
+
documentId: string;
|
|
2609
|
+
migration: EditorStatePolicyMigration;
|
|
2610
|
+
}
|
|
2611
|
+
| {
|
|
2612
|
+
/**
|
|
2613
|
+
* Schema 1.2 — emitted when an unknown namespace is encountered
|
|
2614
|
+
* in the payload (forward-compat warning; preserved opaquely).
|
|
2615
|
+
*/
|
|
2616
|
+
type: "editor_state_unknown_namespace";
|
|
2617
|
+
documentId: string;
|
|
2618
|
+
namespace: string;
|
|
2619
|
+
}
|
|
2620
|
+
| {
|
|
2621
|
+
/**
|
|
2622
|
+
* Emitted once per load-pipeline stage when the staged loader is
|
|
2623
|
+
* active. Purely observational — hosts can render a progress chip
|
|
2624
|
+
* or ignore entirely. See `docs/plans/fastload.md` for the stage
|
|
2625
|
+
* ordering contract.
|
|
2626
|
+
*/
|
|
2627
|
+
type: "load-stage";
|
|
2628
|
+
documentId: string;
|
|
2629
|
+
stage: LoadStage;
|
|
2630
|
+
durationMs: number;
|
|
2631
|
+
}
|
|
2632
|
+
| {
|
|
2633
|
+
/**
|
|
2634
|
+
* Emitted once sub-parts (headers, footers, footnotes, endnotes,
|
|
2635
|
+
* theme, settings) have finished their post-ready idle hydration.
|
|
2636
|
+
* Before this event fires, `canonicalDocument.subParts.*` may be
|
|
2637
|
+
* empty arrays / defaults; after it fires, they carry the parsed
|
|
2638
|
+
* values. Calling `ensureSubPartsHydrated` eagerly forces
|
|
2639
|
+
* hydration synchronously.
|
|
2640
|
+
*/
|
|
2641
|
+
type: "subparts-hydrated";
|
|
2642
|
+
documentId: string;
|
|
2523
2643
|
};
|
|
2524
2644
|
|
|
2645
|
+
/**
|
|
2646
|
+
* Ordered list of stages emitted by the staged load pipeline. The
|
|
2647
|
+
* skeleton-ready stage corresponds to the `ready` event firing.
|
|
2648
|
+
*/
|
|
2649
|
+
export type LoadStage =
|
|
2650
|
+
| "opc"
|
|
2651
|
+
| "body"
|
|
2652
|
+
| "styles-numbering-comments"
|
|
2653
|
+
| "skeleton-ready"
|
|
2654
|
+
| "subparts"
|
|
2655
|
+
| "compatibility";
|
|
2656
|
+
|
|
2525
2657
|
export interface LoadResult {
|
|
2526
2658
|
source?: ExternalDocumentSource;
|
|
2527
2659
|
}
|
|
@@ -2767,6 +2899,35 @@ export interface WordReviewEditorRef {
|
|
|
2767
2899
|
* registered.
|
|
2768
2900
|
*/
|
|
2769
2901
|
convertScopesToExternal(scopeIds: string[]): Promise<void>;
|
|
2902
|
+
/**
|
|
2903
|
+
* Schema 1.2 — editor-state persistence channel. Configures which
|
|
2904
|
+
* subsystems persist in-document / rowstore / key-only. Each call
|
|
2905
|
+
* merges into the current policy map. Mismatched locations with an
|
|
2906
|
+
* already-loaded docx fire `editor_state_policy_migrated` and
|
|
2907
|
+
* schedule the next serialize to honor the new location.
|
|
2908
|
+
*/
|
|
2909
|
+
configureEditorStatePolicy(policy: EditorStatePolicy): void;
|
|
2910
|
+
/**
|
|
2911
|
+
* Register the host's pull-resolver. Required before the editor
|
|
2912
|
+
* loads a 1.2 docx whose policy includes any keyed location.
|
|
2913
|
+
*/
|
|
2914
|
+
registerEditorStateResolver(resolver: EditorStateResolver): void;
|
|
2915
|
+
/**
|
|
2916
|
+
* Register the host's persister. Required before any subsystem
|
|
2917
|
+
* mutation under keyed policy fires a debounced persist.
|
|
2918
|
+
*/
|
|
2919
|
+
registerEditorStatePersister(persister: EditorStatePersister): void;
|
|
2920
|
+
/**
|
|
2921
|
+
* Return the entry key currently associated with a namespace under
|
|
2922
|
+
* keyed policy, or undefined when the namespace is in-document /
|
|
2923
|
+
* key not yet assigned.
|
|
2924
|
+
*/
|
|
2925
|
+
getEditorStateKey(namespace: EditorStateNamespace): string | undefined;
|
|
2926
|
+
/**
|
|
2927
|
+
* Re-attempt any persists that failed since the last successful
|
|
2928
|
+
* write. Pass a namespace to scope the retry; omit to retry all.
|
|
2929
|
+
*/
|
|
2930
|
+
retryPendingPersist(namespace?: EditorStateNamespace): Promise<void>;
|
|
2770
2931
|
setHostAnnotationOverlay(overlay: HostAnnotationOverlay): void;
|
|
2771
2932
|
clearHostAnnotationOverlay(): void;
|
|
2772
2933
|
getHostAnnotationSnapshot(): HostAnnotationSnapshot;
|
|
@@ -2866,6 +3027,38 @@ export interface WordReviewEditorProps {
|
|
|
2866
3027
|
currentUser: EditorUser;
|
|
2867
3028
|
ydoc?: import('yjs').Doc;
|
|
2868
3029
|
awareness?: import("y-protocols/awareness").Awareness;
|
|
3030
|
+
/**
|
|
3031
|
+
* Optional collab session built via `createCollabSession(...)`. When
|
|
3032
|
+
* set, the `"collab"` chrome preset mounts the top nav (presence
|
|
3033
|
+
* strip, role + audience chips, tamper banner, negotiation action
|
|
3034
|
+
* bar, send-to-supplier button). P9a–f components are pure
|
|
3035
|
+
* presentational; this prop is what wires them to live state. Pass
|
|
3036
|
+
* `undefined` to keep the chrome in non-collab mode even when
|
|
3037
|
+
* `chromePreset === "collab"`.
|
|
3038
|
+
*/
|
|
3039
|
+
collabSession?: import("../runtime/collab-session.ts").CollabSession;
|
|
3040
|
+
/**
|
|
3041
|
+
* Transport status signal for the presence strip + role chip (P9b /
|
|
3042
|
+
* P9c). Optional — defaults to "offline" when omitted.
|
|
3043
|
+
*/
|
|
3044
|
+
collabTransportStatus?: "connected" | "syncing" | "offline";
|
|
3045
|
+
/**
|
|
3046
|
+
* Identifier for the currently-focused comment, used by the collab
|
|
3047
|
+
* top nav's audience chip + negotiation action bar (P9c / P9e). The
|
|
3048
|
+
* host derives this from its own selection / rail-focus signal.
|
|
3049
|
+
*/
|
|
3050
|
+
activeCommentId?: string;
|
|
3051
|
+
/**
|
|
3052
|
+
* Baseline metadata for the send-to-supplier flow (P9f). Required
|
|
3053
|
+
* to enable the button; when omitted, the button stays present but
|
|
3054
|
+
* the confirmation modal becomes a no-op pass-through.
|
|
3055
|
+
*/
|
|
3056
|
+
collabSendBaseline?: {
|
|
3057
|
+
originDocumentId: string;
|
|
3058
|
+
originPayloadId: string;
|
|
3059
|
+
originContentHash: string;
|
|
3060
|
+
payloadXml: string;
|
|
3061
|
+
};
|
|
2869
3062
|
initialDocx?: Uint8Array | ArrayBuffer;
|
|
2870
3063
|
initialSessionState?: EditorSessionState;
|
|
2871
3064
|
initialSnapshot?: PersistedEditorSnapshot;
|
|
@@ -1791,22 +1791,46 @@ function combineMappingSteps(
|
|
|
1791
1791
|
// Suggesting mode: creates revision records instead of (or alongside) text mutations
|
|
1792
1792
|
// ---------------------------------------------------------------------------
|
|
1793
1793
|
|
|
1794
|
-
|
|
1795
|
-
|
|
1794
|
+
/**
|
|
1795
|
+
* Builds a suggesting-mode revision id that is deterministic across
|
|
1796
|
+
* clients. The id is a pure function of `(existing, timestamp, authorId)`
|
|
1797
|
+
* so that origin and replica — both of whom see the same `existing`
|
|
1798
|
+
* revisions record after a Yjs-ordered replay and receive the same
|
|
1799
|
+
* `timestamp` + `authorId` on the broadcast command event — produce
|
|
1800
|
+
* the same id for the same authored revision.
|
|
1801
|
+
*
|
|
1802
|
+
* Previously this used a module-level counter; that counter was shared
|
|
1803
|
+
* across every `DocumentRuntime` in a single process and drifted between
|
|
1804
|
+
* origin and replica whenever either side had other suggesting-mode work
|
|
1805
|
+
* bump the counter before replay. Cross-client `change.accept(changeId)`
|
|
1806
|
+
* / `change.reject(changeId)` then missed because the target id only
|
|
1807
|
+
* existed on one side.
|
|
1808
|
+
*/
|
|
1796
1809
|
function createSuggestingRevisionId(
|
|
1797
1810
|
existing: Record<string, unknown>,
|
|
1798
1811
|
timestamp: string,
|
|
1812
|
+
authorId: string,
|
|
1799
1813
|
): string {
|
|
1800
|
-
suggestingRevisionCounter += 1;
|
|
1801
1814
|
const ts = timestamp.replace(/[^0-9]/gu, "");
|
|
1802
|
-
|
|
1815
|
+
const authorMarker = sanitizeAuthorMarker(authorId);
|
|
1816
|
+
const prefix = `change-${authorMarker}-${ts}-s`;
|
|
1817
|
+
let n = 1;
|
|
1818
|
+
let id = `${prefix}${n}`;
|
|
1803
1819
|
while (existing[id]) {
|
|
1804
|
-
|
|
1805
|
-
id =
|
|
1820
|
+
n += 1;
|
|
1821
|
+
id = `${prefix}${n}`;
|
|
1806
1822
|
}
|
|
1807
1823
|
return id;
|
|
1808
1824
|
}
|
|
1809
1825
|
|
|
1826
|
+
function sanitizeAuthorMarker(authorId: string): string {
|
|
1827
|
+
// OOXML `w:id` accepts alphanumerics plus `._-`; keep within that set so
|
|
1828
|
+
// the id survives round-trips unchanged (see `sanitizeRevisionId` on the
|
|
1829
|
+
// serializer side).
|
|
1830
|
+
const cleaned = authorId.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
1831
|
+
return cleaned.length > 0 ? cleaned : "u";
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1810
1834
|
function createAuthoredRevision(
|
|
1811
1835
|
existing: Record<string, unknown>,
|
|
1812
1836
|
kind: "insertion" | "deletion",
|
|
@@ -1816,7 +1840,7 @@ function createAuthoredRevision(
|
|
|
1816
1840
|
timestamp: string,
|
|
1817
1841
|
metadata: Partial<NonNullable<CanonicalRevisionRecord["metadata"]>> = {},
|
|
1818
1842
|
): CanonicalRevisionRecord {
|
|
1819
|
-
const changeId = createSuggestingRevisionId(existing, timestamp);
|
|
1843
|
+
const changeId = createSuggestingRevisionId(existing, timestamp, authorId);
|
|
1820
1844
|
return {
|
|
1821
1845
|
changeId,
|
|
1822
1846
|
kind,
|
|
@@ -2066,6 +2090,7 @@ function applySuggestingInsert(
|
|
|
2066
2090
|
const replacementSuggestionId = createSuggestingRevisionId(
|
|
2067
2091
|
reviewState.document.review.revisions,
|
|
2068
2092
|
context.timestamp,
|
|
2093
|
+
authorId,
|
|
2069
2094
|
);
|
|
2070
2095
|
|
|
2071
2096
|
// Step 3: Create deletion revision for the selected range (text stays in place).
|
|
@@ -2324,7 +2349,7 @@ function applySuggestingInsertUnit(
|
|
|
2324
2349
|
result.mapping,
|
|
2325
2350
|
);
|
|
2326
2351
|
const replacementSuggestionId = from !== to
|
|
2327
|
-
? createSuggestingRevisionId(reviewState.document.review.revisions, context.timestamp)
|
|
2352
|
+
? createSuggestingRevisionId(reviewState.document.review.revisions, context.timestamp, authorId)
|
|
2328
2353
|
: undefined;
|
|
2329
2354
|
|
|
2330
2355
|
// If non-collapsed, mark selected range as deletion (positions are pre-mapping, content preserved)
|
|
@@ -26,7 +26,7 @@ export interface SecondaryStorySearchResult extends SurfaceSearchResult {
|
|
|
26
26
|
storyTarget: EditorStoryTarget;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
interface ProjectedSurfaceText {
|
|
29
|
+
export interface ProjectedSurfaceText {
|
|
30
30
|
text: string;
|
|
31
31
|
offsetMap: Array<number | null>;
|
|
32
32
|
}
|
|
@@ -112,7 +112,20 @@ export function searchSurfaceBlocks(
|
|
|
112
112
|
query: string,
|
|
113
113
|
options: SearchTextOptions = {},
|
|
114
114
|
): SurfaceSearchResult[] {
|
|
115
|
-
|
|
115
|
+
return searchProjectedSurfaceText(projectSurfaceText(blocks), query, options);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Search a pre-projected surface text. Hoist `projectSurfaceText(blocks)` out
|
|
120
|
+
* of a per-query loop to avoid rebuilding the projection N times for N queries
|
|
121
|
+
* against the same surface — L7 Phase 1.5 discovered this cost dominates
|
|
122
|
+
* `collectFieldMarkup` on the CCEP large-tables fixture.
|
|
123
|
+
*/
|
|
124
|
+
export function searchProjectedSurfaceText(
|
|
125
|
+
projection: ProjectedSurfaceText,
|
|
126
|
+
query: string,
|
|
127
|
+
options: SearchTextOptions = {},
|
|
128
|
+
): SurfaceSearchResult[] {
|
|
116
129
|
return findSearchMatches(projection.text, query, options)
|
|
117
130
|
.map((match) => {
|
|
118
131
|
const range = resolveProjectedRuntimeRange(
|
package/src/index.ts
CHANGED
|
@@ -267,4 +267,17 @@ export type {
|
|
|
267
267
|
ScopeMetadataPersistence,
|
|
268
268
|
ScopeMetadataResolver,
|
|
269
269
|
ScopeMetadataStorageRef,
|
|
270
|
+
// Schema 1.2 — editor-state persistence types
|
|
271
|
+
EditorStateNamespace,
|
|
272
|
+
EditorStateLocation,
|
|
273
|
+
EditorStateResolveErrorMode,
|
|
274
|
+
EditorStatePolicyEntry,
|
|
275
|
+
EditorStatePolicy,
|
|
276
|
+
EditorStateStorageRef,
|
|
277
|
+
EditorStateBlob,
|
|
278
|
+
EditorStateResolver,
|
|
279
|
+
EditorStatePersister,
|
|
280
|
+
EditorStatePolicyMigration,
|
|
281
|
+
EditorStatePartLoadFailure,
|
|
282
|
+
EditorStatePartPersistFailure,
|
|
270
283
|
} from "./api/public-types.ts";
|