@a3s-lab/office 0.30.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.
Files changed (45) hide show
  1. package/README.md +25 -5
  2. package/dist/{0~7231.js → 0~4808.js} +190 -3
  3. package/dist/0~4980.js +1 -1
  4. package/dist/0~5050.js +10 -1
  5. package/dist/{0~7360.js → 0~7240.js} +245 -16
  6. package/dist/0~7614.js +1 -1
  7. package/dist/0~document-editor.js +1756 -178
  8. package/dist/0~spreadsheet-editor.js +2 -1
  9. package/dist/0~work-docx-export.js +215 -27
  10. package/dist/0~work-docx-import.js +27 -33
  11. package/dist/0~work-docx-large-document-import.js +1 -1
  12. package/dist/0~work-office-diagnostics.js +28 -7
  13. package/dist/0~work-pptx-import.js +1 -1
  14. package/dist/{1544.js → 3266.js} +17 -1862
  15. package/dist/4121.js +425 -11
  16. package/dist/4166.js +3848 -0
  17. package/dist/{6282.js → 4174.js} +812 -10
  18. package/dist/9333.js +1849 -0
  19. package/dist/core.js +5259 -4
  20. package/dist/index.js +3 -3
  21. package/dist/internal/features/work/editors/document-command-catalog.d.ts +27 -0
  22. package/dist/internal/features/work/editors/document-compare-dialog.d.ts +14 -0
  23. package/dist/internal/features/work/editors/document-dom-selection.d.ts +7 -0
  24. package/dist/internal/features/work/editors/document-editor-support.d.ts +1 -0
  25. package/dist/internal/features/work/editors/document-index-dialog.d.ts +10 -0
  26. package/dist/internal/features/work/editors/document-index-entry-dialog.d.ts +10 -0
  27. package/dist/internal/features/work/editors/document-references-ribbon.d.ts +19 -0
  28. package/dist/internal/features/work/editors/document-toolbar.d.ts +6 -1
  29. package/dist/internal/features/work/editors/use-document-comparison.d.ts +9 -0
  30. package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +3 -0
  31. package/dist/internal/features/work/work-document-changes.d.ts +5 -0
  32. package/dist/internal/features/work/work-document-compare-diff.d.ts +38 -0
  33. package/dist/internal/features/work/work-document-compare-stability.d.ts +5 -0
  34. package/dist/internal/features/work/work-document-compare.d.ts +27 -0
  35. package/dist/internal/features/work/work-document-index-fields.d.ts +19 -0
  36. package/dist/internal/features/work/work-document-index-nodes.d.ts +32 -0
  37. package/dist/internal/features/work/work-document-index.d.ts +63 -0
  38. package/dist/internal/features/work/work-docx-import.d.ts +3 -1
  39. package/dist/internal/features/work/work-docx-index-export.d.ts +14 -0
  40. package/dist/internal/features/work/work-docx-index-import.d.ts +23 -0
  41. package/dist/office-kernel.wasm +0 -0
  42. package/dist/styles.css +490 -0
  43. package/docs/latest/en/browser-editor-architecture.md +37 -0
  44. package/package.json +9 -3
  45. package/dist/4104.js +0 -9336
@@ -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, 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,17 +15,19 @@ 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, DOCUMENT_BOOKMARK_DUPLICATE_MESSAGE, nextDocumentTabAlignment, documentCitationCount, normalizedTabPosition, resolveDocumentPageBorders, documentLazyHtmlChunkFragment, DocumentFontFamily, normalizeDocumentHiddenText, documentLegacyTextEffectsConflict, DOCUMENT_CHUNK_VISIBLE_IDS_META, 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, 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, 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";
22
- import { documentFontNameFromCssFamily, normalizeDocumentRunBorder, parseDocumentScriptFonts, parseDocumentRunShading, DOCUMENT_RUN_BORDER_STYLES, serializeDocumentRunBorder, documentScriptFontDirectFamily, serializeDocumentRunShading, patchDocumentScriptFonts, serializeDocumentScriptFonts, documentRunBorderIsVisible, cssDocumentFontFamily, isDocumentParagraphArtBorderStyle, visibleWorkDocumentOutlineItems, DEFAULT_DOCUMENT_TABLE_OF_CONTENTS_OPTIONS, createOfficeId as createWorkId, normalizeDocumentLanguageTag, currentWorkDocumentOutlineItem, normalizeDocumentNoProof, parseDocumentRunBorder, DocumentParagraphIdentity, parseDocumentProofingLanguages, collectWorkDocumentOutline, documentScriptFontSlotFromHint, documentBorderPresentation, documentScriptFontFamilyForRendering, normalizeDocumentRunShading, normalizeDocumentScriptFontSlot, documentScriptFontSegments, patchDocumentProofingLanguages, serializeDocumentProofingLanguages, documentRunShadingDomAttributes } from "./4121.js";
23
+ import { documentFontNameFromCssFamily, normalizeDocumentRunBorder, parseDocumentScriptFonts, parseDocumentRunShading, DOCUMENT_RUN_BORDER_STYLES, serializeDocumentRunBorder, documentScriptFontDirectFamily, serializeDocumentRunShading, patchDocumentScriptFonts, serializeDocumentScriptFonts, documentRunBorderIsVisible, cssDocumentFontFamily, isDocumentParagraphArtBorderStyle, visibleWorkDocumentOutlineItems, DEFAULT_DOCUMENT_TABLE_OF_CONTENTS_OPTIONS, createOfficeId as createWorkId, DEFAULT_DOCUMENT_INDEX_OPTIONS, normalizeDocumentLanguageTag, currentWorkDocumentOutlineItem, normalizeDocumentNoProof, parseDocumentRunBorder, DocumentParagraphIdentity, parseDocumentProofingLanguages, collectWorkDocumentOutline, documentScriptFontSlotFromHint, documentBorderPresentation, documentScriptFontFamilyForRendering, normalizeDocumentRunShading, normalizeDocumentScriptFontSlot, documentScriptFontSegments, patchDocumentProofingLanguages, serializeDocumentProofingLanguages, documentRunShadingDomAttributes } from "./4121.js";
23
24
  import { normalizeDocumentHref, DOCUMENT_LINK_VALIDATION_MESSAGE } from "./0~work-document-links.js";
24
25
  import { OfficeColorPicker, isOfficeShortcutBlocked } from "./0~3635.js";
25
26
  import { OfficeTableInsertPopover, readOfficeFileAsDataUrl } from "./0~9445.js";
