@beyondwork/docx-react-component 1.0.80 → 1.0.82
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 +1 -1
- package/src/api/public-types.ts +12 -13
- package/src/api/v3/_runtime-handle.ts +4 -0
- package/src/api/v3/runtime/document.ts +61 -3
- package/src/api/v3/runtime/review.ts +55 -2
- package/src/api/v3/ui/chrome-composition.ts +10 -2
- package/src/io/normalize/normalize-text.ts +4 -1
- package/src/io/ooxml/parse-drawing.ts +4 -0
- package/src/model/canonical-document.ts +2 -0
- package/src/ui/WordReviewEditor.tsx +132 -3
- package/src/ui/editor-shell-view.tsx +1 -0
- package/src/ui-tailwind/chrome/editor-action-registry.ts +373 -0
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +59 -35
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +2 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
- package/src/ui-tailwind/chrome/use-context-menu-controller.ts +15 -10
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +1 -0
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +256 -37
- package/src/ui-tailwind/editor-surface/pm-schema.ts +54 -1
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +31 -1
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +15 -0
- package/src/ui-tailwind/page-stack/tw-floating-image-layer.tsx +24 -5
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +35 -6
- package/src/ui-tailwind/page-stack/use-visible-block-range.ts +333 -43
- package/src/ui-tailwind/review-workspace/types.ts +1 -0
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +273 -24
- package/src/ui-tailwind/review-workspace/use-workspace-composition.ts +46 -6
- package/src/ui-tailwind/theme/editor-theme.css +3 -5
- package/src/ui-tailwind/tw-review-workspace.tsx +117 -14
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.82",
|
|
5
5
|
"description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": [
|
package/src/api/public-types.ts
CHANGED
|
@@ -5611,17 +5611,16 @@ export interface WordReviewEditorProps {
|
|
|
5611
5611
|
import("../ui/headless/chrome-registry").SelectionToolRegistryEntry
|
|
5612
5612
|
>;
|
|
5613
5613
|
/**
|
|
5614
|
-
*
|
|
5615
|
-
*
|
|
5616
|
-
*
|
|
5617
|
-
*
|
|
5618
|
-
*
|
|
5619
|
-
*
|
|
5620
|
-
*
|
|
5621
|
-
*
|
|
5622
|
-
*
|
|
5623
|
-
*
|
|
5624
|
-
* Phase C.γ surfaces shipped in `5fce913a`.
|
|
5614
|
+
* Optional host-callback extension bag for workspace command chrome.
|
|
5615
|
+
* The default `<WordReviewEditor />` path now mounts
|
|
5616
|
+
* `TwWorkspaceChromeHost` with product-backed commands for formatting,
|
|
5617
|
+
* paragraph/list/style/font/color actions, search/navigation host
|
|
5618
|
+
* delegation, comments, and table insertion/structure.
|
|
5619
|
+
* Supplying this bag overrides or extends those defaults for host-owned
|
|
5620
|
+
* actions such as custom table properties, hyperlink handling, or
|
|
5621
|
+
* object metadata. Actions without a wired callback are hidden from
|
|
5622
|
+
* every surface — progressive disclosure per `designsystem.md §2.1
|
|
5623
|
+
* principle 4`.
|
|
5625
5624
|
*/
|
|
5626
5625
|
editorActionHost?: import("../ui-tailwind/chrome/editor-action-registry").EditorActionHostCallbacks;
|
|
5627
5626
|
/**
|
|
@@ -5637,8 +5636,8 @@ export interface WordReviewEditorProps {
|
|
|
5637
5636
|
/**
|
|
5638
5637
|
* Suppress the global Ctrl/Cmd+K palette listener — e.g. when a
|
|
5639
5638
|
* higher-priority modal captures keyboard focus. Defaults to
|
|
5640
|
-
* `false` (palette listener is active
|
|
5641
|
-
*
|
|
5639
|
+
* `false` (the default product command palette listener is active
|
|
5640
|
+
* unless this prop suppresses it).
|
|
5642
5641
|
*/
|
|
5643
5642
|
commandPaletteDisabled?: boolean;
|
|
5644
5643
|
/**
|
|
@@ -43,6 +43,7 @@ export type RuntimeApiHandle = Pick<
|
|
|
43
43
|
DocumentRuntime,
|
|
44
44
|
// Session + export (runtime.document family)
|
|
45
45
|
| "getSessionState"
|
|
46
|
+
| "setDocumentMode"
|
|
46
47
|
| "exportDocx"
|
|
47
48
|
| "getCompatibilityReport"
|
|
48
49
|
| "getWarnings"
|
|
@@ -54,6 +55,7 @@ export type RuntimeApiHandle = Pick<
|
|
|
54
55
|
| "findAllText"
|
|
55
56
|
// Review (runtime.review family)
|
|
56
57
|
| "getReviewWorkSnapshot"
|
|
58
|
+
| "getSuggestionsSnapshot"
|
|
57
59
|
| "acceptChange"
|
|
58
60
|
| "rejectChange"
|
|
59
61
|
| "resolveComment"
|
|
@@ -136,6 +138,7 @@ export type RuntimeApiHandle = Pick<
|
|
|
136
138
|
*/
|
|
137
139
|
export const RUNTIME_API_HANDLE_SHAPE_CHECK: Record<keyof RuntimeApiHandle, true> = {
|
|
138
140
|
getSessionState: true,
|
|
141
|
+
setDocumentMode: true,
|
|
139
142
|
exportDocx: true,
|
|
140
143
|
getCompatibilityReport: true,
|
|
141
144
|
getWarnings: true,
|
|
@@ -143,6 +146,7 @@ export const RUNTIME_API_HANDLE_SHAPE_CHECK: Record<keyof RuntimeApiHandle, true
|
|
|
143
146
|
getCanonicalDocument: true,
|
|
144
147
|
findAllText: true,
|
|
145
148
|
getReviewWorkSnapshot: true,
|
|
149
|
+
getSuggestionsSnapshot: true,
|
|
146
150
|
acceptChange: true,
|
|
147
151
|
rejectChange: true,
|
|
148
152
|
resolveComment: true,
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @endStateApi v3 — `runtime.document` family.
|
|
3
3
|
*
|
|
4
|
-
* See docs/reference/public-api.md § runtime.document.
|
|
4
|
+
* See docs/reference/public-api.md § runtime.document.
|
|
5
5
|
* `load` (partial — runtime pre-load is the caller's responsibility; v3
|
|
6
|
-
* exposes a re-mount semantic later), `
|
|
7
|
-
*
|
|
6
|
+
* exposes a re-mount semantic later), `getMode` / `setMode` (live;
|
|
7
|
+
* delegates to the runtime view-state posture), `export` (live; delegates
|
|
8
|
+
* to `runtime.exportDocx`), `validate` (partial; read live, write mock).
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import type { RuntimeApiHandle } from "../_runtime-handle.ts";
|
|
11
12
|
import type {
|
|
13
|
+
DocumentMode,
|
|
12
14
|
EditorError,
|
|
13
15
|
ExportDocxOptions,
|
|
14
16
|
ExportResult,
|
|
@@ -88,6 +90,42 @@ export const loadMetadata: ApiV3FnMetadata = {
|
|
|
88
90
|
"§Runtime API § runtime.document.load. Graduation (2026-04-22, post-eb7d14fa): `live` via direct delegation to `loadDocxSessionAsync` (src/session/import/loader.ts). Returns a PersistedEditorSnapshot the caller can pass to DocxSession.reopenFromSnapshot or persist for later rehydrate. Note per arch §R8 Option B: v3 does NOT construct the receiving DocumentRuntime — that's the caller's job via createDocumentRuntime(initialSessionState).",
|
|
89
91
|
};
|
|
90
92
|
|
|
93
|
+
/* ================================================================== */
|
|
94
|
+
/* mode */
|
|
95
|
+
/* ================================================================== */
|
|
96
|
+
|
|
97
|
+
export const getModeMetadata: ApiV3FnMetadata = {
|
|
98
|
+
name: "runtime.document.getMode",
|
|
99
|
+
status: "live",
|
|
100
|
+
sourceLayer: "runtime-core",
|
|
101
|
+
liveEvidence: {
|
|
102
|
+
runnerTest: "test/api/v3/create-accepts-handle.test.ts",
|
|
103
|
+
commit: "refactor-03-tracked-changes-v1-api-adapter",
|
|
104
|
+
},
|
|
105
|
+
uxIntent: { uiVisible: false, expectsUxResponse: "none" },
|
|
106
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "document", auditCategory: "document-mode" },
|
|
107
|
+
stateClass: "C-local",
|
|
108
|
+
persistsTo: "none",
|
|
109
|
+
rwdReference:
|
|
110
|
+
"§Runtime API § runtime.document.getMode. Live adapter over the runtime render snapshot's DocumentMode; suggesting remains the tracked-change authoring posture.",
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const setModeMetadata: ApiV3FnMetadata = {
|
|
114
|
+
name: "runtime.document.setMode",
|
|
115
|
+
status: "live",
|
|
116
|
+
sourceLayer: "runtime-core",
|
|
117
|
+
liveEvidence: {
|
|
118
|
+
runnerTest: "test/api/v3/create-accepts-handle.test.ts",
|
|
119
|
+
commit: "refactor-03-tracked-changes-v1-api-adapter",
|
|
120
|
+
},
|
|
121
|
+
uxIntent: { uiVisible: true, expectsUxResponse: "surface-refresh", expectedDelta: "document mode changes" },
|
|
122
|
+
agentMetadata: { readOrMutate: "mutate", boundedScope: "document", auditCategory: "document-mode" },
|
|
123
|
+
stateClass: "C-local",
|
|
124
|
+
persistsTo: "none",
|
|
125
|
+
rwdReference:
|
|
126
|
+
"§Runtime API § runtime.document.setMode. Live adapter over runtime.setDocumentMode(); mode 'suggesting' is the v3 entry to tracked-change authoring.",
|
|
127
|
+
};
|
|
128
|
+
|
|
91
129
|
/* ================================================================== */
|
|
92
130
|
/* export */
|
|
93
131
|
/* ================================================================== */
|
|
@@ -196,6 +234,26 @@ export function createDocumentFamily(runtime: RuntimeApiHandle) {
|
|
|
196
234
|
return result;
|
|
197
235
|
},
|
|
198
236
|
|
|
237
|
+
getMode(): DocumentMode {
|
|
238
|
+
// @endStateApi — live. Reads the runtime view-state posture that
|
|
239
|
+
// render snapshots already expose.
|
|
240
|
+
return runtime.getRenderSnapshot().documentMode;
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
setMode(mode: DocumentMode): void {
|
|
244
|
+
// @endStateApi — live. Delegates to the runtime's document-mode
|
|
245
|
+
// setter; `suggesting` is the tracked-change authoring posture.
|
|
246
|
+
runtime.setDocumentMode(mode);
|
|
247
|
+
emitUxResponse(runtime, {
|
|
248
|
+
apiFn: setModeMetadata.name,
|
|
249
|
+
intent: setModeMetadata.uxIntent.expectedDelta ?? "",
|
|
250
|
+
mockOrLive: "live",
|
|
251
|
+
uiVisible: true,
|
|
252
|
+
expectedDelta: setModeMetadata.uxIntent.expectedDelta,
|
|
253
|
+
actualDelta: { kind: "surface-refresh", payload: { mode } },
|
|
254
|
+
});
|
|
255
|
+
},
|
|
256
|
+
|
|
199
257
|
async export(options?: ExportDocxOptions): Promise<ExportResult> {
|
|
200
258
|
// @endStateApi — live. Delegates to the shipped runtime export path.
|
|
201
259
|
const result = await runtime.exportDocx(options);
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @endStateApi v3 — `runtime.review` family.
|
|
3
3
|
*
|
|
4
|
-
* getComments (live) / getChanges (live) /
|
|
5
|
-
* resolveComment (live).
|
|
4
|
+
* getComments (live) / getChanges (live) / getSuggestions (live) /
|
|
5
|
+
* acceptChange (live) / rejectChange (live) / resolveComment (live).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { RuntimeApiHandle } from "../_runtime-handle.ts";
|
|
9
9
|
import type { ApiV3FnMetadata } from "../_layer-metadata.ts";
|
|
10
10
|
import type {
|
|
11
11
|
CommentSidebarThreadSnapshot,
|
|
12
|
+
SuggestionsSnapshot,
|
|
12
13
|
TrackedChangeEntrySnapshot,
|
|
13
14
|
} from "../../public-types.ts";
|
|
14
15
|
import { emitUxResponse } from "../_ux-response.ts";
|
|
@@ -51,6 +52,22 @@ export const getChangesMetadata: ApiV3FnMetadata = {
|
|
|
51
52
|
rwdReference: "§Runtime API § runtime.review.getChanges",
|
|
52
53
|
};
|
|
53
54
|
|
|
55
|
+
export const getSuggestionsMetadata: ApiV3FnMetadata = {
|
|
56
|
+
name: "runtime.review.getSuggestions",
|
|
57
|
+
status: "live",
|
|
58
|
+
sourceLayer: "workflow-review",
|
|
59
|
+
liveEvidence: {
|
|
60
|
+
runnerTest: "test/api/v3/create-accepts-handle.test.ts",
|
|
61
|
+
commit: "refactor-03-tracked-changes-v1-api-adapter",
|
|
62
|
+
},
|
|
63
|
+
uxIntent: { uiVisible: false, expectsUxResponse: "none" },
|
|
64
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "document", auditCategory: "review-read" },
|
|
65
|
+
stateClass: "A-canonical",
|
|
66
|
+
persistsTo: "canonical",
|
|
67
|
+
rwdReference:
|
|
68
|
+
"§Runtime API § runtime.review.getSuggestions. Live adapter over runtime.getSuggestionsSnapshot(); semantic suggestion readback is grouped by the runtime, not by v3.",
|
|
69
|
+
};
|
|
70
|
+
|
|
54
71
|
export const acceptChangeMetadata: ApiV3FnMetadata = {
|
|
55
72
|
name: "runtime.review.acceptChange",
|
|
56
73
|
status: "live",
|
|
@@ -70,6 +87,23 @@ export const acceptChangeMetadata: ApiV3FnMetadata = {
|
|
|
70
87
|
rwdReference: "§Runtime API § runtime.review.acceptChange",
|
|
71
88
|
};
|
|
72
89
|
|
|
90
|
+
export const rejectChangeMetadata: ApiV3FnMetadata = {
|
|
91
|
+
name: "runtime.review.rejectChange",
|
|
92
|
+
status: "live",
|
|
93
|
+
sourceLayer: "workflow-review",
|
|
94
|
+
liveEvidence: {
|
|
95
|
+
runnerTest: "test/api/v3/create-accepts-handle.test.ts",
|
|
96
|
+
commit: "refactor-03-tracked-changes-v1-api-adapter",
|
|
97
|
+
},
|
|
98
|
+
uxIntent: { uiVisible: true, expectsUxResponse: "inline-change", expectedDelta: "change mark disappears and text restores" },
|
|
99
|
+
agentMetadata: { readOrMutate: "mutate", boundedScope: "scope", auditCategory: "change-reject" },
|
|
100
|
+
stateClass: "A-canonical",
|
|
101
|
+
persistsTo: "canonical",
|
|
102
|
+
broadcastsVia: "crdt",
|
|
103
|
+
rwdReference:
|
|
104
|
+
"§Runtime API § runtime.review.rejectChange. Live adapter over runtime.rejectChange; mirrors acceptChange for individual tracked-change review.",
|
|
105
|
+
};
|
|
106
|
+
|
|
73
107
|
export const resolveCommentMetadata: ApiV3FnMetadata = {
|
|
74
108
|
name: "runtime.review.resolveComment",
|
|
75
109
|
status: "live",
|
|
@@ -100,6 +134,12 @@ export function createReviewFamily(runtime: RuntimeApiHandle) {
|
|
|
100
134
|
return runtime.getRenderSnapshot().trackedChanges.revisions;
|
|
101
135
|
},
|
|
102
136
|
|
|
137
|
+
getSuggestions(): SuggestionsSnapshot {
|
|
138
|
+
// @endStateApi — live. Delegates to the runtime's semantic
|
|
139
|
+
// suggestion grouping rather than regrouping raw revisions here.
|
|
140
|
+
return runtime.getSuggestionsSnapshot();
|
|
141
|
+
},
|
|
142
|
+
|
|
103
143
|
acceptChange(changeId: string): void {
|
|
104
144
|
// @endStateApi — live. Delegates.
|
|
105
145
|
runtime.acceptChange(changeId);
|
|
@@ -113,6 +153,19 @@ export function createReviewFamily(runtime: RuntimeApiHandle) {
|
|
|
113
153
|
});
|
|
114
154
|
},
|
|
115
155
|
|
|
156
|
+
rejectChange(changeId: string): void {
|
|
157
|
+
// @endStateApi — live. Delegates.
|
|
158
|
+
runtime.rejectChange(changeId);
|
|
159
|
+
emitUxResponse(runtime, {
|
|
160
|
+
apiFn: rejectChangeMetadata.name,
|
|
161
|
+
intent: rejectChangeMetadata.uxIntent.expectedDelta ?? "",
|
|
162
|
+
mockOrLive: "live",
|
|
163
|
+
uiVisible: true,
|
|
164
|
+
expectedDelta: rejectChangeMetadata.uxIntent.expectedDelta,
|
|
165
|
+
actualDelta: { kind: "inline-change", payload: { changeId } },
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
|
|
116
169
|
resolveComment(commentId: string): void {
|
|
117
170
|
// @endStateApi — live.
|
|
118
171
|
runtime.resolveComment(commentId);
|
|
@@ -131,6 +131,7 @@ export interface ChromeCompositionInput {
|
|
|
131
131
|
| "simple"
|
|
132
132
|
| "all";
|
|
133
133
|
readonly activeRailTab?: EditorRailTab | null;
|
|
134
|
+
readonly railOpen?: boolean;
|
|
134
135
|
readonly pinnedRailTabs?: ReadonlySet<EditorRailTab>;
|
|
135
136
|
readonly density?: ChromeDensity;
|
|
136
137
|
readonly containerWidth?: number;
|
|
@@ -255,13 +256,18 @@ function resolveVisibleRailTabs(
|
|
|
255
256
|
railOpen: boolean,
|
|
256
257
|
diagnosticsSignal: DiagnosticsSignal,
|
|
257
258
|
pinned: ReadonlySet<EditorRailTab>,
|
|
259
|
+
mode: EditorChromeMode,
|
|
258
260
|
): ReadonlySet<EditorRailTab> {
|
|
259
261
|
const visible = new Set<EditorRailTab>();
|
|
260
262
|
if (railOpen) {
|
|
261
263
|
visible.add("comments");
|
|
262
264
|
visible.add("changes");
|
|
263
265
|
visible.add("workflow");
|
|
264
|
-
if (
|
|
266
|
+
if (
|
|
267
|
+
diagnosticsSignal.severity !== "none" ||
|
|
268
|
+
diagnosticsSignal.count > 0 ||
|
|
269
|
+
mode === "more"
|
|
270
|
+
) {
|
|
265
271
|
visible.add("health");
|
|
266
272
|
}
|
|
267
273
|
}
|
|
@@ -294,11 +300,13 @@ export function resolveChromeComposition(
|
|
|
294
300
|
const density: ChromeDensity = input.density ?? "standard";
|
|
295
301
|
const pinnedRailTabs: ReadonlySet<EditorRailTab> =
|
|
296
302
|
input.pinnedRailTabs ?? new Set<EditorRailTab>();
|
|
297
|
-
const railOpen =
|
|
303
|
+
const railOpen =
|
|
304
|
+
input.railOpen ?? (options.showReviewRail && visibility.reviewRail);
|
|
298
305
|
const visibleTabs = resolveVisibleRailTabs(
|
|
299
306
|
railOpen,
|
|
300
307
|
diagnosticsSignal,
|
|
301
308
|
pinnedRailTabs,
|
|
309
|
+
mode,
|
|
302
310
|
);
|
|
303
311
|
|
|
304
312
|
// Default active tab: honor caller; otherwise land on the mode-appropriate tab.
|
|
@@ -671,7 +671,10 @@ function normalizeDrawingFrameNode(
|
|
|
671
671
|
const filename = packagePartName.slice(packagePartName.lastIndexOf("/") + 1) || "image.bin";
|
|
672
672
|
state.media.items[node.content.mediaId] = {
|
|
673
673
|
mediaId: node.content.mediaId,
|
|
674
|
-
contentType:
|
|
674
|
+
contentType:
|
|
675
|
+
node.content.contentType ??
|
|
676
|
+
existingMediaItem?.contentType ??
|
|
677
|
+
"application/octet-stream",
|
|
675
678
|
filename,
|
|
676
679
|
packagePartName,
|
|
677
680
|
relationshipId: node.content.blipRef,
|
|
@@ -188,8 +188,12 @@ function resolveContent(
|
|
|
188
188
|
const partPath = normalizePartPath(
|
|
189
189
|
resolveRelationshipTarget(opts.sourcePartPath ?? "/word/document.xml", rel),
|
|
190
190
|
);
|
|
191
|
+
const mediaPart = opts.mediaParts?.get(partPath);
|
|
191
192
|
pic.packagePartName = partPath;
|
|
192
193
|
pic.mediaId = `media:${partPath.slice(1)}`;
|
|
194
|
+
if (mediaPart?.contentType) {
|
|
195
|
+
pic.contentType = mediaPart.contentType;
|
|
196
|
+
}
|
|
193
197
|
}
|
|
194
198
|
// F4.1 — preserve outer drawing XML for lossless round-trip serialization
|
|
195
199
|
pic.rawXml = rawXml;
|
|
@@ -1922,6 +1922,8 @@ export interface PictureContent {
|
|
|
1922
1922
|
mediaId?: string;
|
|
1923
1923
|
/** Absolute package path for media catalog lookup. */
|
|
1924
1924
|
packagePartName?: string;
|
|
1925
|
+
/** MIME resolved from the OPC media part, when known. */
|
|
1926
|
+
contentType?: string;
|
|
1925
1927
|
srcRect?: { top: number; bottom: number; left: number; right: number };
|
|
1926
1928
|
stretch?: boolean;
|
|
1927
1929
|
/**
|
|
@@ -186,6 +186,7 @@ import { EditorShellView } from "./editor-shell-view.tsx";
|
|
|
186
186
|
import { TwDebugPresentation } from "../ui-tailwind/debug/index.ts";
|
|
187
187
|
import { shellPasteFragmentParser as SHELL_PASTE_FRAGMENT_PARSER } from "../shell/paste-adapter.ts";
|
|
188
188
|
import { EditorSurfaceController } from "./editor-surface-controller.tsx";
|
|
189
|
+
import type { EditorActionHostCallbacks } from "../ui-tailwind/chrome/editor-action-registry";
|
|
189
190
|
import type { TwWorkspaceChromeHostController } from "../ui-tailwind/chrome/tw-workspace-chrome-host";
|
|
190
191
|
import {
|
|
191
192
|
resolveChromePreset,
|
|
@@ -3306,6 +3307,100 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
3306
3307
|
},
|
|
3307
3308
|
});
|
|
3308
3309
|
|
|
3310
|
+
const productEditorActionHost = useMemo<EditorActionHostCallbacks>(() => {
|
|
3311
|
+
const runClipboardCommand = (command: "copy" | "cut") => {
|
|
3312
|
+
try {
|
|
3313
|
+
globalThis.document?.execCommand?.(command);
|
|
3314
|
+
} catch {
|
|
3315
|
+
// Browser-native clipboard commands are best-effort fallbacks.
|
|
3316
|
+
}
|
|
3317
|
+
};
|
|
3318
|
+
|
|
3319
|
+
const defaultHost: EditorActionHostCallbacks = {
|
|
3320
|
+
onUndo: commands.onUndo,
|
|
3321
|
+
onRedo: commands.onRedo,
|
|
3322
|
+
onCut: () => runClipboardCommand("cut"),
|
|
3323
|
+
onCopy: () => runClipboardCommand("copy"),
|
|
3324
|
+
onPaste: () => {
|
|
3325
|
+
const readText = globalThis.navigator?.clipboard?.readText;
|
|
3326
|
+
if (typeof readText !== "function") return;
|
|
3327
|
+
void readText.call(globalThis.navigator.clipboard)
|
|
3328
|
+
.then((text: string) => {
|
|
3329
|
+
if (!text) return;
|
|
3330
|
+
dispatchTextCommand(
|
|
3331
|
+
activeRuntime,
|
|
3332
|
+
{ type: "insert-text", text },
|
|
3333
|
+
DISPATCH_CONTEXT,
|
|
3334
|
+
);
|
|
3335
|
+
})
|
|
3336
|
+
.catch(() => {
|
|
3337
|
+
// Clipboard permission failures should not break the menu.
|
|
3338
|
+
});
|
|
3339
|
+
},
|
|
3340
|
+
onToggleBold: commands.onToggleBold,
|
|
3341
|
+
onToggleItalic: commands.onToggleItalic,
|
|
3342
|
+
onToggleUnderline: commands.onToggleUnderline,
|
|
3343
|
+
onToggleStrikethrough: commands.onToggleStrikethrough,
|
|
3344
|
+
onSetParagraphStyle: (styleId) => {
|
|
3345
|
+
const resolvedStyleId = resolveProductParagraphStyleId(styleCatalog, styleId);
|
|
3346
|
+
if (!resolvedStyleId) {
|
|
3347
|
+
activeRuntime.emitBlockedCommand("setParagraphStyle", [{
|
|
3348
|
+
code: "unsupported_surface",
|
|
3349
|
+
message: `${styleId} is not available in this document's style catalog.`,
|
|
3350
|
+
}]);
|
|
3351
|
+
return;
|
|
3352
|
+
}
|
|
3353
|
+
commands.onSetParagraphStyle?.(resolvedStyleId);
|
|
3354
|
+
},
|
|
3355
|
+
onSetFontFamily: commands.onSetFontFamily,
|
|
3356
|
+
onSetFontSize: commands.onSetFontSize,
|
|
3357
|
+
onSetTextColor: commands.onSetTextColor,
|
|
3358
|
+
onSetHighlightColor: commands.onSetHighlightColor,
|
|
3359
|
+
onToggleBulletedList: commands.onToggleBulletedList,
|
|
3360
|
+
onToggleNumberedList: commands.onToggleNumberedList,
|
|
3361
|
+
onOutdent: commands.onOutdent,
|
|
3362
|
+
onIndent: commands.onIndent,
|
|
3363
|
+
onSetAlignment: (alignment) => commands.onSetAlignment?.(alignment),
|
|
3364
|
+
onInsertPageBreak: commands.onInsertPageBreak,
|
|
3365
|
+
onInsertSectionBreak: (type) => commands.onInsertSectionBreak?.(type),
|
|
3366
|
+
onInsertTable: commands.onInsertTable,
|
|
3367
|
+
onAddComment: commands.onAddComment,
|
|
3368
|
+
onFindRequested: onFindRequested
|
|
3369
|
+
? () => onFindRequested({ selectionText: "", selectionRange: snapshot.selection })
|
|
3370
|
+
: undefined,
|
|
3371
|
+
onReplaceRequested: onReplaceRequested
|
|
3372
|
+
? () => onReplaceRequested({ selectionText: "", selectionRange: snapshot.selection })
|
|
3373
|
+
: undefined,
|
|
3374
|
+
onPrintRequested,
|
|
3375
|
+
onGoToRequested: onGoToRequested
|
|
3376
|
+
? () => onGoToRequested({ selectionText: "", selectionRange: snapshot.selection })
|
|
3377
|
+
: undefined,
|
|
3378
|
+
onInsertRowAbove: commands.onAddRowBefore,
|
|
3379
|
+
onInsertRowBelow: commands.onAddRowAfter,
|
|
3380
|
+
onInsertColumnBefore: commands.onAddColumnBefore,
|
|
3381
|
+
onInsertColumnAfter: commands.onAddColumnAfter,
|
|
3382
|
+
onDeleteRow: commands.onDeleteRow,
|
|
3383
|
+
onDeleteColumn: commands.onDeleteColumn,
|
|
3384
|
+
onDeleteTable: commands.onDeleteTable,
|
|
3385
|
+
onMergeCells: commands.onMergeCells,
|
|
3386
|
+
onSplitCell: commands.onSplitCell,
|
|
3387
|
+
};
|
|
3388
|
+
|
|
3389
|
+
return editorActionHost
|
|
3390
|
+
? { ...defaultHost, ...editorActionHost }
|
|
3391
|
+
: defaultHost;
|
|
3392
|
+
}, [
|
|
3393
|
+
activeRuntime,
|
|
3394
|
+
commands,
|
|
3395
|
+
editorActionHost,
|
|
3396
|
+
onFindRequested,
|
|
3397
|
+
onGoToRequested,
|
|
3398
|
+
onPrintRequested,
|
|
3399
|
+
onReplaceRequested,
|
|
3400
|
+
snapshot.selection,
|
|
3401
|
+
styleCatalog,
|
|
3402
|
+
]);
|
|
3403
|
+
|
|
3309
3404
|
const harnessShowUnsupportedPreviews = readHarnessDebugPortsFlag(__harnessDebugPorts, "unsupportedObjectPreviews");
|
|
3310
3405
|
const effectiveShowUnsupportedPreviews = computeEffectiveShowUnsupportedPreviews({
|
|
3311
3406
|
harnessShowUnsupportedPreviews,
|
|
@@ -3342,7 +3437,7 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
3342
3437
|
activeWorkflowScopeIds={workflowScopeSnapshot?.activeWorkItem?.scopeIds ?? []}
|
|
3343
3438
|
workflowMetadata={workflowMarkupSnapshot?.metadata}
|
|
3344
3439
|
onSelectionToolbarAnchorChange={handleSelectionToolbarAnchorChange}
|
|
3345
|
-
{
|
|
3440
|
+
onContextMenuRequested={handleContextMenuRequested}
|
|
3346
3441
|
{...editorCallbacks}
|
|
3347
3442
|
dispatchRuntimeCommand={(command) =>
|
|
3348
3443
|
activeRuntime.applyActiveStoryTextCommand(command as never)
|
|
@@ -3420,8 +3515,9 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
3420
3515
|
interactionGuardSnapshot={interactionGuardSnapshot}
|
|
3421
3516
|
chromePreset={effectiveChromePreset}
|
|
3422
3517
|
chromeOptions={chromeOptions}
|
|
3423
|
-
{
|
|
3424
|
-
|
|
3518
|
+
density={density}
|
|
3519
|
+
editorActionHost={productEditorActionHost}
|
|
3520
|
+
chromeControllerRef={teedChromeControllerRef}
|
|
3425
3521
|
{...(commandPaletteDisabled !== undefined
|
|
3426
3522
|
? { commandPaletteDisabled }
|
|
3427
3523
|
: {})}
|
|
@@ -4515,6 +4611,39 @@ function createSelectionToolbarStyleBadge(
|
|
|
4515
4611
|
return { label: styleEntry.displayName };
|
|
4516
4612
|
}
|
|
4517
4613
|
|
|
4614
|
+
function resolveProductParagraphStyleId(
|
|
4615
|
+
styleCatalog: StyleCatalogSnapshot,
|
|
4616
|
+
requestedStyleId: string,
|
|
4617
|
+
): string | null {
|
|
4618
|
+
switch (requestedStyleId) {
|
|
4619
|
+
case "Heading1":
|
|
4620
|
+
return resolveHeadingShortcutStyleId(styleCatalog, 1);
|
|
4621
|
+
case "Heading2":
|
|
4622
|
+
return resolveHeadingShortcutStyleId(styleCatalog, 2);
|
|
4623
|
+
case "Heading3":
|
|
4624
|
+
return resolveHeadingShortcutStyleId(styleCatalog, 3);
|
|
4625
|
+
case "Normal": {
|
|
4626
|
+
const defaultStyle = styleCatalog.paragraphs.find((entry) => entry.isDefault);
|
|
4627
|
+
if (defaultStyle) return defaultStyle.styleId;
|
|
4628
|
+
break;
|
|
4629
|
+
}
|
|
4630
|
+
default:
|
|
4631
|
+
break;
|
|
4632
|
+
}
|
|
4633
|
+
|
|
4634
|
+
const requestedToken = normalizeProductStyleToken(requestedStyleId);
|
|
4635
|
+
const match = styleCatalog.paragraphs.find(
|
|
4636
|
+
(entry) =>
|
|
4637
|
+
normalizeProductStyleToken(entry.styleId) === requestedToken ||
|
|
4638
|
+
normalizeProductStyleToken(entry.displayName) === requestedToken,
|
|
4639
|
+
);
|
|
4640
|
+
return match?.styleId ?? null;
|
|
4641
|
+
}
|
|
4642
|
+
|
|
4643
|
+
function normalizeProductStyleToken(value: string): string {
|
|
4644
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4518
4647
|
function createSelectionToolbarListBadge(
|
|
4519
4648
|
viewState: ReturnType<WordReviewEditorRuntime["getViewState"]>,
|
|
4520
4649
|
): SelectionToolbarModel["badges"][number] | null {
|
|
@@ -83,6 +83,7 @@ export interface EditorShellViewProps {
|
|
|
83
83
|
interactionGuardSnapshot?: InteractionGuardSnapshot;
|
|
84
84
|
chromePreset?: WordReviewEditorChromePreset;
|
|
85
85
|
chromeOptions?: Partial<WordReviewEditorChromeOptions>;
|
|
86
|
+
density?: "compact" | "standard" | "comfortable";
|
|
86
87
|
editorActionHost?: import("../ui-tailwind/chrome/editor-action-registry.ts").EditorActionHostCallbacks;
|
|
87
88
|
chromeControllerRef?: React.Ref<
|
|
88
89
|
import("../ui-tailwind/chrome/tw-workspace-chrome-host.tsx").TwWorkspaceChromeHostController
|