@a3s-lab/office 0.31.0 → 0.32.0

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.
@@ -6,8 +6,8 @@ import { Fragment as external_react_Fragment, forwardRef, useCallback, useEffect
6
6
  import { Extension, createNodeFromContent } from "@tiptap/core";
7
7
  import { NodeSelection, Plugin, PluginKey, TextSelection } from "@tiptap/pm/state";
8
8
  import { Decoration, DecorationSet } from "@tiptap/pm/view";
9
- import { AArrowDown, AArrowUp, ALargeSmall, AlignCenter, AlignJustify, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowUp, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, BookMarked, BookOpen, Bookmark, CaseUpper, Check, CheckCheck, CheckCircle2, ChevronDown, ChevronRight, ChevronUp, ClipboardCopy, ClipboardPaste, Cloud, Columns3, Eraser, Eye, FileDiff, FilePlus2, FileText, Globe2, Grid2X2, Hash, Highlighter, Image, IndentDecrease, IndentIncrease, Italic, Languages, Link2, List, ListChecks, ListOrdered, ListTree, MessageSquarePlus, MessageSquareReply, MessagesSquare, MoveVertical, Paintbrush, Palette, PanelBottom, PanelBottomOpen, PanelLeftOpen, PanelTop, PanelTopOpen, Pilcrow, PilcrowLeft, PilcrowRight, Plus, Quote, Redo2, RefreshCw, Replace, ReplaceAll, RotateCcw, Rows3, Ruler, Scan, Search, Settings2, Square, StretchHorizontal, Strikethrough, Subscript, Superscript, Table2, TableCellsMerge, TableCellsSplit, TableProperties, Tags, TextWrap, Trash2, TriangleAlert, Type, Underline, Undo2, Unlink, Unlink2, X, XCircle, ZoomIn, ZoomOut } from "lucide-react";
10
- import { Node as model_Node } from "@tiptap/pm/model";
9
+ import { AArrowDown, AArrowUp, ALargeSmall, AlignCenter, AlignJustify, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowUp, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, BookMarked, BookOpen, Bookmark, CaseUpper, Check, CheckCheck, CheckCircle2, ChevronDown, ChevronRight, ChevronUp, ClipboardCopy, ClipboardPaste, Cloud, Columns3, Eraser, Eye, FileCheck2, FileDiff, FilePlus2, FileStack, FileText, FileUp, GitCompareArrows, Globe2, Grid2X2, Hash, Highlighter, Image, IndentDecrease, IndentIncrease, Italic, Languages, Link2, List, ListChecks, ListOrdered, ListTree, MessageSquarePlus, MessageSquareReply, MessagesSquare, MoveVertical, Paintbrush, Palette, PanelBottom, PanelBottomOpen, PanelLeftOpen, PanelTop, PanelTopOpen, Pilcrow, PilcrowLeft, PilcrowRight, Plus, Quote, Redo2, RefreshCw, Replace, ReplaceAll, RotateCcw, Rows3, Ruler, Scan, Search, Settings2, Square, StretchHorizontal, Strikethrough, Subscript, Superscript, Table2, TableCellsMerge, TableCellsSplit, TableProperties, Tags, TextWrap, Trash2, TriangleAlert, Type, Underline, Undo2, Unlink, Unlink2, X, XCircle, ZoomIn, ZoomOut } from "lucide-react";
10
+ import { Fragment as model_Fragment, Node as model_Node } from "@tiptap/pm/model";
11
11
  import extension_color from "@tiptap/extension-color";
12
12
  import { TableKit } from "@tiptap/extension-table";
13
13
  import extension_text_align from "@tiptap/extension-text-align";
@@ -15,7 +15,8 @@ import font_size from "@tiptap/extension-text-style/font-size";
15
15
  import starter_kit from "@tiptap/starter-kit";
16
16
  import { BubbleMenu } from "@tiptap/react/menus";
17
17
  import { TableMap, isInTable, selectedRect } from "@tiptap/pm/tables";
18
- import { documentTableSizing, transferChangedDocumentTextStatistics, documentPageBordersVisible, documentUnderlineStyle, DOCUMENT_PAGE_BORDER_EDGES, documentTableRowOptions, normalizeDocumentColumns, clampDocumentMargin, materializeLazyDocumentEditorRoot, documentKerningThresholdPoints, selectedDocumentIndexOptions, nextDocumentTabAlignment, DOCUMENT_BOOKMARK_DUPLICATE_MESSAGE, normalizedTabPosition, documentCitationCount, resolveDocumentPageBorders, documentLazyHtmlChunkFragment, DocumentFontFamily, normalizeDocumentHiddenText, documentLegacyTextEffectsConflict, DOCUMENT_CHUNK_VISIBLE_IDS_META, selectedDocumentIndexDraft, MAX_DOCUMENT_IMAGE_RELATIVE_HEIGHT, retainAnchoredDocumentComments, DocumentTableRowIdentity, work_document_page_margins_twipsToMillimeters, DEFAULT_DOCUMENT_TABLE_CELL_FORMAT, resolveWorkDocumentEditorInput, documentCharacterPositionPoints, documentBulletListStyle, collectDocumentTextLayoutParagraphs, readOfficeDocumentCollaboration as readWorkOfficeDocumentCollaboration, normalizeDocumentCharacterScalePercent, documentOrderedListState, normalizeDocumentPageChrome, documentTextStatistics, documentPageMarginsForLayout, invalidateDocumentLazyHtmlProjection, documentTableCellFormat, normalizeDocumentPageGeometry, normalizeDocumentImageLayer, documentImageProperties, isValidDocumentCitationTag, sanitizeDocumentPageChromeHtml, pageTwipsToMillimeters, normalizeDocumentPageBorders, activeDocumentSection, DocumentParagraphFormatting, normalizeDocumentHtml, documentInitialSectionLayout, documentModelHasTrustedInitialIntegrityFeatures, documentModelUsesWindowing, documentParagraphDirection, documentParagraphPagination, documentPageChromeLegacyFields, documentChunkMountedIds, collectDocumentCommentAnchors, documentTextCaseKeyboardShortcuts, DocumentTextStyle, canChangeDocumentIndent, documentCharacterSpacingPoints, documentKerningIsEffective, DocumentSuperscript, documentSectionById, activeDocumentBookmark, DocumentEquation, documentLazyHtmlProjectionFingerprint, documentLazyHtmlProjection, normalizeDocumentParagraphIndent, updateDocumentGutterPosition, DocumentStrike, mountWorkLiveDocumentCapture, registerDocumentPageSurfaceGeometry, documentHiddenTextKeyboardShortcut, documentHasIndex, syncDocumentContentFromHtml, documentLegacyTextEffectsCss, editorDocumentCaptionTargets, resolveDocumentPageChrome, documentImageLayoutOptions, MAX_DOCUMENT_NUMBERING_START, resolveDocumentPageSize, editorDocumentBookmarkReferenceTargets, serializeWorkDocumentNode, DOCUMENT_CHUNK_PAGINATION_META, createOfficeDocumentCollaborationBinding as createWorkOfficeDocumentCollaborationBinding, documentModelForContent, documentLegacyTextEffectsFromTextStyleAttributes, normalizeDocumentEmphasisMark, selectedDocumentChunkId, documentCommentViews, documentParagraphSpacing, normalizeDocumentTabStops, patchDocumentLazyHtmlProjection, normalizeDocumentTextCase, documentUnderlineColor, documentBookmarkNameExists, collectDocumentChanges, DocumentImage, selectedDocumentTableOfContentsOptions, resolveDocumentPageMargins, updateDocumentPageMarginMode, canSetDocumentTableRowRepeatHeader, normalizeDocumentCharacterSpacingTwips, DocumentCharacterFormatting, millimetersToPixels as work_document_layout_millimetersToPixels, DOCUMENT_CHUNK_HYDRATION_META, documentTabLeaderLabel, createSchemaDerivedWorkDocumentModel, updateDocumentCustomPageMillimeters, DocumentUnderline, documentCommentDraftRange, documentParagraphTabStops, documentTableHorizontalAlignment, documentUnderlineKeyboardShortcuts, documentParagraphIndent, DocumentScriptFontFormatting, canInsertDocumentComment, documentHasTableOfContents, documentTextLayoutBatches, normalizeDocumentImageWrapSide, updateDocumentPageOrientation, documentCitationStyleDetails, validateDocumentBookmarkName, setCustomDocumentColumns, updateDocumentPageMarginMillimeters, documentCitationStyle, normalizeDocumentCharacterPositionHalfPoints, documentTransactionsOnlyHydrateChunks, normalizeDocumentKerningThresholdHalfPoints, DEFAULT_DOCUMENT_TABLE_CELL_MARGINS, DOCUMENT_TABLE_STYLE_OPTIONS, updateDocumentColumnWidth, updateDocumentPaperSizePreset, documentPageMetrics, documentStrikeStyle, DocumentHighlight, createDocumentBibliography, DocumentSubscript, selectedDocumentIndexEntry, updateDocumentPageChromeVariant, updateDocumentMirrorMargins, activeDocumentTableStyle, measureDocumentLayoutBlocksIncrementally, documentWordLineHeightFactor, normalizeDocumentLegacyTextEffect, createWorkDocumentExtensions } from "./6282.js";
18
+ import { closeHistory } from "@tiptap/pm/history";
19
+ import { documentTableSizing, transferChangedDocumentTextStatistics, documentPageBordersVisible, documentUnderlineStyle, DOCUMENT_PAGE_BORDER_EDGES, documentTableRowOptions, normalizeDocumentColumns, clampDocumentMargin, materializeLazyDocumentEditorRoot, documentKerningThresholdPoints, selectedDocumentIndexOptions, nextDocumentTabAlignment, DOCUMENT_BOOKMARK_DUPLICATE_MESSAGE, normalizedTabPosition, documentCitationCount, resolveDocumentPageBorders, documentLazyHtmlChunkFragment, DocumentFontFamily, normalizeDocumentHiddenText, documentLegacyTextEffectsConflict, DOCUMENT_CHUNK_VISIBLE_IDS_META, selectedDocumentIndexDraft, MAX_DOCUMENT_IMAGE_RELATIVE_HEIGHT, retainAnchoredDocumentComments, DocumentTableRowIdentity, work_document_page_margins_twipsToMillimeters, DEFAULT_DOCUMENT_TABLE_CELL_FORMAT, resolveWorkDocumentEditorInput, documentCharacterPositionPoints, documentBulletListStyle, collectDocumentTextLayoutParagraphs, readOfficeDocumentCollaboration as readWorkOfficeDocumentCollaboration, normalizeDocumentCharacterScalePercent, DOCUMENT_PARAGRAPH_FORMAT_ATTRIBUTES, documentOrderedListState, normalizeDocumentPageChrome, documentTextStatistics, documentPageMarginsForLayout, invalidateDocumentLazyHtmlProjection, documentTableCellFormat, normalizeDocumentPageGeometry, normalizeDocumentImageLayer, documentImageProperties, isValidDocumentCitationTag, sanitizeDocumentPageChromeHtml, pageTwipsToMillimeters, normalizeDocumentPageBorders, importWorkDocumentFile, DocumentParagraphFormatting, documentModelHasTrustedInitialIntegrityFeatures, normalizeDocumentHtml, documentInitialSectionLayout, activeDocumentSection, documentParagraphDirection, documentParagraphPagination, isDocumentCharacterFormatMark, documentModelUsesWindowing, documentPageChromeLegacyFields, documentChunkMountedIds, collectDocumentCommentAnchors, documentTextCaseKeyboardShortcuts, DocumentTextStyle, canChangeDocumentIndent, documentCharacterSpacingPoints, documentKerningIsEffective, DocumentSuperscript, documentSectionById, activeDocumentBookmark, DocumentEquation, documentLazyHtmlProjectionFingerprint, documentLazyHtmlProjection, normalizeDocumentParagraphIndent, updateDocumentGutterPosition, DocumentStrike, mountWorkLiveDocumentCapture, registerDocumentPageSurfaceGeometry, documentHiddenTextKeyboardShortcut, documentHasIndex, syncDocumentContentFromHtml, documentLegacyTextEffectsCss, editorDocumentCaptionTargets, resolveDocumentPageChrome, documentImageLayoutOptions, MAX_DOCUMENT_NUMBERING_START, resolveDocumentPageSize, editorDocumentBookmarkReferenceTargets, serializeWorkDocumentNode, DOCUMENT_CHUNK_PAGINATION_META, documentModelForContent, createOfficeDocumentCollaborationBinding as createWorkOfficeDocumentCollaborationBinding, documentLegacyTextEffectsFromTextStyleAttributes, normalizeDocumentEmphasisMark, selectedDocumentChunkId, documentCommentViews, documentParagraphSpacing, normalizeDocumentTabStops, patchDocumentLazyHtmlProjection, normalizeDocumentTextCase, documentUnderlineColor, documentBookmarkNameExists, collectDocumentChanges, DocumentImage, selectedDocumentTableOfContentsOptions, resolveDocumentPageMargins, updateDocumentPageMarginMode, serializeDocumentParagraphFormatting, canSetDocumentTableRowRepeatHeader, normalizeDocumentCharacterSpacingTwips, DocumentCharacterFormatting, millimetersToPixels as work_document_layout_millimetersToPixels, DOCUMENT_PARAGRAPH_CHANGE_ATTRIBUTES, serializeDocumentCharacterFormatting, DOCUMENT_CHUNK_HYDRATION_META, documentTabLeaderLabel, createSchemaDerivedWorkDocumentModel, updateDocumentCustomPageMillimeters, DocumentUnderline, documentCommentDraftRange, documentParagraphTabStops, documentTableHorizontalAlignment, documentUnderlineKeyboardShortcuts, documentParagraphIndent, DocumentScriptFontFormatting, canInsertDocumentComment, resolveAllDocumentChanges, documentHasTableOfContents, documentTextLayoutBatches, normalizeDocumentImageWrapSide, updateDocumentPageOrientation, documentCitationStyleDetails, validateDocumentBookmarkName, setCustomDocumentColumns, updateDocumentPageMarginMillimeters, documentCitationStyle, normalizeDocumentCharacterPositionHalfPoints, documentTransactionsOnlyHydrateChunks, normalizeDocumentKerningThresholdHalfPoints, forgetSourceBlob as forgetWorkSourceBlob, DEFAULT_DOCUMENT_TABLE_CELL_MARGINS, DOCUMENT_TABLE_STYLE_OPTIONS, updateDocumentColumnWidth, updateDocumentPaperSizePreset, documentPageMetrics, documentStrikeStyle, DocumentHighlight, createDocumentBibliography, DocumentSubscript, selectedDocumentIndexEntry, updateDocumentPageChromeVariant, updateDocumentMirrorMargins, activeDocumentTableStyle, measureDocumentLayoutBlocksIncrementally, documentWordLineHeightFactor, normalizeDocumentLegacyTextEffect, createWorkDocumentExtensions } from "./4174.js";
19
20
  import { useOfficeTaskPaneEscape, normalizeOfficeFontFamily, OfficeCheckbox, useOfficeDialog, useOfficeTaskPaneModal, CommittedOfficeNumberField, officeFontFamilyLabel, officeFontFamilies, CollectionState, createOfficeKernelClient, OfficeNumberField } from "./0~7048.js";
20
21
  import { moveOfficeMenuFocus, office_text_field_OfficeTextArea, Popover, officeOverlayPortalRoot, button_Button, useOfficePublishPresenceLocation, OfficeTiptapPresenceLayer, icon_button_IconButton, office_text_field_OfficeTextField, useDialogFocusScope, useOfficeEditorWheelZoom, office_text_field_OfficeFileInput } from "./0~6090.js";
21
22
  import { WorkOfficePreviewBar, isWorkspaceContextMenuKeyboardEvent, Tabs, WorkOfficeZoomControls, WorkOfficeStatusBar, Dialog, WorkOfficeRibbon, workspaceContextMenuPosition, OfficeSelect, WorkspaceContextMenu, WorkOfficeRibbonGroup, WorkOfficeRibbonButton } from "./0~4595.js";
@@ -26,6 +27,7 @@ import { OfficeTableInsertPopover, readOfficeFileAsDataUrl } from "./0~9445.js";
26
27
  import { mergeOfficeTiptapExtensions, documentHasRefreshableFields, documentCurrentPage, documentAgentMenuItems, documentPageCount, createWorkDocumentSelectionSnapshot, documentPlainTextAsHtml, createWorkDocumentSelectionAction, documentCustomSelectionMenuItems } from "./0~5050.js";
27
28
  import { WorkEditorLoadingState, documentLayoutFontKey, useOfficeCollaborationLocationNavigator, OFFICE_DOCUMENT_LAYOUT_HEBREW_FONT_ID, useOfficeEditorInitialFocus, OFFICE_DOCUMENT_LAYOUT_FONT_ID, OFFICE_DOCUMENT_LAYOUT_ARABIC_FONT_ID, OFFICE_DOCUMENT_LAYOUT_LATIN_FONT_ID } from "./432.js";
28
29
  import { useOfficeDraft, InlineNotice } from "./0~345.js";
30
+ import { showToast } from "./6489.js";
29
31
  import { documentPageColor, normalizeDocumentPageColor } from "./0~work-document-page-color.js";
30
32
  var document_editor_namespaceObject = {};
31
33
  __webpack_require__.r(document_editor_namespaceObject);
@@ -15143,7 +15145,7 @@ function headingLevelForShortcut(code, key) {
15143
15145
  if ('3' === value) return 3;
15144
15146
  return null;
15145
15147
  }
15146
- function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly = false, suggestionOnly = false, history, layoutOpen, layout, layoutFonts = [], navigationOpen, pageColor, showPageNumbers, showHiddenText, showRulers, spellcheckEnabled, viewMode, zoom, pageChromeEditor, pageChromeEditingPart, pageChromeShowPageNumber, onRequestImage, onPageChromeEditingPartChange, onClosePageChrome, onTogglePageChromePageNumber, onToggleLayout, onLayoutChange, onOpenLayout, onToggleNavigation, onTogglePageNumbers, onToggleHiddenText, onToggleRulers, onPageColorChange, onToggleSpellcheck, onViewModeChange, onZoomChange, onZoomFit, onInsertSection, onInsertNote, onInsertCaption, onInsertCrossReference, onOpenTableOfContents, onOpenIndexEntry, onOpenIndex, citationsOpen, citationSourceCount, onToggleCitations, onInsertField, onRefreshFields, onRefreshIndex, onRefreshTableOfContents, canInsertComment, onInsertComment, commentsOpen, commentCount, onToggleComments, trackChanges, changesOpen, changeCount, findReplaceMode, fileActions, onRibbonTabChange, onToggleTrackChanges, onToggleChanges, onDecideChange, onOpenWordCount, onOpenFindReplace }) {
15148
+ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly = false, suggestionOnly = false, history, layoutOpen, layout, layoutFonts = [], navigationOpen, pageColor, showPageNumbers, showHiddenText, showRulers, spellcheckEnabled, viewMode, zoom, pageChromeEditor, pageChromeEditingPart, pageChromeShowPageNumber, onRequestImage, onPageChromeEditingPartChange, onClosePageChrome, onTogglePageChromePageNumber, onToggleLayout, onLayoutChange, onOpenLayout, onToggleNavigation, onTogglePageNumbers, onToggleHiddenText, onToggleRulers, onPageColorChange, onToggleSpellcheck, onViewModeChange, onZoomChange, onZoomFit, onInsertSection, onInsertNote, onInsertCaption, onInsertCrossReference, onOpenTableOfContents, onOpenIndexEntry, onOpenIndex, citationsOpen, citationSourceCount, onToggleCitations, onInsertField, onRefreshFields, onRefreshIndex, onRefreshTableOfContents, canInsertComment, onInsertComment, commentsOpen, commentCount, onToggleComments, trackChanges, changesOpen, changeCount, findReplaceMode, fileActions, onRibbonTabChange, onToggleTrackChanges, onToggleChanges, onOpenComparison, onDecideChange, onOpenWordCount, onOpenFindReplace }) {
15147
15149
  const [activeTab, setActiveTab] = useState(reviewOnly ? 'review' : 'home');
15148
15150
  const [fontDialogRequest, setFontDialogRequest] = useState(null);
15149
15151
  const [proofingDialogRequest, setProofingDialogRequest] = useState(null);
@@ -15705,6 +15707,30 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly =
15705
15707
  ]