26
- import { mergeOfficeTiptapExtensions, documentCurrentPage, documentPageCount, documentAgentMenuItems, createWorkDocumentSelectionSnapshot, documentPlainTextAsHtml, documentCustomSelectionMenuItems, createWorkDocumentSelectionAction } from "./0~5050.js";
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);
@@ -7483,6 +7485,33 @@ const documentCommandCatalog = {
7483
7485
  group: 'tableOfContents'
7484
7486
  }
7485
7487
  },
7488
+ markIndexEntry: {
7489
+ id: 'references.markIndexEntry',
7490
+ label: '标记索引项',
7491
+ location: {
7492
+ area: 'ribbon',
7493
+ tab: 'references',
7494
+ group: 'index'
7495
+ }
7496
+ },
7497
+ index: {
7498
+ id: 'references.index',
7499
+ label: '插入或自定义索引',
7500
+ location: {
7501
+ area: 'ribbon',
7502
+ tab: 'references',
7503
+ group: 'index'
7504
+ }
7505
+ },
7506
+ refreshIndex: {
7507
+ id: 'references.refreshIndex',
7508
+ label: '更新索引',
7509
+ location: {
7510
+ area: 'ribbon',
7511
+ tab: 'references',
7512
+ group: 'index'
7513
+ }
7514
+ },
7486
7515
  refreshFields: {
7487
7516
  id: 'references.refreshFields',
7488
7517
  label: '更新域',
@@ -8069,6 +8098,28 @@ function DocumentStatusBar({ bibliographyCount, citationCount, currentPage, page
8069
8098
  ]
8070
8099
  });
8071
8100
  }
