@beyondwork/docx-react-component 1.0.84 → 1.0.85
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/internal/build-ref-projections.ts +3 -0
- package/src/api/public-types.ts +38 -0
- package/src/api/v3/_runtime-handle.ts +11 -0
- package/src/api/v3/runtime/content.ts +148 -1
- package/src/api/v3/runtime/formatting.ts +41 -0
- package/src/api/v3/runtime/review.ts +98 -0
- package/src/core/commands/index.ts +81 -25
- package/src/core/state/editor-state.ts +15 -0
- package/src/io/ooxml/header-footer-reference.ts +38 -0
- package/src/io/ooxml/parse-headers-footers.ts +11 -23
- package/src/io/ooxml/parse-main-document.ts +7 -10
- package/src/model/canonical-document.ts +9 -0
- package/src/model/review/comment-types.ts +2 -0
- package/src/runtime/document-runtime.ts +677 -54
- package/src/runtime/formatting/field/resolver.ts +73 -8
- package/src/runtime/layout/layout-engine-version.ts +31 -12
- package/src/runtime/layout/paginated-layout-engine.ts +18 -11
- package/src/runtime/layout/public-facet.ts +119 -16
- package/src/runtime/layout/resolve-page-fields.ts +68 -6
- package/src/runtime/layout/resolve-page-previews.ts +1 -1
- package/src/runtime/suggestions-snapshot.ts +24 -0
- package/src/runtime/surface-projection.ts +59 -2
- package/src/shell/ref-commands.ts +3 -354
- package/src/shell/session-bootstrap.ts +8 -0
- package/src/ui/WordReviewEditor.tsx +95 -9
- package/src/ui/editor-command-bag.ts +3 -1
- package/src/ui/headless/revision-decoration-model.ts +13 -0
- package/src/ui/headless/selection-tool-types.ts +2 -0
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +7 -3
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +175 -25
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +1 -1
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +12 -0
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +18 -30
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +1 -1
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +1 -1
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +20 -11
- package/src/ui-tailwind/page-stack/tw-page-footer-band.tsx +9 -4
- package/src/ui-tailwind/page-stack/tw-page-header-band.tsx +12 -7
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +29 -10
- package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +1 -1
- package/src/ui-tailwind/review-workspace/types.ts +3 -2
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +11 -1
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +2 -1
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +2 -1
- package/src/ui-tailwind/tw-review-workspace.tsx +18 -2
|
@@ -188,7 +188,11 @@ import {
|
|
|
188
188
|
insertScopeMarkers,
|
|
189
189
|
removeScopeMarkers,
|
|
190
190
|
} from "../core/commands/add-scope.ts";
|
|
191
|
-
import {
|
|
191
|
+
import {
|
|
192
|
+
applyFormattingOperationToDocument,
|
|
193
|
+
type FormattingOperation,
|
|
194
|
+
} from "../core/commands/formatting-commands.ts";
|
|
195
|
+
import { resolveActiveParagraphIndex } from "../core/commands/paragraph-layout-commands.ts";
|
|
192
196
|
import { buildCompatibilityReport } from "../validation/compatibility-engine.ts";
|
|
193
197
|
import { mergeCompatibilityReports } from "../validation/compatibility-report.ts";
|
|
194
198
|
import { createEditorSurfaceSnapshot } from "./surface-projection.ts";
|
|
@@ -207,7 +211,6 @@ import {
|
|
|
207
211
|
} from "./workflow/rail/compose.ts";
|
|
208
212
|
import {
|
|
209
213
|
createDocumentNavigationSnapshot,
|
|
210
|
-
findPageForOffset,
|
|
211
214
|
} from "./document-navigation.ts";
|
|
212
215
|
import {
|
|
213
216
|
createDocxFontLoader,
|
|
@@ -326,7 +329,8 @@ import {
|
|
|
326
329
|
import type { EditorStatePayload } from "../io/ooxml/workflow-payload.ts";
|
|
327
330
|
import type { SharedWorkflowState } from "./collab/workflow-shared.ts";
|
|
328
331
|
import { mapLocalSelectionOnRemoteReplay } from "./collab/map-local-selection-on-remote-replay.ts";
|
|
329
|
-
import {
|
|
332
|
+
import { resolvePageFieldDisplayText } from "./layout/resolve-page-fields.ts";
|
|
333
|
+
import type { RuntimePageGraph, RuntimePageNode } from "./layout/page-graph.ts";
|
|
330
334
|
|
|
331
335
|
/** Internal extension of ExportDocxOptions that threads the collected
|
|
332
336
|
* editorState payload from the runtime to the docx serializer. */
|
|
@@ -371,6 +375,7 @@ export interface DocumentRuntime {
|
|
|
371
375
|
name: string,
|
|
372
376
|
): NonNullable<CanonicalDocumentEnvelope["fontTable"]>["fonts"][string] | undefined;
|
|
373
377
|
replaceText(text: string, target?: EditorAnchorProjection, formatting?: TextFormattingDirective): void;
|
|
378
|
+
applyFormattingOperation(operation: FormattingOperation): void;
|
|
374
379
|
insertFragment(fragment: CanonicalDocumentFragment, target?: EditorAnchorProjection): void;
|
|
375
380
|
/**
|
|
376
381
|
* I2 Tier B Slice 4b — serialize the selection range to a
|
|
@@ -516,6 +521,16 @@ export interface DocumentRuntime {
|
|
|
516
521
|
body: string,
|
|
517
522
|
authorId?: string,
|
|
518
523
|
): AddCommentReplyResult | null;
|
|
524
|
+
getCommentThreadForChange(changeId: string): CommentSidebarThreadSnapshot | null;
|
|
525
|
+
ensureCommentThreadForChange(
|
|
526
|
+
changeId: string,
|
|
527
|
+
authorId?: string,
|
|
528
|
+
): AddCommentResult | null;
|
|
529
|
+
addReplyToChange(
|
|
530
|
+
changeId: string,
|
|
531
|
+
body: string,
|
|
532
|
+
authorId?: string,
|
|
533
|
+
): AddCommentReplyResult | null;
|
|
519
534
|
editCommentBody(commentId: string, body: string): void;
|
|
520
535
|
addScope(params: AddScopeParams): AddScopeResult;
|
|
521
536
|
getScope(scopeId: string): WorkflowScope | null;
|
|
@@ -3079,6 +3094,167 @@ export function createDocumentRuntime(
|
|
|
3079
3094
|
emitError(toRuntimeError(error));
|
|
3080
3095
|
}
|
|
3081
3096
|
},
|
|
3097
|
+
applyFormattingOperation(operation) {
|
|
3098
|
+
try {
|
|
3099
|
+
const commandName = getFormattingOperationCommandName(operation);
|
|
3100
|
+
const snapshot = cachedRenderSnapshot;
|
|
3101
|
+
if (!snapshot.isReady || snapshot.readOnly || snapshot.fatalError) {
|
|
3102
|
+
return;
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
const blockedReasons = workflowCoordinator.evaluateBlockedReasons(
|
|
3106
|
+
state.selection,
|
|
3107
|
+
commandName,
|
|
3108
|
+
);
|
|
3109
|
+
if (blockedReasons.length > 0) {
|
|
3110
|
+
this.emitBlockedCommand(commandName, blockedReasons);
|
|
3111
|
+
return;
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
const persistedDocument = state.document;
|
|
3115
|
+
const localDocument =
|
|
3116
|
+
activeStory.kind === "main"
|
|
3117
|
+
? persistedDocument
|
|
3118
|
+
: {
|
|
3119
|
+
...persistedDocument,
|
|
3120
|
+
content: {
|
|
3121
|
+
type: "doc" as const,
|
|
3122
|
+
children: [...getStoryBlocks(persistedDocument, activeStory)],
|
|
3123
|
+
},
|
|
3124
|
+
};
|
|
3125
|
+
const localSnapshot: RuntimeRenderSnapshot =
|
|
3126
|
+
activeStory.kind === "main"
|
|
3127
|
+
? snapshot
|
|
3128
|
+
: {
|
|
3129
|
+
...snapshot,
|
|
3130
|
+
activeStory: MAIN_STORY_TARGET,
|
|
3131
|
+
selection: stripStoryTarget(snapshot.selection),
|
|
3132
|
+
};
|
|
3133
|
+
|
|
3134
|
+
const suggesting =
|
|
3135
|
+
workflowCoordinator.getEffectiveDocumentMode(state.selection) === "suggesting";
|
|
3136
|
+
const timestamp = clock();
|
|
3137
|
+
|
|
3138
|
+
if (suggesting) {
|
|
3139
|
+
if (activeStory.kind !== "main") {
|
|
3140
|
+
this.emitBlockedCommand(commandName, [{
|
|
3141
|
+
code: "suggesting_unsupported",
|
|
3142
|
+
message: `"${commandName}" is not supported in suggesting mode for this story.`,
|
|
3143
|
+
}]);
|
|
3144
|
+
return;
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
if (
|
|
3148
|
+
operation.type === "set-alignment" ||
|
|
3149
|
+
operation.type === "indent" ||
|
|
3150
|
+
operation.type === "outdent"
|
|
3151
|
+
) {
|
|
3152
|
+
const paragraphContext = resolveActiveParagraphContext(localSnapshot);
|
|
3153
|
+
if (!paragraphContext) {
|
|
3154
|
+
return;
|
|
3155
|
+
}
|
|
3156
|
+
const beforeXml = buildParagraphPropertyBeforeXml(paragraphContext.paragraph);
|
|
3157
|
+
const result = applyFormattingOperationToDocument(
|
|
3158
|
+
localDocument,
|
|
3159
|
+
localSnapshot,
|
|
3160
|
+
operation,
|
|
3161
|
+
);
|
|
3162
|
+
if (!result.changed) {
|
|
3163
|
+
return;
|
|
3164
|
+
}
|
|
3165
|
+
const nextDocument = appendPropertyChangeSuggestion(
|
|
3166
|
+
result.document,
|
|
3167
|
+
{
|
|
3168
|
+
from: paragraphContext.paragraph.from,
|
|
3169
|
+
to: paragraphContext.paragraph.to,
|
|
3170
|
+
},
|
|
3171
|
+
{
|
|
3172
|
+
originalRevisionType: "pPrChange",
|
|
3173
|
+
xmlTag: "pPrChange",
|
|
3174
|
+
beforeXml,
|
|
3175
|
+
semanticKind: "paragraph-property-change",
|
|
3176
|
+
storyTarget: activeStory,
|
|
3177
|
+
authorId: defaultAuthorId ?? undefined,
|
|
3178
|
+
},
|
|
3179
|
+
timestamp,
|
|
3180
|
+
);
|
|
3181
|
+
this.dispatch({
|
|
3182
|
+
type: "document.replace",
|
|
3183
|
+
document: { ...nextDocument, updatedAt: timestamp },
|
|
3184
|
+
mapping: createEmptyMapping(),
|
|
3185
|
+
selection: toInternalSelectionSnapshot(result.selection),
|
|
3186
|
+
origin: createOrigin("api", timestamp),
|
|
3187
|
+
});
|
|
3188
|
+
return;
|
|
3189
|
+
}
|
|
3190
|
+
|
|
3191
|
+
const segment = findSingleSelectedTextSegment(localSnapshot);
|
|
3192
|
+
if (!segment) {
|
|
3193
|
+
this.emitBlockedCommand(commandName, [{
|
|
3194
|
+
code: "suggesting_unsupported",
|
|
3195
|
+
message: `"${commandName}" requires one bounded text segment in suggesting mode.`,
|
|
3196
|
+
}]);
|
|
3197
|
+
return;
|
|
3198
|
+
}
|
|
3199
|
+
const beforeXml = buildRunPropertyBeforeXml(segment);
|
|
3200
|
+
const result = applyFormattingOperationToDocument(
|
|
3201
|
+
localDocument,
|
|
3202
|
+
localSnapshot,
|
|
3203
|
+
operation,
|
|
3204
|
+
);
|
|
3205
|
+
if (!result.changed) {
|
|
3206
|
+
return;
|
|
3207
|
+
}
|
|
3208
|
+
const nextDocument = appendPropertyChangeSuggestion(
|
|
3209
|
+
result.document,
|
|
3210
|
+
{ from: segment.from, to: segment.to },
|
|
3211
|
+
{
|
|
3212
|
+
originalRevisionType: "rPrChange",
|
|
3213
|
+
xmlTag: "rPrChange",
|
|
3214
|
+
beforeXml,
|
|
3215
|
+
semanticKind: "formatting-change",
|
|
3216
|
+
storyTarget: activeStory,
|
|
3217
|
+
authorId: defaultAuthorId ?? undefined,
|
|
3218
|
+
},
|
|
3219
|
+
timestamp,
|
|
3220
|
+
);
|
|
3221
|
+
this.dispatch({
|
|
3222
|
+
type: "document.replace",
|
|
3223
|
+
document: { ...nextDocument, updatedAt: timestamp },
|
|
3224
|
+
mapping: createEmptyMapping(),
|
|
3225
|
+
selection: toInternalSelectionSnapshot(result.selection),
|
|
3226
|
+
origin: createOrigin("api", timestamp),
|
|
3227
|
+
});
|
|
3228
|
+
return;
|
|
3229
|
+
}
|
|
3230
|
+
|
|
3231
|
+
const result = applyFormattingOperationToDocument(
|
|
3232
|
+
localDocument,
|
|
3233
|
+
localSnapshot,
|
|
3234
|
+
operation,
|
|
3235
|
+
);
|
|
3236
|
+
if (!result.changed) {
|
|
3237
|
+
return;
|
|
3238
|
+
}
|
|
3239
|
+
const nextDocument =
|
|
3240
|
+
activeStory.kind === "main"
|
|
3241
|
+
? result.document
|
|
3242
|
+
: replaceStoryBlocks(
|
|
3243
|
+
persistedDocument,
|
|
3244
|
+
activeStory,
|
|
3245
|
+
result.document.content.children,
|
|
3246
|
+
);
|
|
3247
|
+
this.dispatch({
|
|
3248
|
+
type: "document.replace",
|
|
3249
|
+
document: { ...nextDocument, updatedAt: timestamp },
|
|
3250
|
+
mapping: createEmptyMapping(),
|
|
3251
|
+
selection: toInternalSelectionSnapshot(result.selection),
|
|
3252
|
+
origin: createOrigin("api", timestamp),
|
|
3253
|
+
});
|
|
3254
|
+
} catch (error) {
|
|
3255
|
+
emitError(toRuntimeError(error));
|
|
3256
|
+
}
|
|
3257
|
+
},
|
|
3082
3258
|
applyScopeReplacement(plan: RuntimeOperationPlan) {
|
|
3083
3259
|
// Layer-08 Slice 5. Each step lowers to an existing command; the
|
|
3084
3260
|
// apply pipeline (`src/runtime/scopes/replacement/apply.ts`) owns
|
|
@@ -3534,6 +3710,137 @@ export function createDocumentRuntime(
|
|
|
3534
3710
|
const last = entries[entries.length - 1]!;
|
|
3535
3711
|
return { commentId, entryId: last.entryId };
|
|
3536
3712
|
},
|
|
3713
|
+
getCommentThreadForChange(changeId) {
|
|
3714
|
+
return (
|
|
3715
|
+
this.getRenderSnapshot().comments.threads.find(
|
|
3716
|
+
(thread) => thread.linkedRevisionId === changeId,
|
|
3717
|
+
) ?? null
|
|
3718
|
+
);
|
|
3719
|
+
},
|
|
3720
|
+
ensureCommentThreadForChange(changeId, authorId) {
|
|
3721
|
+
const existing = this.getCommentThreadForChange(changeId);
|
|
3722
|
+
if (existing) {
|
|
3723
|
+
this.openComment(existing.commentId);
|
|
3724
|
+
return { commentId: existing.commentId, anchor: existing.anchor };
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3727
|
+
if (viewState.documentMode === "viewing") {
|
|
3728
|
+
return null;
|
|
3729
|
+
}
|
|
3730
|
+
const revision = state.document.review.revisions[changeId];
|
|
3731
|
+
if (!revision || revision.anchor.kind !== "range") {
|
|
3732
|
+
return null;
|
|
3733
|
+
}
|
|
3734
|
+
const rejectionReason = commentAnchorRejectionReason(
|
|
3735
|
+
cachedRenderSnapshot.surface,
|
|
3736
|
+
revision.anchor,
|
|
3737
|
+
);
|
|
3738
|
+
if (rejectionReason !== null) {
|
|
3739
|
+
return null;
|
|
3740
|
+
}
|
|
3741
|
+
|
|
3742
|
+
const commentId = createEntityId("comment", state.document.review.comments, clock());
|
|
3743
|
+
const createdBy = authorId ?? defaultAuthorId ?? "unknown";
|
|
3744
|
+
const createdAt = clock();
|
|
3745
|
+
const entryId = `${commentId}-entry-1`;
|
|
3746
|
+
const comment: CommentThreadRecord = {
|
|
3747
|
+
commentId,
|
|
3748
|
+
anchor: revision.anchor,
|
|
3749
|
+
createdAt,
|
|
3750
|
+
createdBy,
|
|
3751
|
+
authorId: createdBy,
|
|
3752
|
+
body: "",
|
|
3753
|
+
entries: [
|
|
3754
|
+
{
|
|
3755
|
+
entryId,
|
|
3756
|
+
authorId: createdBy,
|
|
3757
|
+
body: "",
|
|
3758
|
+
createdAt,
|
|
3759
|
+
},
|
|
3760
|
+
],
|
|
3761
|
+
status: "open",
|
|
3762
|
+
warningIds: [],
|
|
3763
|
+
isResolved: false,
|
|
3764
|
+
metadata: {
|
|
3765
|
+
source: "runtime",
|
|
3766
|
+
linkedRevisionId: changeId,
|
|
3767
|
+
},
|
|
3768
|
+
};
|
|
3769
|
+
|
|
3770
|
+
this.dispatch({
|
|
3771
|
+
type: "comment.add",
|
|
3772
|
+
comment,
|
|
3773
|
+
selection: createSelectionFromPublicAnchor(
|
|
3774
|
+
toPublicAnchorProjection(revision.anchor),
|
|
3775
|
+
),
|
|
3776
|
+
origin: createOrigin("api", clock()),
|
|
3777
|
+
});
|
|
3778
|
+
|
|
3779
|
+
return {
|
|
3780
|
+
commentId,
|
|
3781
|
+
anchor: toPublicAnchorProjection(revision.anchor),
|
|
3782
|
+
};
|
|
3783
|
+
},
|
|
3784
|
+
addReplyToChange(changeId, body, authorId) {
|
|
3785
|
+
const existing = this.getCommentThreadForChange(changeId);
|
|
3786
|
+
if (existing) {
|
|
3787
|
+
return this.addCommentReply(existing.commentId, body, authorId);
|
|
3788
|
+
}
|
|
3789
|
+
|
|
3790
|
+
if (viewState.documentMode === "viewing") {
|
|
3791
|
+
return null;
|
|
3792
|
+
}
|
|
3793
|
+
const revision = state.document.review.revisions[changeId];
|
|
3794
|
+
if (!revision || revision.anchor.kind !== "range") {
|
|
3795
|
+
return null;
|
|
3796
|
+
}
|
|
3797
|
+
const rejectionReason = commentAnchorRejectionReason(
|
|
3798
|
+
cachedRenderSnapshot.surface,
|
|
3799
|
+
revision.anchor,
|
|
3800
|
+
);
|
|
3801
|
+
if (rejectionReason !== null) {
|
|
3802
|
+
return null;
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
const commentId = createEntityId("comment", state.document.review.comments, clock());
|
|
3806
|
+
const createdBy = authorId ?? defaultAuthorId ?? "unknown";
|
|
3807
|
+
const createdAt = clock();
|
|
3808
|
+
const entryId = `${commentId}-entry-1`;
|
|
3809
|
+
const comment: CommentThreadRecord = {
|
|
3810
|
+
commentId,
|
|
3811
|
+
anchor: revision.anchor,
|
|
3812
|
+
createdAt,
|
|
3813
|
+
createdBy,
|
|
3814
|
+
authorId: createdBy,
|
|
3815
|
+
body,
|
|
3816
|
+
entries: [
|
|
3817
|
+
{
|
|
3818
|
+
entryId,
|
|
3819
|
+
authorId: createdBy,
|
|
3820
|
+
body,
|
|
3821
|
+
createdAt,
|
|
3822
|
+
},
|
|
3823
|
+
],
|
|
3824
|
+
status: "open",
|
|
3825
|
+
warningIds: [],
|
|
3826
|
+
isResolved: false,
|
|
3827
|
+
metadata: {
|
|
3828
|
+
source: "runtime",
|
|
3829
|
+
linkedRevisionId: changeId,
|
|
3830
|
+
},
|
|
3831
|
+
};
|
|
3832
|
+
|
|
3833
|
+
this.dispatch({
|
|
3834
|
+
type: "comment.add",
|
|
3835
|
+
comment,
|
|
3836
|
+
selection: createSelectionFromPublicAnchor(
|
|
3837
|
+
toPublicAnchorProjection(revision.anchor),
|
|
3838
|
+
),
|
|
3839
|
+
origin: createOrigin("api", clock()),
|
|
3840
|
+
});
|
|
3841
|
+
|
|
3842
|
+
return { commentId, entryId };
|
|
3843
|
+
},
|
|
3537
3844
|
editCommentBody(commentId, body) {
|
|
3538
3845
|
this.dispatch({
|
|
3539
3846
|
type: "comment.edit-body",
|
|
@@ -3857,13 +4164,16 @@ export function createDocumentRuntime(
|
|
|
3857
4164
|
zoomLevel: viewState.zoomLevel,
|
|
3858
4165
|
},
|
|
3859
4166
|
});
|
|
3860
|
-
const navigation = getCachedDocumentNavigationSnapshot(state, activeStory);
|
|
3861
4167
|
const bookmarkMap = buildBookmarkNameMap(state.document);
|
|
3862
4168
|
const paragraphContexts = collectParagraphContexts(state.document.content.children);
|
|
3863
4169
|
const paragraphOffsets = paragraphContexts.map((p) => p.startOffset);
|
|
3864
4170
|
return createFieldResolver({
|
|
3865
4171
|
pageGraph,
|
|
3866
|
-
activePageIndex:
|
|
4172
|
+
activePageIndex: resolveActivePageIndexFromPageGraph(
|
|
4173
|
+
pageGraph,
|
|
4174
|
+
state.selection.head,
|
|
4175
|
+
activeStory,
|
|
4176
|
+
),
|
|
3867
4177
|
bookmarkMap,
|
|
3868
4178
|
paragraphOffsets,
|
|
3869
4179
|
styles: state.document.styles,
|
|
@@ -4033,10 +4343,19 @@ export function createDocumentRuntime(
|
|
|
4033
4343
|
return buildFieldSnapshot(state.document);
|
|
4034
4344
|
},
|
|
4035
4345
|
updateFields(options?: UpdateFieldsOptions): UpdateFieldsResult {
|
|
4346
|
+
const pageGraph = layoutEngine.getPageGraph({
|
|
4347
|
+
document: state.document,
|
|
4348
|
+
viewState: {
|
|
4349
|
+
activeStory,
|
|
4350
|
+
workspaceMode: viewState.workspaceMode,
|
|
4351
|
+
zoomLevel: viewState.zoomLevel,
|
|
4352
|
+
},
|
|
4353
|
+
});
|
|
4036
4354
|
const refreshed = refreshDocumentFields(
|
|
4037
4355
|
state.document,
|
|
4038
4356
|
state.selection.head,
|
|
4039
4357
|
activeStory,
|
|
4358
|
+
pageGraph,
|
|
4040
4359
|
options,
|
|
4041
4360
|
);
|
|
4042
4361
|
if (refreshed.changed) {
|
|
@@ -6111,6 +6430,7 @@ function toPublicCommentSidebarSnapshot(
|
|
|
6111
6430
|
createdBy: thread.createdBy,
|
|
6112
6431
|
warningCount: thread.warningCount,
|
|
6113
6432
|
anchorLabel: thread.anchorLabel,
|
|
6433
|
+
linkedRevisionId: sourceThread?.metadata?.linkedRevisionId,
|
|
6114
6434
|
detachedReason: sourceThread?.metadata?.detachedReason,
|
|
6115
6435
|
actionabilityNote: sourceThread?.metadata?.actionabilityNote,
|
|
6116
6436
|
isActive: thread.isActive,
|
|
@@ -6129,6 +6449,7 @@ function toPublicTrackedChangesSnapshot(
|
|
|
6129
6449
|
createRevisionStoreFromDocument(state),
|
|
6130
6450
|
);
|
|
6131
6451
|
const storyPlainTextCache = new Map<string, string>();
|
|
6452
|
+
const commentLinks = collectRevisionCommentLinks(state);
|
|
6132
6453
|
|
|
6133
6454
|
return {
|
|
6134
6455
|
pendingChangeIds: projection.activeRevisionIds,
|
|
@@ -6147,6 +6468,7 @@ function toPublicTrackedChangesSnapshot(
|
|
|
6147
6468
|
createDetachedAnchor({ from: 0, to: 0 }, "importAmbiguity"),
|
|
6148
6469
|
getStoryPlainText(state.document, storyTarget, storyPlainTextCache),
|
|
6149
6470
|
);
|
|
6471
|
+
const linkedComments = commentLinks.get(revision.revisionId);
|
|
6150
6472
|
|
|
6151
6473
|
return {
|
|
6152
6474
|
revisionId: revision.revisionId,
|
|
@@ -6170,6 +6492,12 @@ function toPublicTrackedChangesSnapshot(
|
|
|
6170
6492
|
warningCount: revision.warningCount,
|
|
6171
6493
|
canAccept: revision.canAccept,
|
|
6172
6494
|
canReject: revision.canReject,
|
|
6495
|
+
...(linkedComments
|
|
6496
|
+
? {
|
|
6497
|
+
commentThreadIds: linkedComments.commentThreadIds,
|
|
6498
|
+
replyCount: linkedComments.replyCount,
|
|
6499
|
+
}
|
|
6500
|
+
: {}),
|
|
6173
6501
|
importedRevisionForm: sourceRevision?.metadata?.importedRevisionForm,
|
|
6174
6502
|
preserveOnlyReason: revision.preserveOnlyReason,
|
|
6175
6503
|
excerpt: preview.excerpt,
|
|
@@ -6179,6 +6507,23 @@ function toPublicTrackedChangesSnapshot(
|
|
|
6179
6507
|
};
|
|
6180
6508
|
}
|
|
6181
6509
|
|
|
6510
|
+
function collectRevisionCommentLinks(
|
|
6511
|
+
state: Pick<EditorState, "document">,
|
|
6512
|
+
): Map<string, { commentThreadIds: string[]; replyCount: number }> {
|
|
6513
|
+
const links = new Map<string, { commentThreadIds: string[]; replyCount: number }>();
|
|
6514
|
+
for (const thread of Object.values(state.document.review.comments)) {
|
|
6515
|
+
const revisionId = thread.metadata?.linkedRevisionId;
|
|
6516
|
+
if (!revisionId) {
|
|
6517
|
+
continue;
|
|
6518
|
+
}
|
|
6519
|
+
const current = links.get(revisionId) ?? { commentThreadIds: [], replyCount: 0 };
|
|
6520
|
+
current.commentThreadIds.push(thread.commentId);
|
|
6521
|
+
current.replyCount += Math.max(0, (thread.entries?.length ?? 0) - 1);
|
|
6522
|
+
links.set(revisionId, current);
|
|
6523
|
+
}
|
|
6524
|
+
return links;
|
|
6525
|
+
}
|
|
6526
|
+
|
|
6182
6527
|
function createRevisionStoreFromDocument(
|
|
6183
6528
|
state: Pick<EditorState, "document">,
|
|
6184
6529
|
): RevisionStore {
|
|
@@ -6601,6 +6946,7 @@ function refreshDocumentFields(
|
|
|
6601
6946
|
document: CanonicalDocumentEnvelope,
|
|
6602
6947
|
selectionHead: number,
|
|
6603
6948
|
activeStory: EditorStoryTarget,
|
|
6949
|
+
pageGraph: RuntimePageGraph,
|
|
6604
6950
|
options?: UpdateFieldsOptions,
|
|
6605
6951
|
): {
|
|
6606
6952
|
document: CanonicalDocumentEnvelope;
|
|
@@ -6611,7 +6957,11 @@ function refreshDocumentFields(
|
|
|
6611
6957
|
const supportedOnly = options?.supportedOnly ?? true;
|
|
6612
6958
|
const bookmarkMap = buildBookmarkNameMap(document);
|
|
6613
6959
|
const paragraphs = collectParagraphContexts(document.content.children);
|
|
6614
|
-
const
|
|
6960
|
+
const activePageIndex = resolveActivePageIndexFromPageGraph(
|
|
6961
|
+
pageGraph,
|
|
6962
|
+
selectionHead,
|
|
6963
|
+
activeStory,
|
|
6964
|
+
);
|
|
6615
6965
|
let updatedCount = 0;
|
|
6616
6966
|
let changed = false;
|
|
6617
6967
|
let changedFrom: number | undefined;
|
|
@@ -6636,7 +6986,8 @@ function refreshDocumentFields(
|
|
|
6636
6986
|
document,
|
|
6637
6987
|
bookmarkMap,
|
|
6638
6988
|
paragraphs,
|
|
6639
|
-
|
|
6989
|
+
pageGraph,
|
|
6990
|
+
activePageIndex,
|
|
6640
6991
|
storyTarget,
|
|
6641
6992
|
);
|
|
6642
6993
|
if (!display) {
|
|
@@ -7172,7 +7523,8 @@ function resolveSupportedFieldDisplay(
|
|
|
7172
7523
|
document: CanonicalDocumentEnvelope,
|
|
7173
7524
|
bookmarkMap: Map<string, { bookmarkId: string; paragraphIndex: number }>,
|
|
7174
7525
|
paragraphs: readonly ParagraphContext[],
|
|
7175
|
-
|
|
7526
|
+
pageGraph: RuntimePageGraph,
|
|
7527
|
+
activePageIndex: number,
|
|
7176
7528
|
storyTarget: EditorStoryTarget,
|
|
7177
7529
|
): { displayText: string; refreshStatus: FieldRefreshStatus } | undefined {
|
|
7178
7530
|
if (!field.fieldFamily || !isSupportedFieldFamily(field.fieldFamily)) {
|
|
@@ -7187,33 +7539,41 @@ function resolveSupportedFieldDisplay(
|
|
|
7187
7539
|
return { displayText: result.text, refreshStatus: result.refreshStatus };
|
|
7188
7540
|
}
|
|
7189
7541
|
if (field.fieldFamily === "SECTIONPAGES") {
|
|
7190
|
-
const
|
|
7191
|
-
|
|
7192
|
-
:
|
|
7193
|
-
|
|
7194
|
-
if (sectionPages.length === 0) return { displayText: "", refreshStatus: "unresolvable" };
|
|
7195
|
-
const fmt = sectionPages[0]?.layout.pageNumbering?.format;
|
|
7542
|
+
const page = resolveRepresentativePageForStory(pageGraph, activePageIndex, storyTarget);
|
|
7543
|
+
if (!page) {
|
|
7544
|
+
return { displayText: "", refreshStatus: "unresolvable" };
|
|
7545
|
+
}
|
|
7196
7546
|
return {
|
|
7197
|
-
displayText:
|
|
7547
|
+
displayText: resolvePageFieldDisplayText("SECTIONPAGES", "", {
|
|
7548
|
+
page,
|
|
7549
|
+
graph: pageGraph,
|
|
7550
|
+
}),
|
|
7198
7551
|
refreshStatus: "current",
|
|
7199
7552
|
};
|
|
7200
7553
|
}
|
|
7201
7554
|
if (field.fieldFamily === "PAGE") {
|
|
7202
|
-
const page = resolveRepresentativePageForStory(
|
|
7555
|
+
const page = resolveRepresentativePageForStory(pageGraph, activePageIndex, storyTarget);
|
|
7203
7556
|
if (!page) {
|
|
7204
7557
|
return { displayText: "", refreshStatus: "unresolvable" };
|
|
7205
7558
|
}
|
|
7206
7559
|
return {
|
|
7207
|
-
displayText:
|
|
7560
|
+
displayText: resolvePageFieldDisplayText("PAGE", "", {
|
|
7561
|
+
page,
|
|
7562
|
+
graph: pageGraph,
|
|
7563
|
+
}),
|
|
7208
7564
|
refreshStatus: "current",
|
|
7209
7565
|
};
|
|
7210
7566
|
}
|
|
7211
7567
|
if (field.fieldFamily === "NUMPAGES") {
|
|
7212
|
-
|
|
7568
|
+
const page = pageGraph.pages[activePageIndex] ?? firstContentPage(pageGraph);
|
|
7569
|
+
if (!page || pageGraph.contentPageCount === 0) {
|
|
7213
7570
|
return { displayText: "", refreshStatus: "unresolvable" };
|
|
7214
7571
|
}
|
|
7215
7572
|
return {
|
|
7216
|
-
displayText:
|
|
7573
|
+
displayText: resolvePageFieldDisplayText("NUMPAGES", "", {
|
|
7574
|
+
page,
|
|
7575
|
+
graph: pageGraph,
|
|
7576
|
+
}),
|
|
7217
7577
|
refreshStatus: "current",
|
|
7218
7578
|
};
|
|
7219
7579
|
}
|
|
@@ -7238,20 +7598,28 @@ function resolveSupportedFieldDisplay(
|
|
|
7238
7598
|
|
|
7239
7599
|
// \p switch: emit relative position text ("above" / "below" / "on this page")
|
|
7240
7600
|
if (field.switches?.relativePosition === true) {
|
|
7241
|
-
const fieldPage = resolveRepresentativePageForStory(
|
|
7242
|
-
const targetPageIndex =
|
|
7601
|
+
const fieldPage = resolveRepresentativePageForStory(pageGraph, activePageIndex, storyTarget);
|
|
7602
|
+
const targetPageIndex = findPageIndexForRuntimeOffset(
|
|
7603
|
+
pageGraph.pages,
|
|
7604
|
+
paragraph.startOffset,
|
|
7605
|
+
);
|
|
7243
7606
|
const fieldPageIndex = fieldPage
|
|
7244
|
-
?
|
|
7245
|
-
:
|
|
7607
|
+
? pageGraph.pages.findIndex((page) => page.pageId === fieldPage.pageId)
|
|
7608
|
+
: activePageIndex;
|
|
7246
7609
|
if (targetPageIndex < fieldPageIndex) return { displayText: "above", refreshStatus: "current" };
|
|
7247
7610
|
if (targetPageIndex > fieldPageIndex) return { displayText: "below", refreshStatus: "current" };
|
|
7248
7611
|
return { displayText: "on this page", refreshStatus: "current" };
|
|
7249
7612
|
}
|
|
7250
7613
|
|
|
7251
|
-
const
|
|
7252
|
-
const page = navigation.pages[pageIndex] ?? navigation.pages[0];
|
|
7614
|
+
const page = pageForRuntimeOffset(pageGraph, paragraph.startOffset);
|
|
7253
7615
|
return page
|
|
7254
|
-
? {
|
|
7616
|
+
? {
|
|
7617
|
+
displayText: resolvePageFieldDisplayText("PAGE", "", {
|
|
7618
|
+
page,
|
|
7619
|
+
graph: pageGraph,
|
|
7620
|
+
}),
|
|
7621
|
+
refreshStatus: "current",
|
|
7622
|
+
}
|
|
7255
7623
|
: { displayText: "", refreshStatus: "unresolvable" };
|
|
7256
7624
|
}
|
|
7257
7625
|
if (field.fieldFamily === "NOTEREF") {
|
|
@@ -7267,52 +7635,133 @@ function resolveSupportedFieldDisplay(
|
|
|
7267
7635
|
return undefined;
|
|
7268
7636
|
}
|
|
7269
7637
|
|
|
7270
|
-
function
|
|
7271
|
-
|
|
7638
|
+
function resolveActivePageIndexFromPageGraph(
|
|
7639
|
+
graph: RuntimePageGraph,
|
|
7640
|
+
selectionHead: number,
|
|
7272
7641
|
storyTarget: EditorStoryTarget,
|
|
7273
|
-
):
|
|
7642
|
+
): number {
|
|
7643
|
+
if (graph.pages.length === 0) {
|
|
7644
|
+
return 0;
|
|
7645
|
+
}
|
|
7646
|
+
|
|
7274
7647
|
if (storyTarget.kind === "main") {
|
|
7275
|
-
return
|
|
7648
|
+
return findPageForRuntimeOffset(graph.pages, selectionHead);
|
|
7276
7649
|
}
|
|
7277
7650
|
|
|
7278
7651
|
if (storyTarget.kind === "header" || storyTarget.kind === "footer") {
|
|
7279
|
-
const
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7652
|
+
const matchingPageIndex = graph.pages.findIndex((page) => {
|
|
7653
|
+
if (page.isBlankFiller) return false;
|
|
7654
|
+
const activeStory =
|
|
7655
|
+
storyTarget.kind === "header" ? page.stories.header : page.stories.footer;
|
|
7656
|
+
return activeStory ? storyTargetsMatchForPageInstance(activeStory, storyTarget) : false;
|
|
7657
|
+
});
|
|
7658
|
+
if (matchingPageIndex >= 0) {
|
|
7659
|
+
return matchingPageIndex;
|
|
7283
7660
|
}
|
|
7284
|
-
|
|
7285
|
-
|
|
7661
|
+
|
|
7662
|
+
const sectionIndex = storyTarget.sectionIndex ?? graph.pages[0]!.sectionIndex;
|
|
7663
|
+
const sectionPageIndex = graph.pages.findIndex(
|
|
7664
|
+
(page) => page.sectionIndex === sectionIndex && !page.isBlankFiller,
|
|
7665
|
+
);
|
|
7666
|
+
return sectionPageIndex >= 0 ? sectionPageIndex : 0;
|
|
7667
|
+
}
|
|
7668
|
+
|
|
7669
|
+
return findPageForRuntimeOffset(graph.pages, selectionHead);
|
|
7670
|
+
}
|
|
7671
|
+
|
|
7672
|
+
function findPageForRuntimeOffset(
|
|
7673
|
+
pages: RuntimePageGraph["pages"],
|
|
7674
|
+
offset: number,
|
|
7675
|
+
): number {
|
|
7676
|
+
return findPageIndexForRuntimeOffset(pages, offset);
|
|
7677
|
+
}
|
|
7678
|
+
|
|
7679
|
+
function findPageIndexForRuntimeOffset(
|
|
7680
|
+
pages: RuntimePageGraph["pages"],
|
|
7681
|
+
offset: number,
|
|
7682
|
+
): number {
|
|
7683
|
+
let lastContentPageIndex = -1;
|
|
7684
|
+
for (let i = 0; i < pages.length; i += 1) {
|
|
7685
|
+
const page = pages[i]!;
|
|
7686
|
+
if (page.isBlankFiller) {
|
|
7687
|
+
continue;
|
|
7286
7688
|
}
|
|
7287
|
-
|
|
7288
|
-
|
|
7689
|
+
lastContentPageIndex = i;
|
|
7690
|
+
if (offset < page.endOffset) {
|
|
7691
|
+
return i;
|
|
7289
7692
|
}
|
|
7290
|
-
return (
|
|
7291
|
-
sectionPages.find((page) => isDefaultHeaderFooterPage(page)) ??
|
|
7292
|
-
sectionPages[0]
|
|
7293
|
-
);
|
|
7294
7693
|
}
|
|
7694
|
+
if (lastContentPageIndex >= 0) {
|
|
7695
|
+
return lastContentPageIndex;
|
|
7696
|
+
}
|
|
7697
|
+
return Math.max(0, pages.length - 1);
|
|
7698
|
+
}
|
|
7295
7699
|
|
|
7296
|
-
|
|
7700
|
+
function pageForRuntimeOffset(
|
|
7701
|
+
graph: RuntimePageGraph,
|
|
7702
|
+
offset: number,
|
|
7703
|
+
): RuntimePageNode | undefined {
|
|
7704
|
+
return graph.pages[findPageIndexForRuntimeOffset(graph.pages, offset)];
|
|
7297
7705
|
}
|
|
7298
7706
|
|
|
7299
|
-
function
|
|
7300
|
-
|
|
7707
|
+
function resolveRepresentativePageForStory(
|
|
7708
|
+
graph: RuntimePageGraph,
|
|
7709
|
+
activePageIndex: number,
|
|
7710
|
+
storyTarget: EditorStoryTarget,
|
|
7711
|
+
): RuntimePageNode | undefined {
|
|
7712
|
+
if (storyTarget.kind === "main") {
|
|
7713
|
+
return graph.pages[activePageIndex] ?? firstContentPage(graph);
|
|
7714
|
+
}
|
|
7715
|
+
|
|
7716
|
+
if (storyTarget.kind === "header" || storyTarget.kind === "footer") {
|
|
7717
|
+
const matchedPage = graph.pages.find((page) => {
|
|
7718
|
+
if (page.isBlankFiller) return false;
|
|
7719
|
+
const activeStory =
|
|
7720
|
+
storyTarget.kind === "header" ? page.stories.header : page.stories.footer;
|
|
7721
|
+
return activeStory ? storyTargetsMatchForPageInstance(activeStory, storyTarget) : false;
|
|
7722
|
+
});
|
|
7723
|
+
if (matchedPage) {
|
|
7724
|
+
return matchedPage;
|
|
7725
|
+
}
|
|
7726
|
+
if (typeof storyTarget.sectionIndex === "number") {
|
|
7727
|
+
return graph.pages.find(
|
|
7728
|
+
(page) => page.sectionIndex === storyTarget.sectionIndex && !page.isBlankFiller,
|
|
7729
|
+
) ?? firstContentPage(graph);
|
|
7730
|
+
}
|
|
7731
|
+
return firstContentPage(graph);
|
|
7732
|
+
}
|
|
7733
|
+
|
|
7734
|
+
return graph.pages[activePageIndex] ?? firstContentPage(graph);
|
|
7735
|
+
}
|
|
7736
|
+
|
|
7737
|
+
function storyTargetsMatchForPageInstance(
|
|
7738
|
+
activeStory: EditorStoryTarget,
|
|
7739
|
+
requestedStory: EditorStoryTarget,
|
|
7301
7740
|
): boolean {
|
|
7302
|
-
if (
|
|
7741
|
+
if (activeStory.kind !== requestedStory.kind) {
|
|
7303
7742
|
return false;
|
|
7304
7743
|
}
|
|
7305
|
-
if (
|
|
7306
|
-
return (
|
|
7744
|
+
if (activeStory.kind === "header" && requestedStory.kind === "header") {
|
|
7745
|
+
return (
|
|
7746
|
+
activeStory.relationshipId === requestedStory.relationshipId &&
|
|
7747
|
+
activeStory.variant === requestedStory.variant &&
|
|
7748
|
+
(requestedStory.sectionIndex === undefined ||
|
|
7749
|
+
activeStory.sectionIndex === requestedStory.sectionIndex)
|
|
7750
|
+
);
|
|
7751
|
+
}
|
|
7752
|
+
if (activeStory.kind === "footer" && requestedStory.kind === "footer") {
|
|
7753
|
+
return (
|
|
7754
|
+
activeStory.relationshipId === requestedStory.relationshipId &&
|
|
7755
|
+
activeStory.variant === requestedStory.variant &&
|
|
7756
|
+
(requestedStory.sectionIndex === undefined ||
|
|
7757
|
+
activeStory.sectionIndex === requestedStory.sectionIndex)
|
|
7758
|
+
);
|
|
7307
7759
|
}
|
|
7308
|
-
return
|
|
7760
|
+
return storyTargetsEqual(activeStory, requestedStory);
|
|
7309
7761
|
}
|
|
7310
7762
|
|
|
7311
|
-
function
|
|
7312
|
-
page
|
|
7313
|
-
): string {
|
|
7314
|
-
const n = (page.layout.pageNumbering?.start ?? 1) + page.pageInSection;
|
|
7315
|
-
return formatPageNumber(n, page.layout.pageNumbering?.format);
|
|
7763
|
+
function firstContentPage(graph: RuntimePageGraph): RuntimePageNode | undefined {
|
|
7764
|
+
return graph.pages.find((page) => !page.isBlankFiller) ?? graph.pages[0];
|
|
7316
7765
|
}
|
|
7317
7766
|
|
|
7318
7767
|
interface ParagraphContext {
|
|
@@ -7730,6 +8179,93 @@ function isHighlightedSegment(
|
|
|
7730
8179
|
return typeof bg === "string" && bg.length > 0;
|
|
7731
8180
|
}
|
|
7732
8181
|
|
|
8182
|
+
function resolveActiveParagraphContext(
|
|
8183
|
+
snapshot: Pick<RuntimeRenderSnapshot, "surface" | "selection">,
|
|
8184
|
+
): {
|
|
8185
|
+
paragraphIndex: number;
|
|
8186
|
+
paragraph: Extract<SurfaceBlockSnapshot, { kind: "paragraph" }>;
|
|
8187
|
+
atParagraphStart: boolean;
|
|
8188
|
+
isEmpty: boolean;
|
|
8189
|
+
} | null {
|
|
8190
|
+
if (!snapshot.surface) {
|
|
8191
|
+
return null;
|
|
8192
|
+
}
|
|
8193
|
+
|
|
8194
|
+
const paragraphIndex = resolveActiveParagraphIndex(
|
|
8195
|
+
snapshot.surface.blocks,
|
|
8196
|
+
snapshot.selection,
|
|
8197
|
+
);
|
|
8198
|
+
if (paragraphIndex === null) {
|
|
8199
|
+
return null;
|
|
8200
|
+
}
|
|
8201
|
+
|
|
8202
|
+
const selectionPosition =
|
|
8203
|
+
snapshot.selection.activeRange.kind === "node"
|
|
8204
|
+
? snapshot.selection.activeRange.at
|
|
8205
|
+
: snapshot.selection.head;
|
|
8206
|
+
const paragraph = findSurfaceParagraphAtPosition(
|
|
8207
|
+
snapshot.surface.blocks,
|
|
8208
|
+
selectionPosition,
|
|
8209
|
+
);
|
|
8210
|
+
if (!paragraph) {
|
|
8211
|
+
return null;
|
|
8212
|
+
}
|
|
8213
|
+
|
|
8214
|
+
return {
|
|
8215
|
+
paragraphIndex,
|
|
8216
|
+
paragraph,
|
|
8217
|
+
atParagraphStart:
|
|
8218
|
+
snapshot.selection.isCollapsed &&
|
|
8219
|
+
snapshot.selection.activeRange.kind !== "node" &&
|
|
8220
|
+
snapshot.selection.anchor === snapshot.selection.head &&
|
|
8221
|
+
snapshot.selection.head === paragraph.from,
|
|
8222
|
+
isEmpty: isSurfaceParagraphEmpty(paragraph),
|
|
8223
|
+
};
|
|
8224
|
+
}
|
|
8225
|
+
|
|
8226
|
+
function findSurfaceParagraphAtPosition(
|
|
8227
|
+
blocks: readonly SurfaceBlockSnapshot[],
|
|
8228
|
+
position: number,
|
|
8229
|
+
): Extract<SurfaceBlockSnapshot, { kind: "paragraph" }> | null {
|
|
8230
|
+
for (const block of blocks) {
|
|
8231
|
+
if (position < block.from || position > block.to) {
|
|
8232
|
+
continue;
|
|
8233
|
+
}
|
|
8234
|
+
if (block.kind === "paragraph") {
|
|
8235
|
+
return block;
|
|
8236
|
+
}
|
|
8237
|
+
if (block.kind === "table") {
|
|
8238
|
+
for (const row of block.rows) {
|
|
8239
|
+
for (const cell of row.cells) {
|
|
8240
|
+
const paragraph = findSurfaceParagraphAtPosition(cell.content, position);
|
|
8241
|
+
if (paragraph) {
|
|
8242
|
+
return paragraph;
|
|
8243
|
+
}
|
|
8244
|
+
}
|
|
8245
|
+
}
|
|
8246
|
+
continue;
|
|
8247
|
+
}
|
|
8248
|
+
if (block.kind === "sdt_block") {
|
|
8249
|
+
const paragraph = findSurfaceParagraphAtPosition(block.children, position);
|
|
8250
|
+
if (paragraph) {
|
|
8251
|
+
return paragraph;
|
|
8252
|
+
}
|
|
8253
|
+
}
|
|
8254
|
+
}
|
|
8255
|
+
return null;
|
|
8256
|
+
}
|
|
8257
|
+
|
|
8258
|
+
function isSurfaceParagraphEmpty(
|
|
8259
|
+
paragraph: Extract<SurfaceBlockSnapshot, { kind: "paragraph" }>,
|
|
8260
|
+
): boolean {
|
|
8261
|
+
if (paragraph.segments.length === 0) {
|
|
8262
|
+
return true;
|
|
8263
|
+
}
|
|
8264
|
+
return paragraph.segments.every(
|
|
8265
|
+
(segment) => segment.kind === "text" && segment.text.length === 0,
|
|
8266
|
+
);
|
|
8267
|
+
}
|
|
8268
|
+
|
|
7733
8269
|
function forEachParagraphBlock(
|
|
7734
8270
|
blocks: readonly SurfaceBlockSnapshot[],
|
|
7735
8271
|
visit: (paragraph: Extract<SurfaceBlockSnapshot, { kind: "paragraph" }>) => void,
|
|
@@ -7843,6 +8379,44 @@ function buildRunPropertyBeforeXml(
|
|
|
7843
8379
|
return `<w:rPr>${parts.join("")}</w:rPr>`;
|
|
7844
8380
|
}
|
|
7845
8381
|
|
|
8382
|
+
function buildParagraphPropertyBeforeXml(
|
|
8383
|
+
paragraph: Extract<SurfaceBlockSnapshot, { kind: "paragraph" }>,
|
|
8384
|
+
): string {
|
|
8385
|
+
const parts: string[] = [];
|
|
8386
|
+
if (paragraph.styleId) {
|
|
8387
|
+
parts.push(`<w:pStyle w:val="${escapeAttributeXml(paragraph.styleId)}"/>`);
|
|
8388
|
+
}
|
|
8389
|
+
if (paragraph.numbering) {
|
|
8390
|
+
parts.push(
|
|
8391
|
+
`<w:numPr><w:ilvl w:val="${paragraph.numbering.level}"/><w:numId w:val="${escapeAttributeXml(
|
|
8392
|
+
paragraph.numbering.numberingInstanceId.replace(/^num:/u, ""),
|
|
8393
|
+
)}"/></w:numPr>`,
|
|
8394
|
+
);
|
|
8395
|
+
}
|
|
8396
|
+
if (paragraph.alignment) {
|
|
8397
|
+
parts.push(`<w:jc w:val="${escapeAttributeXml(paragraph.alignment)}"/>`);
|
|
8398
|
+
}
|
|
8399
|
+
if (paragraph.indentation) {
|
|
8400
|
+
const attrs: string[] = [];
|
|
8401
|
+
if (paragraph.indentation.left !== undefined) {
|
|
8402
|
+
attrs.push(`w:left="${paragraph.indentation.left}"`);
|
|
8403
|
+
}
|
|
8404
|
+
if (paragraph.indentation.right !== undefined) {
|
|
8405
|
+
attrs.push(`w:right="${paragraph.indentation.right}"`);
|
|
8406
|
+
}
|
|
8407
|
+
if (paragraph.indentation.firstLine !== undefined) {
|
|
8408
|
+
attrs.push(`w:firstLine="${paragraph.indentation.firstLine}"`);
|
|
8409
|
+
}
|
|
8410
|
+
if (paragraph.indentation.hanging !== undefined) {
|
|
8411
|
+
attrs.push(`w:hanging="${paragraph.indentation.hanging}"`);
|
|
8412
|
+
}
|
|
8413
|
+
if (attrs.length > 0) {
|
|
8414
|
+
parts.push(`<w:ind ${attrs.join(" ")}/>`);
|
|
8415
|
+
}
|
|
8416
|
+
}
|
|
8417
|
+
return `<w:pPr>${parts.join("")}</w:pPr>`;
|
|
8418
|
+
}
|
|
8419
|
+
|
|
7846
8420
|
function escapeAttributeXml(value: string): string {
|
|
7847
8421
|
return value
|
|
7848
8422
|
.replace(/&/g, "&")
|
|
@@ -7851,6 +8425,55 @@ function escapeAttributeXml(value: string): string {
|
|
|
7851
8425
|
.replace(/"/g, """);
|
|
7852
8426
|
}
|
|
7853
8427
|
|
|
8428
|
+
function stripStoryTarget(selection: SelectionSnapshot): SelectionSnapshot {
|
|
8429
|
+
const { storyTarget: _storyTarget, ...rest } = selection;
|
|
8430
|
+
return rest;
|
|
8431
|
+
}
|
|
8432
|
+
|
|
8433
|
+
function toInternalSelectionSnapshot(
|
|
8434
|
+
selection: SelectionSnapshot,
|
|
8435
|
+
): EditorState["selection"] {
|
|
8436
|
+
return {
|
|
8437
|
+
anchor: selection.anchor,
|
|
8438
|
+
head: selection.head,
|
|
8439
|
+
isCollapsed: selection.isCollapsed,
|
|
8440
|
+
activeRange:
|
|
8441
|
+
selection.activeRange.kind === "range"
|
|
8442
|
+
? createRangeAnchor(
|
|
8443
|
+
selection.activeRange.from,
|
|
8444
|
+
selection.activeRange.to,
|
|
8445
|
+
selection.activeRange.assoc,
|
|
8446
|
+
)
|
|
8447
|
+
: selection.activeRange.kind === "node"
|
|
8448
|
+
? createNodeAnchor(selection.activeRange.at, selection.activeRange.assoc)
|
|
8449
|
+
: createDetachedAnchor(
|
|
8450
|
+
selection.activeRange.lastKnownRange,
|
|
8451
|
+
selection.activeRange.reason,
|
|
8452
|
+
),
|
|
8453
|
+
};
|
|
8454
|
+
}
|
|
8455
|
+
|
|
8456
|
+
function getFormattingOperationCommandName(operation: FormattingOperation): string {
|
|
8457
|
+
switch (operation.type) {
|
|
8458
|
+
case "toggle":
|
|
8459
|
+
return `toggle${operation.mark.charAt(0).toUpperCase()}${operation.mark.slice(1)}`;
|
|
8460
|
+
case "set-font-family":
|
|
8461
|
+
return "setFontFamily";
|
|
8462
|
+
case "set-font-size":
|
|
8463
|
+
return "setFontSize";
|
|
8464
|
+
case "set-text-color":
|
|
8465
|
+
return "setTextColor";
|
|
8466
|
+
case "set-highlight-color":
|
|
8467
|
+
return "setHighlightColor";
|
|
8468
|
+
case "set-alignment":
|
|
8469
|
+
return "setAlignment";
|
|
8470
|
+
case "indent":
|
|
8471
|
+
return "indent";
|
|
8472
|
+
case "outdent":
|
|
8473
|
+
return "outdent";
|
|
8474
|
+
}
|
|
8475
|
+
}
|
|
8476
|
+
|
|
7854
8477
|
function appendPropertyChangeSuggestion(
|
|
7855
8478
|
document: CanonicalDocumentEnvelope,
|
|
7856
8479
|
anchor: { from: number; to: number },
|