15706
15708
  })
15707
15709
  ]
15710
+ }),
15711
+ !reviewOnly && !suggestionOnly && /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
15712
+ label: "比较",
15713
+ priority: "high",
15714
+ children: [
15715
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15716
+ label: "比较文档",
15717
+ displayLabel: true,
15718
+ title: "将另一个版本转换为可接受或拒绝的修订",
15719
+ onClick: ()=>onOpenComparison('compare'),
15720
+ children: /*#__PURE__*/ jsx(GitCompareArrows, {
15721
+ size: 19
15722
+ })
15723
+ }),
15724
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15725
+ label: "合并文档",
15726
+ displayLabel: true,
15727
+ title: "合入与当前基线一致的带修订审阅副本",
15728
+ onClick: ()=>onOpenComparison('combine'),
15729
+ children: /*#__PURE__*/ jsx(FileStack, {
15730
+ size: 19
15731
+ })
15732
+ })
15733
+ ]
15708
15734
  })
15709
15735
  ]
15710
15736
  }),
@@ -16243,6 +16269,1052 @@ function commentActor(actor) {
16243
16269
  author: CURRENT_COMMENT_AUTHOR
16244
16270
  };
16245
16271
  }
16272
+ const GAP_COST = 1;
16273
+ const ALIGNMENT_EPSILON = 1e-9;
16274
+ function boundedDocumentSequenceDiff(left, right, equal, maximumCells) {
16275
+ if ((left.length + 1) * (right.length + 1) > maximumCells) return null;
16276
+ const columns = right.length + 1;
16277
+ const CellArray = Math.max(left.length, right.length) <= 65535 ? Uint16Array : Uint32Array;
16278
+ const matrix = new CellArray((left.length + 1) * columns);
16279
+ for(let leftIndex = left.length - 1; leftIndex >= 0; leftIndex -= 1)for(let rightIndex = right.length - 1; rightIndex >= 0; rightIndex -= 1){
16280
+ const offset = leftIndex * columns + rightIndex;
16281
+ matrix[offset] = equal(left[leftIndex], right[rightIndex]) ? 1 + matrix[(leftIndex + 1) * columns + rightIndex + 1] : Math.max(matrix[(leftIndex + 1) * columns + rightIndex], matrix[leftIndex * columns + rightIndex + 1]);
16282
+ }
16283
+ const steps = [];
16284
+ let leftIndex = 0;
16285
+ let rightIndex = 0;
16286
+ while(leftIndex < left.length || rightIndex < right.length){
16287
+ if (leftIndex < left.length && rightIndex < right.length && equal(left[leftIndex], right[rightIndex])) {
16288
+ appendSequenceDiff(steps, 'equal', left[leftIndex], right[rightIndex]);
16289
+ leftIndex += 1;
16290
+ rightIndex += 1;
16291
+ continue;
16292
+ }
16293
+ const deleteScore = leftIndex < left.length ? matrix[(leftIndex + 1) * columns + rightIndex] : -1;
16294
+ const insertScore = rightIndex < right.length ? matrix[leftIndex * columns + rightIndex + 1] : -1;
16295
+ if (leftIndex < left.length && deleteScore >= insertScore) {
16296
+ appendSequenceDiff(steps, 'delete', left[leftIndex], null);
16297
+ leftIndex += 1;
16298
+ } else {
16299
+ appendSequenceDiff(steps, 'insert', null, right[rightIndex]);
16300
+ rightIndex += 1;
16301
+ }
16302
+ }
16303
+ return steps;
16304
+ }
16305
+ function boundedDocumentSequenceAlignment(left, right, substitutionCost, maximumCells) {
16306
+ if ((left.length + 1) * (right.length + 1) > maximumCells) return null;
16307
+ const columns = right.length + 1;
16308
+ const matrix = new Float64Array((left.length + 1) * columns);
16309
+ for(let leftIndex = 1; leftIndex <= left.length; leftIndex += 1)matrix[leftIndex * columns] = leftIndex * GAP_COST;
16310
+ for(let rightIndex = 1; rightIndex <= right.length; rightIndex += 1)matrix[rightIndex] = rightIndex * GAP_COST;
16311
+ for(let leftIndex = 1; leftIndex <= left.length; leftIndex += 1)for(let rightIndex = 1; rightIndex <= right.length; rightIndex += 1){
16312
+ const substitution = matrix[(leftIndex - 1) * columns + rightIndex - 1] + substitutionCost(left[leftIndex - 1], right[rightIndex - 1]);
16313
+ const deletion = matrix[(leftIndex - 1) * columns + rightIndex] + GAP_COST;
16314
+ const insertion = matrix[leftIndex * columns + rightIndex - 1] + GAP_COST;
16315
+ matrix[leftIndex * columns + rightIndex] = Math.min(substitution, deletion, insertion);
16316
+ }
16317
+ const reversed = [];
16318
+ let leftIndex = left.length;
16319
+ let rightIndex = right.length;
16320
+ while(leftIndex > 0 || rightIndex > 0){
16321
+ const current = matrix[leftIndex * columns + rightIndex];
16322
+ if (leftIndex > 0 && rightIndex > 0) {
16323
+ const cost = substitutionCost(left[leftIndex - 1], right[rightIndex - 1]);
16324
+ const substitution = matrix[(leftIndex - 1) * columns + rightIndex - 1] + cost;
16325
+ if (approximatelyEqual(current, substitution)) {
16326
+ reversed.push({
16327
+ kind: 0 === cost ? 'equal' : 'substitute',
16328
+ left: left[leftIndex - 1],
16329
+ right: right[rightIndex - 1]
16330
+ });
16331
+ leftIndex -= 1;
16332
+ rightIndex -= 1;
16333
+ continue;
16334
+ }
16335
+ }
16336
+ if (leftIndex > 0 && approximatelyEqual(current, matrix[(leftIndex - 1) * columns + rightIndex] + GAP_COST)) {
16337
+ reversed.push({
16338
+ kind: 'delete',
16339
+ left: left[leftIndex - 1],
16340
+ right: null
16341
+ });
16342
+ leftIndex -= 1;
16343
+ continue;
16344
+ }
16345
+ reversed.push({
16346
+ kind: 'insert',
16347
+ left: null,
16348
+ right: right[rightIndex - 1]
16349
+ });
16350
+ rightIndex -= 1;
16351
+ }
16352
+ return reversed.reverse();
16353
+ }
16354
+ function appendSequenceDiff(changes, kind, left, right) {
16355
+ const previous = changes.at(-1);
16356
+ if (previous?.kind === kind) {
16357
+ if ('equal' === previous.kind) {
16358
+ previous.left.push(left);
16359
+ previous.right.push(right);
16360
+ } else if ('delete' === previous.kind) previous.left.push(left);
16361
+ else previous.right.push(right);
16362
+ return;
16363
+ }
16364
+ if ('equal' === kind) changes.push({
16365
+ kind,
16366
+ left: [
16367
+ left
16368
+ ],
16369
+ right: [
16370
+ right
16371
+ ]
16372
+ });
16373
+ else if ('delete' === kind) changes.push({
16374
+ kind,
16375
+ left: [
16376
+ left
16377
+ ],
16378
+ right: []
16379
+ });
16380
+ else changes.push({
16381
+ kind,
16382
+ left: [],
16383
+ right: [
16384
+ right
16385
+ ]
16386
+ });
16387
+ }
16388
+ function approximatelyEqual(left, right) {
16389
+ return Math.abs(left - right) <= ALIGNMENT_EPSILON;
16390
+ }
16391
+ function stableJson(value) {
16392
+ return JSON.stringify(sortJsonValue(value));
16393
+ }
16394
+ function stableHash(value) {
16395
+ let hash = 0x811c9dc5;
16396
+ for(let index = 0; index < value.length; index += 1){
16397
+ hash ^= value.charCodeAt(index);
16398
+ hash = Math.imul(hash, 0x01000193);
16399
+ }
16400
+ return (hash >>> 0).toString(16).padStart(8, '0');
16401
+ }
16402
+ function wordParagraphId(value) {
16403
+ const number = 0x7fffffff & Number.parseInt(stableHash(value), 16) || 1;
16404
+ return number.toString(16).toUpperCase().padStart(8, '0');
16405
+ }
16406
+ function normalizedDate(value) {
16407
+ const timestamp = Date.parse(value);
16408
+ return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : '1970-01-01T00:00:00.000Z';
16409
+ }
16410
+ function boundedMetadata(value, maximum) {
16411
+ return value.trim().slice(0, maximum);
16412
+ }
16413
+ function sortJsonValue(value) {
16414
+ if (Array.isArray(value)) return value.map(sortJsonValue);
16415
+ if (!value || 'object' != typeof value) return value;
16416
+ return Object.fromEntries(Object.entries(value).sort(([left], [right])=>left.localeCompare(right)).map(([name, child])=>[
16417
+ name,
16418
+ sortJsonValue(child)
16419
+ ]));
16420
+ }
16421
+ const MAX_COMPARISON_BLOCKS = 1024;
16422
+ const MAX_COMPARISON_TEXT = 1000000;
16423
+ const MAX_BLOCK_ALIGNMENT_CELLS = 1100000;
16424
+ const MAX_INLINE_DIFF_CELLS = 1100000;
16425
+ const BLOCK_CHANGE_ATTRIBUTE_NAMES = [
16426
+ 'blockChangeKind',
16427
+ 'blockChangeId',
16428
+ 'blockChangeActorId',
16429
+ 'blockChangeAuthor',
16430
+ 'blockChangeDate'
16431
+ ];
16432
+ const PARAGRAPH_IDENTITY_ATTRIBUTE_NAMES = [
16433
+ 'paragraphId',
16434
+ 'textId'
16435
+ ];
16436
+ const REVIEW_ONLY_MARK_NAMES = new Set([
16437
+ 'documentChange',
16438
+ 'documentComment'
16439
+ ]);
16440
+ function applyDocumentComparison(editor, revisedContent, options) {
16441
+ const revisedDocument = parseComparisonDocument(editor, revisedContent);
16442
+ if (!revisedDocument) return unsupportedResult({
16443
+ code: 'invalid-revised-content',
16444
+ message: 'The selected file cannot be represented by the Writer schema.'
16445
+ });
16446
+ const plan = 'combine' === options.mode ? planDocumentCombine(editor.state.doc, revisedDocument) : planDocumentCompare(editor.state.doc, revisedDocument, options);
16447
+ if ('applied' !== plan.status || !plan.document) return plan;
16448
+ const transaction = editor.state.tr;
16449
+ closeHistory(transaction);
16450
+ transaction.replaceWith(0, editor.state.doc.content.size, plan.document.content).setMeta('documentComparison', {
16451
+ mode: options.mode,
16452
+ sourceName: boundedMetadata(options.sourceName, 512)
16453
+ });
16454
+ if (!transaction.docChanged) return {
16455
+ status: 'unchanged',
16456
+ summary: plan.summary,
16457
+ diagnostics: plan.diagnostics
16458
+ };
16459
+ editor.view.dispatch(transaction);
16460
+ return {
16461
+ status: 'applied',
16462
+ summary: plan.summary,
16463
+ diagnostics: plan.diagnostics
16464
+ };
16465
+ }
16466
+ function planDocumentCompare(current, revised, options) {
16467
+ if (collectDocumentChanges(current).length) return unsupportedResult({
16468
+ code: 'current-revisions-present',
16469
+ message: 'Resolve current revisions before comparing another version.'
16470
+ });
16471
+ if (collectDocumentChanges(revised).length) return unsupportedResult({
16472
+ code: 'revised-revisions-present',
16473
+ message: 'Use Combine for a reviewed copy that already contains revisions.'
16474
+ });
16475
+ const currentSections = documentSections(current);
16476
+ const revisedSections = documentSections(revised);
16477
+ if (!currentSections || !revisedSections || currentSections.length !== revisedSections.length) return unsupportedResult({
16478
+ code: 'section-layout-mismatch',
16479
+ message: 'Comparison requires the same bounded section structure.'
16480
+ });
16481
+ const limits = comparisonLimits(currentSections, revisedSections);
16482
+ if (limits) return unsupportedResult(limits);
16483
+ const diagnostics = [];
16484
+ const factory = comparisonIdentityFactory(current, revised, options);
16485
+ const comparedSections = [];
16486
+ for(let sectionIndex = 0; sectionIndex < currentSections.length; sectionIndex += 1){
16487
+ const currentSection = currentSections[sectionIndex];
16488
+ const revisedSection = revisedSections[sectionIndex];
16489
+ if (sectionLayoutSignature(currentSection) !== sectionLayoutSignature(revisedSection)) {
16490
+ diagnostics.push({
16491
+ code: 'section-layout-mismatch',
16492
+ message: 'Page and section layout differences are not approximated as text revisions.',
16493
+ section: sectionIndex
16494
+ });
16495
+ continue;
16496
+ }
16497
+ const blocks = compareSectionBlocks(currentSection, revisedSection, sectionIndex, factory, diagnostics);
16498
+ if (blocks) comparedSections.push(currentSection.type.create(currentSection.attrs, model_Fragment.fromArray(blocks)));
16499
+ }
16500
+ if (diagnostics.length) return {
16501
+ status: 'unsupported',
16502
+ summary: factory.summary,
16503
+ diagnostics
16504
+ };
16505
+ const document1 = current.type.create(current.attrs, comparedSections);
16506
+ if (document1.eq(current)) return {
16507
+ status: 'unchanged',
16508
+ summary: emptyComparisonSummary(),
16509
+ diagnostics: []
16510
+ };
16511
+ return {
16512
+ status: 'applied',
16513
+ document: document1,
16514
+ summary: factory.summary,
16515
+ diagnostics: []
16516
+ };
16517
+ }
16518
+ function planDocumentCombine(current, reviewed) {
16519
+ if (collectDocumentChanges(current).length) return unsupportedResult({
16520
+ code: 'current-revisions-present',
16521
+ message: 'Resolve current revisions before combining another reviewed copy.'
16522
+ });
16523
+ const reviewedChanges = collectDocumentChanges(reviewed);
16524
+ if (!reviewedChanges.length) return unsupportedResult({
16525
+ code: 'combine-without-revisions',
16526
+ message: 'The selected reviewed copy contains no revisions to combine.'
16527
+ });
16528
+ if (hasStructuralBlockRevisions(reviewed)) return unsupportedResult({
16529
+ code: 'combine-structural-revisions',
16530
+ message: 'This reviewed copy changes the paragraph tree; combine supports inline and formatting revisions.'
16531
+ });
16532
+ const baseline = resolveAllDocumentChanges(reviewed, 'reject');
16533
+ if (!baseline) return unsupportedResult({
16534
+ code: 'combine-resolution-invalid',
16535
+ message: 'The reviewed copy contains a malformed formatting revision.'
16536
+ });
16537
+ if (!simpleDocumentShape(current) || !simpleDocumentShape(baseline)) return unsupportedResult({
16538
+ code: 'changed-complex-structure',
16539
+ message: 'Combine currently accepts text-only paragraph and heading review copies.'
16540
+ });
16541
+ if (comparisonSemanticSignature(current) !== comparisonSemanticSignature(baseline)) return unsupportedResult({
16542
+ code: 'combine-baseline-mismatch',
16543
+ message: 'Rejecting the imported revisions does not reproduce the current document.'
16544
+ });
16545
+ const document1 = transferCombineIdentities(current, reviewed);
16546
+ if (!document1) return unsupportedResult({
16547
+ code: 'combine-structural-revisions',
16548
+ message: 'This reviewed copy changes the paragraph tree; combine supports inline and formatting revisions.'
16549
+ });
16550
+ return {
16551
+ status: 'applied',
16552
+ document: document1,
16553
+ summary: summarizeChanges(reviewedChanges),
16554
+ diagnostics: []
16555
+ };
16556
+ }
16557
+ function compareSectionBlocks(currentSection, revisedSection, sectionIndex, factory, diagnostics) {
16558
+ const currentBlocks = childNodes(currentSection);
16559
+ const revisedBlocks = childNodes(revisedSection);
16560
+ const alignment = boundedDocumentSequenceAlignment(currentBlocks, revisedBlocks, blockSubstitutionCost, MAX_BLOCK_ALIGNMENT_CELLS);
16561
+ if (!alignment) {
16562
+ diagnostics.push({
16563
+ code: 'comparison-limit-exceeded',
16564
+ message: 'The block-alignment matrix exceeds the bounded comparison limit.',
16565
+ section: sectionIndex
16566
+ });
16567
+ return null;
16568
+ }
16569
+ const result = [];
16570
+ for (const [blockIndex, step] of alignment.entries()){
16571
+ if ('equal' === step.kind) {
16572
+ result.push(step.left);
16573
+ continue;
16574
+ }
16575
+ if ('delete' === step.kind) {
16576
+ const deleted = structuralChangeBlock(step.left, 'deletion', factory, `section-${sectionIndex}-delete-${blockIndex}`);
16577
+ if (deleted) result.push(deleted);
16578
+ else diagnostics.push(structuralBlockDiagnostic(step.left, sectionIndex, blockIndex));
16579
+ continue;
16580
+ }
16581
+ if ('insert' === step.kind) {
16582
+ const inserted = structuralChangeBlock(step.right, 'insertion', factory, `section-${sectionIndex}-insert-${blockIndex}`);
16583
+ if (inserted) result.push(inserted);
16584
+ else diagnostics.push(structuralBlockDiagnostic(step.right, sectionIndex, blockIndex));
16585
+ continue;
16586
+ }
16587
+ const compared = comparePairedBlocks(step.left, step.right, factory);
16588
+ if (compared) {
16589
+ result.push(compared);
16590
+ continue;
16591
+ }
16592
+ const deleted = structuralChangeBlock(step.left, 'deletion', factory, `section-${sectionIndex}-replace-delete-${blockIndex}`);
16593
+ const inserted = structuralChangeBlock(step.right, 'insertion', factory, `section-${sectionIndex}-replace-insert-${blockIndex}`);
16594
+ if (deleted && inserted) result.push(deleted, inserted);
16595
+ else diagnostics.push(structuralBlockDiagnostic(step.left, sectionIndex, blockIndex));
16596
+ }
16597
+ return result;
16598
+ }
16599
+ function comparePairedBlocks(current, revised, factory) {
16600
+ if (!isSimpleTextBlock(current) || !isSimpleTextBlock(revised)) return null;
16601
+ if (current.type !== revised.type || blockStructuralSignature(current) !== blockStructuralSignature(revised)) return null;
16602
+ const inline = compareInlineContent(current, revised, factory);
16603
+ if (!inline) return null;
16604
+ const before = serializeDocumentParagraphFormatting(current.attrs);
16605
+ const after = serializeDocumentParagraphFormatting(revised.attrs);
16606
+ let attributes = {
16607
+ ...current.attrs,
16608
+ ...paragraphFormattingAttributes(revised.attrs)
16609
+ };
16610
+ if (before !== after) {
16611
+ const identity = factory.create('paragraph-formatting', before);
16612
+ attributes = {
16613
+ ...attributes,
16614
+ paragraphChangeKind: 'paragraph-formatting',
16615
+ paragraphChangeId: identity.id,
16616
+ paragraphChangeActorId: identity.actorId ?? '',
16617
+ paragraphChangeAuthor: identity.author,
16618
+ paragraphChangeDate: identity.date,
16619
+ paragraphChangeBefore: before
16620
+ };
16621
+ }
16622
+ return current.type.create(attributes, inline);
16623
+ }
16624
+ function compareInlineContent(current, revised, factory) {
16625
+ const currentUnits = inlineUnits(current);
16626
+ const revisedUnits = inlineUnits(revised);
16627
+ if (!currentUnits || !revisedUnits) return null;
16628
+ const diff = boundedDocumentSequenceDiff(currentUnits, revisedUnits, (left, right)=>left.text === right.text, MAX_INLINE_DIFF_CELLS);
16629
+ const changes = diff ?? [
16630
+ {
16631
+ kind: 'delete',
16632
+ left: currentUnits,
16633
+ right: []
16634
+ },
16635
+ {
16636
+ kind: 'insert',
16637
+ left: [],
16638
+ right: revisedUnits
16639
+ }
16640
+ ];
16641
+ const nodes = [];
16642
+ for (const change of changes){
16643
+ if ('delete' === change.kind) {
16644
+ appendRevisionUnits(nodes, change.left, 'deletion', factory.create('deletion'), current.type.schema);
16645
+ continue;
16646
+ }
16647
+ if ('insert' === change.kind) {
16648
+ appendRevisionUnits(nodes, change.right, 'insertion', factory.create('insertion'), current.type.schema);
16649
+ continue;
16650
+ }
16651
+ let formattingIdentity = null;
16652
+ let formattingSignature = '';
16653
+ for(let index = 0; index < change.left.length; index += 1){
16654
+ const left = change.left[index];
16655
+ const right = change.right[index];
16656
+ if (marksEqual(left.marks, right.marks)) {
16657
+ nodes.push(current.type.schema.text(left.text, [
16658
+ ...left.marks
16659
+ ]));
16660
+ formattingIdentity = null;
16661
+ formattingSignature = '';
16662
+ continue;
16663
+ }
16664
+ if (nonCharacterMarksEqual(left.marks, right.marks)) {
16665
+ const before = serializeDocumentCharacterFormatting(left.marks);
16666
+ const after = serializeDocumentCharacterFormatting(right.marks);
16667
+ const signature = `${before}\u0000${after}`;
16668
+ if (!formattingIdentity || formattingSignature !== signature) {
16669
+ formattingIdentity = factory.create('formatting', before);
16670
+ formattingSignature = signature;
16671
+ }
16672
+ nodes.push(current.type.schema.text(right.text, [
16673
+ ...withoutReviewMarks(right.marks),
16674
+ comparisonMark(current, 'formatting', formattingIdentity, before)
16675
+ ]));
16676
+ continue;
16677
+ }
16678
+ const deletion = factory.create('deletion');
16679
+ const insertion = factory.create('insertion');
16680
+ appendRevisionUnits(nodes, [
16681
+ left
16682
+ ], 'deletion', deletion, current.type.schema);
16683
+ appendRevisionUnits(nodes, [
16684
+ right
16685
+ ], 'insertion', insertion, current.type.schema);
16686
+ formattingIdentity = null;
16687
+ formattingSignature = '';
16688
+ }
16689
+ }
16690
+ return model_Fragment.fromArray(nodes);
16691
+ }
16692
+ function structuralChangeBlock(block, kind, factory, identityChannel) {
16693
+ if (!isSimpleTextBlock(block) || !block.textContent) return null;
16694
+ const identity = factory.create(kind);
16695
+ const paragraphIdentity = 'insertion' === kind ? factory.paragraphIdentity(identityChannel) : {
16696
+ paragraphId: block.attrs.paragraphId,
16697
+ textId: block.attrs.textId
16698
+ };
16699
+ const nodes = [];
16700
+ for (const unit of inlineUnits(block) ?? [])appendRevisionUnits(nodes, [
16701
+ unit
16702
+ ], kind, identity, block.type.schema);
16703
+ return block.type.create({
16704
+ ...block.attrs,
16705
+ ...paragraphIdentity,
16706
+ blockChangeKind: kind,
16707
+ blockChangeId: identity.id,
16708
+ blockChangeActorId: identity.actorId ?? '',
16709
+ blockChangeAuthor: identity.author,
16710
+ blockChangeDate: identity.date
16711
+ }, model_Fragment.fromArray(nodes));
16712
+ }
16713
+ function appendRevisionUnits(target, units, kind, identity, schema) {
16714
+ for (const unit of units)target.push(schema.text(unit.text, [
16715
+ ...withoutReviewMarks(unit.marks),
16716
+ schema.marks.documentChange.create({
16717
+ kind,
16718
+ id: identity.id,
16719
+ actorId: identity.actorId ?? '',
16720
+ author: identity.author,
16721
+ date: identity.date,
16722
+ before: ''
16723
+ })
16724
+ ]));
16725
+ }
16726
+ function comparisonMark(node, kind, identity, before) {
16727
+ return node.type.schema.marks.documentChange.create({
16728
+ kind,
16729
+ id: identity.id,
16730
+ actorId: identity.actorId ?? '',
16731
+ author: identity.author,
16732
+ date: identity.date,
16733
+ before
16734
+ });
16735
+ }
16736
+ function parseComparisonDocument(editor, content) {
16737
+ try {
16738
+ const parsed = createNodeFromContent(content, editor.schema, {
16739
+ errorOnInvalidContent: true,
16740
+ slice: false
16741
+ });
16742
+ return parsed instanceof model_Node ? parsed : null;
16743
+ } catch {
16744
+ return null;
16745
+ }
16746
+ }
16747
+ function documentSections(document1) {
16748
+ const sections = childNodes(document1);
16749
+ return sections.every((node)=>'documentSection' === node.type.name) ? sections : null;
16750
+ }
16751
+ function comparisonLimits(current, revised) {
16752
+ const currentBlocks = current.reduce((sum, section)=>sum + section.childCount, 0);
16753
+ const revisedBlocks = revised.reduce((sum, section)=>sum + section.childCount, 0);
16754
+ const textLength = current.reduce((sum, section)=>sum + section.textContent.length, 0) + revised.reduce((sum, section)=>sum + section.textContent.length, 0);
16755
+ return currentBlocks > MAX_COMPARISON_BLOCKS || revisedBlocks > MAX_COMPARISON_BLOCKS || textLength > MAX_COMPARISON_TEXT ? {
16756
+ code: 'comparison-limit-exceeded',
16757
+ message: `Comparison accepts at most ${MAX_COMPARISON_BLOCKS} blocks per version and ${MAX_COMPARISON_TEXT} combined text characters.`
16758
+ } : null;
16759
+ }
16760
+ function blockSubstitutionCost(current, revised) {
16761
+ if (comparisonSemanticSignature(current) === comparisonSemanticSignature(revised)) return 0;
16762
+ if (!isSimpleTextBlock(current) || !isSimpleTextBlock(revised)) return 2.1;
16763
+ if (current.type !== revised.type) return 2.1;
16764
+ if (!current.textContent && !revised.textContent) return 0.8;
16765
+ return documentTextSimilarity(current.textContent, revised.textContent) >= 0.18 ? 0.8 : 2.1;
16766
+ }
16767
+ function documentTextSimilarity(left, right) {
16768
+ if (left === right) return 1;
16769
+ if (!left || !right) return 0;
16770
+ const maximum = Math.max(left.length, right.length);
16771
+ let prefix = 0;
16772
+ while(prefix < Math.min(left.length, right.length) && left[prefix] === right[prefix])prefix += 1;
16773
+ let suffix = 0;
16774
+ while(suffix < Math.min(left.length, right.length) - prefix && left[left.length - suffix - 1] === right[right.length - suffix - 1])suffix += 1;
16775
+ const boundaryScore = (prefix + suffix) / maximum;
16776
+ const leftWords = comparisonWords(left);
16777
+ const rightWords = comparisonWords(right);
16778
+ let shared = 0;
16779
+ for (const word of leftWords)if (rightWords.has(word)) shared += 1;
16780
+ const union = new Set([
16781
+ ...leftWords,
16782
+ ...rightWords
16783
+ ]).size;
16784
+ return Math.max(boundaryScore, union ? shared / union : 0);
16785
+ }
16786
+ function comparisonWords(value) {
16787
+ return new Set((value.toLocaleLowerCase().match(/[\p{L}\p{N}_]+/gu) ?? []).slice(0, 256));
16788
+ }
16789
+ function isSimpleTextBlock(node) {
16790
+ if ('paragraph' !== node.type.name && 'heading' !== node.type.name) return false;
16791
+ let simple = true;
16792
+ node.forEach((child)=>{
16793
+ if (!child.isText || child.marks.some((mark)=>REVIEW_ONLY_MARK_NAMES.has(mark.type.name))) simple = false;
16794
+ });
16795
+ return simple;
16796
+ }
16797
+ function simpleDocumentShape(document1) {
16798
+ const sections = documentSections(document1);
16799
+ return Boolean(sections?.every((section)=>childNodes(section).every((block)=>isSimpleTextBlock(block))));
16800
+ }
16801
+ function hasStructuralBlockRevisions(document1) {
16802
+ let found = false;
16803
+ document1.descendants((node)=>{
16804
+ if (('paragraph' === node.type.name || 'heading' === node.type.name) && ('insertion' === node.attrs.blockChangeKind || 'deletion' === node.attrs.blockChangeKind)) found = true;
16805
+ return !found;
16806
+ });
16807
+ return found;
16808
+ }
16809
+ function inlineUnits(node) {
16810
+ if (!isSimpleTextBlock(node)) return null;
16811
+ const units = [];
16812
+ node.forEach((child)=>{
16813
+ const text = child.text ?? '';
16814
+ for (const part of text.match(/\s+|[\p{L}\p{N}_]+|[^\s\p{L}\p{N}_]/gu) ?? [])units.push({
16815
+ text: part,
16816
+ marks: child.marks
16817
+ });
16818
+ });
16819
+ return units;
16820
+ }
16821
+ function marksEqual(left, right) {
16822
+ const normalizedLeft = withoutReviewMarks(left);
16823
+ const normalizedRight = withoutReviewMarks(right);
16824
+ return normalizedLeft.length === normalizedRight.length && normalizedLeft.every((mark, index)=>mark.eq(normalizedRight[index]));
16825
+ }
16826
+ function nonCharacterMarksEqual(left, right) {
16827
+ const normalizedLeft = withoutReviewMarks(left).filter((mark)=>!isDocumentCharacterFormatMark(mark.type.name));
16828
+ const normalizedRight = withoutReviewMarks(right).filter((mark)=>!isDocumentCharacterFormatMark(mark.type.name));
16829
+ return normalizedLeft.length === normalizedRight.length && normalizedLeft.every((mark, index)=>mark.eq(normalizedRight[index]));
16830
+ }
16831
+ function withoutReviewMarks(marks) {
16832
+ return marks.filter((mark)=>!REVIEW_ONLY_MARK_NAMES.has(mark.type.name));
16833
+ }
16834
+ function blockStructuralSignature(node) {
16835
+ const ignored = new Set([
16836
+ ...DOCUMENT_PARAGRAPH_FORMAT_ATTRIBUTES,
16837
+ ...DOCUMENT_PARAGRAPH_CHANGE_ATTRIBUTES,
16838
+ ...BLOCK_CHANGE_ATTRIBUTE_NAMES,
16839
+ ...PARAGRAPH_IDENTITY_ATTRIBUTE_NAMES
16840
+ ]);
16841
+ return stableJson({
16842
+ attrs: filteredAttributes(node.attrs, ignored),
16843
+ type: node.type.name
16844
+ });
16845
+ }
16846
+ function paragraphFormattingAttributes(attributes) {
16847
+ return Object.fromEntries(DOCUMENT_PARAGRAPH_FORMAT_ATTRIBUTES.map((name)=>[
16848
+ name,
16849
+ attributes[name]
16850
+ ]));
16851
+ }
16852
+ function sectionLayoutSignature(section) {
16853
+ return stableJson(filteredAttributes(section.attrs, new Set([
16854
+ 'id'
16855
+ ])));
16856
+ }
16857
+ function comparisonSemanticSignature(node) {
16858
+ return stableJson(comparisonSemanticValue(node));
16859
+ }
16860
+ function comparisonSemanticValue(node) {
16861
+ const ignored = new Set([
16862
+ ...DOCUMENT_PARAGRAPH_CHANGE_ATTRIBUTES,
16863
+ ...BLOCK_CHANGE_ATTRIBUTE_NAMES,
16864
+ ...PARAGRAPH_IDENTITY_ATTRIBUTE_NAMES,
16865
+ ...'documentSection' === node.type.name ? [
16866
+ 'id'
16867
+ ] : []
16868
+ ]);
16869
+ return {
16870
+ type: node.type.name,
16871
+ ...node.isText ? {
16872
+ text: node.text
16873
+ } : {},
16874
+ ...Object.keys(node.attrs).length ? {
16875
+ attrs: filteredAttributes(node.attrs, ignored)
16876
+ } : {},
16877
+ ...node.marks.length ? {
16878
+ marks: withoutReviewMarks(node.marks).map((mark)=>mark.toJSON())
16879
+ } : {},
16880
+ ...node.childCount ? {
16881
+ content: childNodes(node).map(comparisonSemanticValue)
16882
+ } : {}
16883
+ };
16884
+ }
16885
+ function transferCombineIdentities(current, reviewed) {
16886
+ if (current.type !== reviewed.type) return null;
16887
+ if ('paragraph' === reviewed.type.name || 'heading' === reviewed.type.name) return reviewed.type.create({
16888
+ ...reviewed.attrs,
16889
+ paragraphId: current.attrs.paragraphId,
16890
+ textId: current.attrs.textId
16891
+ }, reviewed.content, reviewed.marks);
16892
+ if (current.childCount !== reviewed.childCount) return null;
16893
+ const content = [];
16894
+ for(let index = 0; index < current.childCount; index += 1){
16895
+ const currentChild = current.child(index);
16896
+ const reviewedChild = reviewed.child(index);
16897
+ const transferred = transferCombineIdentities(currentChild, reviewedChild);
16898
+ if (!transferred) return null;
16899
+ content.push(transferred);
16900
+ }
16901
+ const attributes = {
16902
+ ...reviewed.attrs
16903
+ };
16904
+ if ('documentSection' === reviewed.type.name) attributes.id = current.attrs.id;
16905
+ return reviewed.isText ? reviewed : reviewed.type.create(attributes, model_Fragment.fromArray(content), reviewed.marks);
16906
+ }
16907
+ function comparisonIdentityFactory(current, revised, options) {
16908
+ const seed = stableHash(`${options.mode}\u0000${comparisonSemanticSignature(current)}\u0000${comparisonSemanticSignature(revised)}`);
16909
+ const author = boundedMetadata(options.author, 256) || 'A3S Work user';
16910
+ const date = normalizedDate(options.date);
16911
+ const summary = emptyComparisonSummary();
16912
+ let sequence = 0;
16913
+ return {
16914
+ summary,
16915
+ create (kind) {
16916
+ sequence += 1;
16917
+ if ('insertion' === kind) summary.insertions += 1;
16918
+ else if ('deletion' === kind) summary.deletions += 1;
16919
+ else if ('formatting' === kind) summary.formatting += 1;
16920
+ else summary.paragraphFormatting += 1;
16921
+ return {
16922
+ id: `compare-${seed}-${kind}-${sequence.toString(36)}`,
16923
+ author,
16924
+ date
16925
+ };
16926
+ },
16927
+ paragraphIdentity (channel) {
16928
+ return {
16929
+ paragraphId: wordParagraphId(`${seed}:${channel}:paragraph`),
16930
+ textId: wordParagraphId(`${seed}:${channel}:text`)
16931
+ };
16932
+ }
16933
+ };
16934
+ }
16935
+ function summarizeChanges(changes) {
16936
+ const summary = emptyComparisonSummary();
16937
+ for (const change of changes)if ('insertion' === change.kind) summary.insertions += 1;
16938
+ else if ('deletion' === change.kind) summary.deletions += 1;
16939
+ else if ('formatting' === change.kind) summary.formatting += 1;
16940
+ else summary.paragraphFormatting += 1;
16941
+ return summary;
16942
+ }
16943
+ function structuralBlockDiagnostic(block, section, blockIndex) {
16944
+ return 'paragraph' === block.type.name || 'heading' === block.type.name ? {
16945
+ code: 'empty-structural-change',
16946
+ message: 'An empty inserted or deleted paragraph cannot carry a reviewable text revision.',
16947
+ section,
16948
+ block: blockIndex
16949
+ } : {
16950
+ code: 'changed-complex-structure',
16951
+ message: `Changed ${block.type.name} content is not approximated as plain text.`,
16952
+ section,
16953
+ block: blockIndex
16954
+ };
16955
+ }
16956
+ function unsupportedResult(diagnostic) {
16957
+ return {
16958
+ status: 'unsupported',
16959
+ summary: emptyComparisonSummary(),
16960
+ diagnostics: [
16961
+ diagnostic
16962
+ ]
16963
+ };
16964
+ }
16965
+ function emptyComparisonSummary() {
16966
+ return {
16967
+ deletions: 0,
16968
+ formatting: 0,
16969
+ insertions: 0,
16970
+ paragraphFormatting: 0
16971
+ };
16972
+ }
16973
+ function childNodes(node) {
16974
+ const children = [];
16975
+ node.forEach((child)=>{
16976
+ children.push(child);
16977
+ });
16978
+ return children;
16979
+ }
16980
+ function filteredAttributes(attributes, ignored) {
16981
+ return Object.fromEntries(Object.entries(attributes).filter(([name])=>!ignored.has(name)));
16982
+ }
16983
+ function DocumentCompareDialog({ initialMode, restoreFocusTarget, onApplied, onClose, onSubmit }) {
16984
+ const [mode, setMode] = useState(initialMode);
16985
+ const [file, setFile] = useState(null);
16986
+ const [author, setAuthor] = useState('审阅者');
16987
+ const [authorTouched, setAuthorTouched] = useState(false);
16988
+ const [busy, setBusy] = useState(false);
16989
+ const [result, setResult] = useState(null);
16990
+ const fileInputRef = useRef(null);
16991
+ const formId = useId();
16992
+ const authorId = useId();
16993
+ const submit = async (event)=>{
16994
+ event.preventDefault();
16995
+ if (!file || busy || 'compare' === mode && !author.trim()) return;
16996
+ setBusy(true);
16997
+ setResult(null);
16998
+ try {
16999
+ const next = await onSubmit({
17000
+ author: author.trim(),
17001
+ file,
17002
+ mode
17003
+ });
17004
+ setResult(next);
17005
+ if ('applied' === next.status) onApplied(next);
17006
+ } catch {
17007
+ setResult({
17008
+ status: 'unsupported',
17009
+ summary: {
17010
+ deletions: 0,
17011
+ formatting: 0,
17012
+ insertions: 0,
17013
+ paragraphFormatting: 0
17014
+ },
17015
+ diagnostics: [
17016
+ {
17017
+ code: 'invalid-revised-content',
17018
+ message: 'The selected file could not be imported.'
17019
+ }
17020
+ ]
17021
+ });
17022
+ } finally{
17023
+ setBusy(false);
17024
+ }
17025
+ };
17026
+ const selectFile = (next)=>{
17027
+ setFile(next);
17028
+ setResult(null);
17029
+ if (!authorTouched) setAuthor(fileStem(next.name) || '审阅者');
17030
+ };
17031
+ return /*#__PURE__*/ jsx(Dialog, {
17032
+ title: "比较与合并文档",
17033
+ description: "把另一版本转换为可逐项接受或拒绝的 Writer 修订。",
17034
+ className: "work-document-compare-dialog",
17035
+ closeDisabled: busy,
17036
+ restoreFocusTarget: restoreFocusTarget,
17037
+ onClose: onClose,
17038
+ footer: /*#__PURE__*/ jsxs(Fragment, {
17039
+ children: [
17040
+ /*#__PURE__*/ jsx(button_Button, {
17041
+ tone: "quiet",
17042
+ disabled: busy,
17043
+ onClick: onClose,
17044
+ children: "取消"
17045
+ }),
17046
+ /*#__PURE__*/ jsx(button_Button, {
17047
+ tone: "primary",
17048
+ type: "submit",
17049
+ form: formId,
17050
+ disabled: !file || busy || 'compare' === mode && !author.trim(),
17051
+ children: busy ? '正在处理…' : 'compare' === mode ? '生成比较结果' : '合并修订'
17052
+ })
17053
+ ]
17054
+ }),
17055
+ children: /*#__PURE__*/ jsxs("form", {
17056
+ id: formId,
17057
+ onSubmit: submit,
17058
+ children: [
17059
+ /*#__PURE__*/ jsxs("fieldset", {
17060
+ className: "work-document-compare-mode",
17061
+ children: [
17062
+ /*#__PURE__*/ jsx("legend", {
17063
+ children: "处理方式"
17064
+ }),
17065
+ /*#__PURE__*/ jsxs("label", {
17066
+ children: [
17067
+ /*#__PURE__*/ jsx("input", {
17068
+ type: "radio",
17069
+ name: "document-comparison-mode",
17070
+ value: "compare",
17071
+ checked: 'compare' === mode,
17072
+ onChange: ()=>{
17073
+ setMode('compare');
17074
+ setResult(null);
17075
+ }
17076
+ }),
17077
+ /*#__PURE__*/ jsxs("span", {
17078
+ children: [
17079
+ /*#__PURE__*/ jsx(GitCompareArrows, {
17080
+ size: 18,
17081
+ "aria-hidden": "true"
17082
+ }),
17083
+ /*#__PURE__*/ jsx("strong", {
17084
+ children: "比较"
17085
+ }),
17086
+ /*#__PURE__*/ jsx("small", {
17087
+ children: "当前文档作为原稿,导入文件作为修订稿。"
17088
+ })
17089
+ ]
17090
+ })
17091
+ ]
17092
+ }),
17093
+ /*#__PURE__*/ jsxs("label", {
17094
+ children: [
17095
+ /*#__PURE__*/ jsx("input", {
17096
+ type: "radio",
17097
+ name: "document-comparison-mode",
17098
+ value: "combine",
17099
+ checked: 'combine' === mode,
17100
+ onChange: ()=>{
17101
+ setMode('combine');
17102
+ setResult(null);
17103
+ }
17104
+ }),
17105
+ /*#__PURE__*/ jsxs("span", {
17106
+ children: [
17107
+ /*#__PURE__*/ jsx(FileCheck2, {
17108
+ size: 18,
17109
+ "aria-hidden": "true"
17110
+ }),
17111
+ /*#__PURE__*/ jsx("strong", {
17112
+ children: "合并"
17113
+ }),
17114
+ /*#__PURE__*/ jsx("small", {
17115
+ children: "导入带修订的审阅副本,并核验其原始基线。"
17116
+ })
17117
+ ]
17118
+ })
17119
+ ]
17120
+ })
17121
+ ]
17122
+ }),
17123
+ /*#__PURE__*/ jsx(office_text_field_OfficeFileInput, {
17124
+ ref: fileInputRef,
17125
+ accept: ".docx,.html,.htm,.txt,application/vnd.openxmlformats-officedocument.wordprocessingml.document,text/html,text/plain",
17126
+ "aria-label": 'compare' === mode ? '选择修订版本文件' : '选择审阅副本文件',
17127
+ disabled: busy,
17128
+ onFileSelect: selectFile
17129
+ }),
17130
+ /*#__PURE__*/ jsxs("button", {
17131
+ type: "button",
17132
+ className: "work-document-compare-file",
17133
+ "data-autofocus": "true",
17134
+ disabled: busy,
17135
+ onClick: ()=>fileInputRef.current?.click(),
17136
+ children: [
17137
+ /*#__PURE__*/ jsx(FileUp, {
17138
+ size: 20,
17139
+ "aria-hidden": "true"
17140
+ }),
17141
+ /*#__PURE__*/ jsxs("span", {
17142
+ children: [
17143
+ /*#__PURE__*/ jsx("strong", {
17144
+ children: file?.name ?? ('compare' === mode ? '选择修订版本' : '选择带修订的审阅副本')
17145
+ }),
17146
+ /*#__PURE__*/ jsx("small", {
17147
+ children: file ? `${fileTypeLabel(file.name)} · ${formatFileSize(file.size)}` : '支持 DOCX、HTML 和 TXT;当前文档不会在预检失败时改变。'
17148
+ })
17149
+ ]
17150
+ })
17151
+ ]
17152
+ }),
17153
+ 'compare' === mode && /*#__PURE__*/ jsxs("label", {
17154
+ className: "work-document-compare-author",
17155
+ htmlFor: authorId,
17156
+ children: [
17157
+ /*#__PURE__*/ jsx("span", {
17158
+ children: "修订者名称"
17159
+ }),
17160
+ /*#__PURE__*/ jsx(office_text_field_OfficeTextField, {
17161
+ id: authorId,
17162
+ "aria-label": "比较结果修订者名称",
17163
+ value: author,
17164
+ disabled: busy,
17165
+ maxLength: 256,
17166
+ onChange: (event)=>{
17167
+ setAuthorTouched(true);
17168
+ setAuthor(event.currentTarget.value);
17169
+ }
17170
+ }),
17171
+ /*#__PURE__*/ jsx("small", {
17172
+ children: "该名称会显示在生成的插入、删除与格式修订中。"
17173
+ })
17174
+ ]
17175
+ }),
17176
+ /*#__PURE__*/ jsx(ComparisonBoundary, {
17177
+ mode: mode
17178
+ }),
17179
+ result?.status === 'unchanged' && /*#__PURE__*/ jsx("p", {
17180
+ className: "work-document-compare-status",
17181
+ role: "status",
17182
+ children: "两份文档在支持的比较范围内完全一致,没有生成修订。"
17183
+ }),
17184
+ result?.status === 'unsupported' && /*#__PURE__*/ jsxs("div", {
17185
+ className: "work-document-compare-errors",
17186
+ role: "alert",
17187
+ children: [
17188
+ /*#__PURE__*/ jsx("strong", {
17189
+ children: "无法安全处理这份文档"
17190
+ }),
17191
+ /*#__PURE__*/ jsx("ul", {
17192
+ children: result.diagnostics.map((diagnostic, index)=>/*#__PURE__*/ jsx("li", {
17193
+ children: comparisonDiagnosticText(diagnostic)
17194
+ }, `${diagnostic.code}-${index}`))
17195
+ })
17196
+ ]
17197
+ })
17198
+ ]
17199
+ })
17200
+ });
17201
+ }
17202
+ function ComparisonBoundary({ mode }) {
17203
+ return /*#__PURE__*/ jsxs("div", {
17204
+ className: "work-document-compare-boundary",
17205
+ children: [
17206
+ /*#__PURE__*/ jsx("strong", {
17207
+ children: 'compare' === mode ? '确定性比较边界' : '安全合并边界'
17208
+ }),
17209
+ /*#__PURE__*/ jsx("p", {
17210
+ children: 'compare' === mode ? '支持同一分节布局中的段落、标题、文字和格式差异;复杂对象或节布局变化会明确停止。' : '审阅副本必须包含修订,且拒绝全部修订后与当前文档一致;现有修订须先处理。'
17211
+ })
17212
+ ]
17213
+ });
17214
+ }
17215
+ function comparisonDiagnosticText(diagnostic) {
17216
+ const location = void 0 === diagnostic.section ? '' : `(第 ${diagnostic.section + 1} 节${void 0 === diagnostic.block ? '' : `,差异块 ${diagnostic.block + 1}`})`;
17217
+ const messages = {
17218
+ 'changed-complex-structure': '检测到表格、图片、列表或其他复杂结构变化,未降级为纯文字。',
17219
+ 'combine-baseline-mismatch': '审阅副本的原始基线与当前文档不一致。',
17220
+ 'combine-resolution-invalid': '审阅副本包含损坏或无法拒绝的格式修订。',
17221
+ 'combine-structural-revisions': '审阅副本改变了段落树;当前合并路径只接受行内和格式修订。',
17222
+ 'combine-without-revisions': '审阅副本中没有可合并的修订。',
17223
+ 'comparison-limit-exceeded': '文档超过本地有界比较限制。',
17224
+ 'current-revisions-present': '当前文档仍有未处理修订,请先接受或拒绝。',
17225
+ 'empty-structural-change': '空段落的结构变化无法承载可审阅文字修订。',
17226
+ 'invalid-revised-content': '文件无法转换为当前 Writer 文档模型。',
17227
+ 'revised-revisions-present': '修订稿已经包含修订,请改用“合并”。',
17228
+ 'section-layout-mismatch': '分节数量或页面布局不一致。',
17229
+ 'unsupported-inline-review-state': '导入内容包含无法安全迁移的批注或审阅状态。'
17230
+ };
17231
+ return `${messages[diagnostic.code]}${location}`;
17232
+ }
17233
+ function fileStem(name) {
17234
+ return name.replace(/\.[^.]+$/, '').trim();
17235
+ }
17236
+ function fileTypeLabel(name) {
17237
+ return name.split('.').at(-1)?.toLocaleUpperCase() || '文件';
17238
+ }
17239
+ function formatFileSize(bytes) {
17240
+ if (bytes < 1024) return `${bytes} B`;
17241
+ if (bytes < 1048576) return `${Math.max(0.1, bytes / 1024).toFixed(1)} KB`;
17242
+ return `${Math.max(0.1, bytes / 1048576).toFixed(1)} MB`;
17243
+ }
17244
+ function useDocumentComparison({ editor, onApplied }) {
17245
+ const [dialog, setDialog] = useState(null);
17246
+ const editorRef = useRef(editor);
17247
+ editorRef.current = editor;
17248
+ const open = (mode)=>{
17249
+ const current = editorRef.current;
17250
+ if (!current || current.isDestroyed) return;
17251
+ const active = document.activeElement;
17252
+ setDialog({
17253
+ mode,
17254
+ restoreFocusTarget: active instanceof HTMLElement && active.isConnected ? active : current.view.dom
17255
+ });
17256
+ };
17257
+ const close = ()=>setDialog(null);
17258
+ const submit = async (request)=>{
17259
+ const current = editorRef.current;
17260
+ if (!current || current.isDestroyed) return invalidComparisonResult();
17261
+ const extension = comparisonFileExtension(request.file.name);
17262
+ if (!extension) return invalidComparisonResult();
17263
+ const artifact = await importWorkDocumentFile(request.file, extension);
17264
+ try {
17265
+ if ('document' !== artifact.content.type) return invalidComparisonResult();
17266
+ const content = artifact.content;
17267
+ const model = documentModelForContent(content);
17268
+ const fallbackHtml = model ? content.html : normalizeDocumentHtml(content);
17269
+ const input = resolveWorkDocumentEditorInput(content, fallbackHtml, model);
17270
+ return applyDocumentComparison(current, input.source, {
17271
+ author: request.author,
17272
+ date: new Date().toISOString(),
17273
+ mode: request.mode,
17274
+ sourceName: request.file.name
17275
+ });
17276
+ } finally{
17277
+ forgetWorkSourceBlob(artifact.id);
17278
+ }
17279
+ };
17280
+ const applied = (result)=>{
17281
+ const count = result.summary.insertions + result.summary.deletions + result.summary.formatting + result.summary.paragraphFormatting;
17282
+ close();
17283
+ onApplied();
17284
+ showToast(`已生成 ${count} 项可审阅修订`, 'success');
17285
+ };
17286
+ return {
17287
+ dialog: dialog ? /*#__PURE__*/ jsx(DocumentCompareDialog, {
17288
+ initialMode: dialog.mode,
17289
+ restoreFocusTarget: ()=>dialog.restoreFocusTarget?.isConnected ? dialog.restoreFocusTarget : editorRef.current?.view.dom ?? null,
17290
+ onApplied: applied,
17291
+ onClose: close,
17292
+ onSubmit: submit
17293
+ }) : null,
17294
+ open
17295
+ };
17296
+ }
17297
+ function comparisonFileExtension(name) {
17298
+ const extension = name.split('.').at(-1)?.toLocaleLowerCase() ?? '';
17299
+ return 'docx' === extension || 'html' === extension || 'htm' === extension || 'txt' === extension ? extension : null;
17300
+ }
17301
+ function invalidComparisonResult() {
17302
+ return {
17303
+ status: 'unsupported',
17304
+ summary: {
17305
+ deletions: 0,
17306
+ formatting: 0,
17307
+ insertions: 0,
17308
+ paragraphFormatting: 0
17309
+ },
17310
+ diagnostics: [
17311
+ {
17312
+ code: 'invalid-revised-content',
17313
+ message: 'The selected file cannot be imported as a Writer document.'
17314
+ }
17315
+ ]
17316
+ };
17317
+ }
16246
17318
  function DocumentTableOfContentsDialog({ editing, options, restoreFocusTarget, onCancel, onOptionsChange, onSubmit }) {
16247
17319
  const updateOptions = (update)=>onOptionsChange({
16248
17320
  ...options,
@@ -18886,6 +19958,13 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
18886
19958
  requestEditorViewChange,
18887
19959
  taskPane
18888
19960
  ]);
19961
+ const documentComparison = useDocumentComparison({
19962
+ editor,
19963
+ onApplied: ()=>{
19964
+ if (editor && !editor.isDestroyed) taskPaneInvokerRef.current = editor.view.dom;
19965
+ setTaskPane('changes');
19966
+ }
19967
+ });
18889
19968
  const startCommentDraft = useCallback(async ()=>{
18890
19969
  rememberTaskPaneInvoker();
18891
19970
  if (!await requestEditorViewChange(null, false)) return;
@@ -19308,6 +20387,7 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
19308
20387
  restoreDocumentBodyFocus();
19309
20388
  },
19310
20389
  onToggleChanges: ()=>void toggleTaskPane('changes'),
20390
+ onOpenComparison: documentComparison.open,
19311
20391
  onDecideChange: (change, decision)=>decideDocumentChanges([
19312
20392
  change
19313
20393
  ], decision),
@@ -19635,6 +20715,7 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
19635
20715
  onClose: ()=>setSelectionMenu(null)
19636
20716
  }),
19637
20717
  canEditDocument && documentInsert.dialog,
20718
+ canEditDocument && documentComparison.dialog,
19638
20719
  !preview && taskPaneDialog.dialog,
19639
20720
  !preview && statisticsOpen && /*#__PURE__*/ jsx(DocumentStatisticsDialog, {
19640
20721
  pageCount: pageCount,