8101
+ function synchronizeDocumentEditorSelectionFromDom(editor) {
8102
+ if (editor.isDestroyed) return false;
8103
+ const { view } = editor;
8104
+ const domSelection = view.dom.ownerDocument.getSelection();
8105
+ if (!domSelection || domSelection.isCollapsed || 1 !== domSelection.rangeCount) return false;
8106
+ const range = domSelection.getRangeAt(0);
8107
+ if (!view.dom.contains(range.startContainer) || !view.dom.contains(range.endContainer)) return false;
8108
+ let from;
8109
+ let to;
8110
+ try {
8111
+ from = view.posAtDOM(range.startContainer, range.startOffset, 1);
8112
+ to = view.posAtDOM(range.endContainer, range.endOffset, -1);
8113
+ } catch {
8114
+ return false;
8115
+ }
8116
+ const maximum = editor.state.doc.content.size;
8117
+ if (from < 0 || to <= from || to > maximum) return false;
8118
+ const nextSelection = TextSelection.between(editor.state.doc.resolve(from), editor.state.doc.resolve(to), 1);
8119
+ if (nextSelection.empty || nextSelection.eq(editor.state.selection)) return false;
8120
+ view.dispatch(editor.state.tr.setSelection(nextSelection).setMeta('addToHistory', false));
8121
+ return true;
8122
+ }
8072
8123
  const proofingStateOptions = [
8073
8124
  {
8074
8125
  value: 'mixed',
@@ -13137,6 +13188,158 @@ function PictureButton({ label, active = false, disabled = false, onClick, child
13137
13188
  children: children
13138
13189
  });
13139
13190
  }
13191
+ function DocumentReferencesRibbon({ editor, citationsOpen, citationSourceCount, onInsertNote, onInsertCaption, onInsertCrossReference, onOpenTableOfContents, onRefreshTableOfContents, onOpenIndexEntry, onOpenIndex, onRefreshIndex, onToggleCitations, onRefreshFields }) {
13192
+ const hasTableOfContents = documentHasTableOfContents(editor);
13193
+ const hasIndex = documentHasIndex(editor);
13194
+ const canMarkIndexEntry = Boolean(selectedDocumentIndexDraft(editor));
13195
+ const hasRefreshableFields = documentHasRefreshableFields(editor);
13196
+ const refreshFieldsCommand = getDocumentCommandDefinition('refreshFields');
13197
+ return /*#__PURE__*/ jsxs(Fragment, {
13198
+ children: [
13199
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
13200
+ label: "目录",
13201
+ priority: "high",
13202
+ children: [
13203
+ /*#__PURE__*/ jsx(ReferencesButton, {
13204
+ label: "插入或自定义目录",
13205
+ onClick: onOpenTableOfContents,
13206
+ children: /*#__PURE__*/ jsx(ListTree, {
13207
+ size: 19
13208
+ })
13209
+ }),
13210
+ /*#__PURE__*/ jsx(ReferencesButton, {
13211
+ label: "更新目录",
13212
+ disabled: !hasTableOfContents,
13213
+ title: hasTableOfContents ? '根据当前标题和页码更新目录' : '文档中没有可更新的目录',
13214
+ onClick: onRefreshTableOfContents,
13215
+ children: /*#__PURE__*/ jsx(RefreshCw, {
13216
+ size: 19
13217
+ })
13218
+ })
13219
+ ]
13220
+ }),
13221
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
13222
+ label: "脚注",
13223
+ priority: "high",
13224
+ children: [
13225
+ /*#__PURE__*/ jsx(ReferencesButton, {
13226
+ label: "插入脚注",
13227
+ onClick: ()=>onInsertNote('footnote'),
13228
+ children: /*#__PURE__*/ jsx("span", {
13229
+ className: "work-ribbon-glyph",
13230
+ children: "\xb9"
13231
+ })
13232
+ }),
13233
+ /*#__PURE__*/ jsx(ReferencesButton, {
13234
+ label: "插入尾注",
13235
+ onClick: ()=>onInsertNote('endnote'),
13236
+ children: /*#__PURE__*/ jsx("span", {
13237
+ className: "work-ribbon-glyph",
13238
+ children: "ⅰ"
13239
+ })
13240
+ })
13241
+ ]
13242
+ }),
13243
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
13244
+ label: "题注",
13245
+ children: [
13246
+ /*#__PURE__*/ jsx(ReferencesButton, {
13247
+ label: "插入图片题注",
13248
+ onClick: ()=>onInsertCaption('figure'),
13249
+ children: /*#__PURE__*/ jsx(Image, {
13250
+ size: 19
13251
+ })
13252
+ }),
13253
+ /*#__PURE__*/ jsx(ReferencesButton, {
13254
+ label: "插入表格题注",
13255
+ onClick: ()=>onInsertCaption('table'),
13256
+ children: /*#__PURE__*/ jsx(Table2, {
13257
+ size: 19
13258
+ })
13259
+ }),
13260
+ /*#__PURE__*/ jsx(ReferencesButton, {
13261
+ label: "插入交叉引用",
13262
+ onClick: onInsertCrossReference,
13263
+ children: /*#__PURE__*/ jsx(Link2, {
13264
+ size: 19
13265
+ })
13266
+ })
13267
+ ]
13268
+ }),
13269
+ /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
13270
+ label: "引文和书目",
13271
+ priority: "high",
13272
+ children: /*#__PURE__*/ jsx(ReferencesButton, {
13273
+ label: `文献库${citationSourceCount ? `(${citationSourceCount})` : ''}`,
13274
+ active: citationsOpen,
13275
+ onClick: onToggleCitations,
13276
+ children: /*#__PURE__*/ jsx(BookOpen, {
13277
+ size: 19
13278
+ })
13279
+ })
13280
+ }),
13281
+ /*#__PURE__*/ jsxs(WorkOfficeRibbonGroup, {
13282
+ label: "索引",
13283
+ priority: "high",
13284
+ children: [
13285
+ /*#__PURE__*/ jsx(ReferencesButton, {
13286
+ label: "标记索引项",
13287
+ disabled: !canMarkIndexEntry,
13288
+ title: canMarkIndexEntry ? '把当前选中文字标记为索引项' : '请先选择正文文字或已有索引项',
13289
+ onClick: onOpenIndexEntry,
13290
+ children: /*#__PURE__*/ jsx(Tags, {
13291
+ size: 19
13292
+ })
13293
+ }),
13294
+ /*#__PURE__*/ jsx(ReferencesButton, {
13295
+ label: "插入或自定义索引",
13296
+ onClick: onOpenIndex,
13297
+ children: /*#__PURE__*/ jsx(ListOrdered, {
13298
+ size: 19
13299
+ })
13300
+ }),
13301
+ /*#__PURE__*/ jsx(ReferencesButton, {
13302
+ label: "更新索引",
13303
+ disabled: !hasIndex,
13304
+ title: hasIndex ? '根据当前索引项和页码更新索引' : '文档中没有可更新的索引',
13305
+ onClick: onRefreshIndex,
13306
+ children: /*#__PURE__*/ jsx(RefreshCw, {
13307
+ size: 19
13308
+ })
13309
+ })
13310
+ ]
13311
+ }),
13312
+ /*#__PURE__*/ jsx(WorkOfficeRibbonGroup, {
13313
+ label: "更新",
13314
+ priority: "low",
13315
+ children: /*#__PURE__*/ jsx(ReferencesButton, {
13316
+ label: "更新页码和日期",
13317
+ shortcut: refreshFieldsCommand.shortcut?.label,
13318
+ ariaKeyShortcuts: refreshFieldsCommand.shortcut?.aria,
13319
+ disabled: !hasRefreshableFields,
13320
+ title: hasRefreshableFields ? `更新页码和日期(${refreshFieldsCommand.shortcut?.label})` : '文档中没有可更新的页码或日期',
13321
+ onClick: onRefreshFields,
13322
+ children: /*#__PURE__*/ jsx(RefreshCw, {
13323
+ size: 19
13324
+ })
13325
+ })
13326
+ })
13327
+ ]
13328
+ });
13329
+ }
13330
+ function ReferencesButton({ label, title, shortcut, ariaKeyShortcuts, active = false, disabled = false, onClick, children }) {
13331
+ return /*#__PURE__*/ jsx(WorkOfficeRibbonButton, {
13332
+ label: label,
13333
+ visibleLabel: label.replace(/(\d+)$/, ''),
13334
+ title: title ?? (shortcut ? `${label}(${shortcut})` : label),
13335
+ "aria-keyshortcuts": ariaKeyShortcuts,
13336
+ active: active,
13337
+ displayLabel: true,
13338
+ disabled: disabled,
13339
+ onClick: onClick,
13340
+ children: children
13341
+ });
13342
+ }
13140
13343
  function selectedDocumentChangeIndex(changes, selection) {
13141
13344
  const index = changes.findIndex((change)=>selection.from === selection.to ? change.from <= selection.from && selection.from <= change.to : change.from < selection.to && change.to > selection.from);
13142
13345
  return index >= 0 ? index : null;
@@ -14942,7 +15145,7 @@ function headingLevelForShortcut(code, key) {
14942
15145
  if ('3' === value) return 3;
14943
15146
  return null;
14944
15147
  }
14945
- 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, citationsOpen, citationSourceCount, onToggleCitations, onInsertField, onRefreshFields, 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 }) {
14946
15149
  const [activeTab, setActiveTab] = useState(reviewOnly ? 'review' : 'home');
14947
15150
  const [fontDialogRequest, setFontDialogRequest] = useState(null);
14948
15151
  const [proofingDialogRequest, setProofingDialogRequest] = useState(null);
@@ -14952,19 +15155,18 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly =
14952
15155
  const tableSelected = editor.isActive('table');
14953
15156
  const activeBookmark = activeDocumentBookmark(editor);
14954
15157
  const hasRefreshableFields = documentHasRefreshableFields(editor);
14955
- const hasTableOfContents = documentHasTableOfContents(editor);
14956
15158
  const documentChanges = collectDocumentChanges(editor.state.doc);
14957
15159
  const previousChangeIndex = adjacentDocumentChangeIndex(documentChanges, editor.state.selection, -1);
14958
15160
  const nextChangeIndex = adjacentDocumentChangeIndex(documentChanges, editor.state.selection, 1);
14959
15161
  const actionableChangeIndex = actionableDocumentChangeIndex(documentChanges, editor.state.selection);
14960
15162
  const undoCommand = getDocumentCommandDefinition('undo');
14961
15163
  const redoCommand = getDocumentCommandDefinition('redo');
14962
- const refreshFieldsCommand = getDocumentCommandDefinition('refreshFields');
14963
15164
  const spellingCommand = getDocumentCommandDefinition('spelling');
14964
15165
  const insertCommentCommand = getDocumentCommandDefinition('insertComment');
14965
15166
  const trackChangesCommand = getDocumentCommandDefinition('trackChanges');
14966
15167
  const openFontDialog = useCallback((target)=>{
14967
15168
  if (target.isDestroyed) return;
15169
+ synchronizeDocumentEditorSelectionFromDom(target);
14968
15170
  const { from, to } = target.state.selection;
14969
15171
  setFontDialogRequest({
14970
15172
  editor: target,
@@ -14977,6 +15179,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly =
14977
15179
  }, []);
14978
15180
  const openProofingDialog = useCallback((target)=>{
14979
15181
  if (target.isDestroyed) return;
15182
+ synchronizeDocumentEditorSelectionFromDom(target);
14980
15183
  const { from, to } = target.state.selection;
14981
15184
  setProofingDialogRequest({
14982
15185
  editor: target,
@@ -15360,115 +15563,20 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly =
15360
15563
  onPageColorChange: onPageColorChange,
15361
15564
  onInsertSection: onInsertSection
15362
15565
  }),
15363
- references: reviewOnly ? null : /*#__PURE__*/ jsxs(Fragment, {
15364
- children: [
15365
- /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
15366
- label: "目录",
15367
- priority: "high",
15368
- children: [
15369
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15370
- label: "插入或自定义目录",
15371
- displayLabel: true,
15372
- onClick: onOpenTableOfContents,
15373
- children: /*#__PURE__*/ jsx(ListTree, {
15374
- size: 19
15375
- })
15376
- }),
15377
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15378
- label: "更新目录",
15379
- displayLabel: true,
15380
- disabled: !hasTableOfContents,
15381
- title: hasTableOfContents ? '根据当前标题和页码更新目录' : '文档中没有可更新的目录',
15382
- onClick: onRefreshTableOfContents,
15383
- children: /*#__PURE__*/ jsx(RefreshCw, {
15384
- size: 19
15385
- })
15386
- })
15387
- ]
15388
- }),
15389
- /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
15390
- label: "脚注",
15391
- priority: "high",
15392
- children: [
15393
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15394
- label: "插入脚注",
15395
- displayLabel: true,
15396
- onClick: ()=>onInsertNote('footnote'),
15397
- children: /*#__PURE__*/ jsx("span", {
15398
- className: "work-ribbon-glyph",
15399
- children: "\xb9"
15400
- })
15401
- }),
15402
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15403
- label: "插入尾注",
15404
- displayLabel: true,
15405
- onClick: ()=>onInsertNote('endnote'),
15406
- children: /*#__PURE__*/ jsx("span", {
15407
- className: "work-ribbon-glyph",
15408
- children: "ⅰ"
15409
- })
15410
- })
15411
- ]
15412
- }),
15413
- /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
15414
- label: "题注",
15415
- children: [
15416
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15417
- label: "插入图片题注",
15418
- displayLabel: true,
15419
- onClick: ()=>onInsertCaption('figure'),
15420
- children: /*#__PURE__*/ jsx(Image, {
15421
- size: 19
15422
- })
15423
- }),
15424
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15425
- label: "插入表格题注",
15426
- displayLabel: true,
15427
- onClick: ()=>onInsertCaption('table'),
15428
- children: /*#__PURE__*/ jsx(Table2, {
15429
- size: 19
15430
- })
15431
- }),
15432
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15433
- label: "插入交叉引用",
15434
- displayLabel: true,
15435
- onClick: onInsertCrossReference,
15436
- children: /*#__PURE__*/ jsx(Link2, {
15437
- size: 19
15438
- })
15439
- })
15440
- ]
15441
- }),
15442
- /*#__PURE__*/ jsx(document_toolbar_RibbonGroup, {
15443
- label: "引文和书目",
15444
- priority: "high",
15445
- children: /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15446
- label: `文献库${citationSourceCount ? `(${citationSourceCount})` : ''}`,
15447
- displayLabel: true,
15448
- active: citationsOpen,
15449
- onClick: onToggleCitations,
15450
- children: /*#__PURE__*/ jsx(BookOpen, {
15451
- size: 19
15452
- })
15453
- })
15454
- }),
15455
- /*#__PURE__*/ jsx(document_toolbar_RibbonGroup, {
15456
- label: "更新",
15457
- priority: "low",
15458
- children: /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15459
- label: "更新页码和日期",
15460
- displayLabel: true,
15461
- shortcut: refreshFieldsCommand.shortcut?.label,
15462
- ariaKeyShortcuts: refreshFieldsCommand.shortcut?.aria,
15463
- disabled: !hasRefreshableFields,
15464
- title: hasRefreshableFields ? `更新页码和日期(${refreshFieldsCommand.shortcut?.label})` : '文档中没有可更新的页码或日期',
15465
- onClick: onRefreshFields,
15466
- children: /*#__PURE__*/ jsx(RefreshCw, {
15467
- size: 19
15468
- })
15469
- })
15470
- })
15471
- ]
15566
+ references: reviewOnly ? null : /*#__PURE__*/ jsx(DocumentReferencesRibbon, {
15567
+ editor: editor,
15568
+ citationsOpen: citationsOpen,
15569
+ citationSourceCount: citationSourceCount,
15570
+ onInsertNote: onInsertNote,
15571
+ onInsertCaption: onInsertCaption,
15572
+ onInsertCrossReference: onInsertCrossReference,
15573
+ onOpenTableOfContents: onOpenTableOfContents,
15574
+ onRefreshTableOfContents: onRefreshTableOfContents,
15575
+ onOpenIndexEntry: onOpenIndexEntry,
15576
+ onOpenIndex: onOpenIndex,
15577
+ onRefreshIndex: onRefreshIndex,
15578
+ onToggleCitations: onToggleCitations,
15579
+ onRefreshFields: onRefreshFields
15472
15580
  }),
