@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
|
@@ -97,14 +97,15 @@ import {
|
|
|
97
97
|
} from "../core/commands/style-commands.ts";
|
|
98
98
|
import {
|
|
99
99
|
continueNumbering as continueListNumbering,
|
|
100
|
-
backspaceAtListStart,
|
|
101
|
-
indentListItems,
|
|
102
|
-
outdentListItems,
|
|
103
100
|
restartNumbering as restartListNumbering,
|
|
104
|
-
splitListParagraph,
|
|
105
101
|
toggleBulletedList,
|
|
106
102
|
toggleNumberedList,
|
|
107
103
|
} from "../core/commands/list-commands.ts";
|
|
104
|
+
import {
|
|
105
|
+
dispatchTextCommand,
|
|
106
|
+
type DispatchContext,
|
|
107
|
+
type DispatchTextCommand,
|
|
108
|
+
} from "../runtime/edit-dispatch/index.ts";
|
|
108
109
|
import {
|
|
109
110
|
resolveActiveParagraphIndex,
|
|
110
111
|
setActiveParagraphIndentation,
|
|
@@ -242,6 +243,12 @@ const BROWSER_SAFE_PREVIEW_TYPES = new Set([
|
|
|
242
243
|
"image/gif",
|
|
243
244
|
"image/webp",
|
|
244
245
|
"image/bmp",
|
|
246
|
+
// SVG is served through `<img src="data:image/svg+xml;base64,...">` by
|
|
247
|
+
// `createImageDataUrl`. Chromium sandboxes SVGs loaded via <img> — scripts
|
|
248
|
+
// don't execute, external references are blocked, XSS surface matches PNG.
|
|
249
|
+
// Needed for docs/plans/lane-5-charts.md Stage 0B synthesized chart previews and
|
|
250
|
+
// any host that ships .svg inside `word/media/` as a logo or figure.
|
|
251
|
+
"image/svg+xml",
|
|
245
252
|
]);
|
|
246
253
|
|
|
247
254
|
const ACCESSIBLE_REGION_ORDER = [
|
|
@@ -938,6 +945,22 @@ export function __createWordReviewEditorRefBridge(
|
|
|
938
945
|
pendingConflicts,
|
|
939
946
|
});
|
|
940
947
|
},
|
|
948
|
+
// Schema 1.2 — EditorStateChannel delegation.
|
|
949
|
+
configureEditorStatePolicy: (policy) => {
|
|
950
|
+
runtime.configureEditorStatePolicy(policy);
|
|
951
|
+
},
|
|
952
|
+
registerEditorStateResolver: (resolver) => {
|
|
953
|
+
runtime.registerEditorStateResolver(resolver);
|
|
954
|
+
},
|
|
955
|
+
registerEditorStatePersister: (persister) => {
|
|
956
|
+
runtime.registerEditorStatePersister(persister);
|
|
957
|
+
},
|
|
958
|
+
getEditorStateKey: (namespace) => {
|
|
959
|
+
return runtime.getEditorStateKey(namespace);
|
|
960
|
+
},
|
|
961
|
+
retryPendingPersist: async (namespace) => {
|
|
962
|
+
await runtime.retryPendingPersist(namespace);
|
|
963
|
+
},
|
|
941
964
|
setHostAnnotationOverlay: (overlay) => {
|
|
942
965
|
runtime.setHostAnnotationOverlay(clonePublicValue(overlay));
|
|
943
966
|
},
|
|
@@ -985,16 +1008,9 @@ export function __createWordReviewEditorRefBridge(
|
|
|
985
1008
|
|
|
986
1009
|
export function __applyRuntimeTextCommand(
|
|
987
1010
|
runtime: WordReviewEditorRuntime,
|
|
988
|
-
command:
|
|
989
|
-
| { type: "insert-text"; text: string }
|
|
990
|
-
| { type: "delete-backward" }
|
|
991
|
-
| { type: "delete-forward" }
|
|
992
|
-
| { type: "insert-tab" }
|
|
993
|
-
| { type: "outdent-tab" }
|
|
994
|
-
| { type: "insert-hard-break" }
|
|
995
|
-
| { type: "split-paragraph" },
|
|
1011
|
+
command: DispatchTextCommand,
|
|
996
1012
|
): void {
|
|
997
|
-
|
|
1013
|
+
dispatchTextCommand(runtime, command, DISPATCH_CONTEXT);
|
|
998
1014
|
}
|
|
999
1015
|
|
|
1000
1016
|
export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditorProps>(
|
|
@@ -1023,6 +1039,9 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
1023
1039
|
onWarning,
|
|
1024
1040
|
onReviewSidebarTrackedChanges,
|
|
1025
1041
|
onReviewSidebarComments,
|
|
1042
|
+
onFindRequested,
|
|
1043
|
+
onPrintRequested,
|
|
1044
|
+
onZoomRequested,
|
|
1026
1045
|
readOnly = false,
|
|
1027
1046
|
reviewMode = "review",
|
|
1028
1047
|
suggestionsEnabled = false,
|
|
@@ -1961,6 +1980,22 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
1961
1980
|
pendingConflicts: metadataConflictPendingRef.current,
|
|
1962
1981
|
});
|
|
1963
1982
|
},
|
|
1983
|
+
// Schema 1.2 — EditorStateChannel delegation.
|
|
1984
|
+
configureEditorStatePolicy: (policy) => {
|
|
1985
|
+
activeRuntime.configureEditorStatePolicy(policy);
|
|
1986
|
+
},
|
|
1987
|
+
registerEditorStateResolver: (resolver) => {
|
|
1988
|
+
activeRuntime.registerEditorStateResolver(resolver);
|
|
1989
|
+
},
|
|
1990
|
+
registerEditorStatePersister: (persister) => {
|
|
1991
|
+
activeRuntime.registerEditorStatePersister(persister);
|
|
1992
|
+
},
|
|
1993
|
+
getEditorStateKey: (namespace) => {
|
|
1994
|
+
return activeRuntime.getEditorStateKey(namespace);
|
|
1995
|
+
},
|
|
1996
|
+
retryPendingPersist: async (namespace) => {
|
|
1997
|
+
await activeRuntime.retryPendingPersist(namespace);
|
|
1998
|
+
},
|
|
1964
1999
|
setHostAnnotationOverlay: (overlay) => {
|
|
1965
2000
|
setHostAnnotationOverlayState(clonePublicValue(overlay));
|
|
1966
2001
|
},
|
|
@@ -2109,7 +2144,7 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
2109
2144
|
|
|
2110
2145
|
function addReviewComment(): string | null {
|
|
2111
2146
|
try {
|
|
2112
|
-
const commentId = activeRuntime.addComment({
|
|
2147
|
+
const { commentId } = activeRuntime.addComment({
|
|
2113
2148
|
anchor: snapshot.selection.activeRange,
|
|
2114
2149
|
body: "",
|
|
2115
2150
|
authorId: currentUser.userId,
|
|
@@ -2538,7 +2573,46 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
2538
2573
|
},
|
|
2539
2574
|
);
|
|
2540
2575
|
|
|
2541
|
-
if (shortcut.kind === "none"
|
|
2576
|
+
if (shortcut.kind === "none") {
|
|
2577
|
+
return;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
if (shortcut.kind === "delegate") {
|
|
2581
|
+
// Host-delegated shortcuts: if the host has wired a typed
|
|
2582
|
+
// callback we call it and suppress the browser default; if
|
|
2583
|
+
// not, the event falls through to the browser (Ctrl+F opens
|
|
2584
|
+
// Find, Ctrl+Plus zooms, etc.) — matching the legacy behavior.
|
|
2585
|
+
let handled = false;
|
|
2586
|
+
if (shortcut.shortcut === "find" && onFindRequested) {
|
|
2587
|
+
// selectionText is intentionally empty — hosts that need the
|
|
2588
|
+
// selected text already receive it via the selection_changed
|
|
2589
|
+
// event + canonicalDocument they have via onEvent listeners.
|
|
2590
|
+
// The range is the load-bearing field so host Find panels can
|
|
2591
|
+
// scope their search to the selection or pre-populate from it.
|
|
2592
|
+
onFindRequested({
|
|
2593
|
+
selectionText: "",
|
|
2594
|
+
selectionRange: snapshot.selection,
|
|
2595
|
+
});
|
|
2596
|
+
handled = true;
|
|
2597
|
+
} else if (shortcut.shortcut === "print" && onPrintRequested) {
|
|
2598
|
+
onPrintRequested();
|
|
2599
|
+
handled = true;
|
|
2600
|
+
} else if (
|
|
2601
|
+
(shortcut.shortcut === "zoom-in" ||
|
|
2602
|
+
shortcut.shortcut === "zoom-out" ||
|
|
2603
|
+
shortcut.shortcut === "zoom-reset") &&
|
|
2604
|
+
onZoomRequested
|
|
2605
|
+
) {
|
|
2606
|
+
const direction =
|
|
2607
|
+
shortcut.shortcut === "zoom-in" ? "in" :
|
|
2608
|
+
shortcut.shortcut === "zoom-out" ? "out" : "reset";
|
|
2609
|
+
onZoomRequested(direction);
|
|
2610
|
+
handled = true;
|
|
2611
|
+
}
|
|
2612
|
+
if (handled) {
|
|
2613
|
+
event.preventDefault();
|
|
2614
|
+
event.stopPropagation();
|
|
2615
|
+
}
|
|
2542
2616
|
return;
|
|
2543
2617
|
}
|
|
2544
2618
|
|
|
@@ -2604,13 +2678,13 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
2604
2678
|
onFocus: handleSurfaceFocus,
|
|
2605
2679
|
onBlur: handleSurfaceBlur,
|
|
2606
2680
|
onSelectionChange: dispatchSelection,
|
|
2607
|
-
onInsertText: (text: string) =>
|
|
2608
|
-
onDeleteBackward: () =>
|
|
2609
|
-
onDeleteForward: () =>
|
|
2610
|
-
onInsertTab: () =>
|
|
2611
|
-
onOutdentTab: () =>
|
|
2612
|
-
onInsertHardBreak: () =>
|
|
2613
|
-
onSplitParagraph: () =>
|
|
2681
|
+
onInsertText: (text: string) => dispatchTextCommand(activeRuntime, { type: "insert-text", text }, DISPATCH_CONTEXT),
|
|
2682
|
+
onDeleteBackward: () => dispatchTextCommand(activeRuntime, { type: "delete-backward" }, DISPATCH_CONTEXT),
|
|
2683
|
+
onDeleteForward: () => dispatchTextCommand(activeRuntime, { type: "delete-forward" }, DISPATCH_CONTEXT),
|
|
2684
|
+
onInsertTab: () => dispatchTextCommand(activeRuntime, { type: "insert-tab" }, DISPATCH_CONTEXT),
|
|
2685
|
+
onOutdentTab: () => dispatchTextCommand(activeRuntime, { type: "outdent-tab" }, DISPATCH_CONTEXT),
|
|
2686
|
+
onInsertHardBreak: () => dispatchTextCommand(activeRuntime, { type: "insert-hard-break" }, DISPATCH_CONTEXT),
|
|
2687
|
+
onSplitParagraph: () => dispatchTextCommand(activeRuntime, { type: "split-paragraph" }, DISPATCH_CONTEXT),
|
|
2614
2688
|
onUndo: () => activeRuntime.undo(),
|
|
2615
2689
|
onRedo: () => activeRuntime.redo(),
|
|
2616
2690
|
onBlockedInput: (command: "paste" | "drop", message: string) =>
|
|
@@ -2618,6 +2692,19 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
2618
2692
|
code: "unsupported_surface",
|
|
2619
2693
|
message,
|
|
2620
2694
|
}]),
|
|
2695
|
+
onPasteApplied: (meta: {
|
|
2696
|
+
segmentCount: number;
|
|
2697
|
+
charCount: number;
|
|
2698
|
+
source: "paste" | "drop";
|
|
2699
|
+
}) => {
|
|
2700
|
+
onEventRef.current?.({
|
|
2701
|
+
type: "paste_applied",
|
|
2702
|
+
documentId: props.documentId,
|
|
2703
|
+
segmentCount: meta.segmentCount,
|
|
2704
|
+
charCount: meta.charCount,
|
|
2705
|
+
source: meta.source,
|
|
2706
|
+
});
|
|
2707
|
+
},
|
|
2621
2708
|
};
|
|
2622
2709
|
|
|
2623
2710
|
const reviewCallbacks = {
|
|
@@ -2763,14 +2850,21 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
2763
2850
|
applyRuntimeImageResize(activeRuntime, mediaId, dimensions),
|
|
2764
2851
|
onSetImageFrame: (mediaId, offsets) =>
|
|
2765
2852
|
applyRuntimeImageReposition(activeRuntime, mediaId, offsets),
|
|
2766
|
-
onOpenHeaderStory
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2853
|
+
// P8.11 — `onOpenHeaderStory` / `onOpenFooterStory` retired from the
|
|
2854
|
+
// WordReviewEditor wiring. Per-page header / footer bands rendered
|
|
2855
|
+
// by `TwPageStackChromeLayer` call `onOpenStory(target)` with the
|
|
2856
|
+
// exact `EditorStoryTarget` they represent, so the variant /
|
|
2857
|
+
// relationship resolution happens inside the layout facet instead
|
|
2858
|
+
// of the UI. The deprecated props remain in the workspace type
|
|
2859
|
+
// with a mount-time `console.warn`; hosts that still pass them can
|
|
2860
|
+
// migrate to `onOpenStory` at their leisure.
|
|
2770
2861
|
onOpenHeaderStoryForPage: (pageIndex: number) =>
|
|
2771
2862
|
openStoryForPage(activeRuntime, pageIndex, "header"),
|
|
2772
2863
|
onOpenFooterStoryForPage: (pageIndex: number) =>
|
|
2773
2864
|
openStoryForPage(activeRuntime, pageIndex, "footer"),
|
|
2865
|
+
onOpenStory: (target) => {
|
|
2866
|
+
activeRuntime.openStory(target);
|
|
2867
|
+
},
|
|
2774
2868
|
onDeleteSectionBreak: (sectionIndex) =>
|
|
2775
2869
|
applyRuntimeDeleteSectionBreak(activeRuntime, sectionIndex),
|
|
2776
2870
|
onUpdateSectionLayout: (sectionIndex, patch) =>
|
|
@@ -2901,6 +2995,17 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
|
|
|
2901
2995
|
interactionGuardSnapshot={interactionGuardSnapshot}
|
|
2902
2996
|
chromePreset={effectiveChromePreset}
|
|
2903
2997
|
chromeOptions={chromeOptions}
|
|
2998
|
+
{...(props.collabSession ? { collabSession: props.collabSession } : {})}
|
|
2999
|
+
{...(props.collabTransportStatus
|
|
3000
|
+
? { collabTransportStatus: props.collabTransportStatus }
|
|
3001
|
+
: {})}
|
|
3002
|
+
{...(props.activeCommentId !== undefined
|
|
3003
|
+
? { activeCommentId: props.activeCommentId }
|
|
3004
|
+
: {})}
|
|
3005
|
+
collabActorId={currentUser.userId}
|
|
3006
|
+
{...(props.collabSendBaseline
|
|
3007
|
+
? { collabSendBaseline: props.collabSendBaseline }
|
|
3008
|
+
: {})}
|
|
2904
3009
|
reviewQueue={reviewQueueSnapshot}
|
|
2905
3010
|
documentContextAnalytics={documentContextAnalytics}
|
|
2906
3011
|
selectionContextAnalytics={selectionContextAnalytics}
|
|
@@ -3231,7 +3336,11 @@ function applyRuntimeListToggle(
|
|
|
3231
3336
|
dispatchStoryMutationResult(
|
|
3232
3337
|
runtime,
|
|
3233
3338
|
context,
|
|
3234
|
-
|
|
3339
|
+
{
|
|
3340
|
+
changed: result.affectedParagraphIndexes.length > 0,
|
|
3341
|
+
document: result.document,
|
|
3342
|
+
selection: toRuntimeSelectionSnapshot(context.localSnapshot.selection),
|
|
3343
|
+
},
|
|
3235
3344
|
context.timestamp,
|
|
3236
3345
|
);
|
|
3237
3346
|
}
|
|
@@ -4355,175 +4464,12 @@ function buildTablesFacet(
|
|
|
4355
4464
|
|
|
4356
4465
|
export { buildTablesFacet as __buildTablesFacet };
|
|
4357
4466
|
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
| { type: "insert-tab" }
|
|
4365
|
-
| { type: "outdent-tab" }
|
|
4366
|
-
| { type: "insert-hard-break" }
|
|
4367
|
-
| { type: "split-paragraph" },
|
|
4368
|
-
): void {
|
|
4369
|
-
const snapshot = runtime.getRenderSnapshot();
|
|
4370
|
-
const context = getStoryMutationContext(runtime, getMountedTextCommandName(command));
|
|
4371
|
-
if (!context) {
|
|
4372
|
-
return;
|
|
4373
|
-
}
|
|
4374
|
-
|
|
4375
|
-
const effectiveSelectionMode = runtime.getInteractionGuardSnapshot().effectiveMode;
|
|
4376
|
-
const listAwareResult = applyListAwareTextCommand(context, command);
|
|
4377
|
-
if (effectiveSelectionMode === "suggest" && listAwareResult) {
|
|
4378
|
-
runtime.emitBlockedCommand(getMountedTextCommandName(command), [{
|
|
4379
|
-
code: "suggesting_unsupported",
|
|
4380
|
-
message: "List structure changes are not supported in suggesting mode.",
|
|
4381
|
-
}]);
|
|
4382
|
-
return;
|
|
4383
|
-
}
|
|
4384
|
-
|
|
4385
|
-
if (listAwareResult) {
|
|
4386
|
-
dispatchStoryMutationResult(runtime, context, listAwareResult, context.timestamp);
|
|
4387
|
-
return;
|
|
4388
|
-
}
|
|
4389
|
-
|
|
4390
|
-
switch (command.type) {
|
|
4391
|
-
case "insert-text":
|
|
4392
|
-
runtime.applyActiveStoryTextCommand({ type: "text.insert", text: command.text });
|
|
4393
|
-
return;
|
|
4394
|
-
case "delete-backward":
|
|
4395
|
-
runtime.applyActiveStoryTextCommand({ type: "text.delete-backward" });
|
|
4396
|
-
return;
|
|
4397
|
-
case "delete-forward":
|
|
4398
|
-
runtime.applyActiveStoryTextCommand({ type: "text.delete-forward" });
|
|
4399
|
-
return;
|
|
4400
|
-
case "insert-tab":
|
|
4401
|
-
runtime.applyActiveStoryTextCommand({ type: "text.insert-tab" });
|
|
4402
|
-
return;
|
|
4403
|
-
case "outdent-tab":
|
|
4404
|
-
return;
|
|
4405
|
-
case "insert-hard-break":
|
|
4406
|
-
runtime.applyActiveStoryTextCommand({ type: "text.insert-hard-break" });
|
|
4407
|
-
return;
|
|
4408
|
-
case "split-paragraph":
|
|
4409
|
-
runtime.applyActiveStoryTextCommand({ type: "paragraph.split" });
|
|
4410
|
-
return;
|
|
4411
|
-
}
|
|
4412
|
-
}
|
|
4413
|
-
|
|
4414
|
-
function getMountedTextCommandName(
|
|
4415
|
-
command:
|
|
4416
|
-
| { type: "insert-text"; text: string }
|
|
4417
|
-
| { type: "delete-backward" }
|
|
4418
|
-
| { type: "delete-forward" }
|
|
4419
|
-
| { type: "insert-tab" }
|
|
4420
|
-
| { type: "outdent-tab" }
|
|
4421
|
-
| { type: "insert-hard-break" }
|
|
4422
|
-
| { type: "split-paragraph" },
|
|
4423
|
-
): string {
|
|
4424
|
-
switch (command.type) {
|
|
4425
|
-
case "insert-text":
|
|
4426
|
-
return "text.insert";
|
|
4427
|
-
case "delete-backward":
|
|
4428
|
-
return "text.delete-backward";
|
|
4429
|
-
case "delete-forward":
|
|
4430
|
-
return "text.delete-forward";
|
|
4431
|
-
case "insert-tab":
|
|
4432
|
-
case "outdent-tab":
|
|
4433
|
-
return "text.insert-tab";
|
|
4434
|
-
case "insert-hard-break":
|
|
4435
|
-
return "text.insert-hard-break";
|
|
4436
|
-
case "split-paragraph":
|
|
4437
|
-
return "paragraph.split";
|
|
4438
|
-
}
|
|
4439
|
-
}
|
|
4440
|
-
|
|
4441
|
-
function applyListAwareTextCommand(
|
|
4442
|
-
context: NonNullable<ReturnType<typeof getStoryMutationContext>>,
|
|
4443
|
-
command:
|
|
4444
|
-
| { type: "insert-text"; text: string }
|
|
4445
|
-
| { type: "delete-backward" }
|
|
4446
|
-
| { type: "delete-forward" }
|
|
4447
|
-
| { type: "insert-tab" }
|
|
4448
|
-
| { type: "outdent-tab" }
|
|
4449
|
-
| { type: "insert-hard-break" }
|
|
4450
|
-
| { type: "split-paragraph" },
|
|
4451
|
-
): {
|
|
4452
|
-
changed: boolean;
|
|
4453
|
-
document: EditorSessionState["canonicalDocument"];
|
|
4454
|
-
selection: InternalSelectionSnapshot;
|
|
4455
|
-
} | null {
|
|
4456
|
-
const paragraphContext = resolveActiveParagraphContext(context.localSnapshot);
|
|
4457
|
-
if (!paragraphContext?.paragraph.numbering) {
|
|
4458
|
-
return null;
|
|
4459
|
-
}
|
|
4460
|
-
|
|
4461
|
-
switch (command.type) {
|
|
4462
|
-
case "insert-tab": {
|
|
4463
|
-
const result = indentListItems(
|
|
4464
|
-
context.localDocument,
|
|
4465
|
-
[paragraphContext.paragraphIndex],
|
|
4466
|
-
{ timestamp: context.timestamp },
|
|
4467
|
-
);
|
|
4468
|
-
return createListMutationResult(result, context.localSnapshot.selection);
|
|
4469
|
-
}
|
|
4470
|
-
case "outdent-tab": {
|
|
4471
|
-
const result = outdentListItems(
|
|
4472
|
-
context.localDocument,
|
|
4473
|
-
[paragraphContext.paragraphIndex],
|
|
4474
|
-
{ timestamp: context.timestamp },
|
|
4475
|
-
);
|
|
4476
|
-
return createListMutationResult(result, context.localSnapshot.selection);
|
|
4477
|
-
}
|
|
4478
|
-
case "delete-backward": {
|
|
4479
|
-
if (!paragraphContext.atParagraphStart || !context.localSnapshot.selection.isCollapsed) {
|
|
4480
|
-
return null;
|
|
4481
|
-
}
|
|
4482
|
-
const result = backspaceAtListStart(
|
|
4483
|
-
context.localDocument,
|
|
4484
|
-
paragraphContext.paragraphIndex,
|
|
4485
|
-
{ timestamp: context.timestamp },
|
|
4486
|
-
);
|
|
4487
|
-
return result.handled
|
|
4488
|
-
? createListMutationResult(result, context.localSnapshot.selection)
|
|
4489
|
-
: null;
|
|
4490
|
-
}
|
|
4491
|
-
case "split-paragraph": {
|
|
4492
|
-
if (!context.localSnapshot.selection.isCollapsed || !paragraphContext.isEmpty) {
|
|
4493
|
-
return null;
|
|
4494
|
-
}
|
|
4495
|
-
const result = splitListParagraph(
|
|
4496
|
-
context.localDocument,
|
|
4497
|
-
paragraphContext.paragraphIndex,
|
|
4498
|
-
true,
|
|
4499
|
-
{ timestamp: context.timestamp },
|
|
4500
|
-
);
|
|
4501
|
-
return result.action === "split"
|
|
4502
|
-
? null
|
|
4503
|
-
: createListMutationResult(result, context.localSnapshot.selection);
|
|
4504
|
-
}
|
|
4505
|
-
default:
|
|
4506
|
-
return null;
|
|
4507
|
-
}
|
|
4508
|
-
}
|
|
4509
|
-
|
|
4510
|
-
function createListMutationResult(
|
|
4511
|
-
result: {
|
|
4512
|
-
document: EditorSessionState["canonicalDocument"];
|
|
4513
|
-
affectedParagraphIndexes: number[];
|
|
4514
|
-
},
|
|
4515
|
-
selection: RuntimeRenderSnapshot["selection"],
|
|
4516
|
-
): {
|
|
4517
|
-
changed: boolean;
|
|
4518
|
-
document: EditorSessionState["canonicalDocument"];
|
|
4519
|
-
selection: InternalSelectionSnapshot;
|
|
4520
|
-
} {
|
|
4521
|
-
return {
|
|
4522
|
-
changed: result.affectedParagraphIndexes.length > 0,
|
|
4523
|
-
document: result.document,
|
|
4524
|
-
selection: toRuntimeSelectionSnapshot(selection),
|
|
4525
|
-
};
|
|
4526
|
-
}
|
|
4467
|
+
const DISPATCH_CONTEXT: DispatchContext = {
|
|
4468
|
+
getStoryMutationContext,
|
|
4469
|
+
dispatchStoryMutationResult,
|
|
4470
|
+
resolveActiveParagraphContext,
|
|
4471
|
+
toRuntimeSelectionSnapshot,
|
|
4472
|
+
};
|
|
4527
4473
|
|
|
4528
4474
|
function resolveActiveParagraphContext(
|
|
4529
4475
|
snapshot: Pick<RuntimeRenderSnapshot, "surface" | "selection">,
|
|
@@ -4866,47 +4812,6 @@ function openStoryForPage(
|
|
|
4866
4812
|
runtime.openStory(target);
|
|
4867
4813
|
}
|
|
4868
4814
|
|
|
4869
|
-
function openDefaultStoryVariant(
|
|
4870
|
-
runtime: WordReviewEditorRuntime,
|
|
4871
|
-
pageLayout: PageLayoutSnapshot | undefined,
|
|
4872
|
-
navigation: ReturnType<WordReviewEditorRuntime["getDocumentNavigationSnapshot"]> | undefined,
|
|
4873
|
-
kind: "header" | "footer",
|
|
4874
|
-
): void {
|
|
4875
|
-
const variants =
|
|
4876
|
-
kind === "header"
|
|
4877
|
-
? pageLayout?.headerVariants
|
|
4878
|
-
: pageLayout?.footerVariants;
|
|
4879
|
-
const activePage = navigation?.pages[navigation.activePageIndex];
|
|
4880
|
-
const isFirstPageInSection =
|
|
4881
|
-
activePage !== undefined &&
|
|
4882
|
-
activePage.sectionIndex === pageLayout?.sectionIndex &&
|
|
4883
|
-
activePage.pageInSection === 0;
|
|
4884
|
-
const isEvenDocumentPage = activePage !== undefined && (activePage.pageIndex + 1) % 2 === 0;
|
|
4885
|
-
|
|
4886
|
-
let variant =
|
|
4887
|
-
pageLayout?.differentFirstPage && isFirstPageInSection
|
|
4888
|
-
? variants?.find((entry) => entry.variant === "first")
|
|
4889
|
-
: undefined;
|
|
4890
|
-
|
|
4891
|
-
if (!variant && pageLayout?.differentOddEvenPages && isEvenDocumentPage) {
|
|
4892
|
-
variant = variants?.find((entry) => entry.variant === "even");
|
|
4893
|
-
}
|
|
4894
|
-
|
|
4895
|
-
if (!variant) {
|
|
4896
|
-
variant = variants?.find((entry) => entry.variant === "default") ?? variants?.[0];
|
|
4897
|
-
}
|
|
4898
|
-
|
|
4899
|
-
if (!variant) {
|
|
4900
|
-
return;
|
|
4901
|
-
}
|
|
4902
|
-
runtime.openStory({
|
|
4903
|
-
kind,
|
|
4904
|
-
relationshipId: variant.relationshipId,
|
|
4905
|
-
variant: variant.variant,
|
|
4906
|
-
sectionIndex: pageLayout?.sectionIndex,
|
|
4907
|
-
});
|
|
4908
|
-
}
|
|
4909
|
-
|
|
4910
4815
|
function searchRuntimeDocument(
|
|
4911
4816
|
runtime: WordReviewEditorRuntime,
|
|
4912
4817
|
mountedSurface: TwProseMirrorSurfaceRef | null,
|
|
@@ -2,6 +2,7 @@ import { useMemo, useRef } from "react";
|
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
4
|
CommentSidebarThreadSnapshot,
|
|
5
|
+
EditorStoryTarget,
|
|
5
6
|
FormattingAlignment,
|
|
6
7
|
HeaderFooterLinkPatch,
|
|
7
8
|
InsertImageOptions,
|
|
@@ -87,12 +88,25 @@ export interface EditorCommandBag {
|
|
|
87
88
|
onAcceptAllChanges(): void;
|
|
88
89
|
onRejectAllChanges(): void;
|
|
89
90
|
onCloseStory?(): void;
|
|
91
|
+
/**
|
|
92
|
+
* @deprecated P8.11 — see the matching prop on `TwReviewWorkspaceProps`.
|
|
93
|
+
* Kept optional for back-compat; per-page bands use `onOpenStory`.
|
|
94
|
+
*/
|
|
90
95
|
onOpenHeaderStory?(): void;
|
|
96
|
+
/**
|
|
97
|
+
* @deprecated P8.11 — see `onOpenHeaderStory`.
|
|
98
|
+
*/
|
|
91
99
|
onOpenFooterStory?(): void;
|
|
92
100
|
/** Open the header story for a specific page (double-click on its band). */
|
|
93
101
|
onOpenHeaderStoryForPage?(pageIndex: number): void;
|
|
94
102
|
/** Open the footer story for a specific page (double-click on its band). */
|
|
95
103
|
onOpenFooterStoryForPage?(pageIndex: number): void;
|
|
104
|
+
/**
|
|
105
|
+
* P8.11 — per-page header/footer band click handler. Receives the
|
|
106
|
+
* exact `EditorStoryTarget` the band represents; the command bag wires
|
|
107
|
+
* this to `runtime.openStory(target)`.
|
|
108
|
+
*/
|
|
109
|
+
onOpenStory?(target: EditorStoryTarget): void;
|
|
96
110
|
onSetParagraphIndentation?(indentation: {
|
|
97
111
|
left?: number;
|
|
98
112
|
right?: number;
|