15473
15581
  review: /*#__PURE__*/ jsxs(Fragment, {
15474
15582
  children: [
@@ -15599,6 +15707,30 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly =
15599
15707
  ]
15600
15708
  })
15601
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
+ ]
15602
15734
  })
15603
15735
  ]
15604
15736
  }),
@@ -15838,15 +15970,6 @@ function DocumentFieldSelect({ onInsertField }) {
15838
15970
  }
15839
15971
  });
15840
15972
  }
15841
- function documentHasRefreshableFields(editor) {
15842
- let found = false;
15843
- editor.state.doc.descendants((node)=>{
15844
- if ('documentField' !== node.type.name) return !found;
15845
- found = true;
15846
- return false;
15847
- });
15848
- return found;
15849
- }
15850
15973
  function DocumentVerticalRuler({ layout, onLayoutChange }) {
15851
15974
  const rulerRef = useRef(null);
15852
15975
  const pageHeight = documentRulerPageHeight(layout);
@@ -16146,59 +16269,1105 @@ function commentActor(actor) {
16146
16269
  author: CURRENT_COMMENT_AUTHOR
16147
16270
  };
16148
16271
  }
16149
- function DocumentTableOfContentsDialog({ editing, options, restoreFocusTarget, onCancel, onOptionsChange, onSubmit }) {
16150
- const updateOptions = (update)=>onOptionsChange({
16151
- ...options,
16152
- ...update
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]
16153
16349
  });
16154
- return /*#__PURE__*/ jsxs(Dialog, {
16155
- title: editing ? '自定义目录' : '插入目录',
16156
- description: "从文档标题生成可更新目录,并保留原生 DOCX 目录域。",
16157
- className: "work-document-table-of-contents-dialog",
16158
- restoreFocusTarget: restoreFocusTarget,
16159
- onClose: onCancel,
16160
- footer: /*#__PURE__*/ jsxs(Fragment, {
16161
- children: [
16162
- /*#__PURE__*/ jsx(button_Button, {
16163
- tone: "quiet",
16164
- onClick: onCancel,
16165
- children: "取消"
16166
- }),
16167
- /*#__PURE__*/ jsx(button_Button, {
16168
- tone: "primary",
16169
- onClick: onSubmit,
16170
- children: editing ? '应用' : '插入目录'
16171
- })
16172
- ]
16173
- }),
16174
- children: [
16175
- /*#__PURE__*/ jsxs("div", {
16176
- className: "work-document-table-of-contents-dialog-grid",
16177
- children: [
16178
- /*#__PURE__*/ jsxs("div", {
16179
- className: "work-document-table-of-contents-dialog-field",
16180
- children: [
16181
- /*#__PURE__*/ jsx("span", {
16182
- children: "起始标题级别"
16183
- }),
16184
- /*#__PURE__*/ jsx(OfficeSelect, {
16185
- initialFocus: true,
16186
- ariaLabel: "起始标题级别",
16187
- value: String(options.minLevel),
16188
- options: tableOfContentsLevelOptions,
16189
- onValueChange: (value)=>{
16190
- const minLevel = Number(value);
16191
- updateOptions({
16192
- minLevel,
16193
- maxLevel: Math.max(minLevel, options.maxLevel)
16194
- });
16195
- }
16196
- })
16197
- ]
16198
- }),
16199
- /*#__PURE__*/ jsxs("div", {
16200
- className: "work-document-table-of-contents-dialog-field",
16201
- children: [
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
+ }
17318
+ function DocumentTableOfContentsDialog({ editing, options, restoreFocusTarget, onCancel, onOptionsChange, onSubmit }) {
17319
+ const updateOptions = (update)=>onOptionsChange({
17320
+ ...options,
17321
+ ...update
17322
+ });
17323
+ return /*#__PURE__*/ jsxs(Dialog, {
17324
+ title: editing ? '自定义目录' : '插入目录',
17325
+ description: "从文档标题生成可更新目录,并保留原生 DOCX 目录域。",
17326
+ className: "work-document-table-of-contents-dialog",
17327
+ restoreFocusTarget: restoreFocusTarget,
17328
+ onClose: onCancel,
17329
+ footer: /*#__PURE__*/ jsxs(Fragment, {
17330
+ children: [
17331
+ /*#__PURE__*/ jsx(button_Button, {
17332
+ tone: "quiet",
17333
+ onClick: onCancel,
17334
+ children: "取消"
17335
+ }),
17336
+ /*#__PURE__*/ jsx(button_Button, {
17337
+ tone: "primary",
17338
+ onClick: onSubmit,
17339
+ children: editing ? '应用' : '插入目录'
17340
+ })
17341
+ ]
17342
+ }),
17343
+ children: [
17344
+ /*#__PURE__*/ jsxs("div", {
17345
+ className: "work-document-table-of-contents-dialog-grid",
17346
+ children: [
17347
+ /*#__PURE__*/ jsxs("div", {
17348
+ className: "work-document-table-of-contents-dialog-field",
17349
+ children: [
17350
+ /*#__PURE__*/ jsx("span", {
17351
+ children: "起始标题级别"
17352
+ }),
17353
+ /*#__PURE__*/ jsx(OfficeSelect, {
17354
+ initialFocus: true,
17355
+ ariaLabel: "起始标题级别",
17356
+ value: String(options.minLevel),
17357
+ options: tableOfContentsLevelOptions,
17358
+ onValueChange: (value)=>{
17359
+ const minLevel = Number(value);
17360
+ updateOptions({
17361
+ minLevel,
17362
+ maxLevel: Math.max(minLevel, options.maxLevel)
17363
+ });
17364
+ }
17365
+ })
17366
+ ]
17367
+ }),
17368
+ /*#__PURE__*/ jsxs("div", {
17369
+ className: "work-document-table-of-contents-dialog-field",
17370
+ children: [
16202
17371
  /*#__PURE__*/ jsx("span", {
16203
17372
  children: "结束标题级别"
16204
17373
  }),
@@ -16322,6 +17491,320 @@ const tableOfContentsLeaderOptions = [
16322
17491
  label: '无'
16323
17492
  }
16324
17493
  ];
17494
+ function DocumentIndexDialog({ editing, options, restoreFocusTarget, onCancel, onOptionsChange, onSubmit }) {
17495
+ const update = (patch)=>onOptionsChange({
17496
+ ...options,
17497
+ ...patch
17498
+ });
17499
+ return /*#__PURE__*/ jsxs(Dialog, {
17500
+ title: editing ? '自定义索引' : '插入索引',
17501
+ description: "从已标记的索引项生成可更新索引,并保留原生 DOCX INDEX 域。",
17502
+ className: "work-document-index-dialog",
17503
+ restoreFocusTarget: restoreFocusTarget,
17504
+ onClose: onCancel,
17505
+ footer: /*#__PURE__*/ jsxs(Fragment, {
17506
+ children: [
17507
+ /*#__PURE__*/ jsx(button_Button, {
17508
+ tone: "quiet",
17509
+ onClick: onCancel,
17510
+ children: "取消"
17511
+ }),
17512
+ /*#__PURE__*/ jsx(button_Button, {
17513
+ tone: "primary",
17514
+ onClick: onSubmit,
17515
+ children: editing ? '应用' : '插入索引'
17516
+ })
17517
+ ]
17518
+ }),
17519
+ children: [
17520
+ /*#__PURE__*/ jsxs("div", {
17521
+ className: "work-document-index-dialog-grid",
17522
+ children: [
17523
+ /*#__PURE__*/ jsxs("div", {
17524
+ className: "work-document-index-dialog-field",
17525
+ children: [
17526
+ /*#__PURE__*/ jsx("span", {
17527
+ children: "栏数"
17528
+ }),
17529
+ /*#__PURE__*/ jsx(OfficeSelect, {
17530
+ initialFocus: true,
17531
+ ariaLabel: "索引栏数",
17532
+ value: String(options.columns),
17533
+ options: indexColumnOptions,
17534
+ onValueChange: (columns)=>update({
17535
+ columns: Number(columns)
17536
+ })
17537
+ })
17538
+ ]
17539
+ }),
17540
+ /*#__PURE__*/ jsxs("div", {
17541
+ className: "work-document-index-dialog-field",
17542
+ children: [
17543
+ /*#__PURE__*/ jsx("span", {
17544
+ children: "次索引项布局"
17545
+ }),
17546
+ /*#__PURE__*/ jsx(OfficeSelect, {
17547
+ ariaLabel: "索引布局",
17548
+ value: options.format,
17549
+ options: indexFormatOptions,
17550
+ onValueChange: (format)=>update({
17551
+ format
17552
+ })
17553
+ })
17554
+ ]
17555
+ })
17556
+ ]
17557
+ }),
17558
+ /*#__PURE__*/ jsxs("div", {
17559
+ className: "work-document-index-dialog-options",
17560
+ children: [
17561
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
17562
+ ariaLabel: "页码右对齐",
17563
+ checked: options.rightAlignPageNumbers,
17564
+ onCheckedChange: (rightAlignPageNumbers)=>update({
17565
+ rightAlignPageNumbers
17566
+ }),
17567
+ children: "页码右对齐"
17568
+ }),
17569
+ /*#__PURE__*/ jsxs("div", {
17570
+ className: "work-document-index-dialog-field",
17571
+ children: [
17572
+ /*#__PURE__*/ jsx("span", {
17573
+ children: "前导符"
17574
+ }),
17575
+ /*#__PURE__*/ jsx(OfficeSelect, {
17576
+ ariaLabel: "索引前导符",
17577
+ value: options.leader,
17578
+ options: indexLeaderOptions,
17579
+ disabled: !options.rightAlignPageNumbers,
17580
+ onValueChange: (leader)=>update({
17581
+ leader
17582
+ })
17583
+ })
17584
+ ]
17585
+ })
17586
+ ]
17587
+ }),
17588
+ /*#__PURE__*/ jsxs("fieldset", {
17589
+ className: "work-document-index-dialog-preview",
17590
+ "aria-label": "索引预览",
17591
+ "data-index-format": options.format,
17592
+ "data-index-leader": options.leader,
17593
+ "data-index-right-align-page-numbers": String(options.rightAlignPageNumbers),
17594
+ children: [
17595
+ /*#__PURE__*/ jsx("legend", {
17596
+ children: "索引"
17597
+ }),
17598
+ /*#__PURE__*/ jsxs("span", {
17599
+ children: [
17600
+ "Architecture ",
17601
+ /*#__PURE__*/ jsx("i", {
17602
+ "aria-hidden": "true"
17603
+ }),
17604
+ " 1, 3"
17605
+ ]
17606
+ }),
17607
+ /*#__PURE__*/ jsxs("span", {
17608
+ className: "nested",
17609
+ children: [
17610
+ "Runtime ",
17611
+ /*#__PURE__*/ jsx("i", {
17612
+ "aria-hidden": "true"
17613
+ }),
17614
+ " 5"
17615
+ ]
17616
+ })
17617
+ ]
17618
+ })
17619
+ ]
17620
+ });
17621
+ }
17622
+ const indexColumnOptions = [
17623
+ {
17624
+ value: '1',
17625
+ label: '一栏'
17626
+ },
17627
+ {
17628
+ value: '2',
17629
+ label: '两栏'
17630
+ },
17631
+ {
17632
+ value: '3',
17633
+ label: '三栏'
17634
+ },
17635
+ {
17636
+ value: '4',
17637
+ label: '四栏'
17638
+ }
17639
+ ];
17640
+ const indexFormatOptions = [
17641
+ {
17642
+ value: 'indented',
17643
+ label: '缩进式'
17644
+ },
17645
+ {
17646
+ value: 'run-in',
17647
+ label: '连续式'
17648
+ }
17649
+ ];
17650
+ const indexLeaderOptions = [
17651
+ {
17652
+ value: 'dot',
17653
+ label: '点线(……)'
17654
+ },
17655
+ {
17656
+ value: 'dash',
17657
+ label: '短横线(----)'
17658
+ },
17659
+ {
17660
+ value: 'underline',
17661
+ label: '下划线(____)'
17662
+ },
17663
+ {
17664
+ value: 'none',
17665
+ label: '无'
17666
+ }
17667
+ ];
17668
+ function DocumentIndexEntryDialog({ editing, value, restoreFocusTarget, onCancel, onChange, onSubmit }) {
17669
+ const update = (patch)=>onChange({
17670
+ ...value,
17671
+ ...patch
17672
+ });
17673
+ const crossReferenceEnabled = Boolean(value.crossReference);
17674
+ const mainEntryId = useId();
17675
+ const subEntryId = useId();
17676
+ const crossReferenceId = useId();
17677
+ return /*#__PURE__*/ jsxs(Dialog, {
17678
+ title: editing ? '编辑索引项' : '标记索引项',
17679
+ description: "标记主索引项、次索引项或交叉引用,并保留原生 DOCX XE 域。",
17680
+ className: "work-document-index-entry-dialog",
17681
+ restoreFocusTarget: restoreFocusTarget,
17682
+ onClose: onCancel,
17683
+ footer: /*#__PURE__*/ jsxs(Fragment, {
17684
+ children: [
17685
+ /*#__PURE__*/ jsx(button_Button, {
17686
+ tone: "quiet",
17687
+ onClick: onCancel,
17688
+ children: "取消"
17689
+ }),
17690
+ /*#__PURE__*/ jsx(button_Button, {
17691
+ tone: "primary",
17692
+ disabled: !value.mainEntry.trim(),
17693
+ onClick: onSubmit,
17694
+ children: editing ? '应用' : '标记'
17695
+ })
17696
+ ]
17697
+ }),
17698
+ children: [
17699
+ /*#__PURE__*/ jsxs("div", {
17700
+ className: "work-document-index-entry-dialog-fields",
17701
+ children: [
17702
+ /*#__PURE__*/ jsxs("label", {
17703
+ htmlFor: mainEntryId,
17704
+ children: [
17705
+ /*#__PURE__*/ jsx("span", {
17706
+ children: "主索引项"
17707
+ }),
17708
+ /*#__PURE__*/ jsx(office_text_field_OfficeTextField, {
17709
+ id: mainEntryId,
17710
+ "data-autofocus": true,
17711
+ "aria-label": "主索引项",
17712
+ value: value.mainEntry,
17713
+ maxLength: 240,
17714
+ onChange: (event)=>update({
17715
+ mainEntry: event.target.value
17716
+ }),
17717
+ onKeyDown: (event)=>{
17718
+ if ('Enter' !== event.key || !value.mainEntry.trim()) return;
17719
+ event.preventDefault();
17720
+ onSubmit();
17721
+ }
17722
+ })
17723
+ ]
17724
+ }),
17725
+ /*#__PURE__*/ jsxs("label", {
17726
+ htmlFor: subEntryId,
17727
+ children: [
17728
+ /*#__PURE__*/ jsx("span", {
17729
+ children: "次索引项"
17730
+ }),
17731
+ /*#__PURE__*/ jsx(office_text_field_OfficeTextField, {
17732
+ id: subEntryId,
17733
+ "aria-label": "次索引项",
17734
+ value: value.subEntry,
17735
+ maxLength: 240,
17736
+ placeholder: "可选",
17737
+ onChange: (event)=>update({
17738
+ subEntry: event.target.value
17739
+ })
17740
+ })
17741
+ ]
17742
+ })
17743
+ ]
17744
+ }),
17745
+ /*#__PURE__*/ jsxs("div", {
17746
+ className: "work-document-index-entry-dialog-mode",
17747
+ children: [
17748
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
17749
+ ariaLabel: "使用交叉引用",
17750
+ checked: crossReferenceEnabled,
17751
+ onCheckedChange: (enabled)=>update({
17752
+ crossReference: enabled ? value.crossReference || '参见' : '',
17753
+ pageBold: enabled ? false : value.pageBold,
17754
+ pageItalic: enabled ? false : value.pageItalic
17755
+ }),
17756
+ children: "使用交叉引用"
17757
+ }),
17758
+ crossReferenceEnabled && /*#__PURE__*/ jsxs("label", {
17759
+ htmlFor: crossReferenceId,
17760
+ children: [
17761
+ /*#__PURE__*/ jsx("span", {
17762
+ children: "引用目标"
17763
+ }),
17764
+ /*#__PURE__*/ jsx(office_text_field_OfficeTextField, {
17765
+ id: crossReferenceId,
17766
+ "aria-label": "交叉引用目标",
17767
+ value: value.crossReference,
17768
+ maxLength: 240,
17769
+ placeholder: "例如:Architecture",
17770
+ onChange: (event)=>update({
17771
+ crossReference: event.target.value
17772
+ })
17773
+ })
17774
+ ]
17775
+ })
17776
+ ]
17777
+ }),
17778
+ /*#__PURE__*/ jsxs("fieldset", {
17779
+ className: "work-document-index-entry-dialog-page-style",
17780
+ disabled: crossReferenceEnabled,
17781
+ children: [
17782
+ /*#__PURE__*/ jsx("legend", {
17783
+ children: "当前页码格式"
17784
+ }),
17785
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
17786
+ ariaLabel: "页码加粗",
17787
+ checked: value.pageBold,
17788
+ disabled: crossReferenceEnabled,
17789
+ onCheckedChange: (pageBold)=>update({
17790
+ pageBold
17791
+ }),
17792
+ children: "页码加粗"
17793
+ }),
17794
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
17795
+ ariaLabel: "页码倾斜",
17796
+ checked: value.pageItalic,
17797
+ disabled: crossReferenceEnabled,
17798
+ onCheckedChange: (pageItalic)=>update({
17799
+ pageItalic
17800
+ }),
17801
+ children: "页码倾斜"
17802
+ })
17803
+ ]
17804
+ })
17805
+ ]
17806
+ });
17807
+ }
16325
17808
  function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext }) {
16326
17809
  const officeDialog = useOfficeDialog();
16327
17810
  const [insertDialog, setInsertDialog] = useState(null);
@@ -16428,6 +17911,37 @@ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext })
16428
17911
  editor,
16429
17912
  rememberInvoker
16430
17913
  ]);
17914
+ const openIndexEntry = useCallback(()=>{
17915
+ if (!editor) return;
17916
+ const value = selectedDocumentIndexDraft(editor);
17917
+ if (!value) return void officeDialog.notice({
17918
+ title: '请选择索引文字',
17919
+ description: '请先选择要标记的正文文字,或选中一个已有索引项。'
17920
+ });
17921
+ rememberInvoker();
17922
+ setInsertDialog({
17923
+ kind: 'indexEntry',
17924
+ editing: Boolean(selectedDocumentIndexEntry(editor)),
17925
+ value
17926
+ });
17927
+ }, [
17928
+ editor,
17929
+ officeDialog,
17930
+ rememberInvoker
17931
+ ]);
17932
+ const openIndex = useCallback(()=>{
17933
+ if (!editor) return;
17934
+ rememberInvoker();
17935
+ const selected = selectedDocumentIndexOptions(editor);
17936
+ setInsertDialog({
17937
+ kind: 'index',
17938
+ editing: Boolean(selected),
17939
+ options: selected ?? DEFAULT_DOCUMENT_INDEX_OPTIONS
17940
+ });
17941
+ }, [
17942
+ editor,
17943
+ rememberInvoker
17944
+ ]);
16431
17945
  const refreshFields = useCallback(()=>editor?.commands.refreshDocumentFields(contentRef.current, {
16432
17946
  resolveContext: resolveFieldContext ?? void 0
16433
17947
  }) ?? false, [
@@ -16441,6 +17955,12 @@ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext })
16441
17955
  editor,
16442
17956
  resolveFieldContext
16443
17957
  ]);
17958
+ const refreshIndex = useCallback(()=>editor?.commands.refreshDocumentIndexes({
17959
+ resolveContext: resolveFieldContext ?? void 0
17960
+ }) ?? false, [
17961
+ editor,
17962
+ resolveFieldContext
17963
+ ]);
16444
17964
  const submitCaption = ()=>{
16445
17965
  if (!editor || insertDialog?.kind !== 'caption') return;
16446
17966
  const inserted = editor.chain().focus().insertDocumentCaption(insertDialog.captionKind, insertDialog.title.trim()).run();
@@ -16467,6 +17987,23 @@ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext })
16467
17987
  invokerRef.current = editor.view.dom;
16468
17988
  setInsertDialog(null);
16469
17989
  };
17990
+ const submitIndexEntry = ()=>{
17991
+ if (!editor || insertDialog?.kind !== 'indexEntry') return;
17992
+ const applied = insertDialog.editing ? editor.commands.updateDocumentIndexEntry(insertDialog.value) : editor.commands.markDocumentIndexEntry(insertDialog.value);
17993
+ if (!applied) return;
17994
+ invokerRef.current = editor.view.dom;
17995
+ setInsertDialog(null);
17996
+ };
17997
+ const submitIndex = ()=>{
17998
+ if (!editor || insertDialog?.kind !== 'index') return;
17999
+ const buildOptions = {
18000
+ resolveContext: resolveFieldContext ?? void 0
18001
+ };
18002
+ const applied = insertDialog.editing ? editor.commands.updateDocumentIndex(insertDialog.options, buildOptions) : editor.commands.insertDocumentIndex(insertDialog.options, buildOptions);
18003
+ if (!applied) return;
18004
+ invokerRef.current = editor.view.dom;
18005
+ setInsertDialog(null);
18006
+ };
16470
18007
  const moveReferenceSelection = (event, currentIndex)=>{
16471
18008
  if (insertDialog?.kind !== 'crossReference') return;
16472
18009
  let nextIndex = currentIndex;
@@ -16608,6 +18145,28 @@ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext })
16608
18145
  options
16609
18146
  }),
16610
18147
  onSubmit: submitTableOfContents
18148
+ }),
18149
+ insertDialog?.kind === 'indexEntry' && /*#__PURE__*/ jsx(DocumentIndexEntryDialog, {
18150
+ editing: insertDialog.editing,
18151
+ value: insertDialog.value,
18152
+ restoreFocusTarget: ()=>invokerRef.current,
18153
+ onCancel: ()=>setInsertDialog(null),
18154
+ onChange: (value)=>setInsertDialog({
18155
+ ...insertDialog,
18156
+ value
18157
+ }),
18158
+ onSubmit: submitIndexEntry
18159
+ }),
18160
+ insertDialog?.kind === 'index' && /*#__PURE__*/ jsx(DocumentIndexDialog, {
18161
+ editing: insertDialog.editing,
18162
+ options: insertDialog.options,
18163
+ restoreFocusTarget: ()=>invokerRef.current,
18164
+ onCancel: ()=>setInsertDialog(null),
18165
+ onOptionsChange: (options)=>setInsertDialog({
18166
+ ...insertDialog,
18167
+ options
18168
+ }),
18169
+ onSubmit: submitIndex
16611
18170
  })
16612
18171
  ]
16613
18172
  });
@@ -16618,8 +18177,11 @@ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext })
16618
18177
  insertField,
16619
18178
  insertImage,
16620
18179
  insertNote,
18180
+ openIndexEntry,
18181
+ openIndex,
16621
18182
  openTableOfContents,
16622
18183
  refreshFields,
18184
+ refreshIndex,
16623
18185
  refreshTableOfContents
16624
18186
  };
16625
18187
  }
@@ -18396,6 +19958,13 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
18396
19958
  requestEditorViewChange,
18397
19959
  taskPane
18398
19960
  ]);
19961
+ const documentComparison = useDocumentComparison({
19962
+ editor,
19963
+ onApplied: ()=>{
19964
+ if (editor && !editor.isDestroyed) taskPaneInvokerRef.current = editor.view.dom;
19965
+ setTaskPane('changes');
19966
+ }
19967
+ });
18399
19968
  const startCommentDraft = useCallback(async ()=>{
18400
19969
  rememberTaskPaneInvoker();
18401
19970
  if (!await requestEditorViewChange(null, false)) return;
@@ -18689,6 +20258,10 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
18689
20258
  documentInsert.refreshTableOfContents();
18690
20259
  restoreDocumentBodyFocus();
18691
20260
  };
20261
+ const refreshDocumentIndex = ()=>{
20262
+ documentInsert.refreshIndex();
20263
+ restoreDocumentBodyFocus();
20264
+ };
18692
20265
  const openSelectionContextMenu = (event)=>{
18693
20266
  if (!canEditDocument || !getSelectionMenuItems && !onAgentRequest) return false;
18694
20267
  const snapshot = createWorkDocumentSelectionSnapshot(editor, contentRef.current);
@@ -18786,11 +20359,14 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
18786
20359
  onInsertCaption: documentInsert.insertCaption,
18787
20360
  onInsertCrossReference: documentInsert.insertCrossReference,
18788
20361
  onOpenTableOfContents: documentInsert.openTableOfContents,
20362
+ onOpenIndexEntry: documentInsert.openIndexEntry,
20363
+ onOpenIndex: documentInsert.openIndex,
18789
20364
  citationsOpen: citationsOpen,
18790
20365
  citationSourceCount: currentContent.bibliography?.sources.length ?? 0,
18791
20366
  onToggleCitations: ()=>void toggleTaskPane('citations'),
18792
20367
  onInsertField: documentInsert.insertField,
18793
20368
  onRefreshFields: refreshDocumentFields,
20369
+ onRefreshIndex: refreshDocumentIndex,
18794
20370
  onRefreshTableOfContents: refreshDocumentTableOfContents,
18795
20371
  canInsertComment: documentComments.canInsert,
18796
20372
  onInsertComment: ()=>void startCommentDraft(),
@@ -18811,6 +20387,7 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
18811
20387
  restoreDocumentBodyFocus();
18812
20388
  },
18813
20389
  onToggleChanges: ()=>void toggleTaskPane('changes'),
20390
+ onOpenComparison: documentComparison.open,
18814
20391
  onDecideChange: (change, decision)=>decideDocumentChanges([
18815
20392
  change
18816
20393
  ], decision),
@@ -19138,6 +20715,7 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
19138
20715
  onClose: ()=>setSelectionMenu(null)
19139
20716
  }),
19140
20717
  canEditDocument && documentInsert.dialog,
20718
+ canEditDocument && documentComparison.dialog,
19141
20719
  !preview && taskPaneDialog.dialog,
19142
20720
  !preview && statisticsOpen && /*#__PURE__*/ jsx(DocumentStatisticsDialog, {
19143
20721
  pageCount: pageCount,