@a3s-lab/office 0.28.0 → 0.30.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 (69) hide show
  1. package/README.md +126 -3
  2. package/dist/0~1403.js +1 -1
  3. package/dist/0~4980.js +1 -1
  4. package/dist/0~7048.js +1 -1
  5. package/dist/{0~7043.js → 0~7231.js} +400 -242
  6. package/dist/{0~5093.js → 0~7360.js} +293 -53
  7. package/dist/0~7614.js +1 -1
  8. package/dist/0~document-editor.js +1535 -102
  9. package/dist/0~presentation-editor.js +1 -1
  10. package/dist/0~spreadsheet-editor.js +184 -36
  11. package/dist/0~work-docx-export.js +437 -11
  12. package/dist/0~work-docx-import.js +32 -7
  13. package/dist/0~work-office-diagnostics.js +170 -7
  14. package/dist/0~work-pptx-export.js +1 -1
  15. package/dist/0~work-pptx-import.js +1 -1
  16. package/dist/0~work-presentation-charts.js +1 -1
  17. package/dist/1544.js +14 -2
  18. package/dist/4104.js +31 -8
  19. package/dist/4121.js +3448 -0
  20. package/dist/6282.js +558 -1688
  21. package/dist/8715.js +1 -1
  22. package/dist/core.js +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/internal/features/work/editors/document-command-catalog.d.ts +27 -0
  25. package/dist/internal/features/work/editors/document-font-dialog-model.d.ts +8 -2
  26. package/dist/internal/features/work/editors/document-font-dialog-run-border-model.d.ts +24 -0
  27. package/dist/internal/features/work/editors/document-font-dialog-run-border-section.d.ts +10 -0
  28. package/dist/internal/features/work/editors/document-font-dialog-run-shading-model.d.ts +23 -0
  29. package/dist/internal/features/work/editors/document-font-dialog-run-shading-section.d.ts +8 -0
  30. package/dist/internal/features/work/editors/document-page-chrome-ribbon.d.ts +2 -1
  31. package/dist/internal/features/work/editors/document-proofing-dialog-model.d.ts +22 -0
  32. package/dist/internal/features/work/editors/document-proofing-dialog.d.ts +7 -0
  33. package/dist/internal/features/work/editors/document-table-of-contents-dialog.d.ts +10 -0
  34. package/dist/internal/features/work/editors/document-toolbar.d.ts +3 -1
  35. package/dist/internal/features/work/editors/spreadsheet-data-validation.d.ts +7 -1
  36. package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +2 -0
  37. package/dist/internal/features/work/work-document-character-formatting.d.ts +12 -0
  38. package/dist/internal/features/work/work-document-format-changes.d.ts +7 -0
  39. package/dist/internal/features/work/work-document-highlight.d.ts +9 -0
  40. package/dist/internal/features/work/work-document-outline.d.ts +1 -0
  41. package/dist/internal/features/work/work-document-paragraph-shading.d.ts +6 -0
  42. package/dist/internal/features/work/work-document-proofing.d.ts +19 -0
  43. package/dist/internal/features/work/work-document-run-border.d.ts +11 -0
  44. package/dist/internal/features/work/work-document-run-shading.d.ts +12 -0
  45. package/dist/internal/features/work/work-document-table-of-contents-node.d.ts +18 -0
  46. package/dist/internal/features/work/work-document-table-of-contents.d.ts +51 -0
  47. package/dist/internal/features/work/work-document-word-line-metrics.d.ts +4 -0
  48. package/dist/internal/features/work/work-docx-field-instructions.d.ts +1 -1
  49. package/dist/internal/features/work/work-docx-import.d.ts +3 -1
  50. package/dist/internal/features/work/work-docx-proofing-diagnostics.d.ts +3 -0
  51. package/dist/internal/features/work/work-docx-proofing.d.ts +38 -0
  52. package/dist/internal/features/work/work-docx-run-border-diagnostics.d.ts +3 -0
  53. package/dist/internal/features/work/work-docx-run-border-export.d.ts +17 -0
  54. package/dist/internal/features/work/work-docx-run-border.d.ts +20 -0
  55. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +9 -0
  56. package/dist/internal/features/work/work-docx-run-shading-diagnostics.d.ts +3 -0
  57. package/dist/internal/features/work/work-docx-run-shading-export.d.ts +17 -0
  58. package/dist/internal/features/work/work-docx-run-shading.d.ts +20 -0
  59. package/dist/internal/features/work/work-docx-table-of-contents-export.d.ts +11 -0
  60. package/dist/internal/features/work/work-docx-table-of-contents-import.d.ts +15 -0
  61. package/dist/internal/features/work/work-spreadsheet-data-validation.d.ts +7 -0
  62. package/dist/internal/features/work/work-types.d.ts +9 -0
  63. package/dist/internal/kernel/office-kernel-protocol.d.ts +1 -0
  64. package/dist/office-kernel.wasm +0 -0
  65. package/dist/styles.css +339 -1
  66. package/docs/latest/en/browser-editor-architecture.md +72 -1
  67. package/package.json +15 -2
  68. package/dist/5184.js +0 -662
  69. package/dist/9424.js +0 -16
@@ -6,7 +6,7 @@ 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, Link2, List, ListChecks, ListOrdered, 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, StretchHorizontal, Strikethrough, Subscript, Superscript, Table2, TableCellsMerge, TableCellsSplit, TableProperties, TextWrap, Trash2, TriangleAlert, Type, Underline, Undo2, Unlink, Unlink2, X, XCircle, ZoomIn, ZoomOut } from "lucide-react";
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
10
  import { 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";
@@ -15,11 +15,11 @@ 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, documentScriptFontDirectFamily, normalizeDocumentColumns, clampDocumentMargin, materializeLazyDocumentEditorRoot, patchDocumentScriptFonts, documentKerningThresholdPoints, nextDocumentTabAlignment, documentTableRowOptions, normalizedTabPosition, DOCUMENT_BOOKMARK_DUPLICATE_MESSAGE, documentCitationCount, isDocumentParagraphArtBorderStyle, 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, parseDocumentScriptFonts, DocumentParagraphFormatting, activeDocumentSection, normalizeDocumentHtml, documentInitialSectionLayout, documentModelHasTrustedInitialIntegrityFeatures, documentParagraphDirection, documentParagraphPagination, documentModelUsesWindowing, documentPageChromeLegacyFields, documentChunkMountedIds, collectDocumentCommentAnchors, documentTextCaseKeyboardShortcuts, DocumentTextStyle, canChangeDocumentIndent, cssDocumentFontFamily, documentCharacterSpacingPoints, documentKerningIsEffective, DocumentSuperscript, documentSectionById, activeDocumentBookmark, DocumentEquation, documentLazyHtmlProjectionFingerprint, documentLazyHtmlProjection, normalizeDocumentParagraphIndent, updateDocumentGutterPosition, DocumentStrike, mountWorkLiveDocumentCapture, registerDocumentPageSurfaceGeometry, documentHiddenTextKeyboardShortcut, syncDocumentContentFromHtml, documentLegacyTextEffectsCss, documentScriptFontSlotFromHint, editorDocumentCaptionTargets, resolveDocumentPageChrome, documentScriptFontFamilyForRendering, documentImageLayoutOptions, MAX_DOCUMENT_NUMBERING_START, resolveDocumentPageSize, editorDocumentBookmarkReferenceTargets, serializeWorkDocumentNode, DOCUMENT_CHUNK_PAGINATION_META, createOfficeDocumentCollaborationBinding as createWorkOfficeDocumentCollaborationBinding, documentModelForContent, documentLegacyTextEffectsFromTextStyleAttributes, normalizeDocumentEmphasisMark, selectedDocumentChunkId, documentCommentViews, documentParagraphSpacing, normalizeDocumentTabStops, patchDocumentLazyHtmlProjection, normalizeDocumentTextCase, serializeDocumentScriptFonts, documentUnderlineColor, documentBookmarkNameExists, collectDocumentChanges, DocumentImage, resolveDocumentPageMargins, updateDocumentPageMarginMode, canSetDocumentTableRowRepeatHeader, normalizeDocumentCharacterSpacingTwips, DocumentCharacterFormatting, millimetersToPixels as work_document_layout_millimetersToPixels, DOCUMENT_CHUNK_HYDRATION_META, documentTabLeaderLabel, createSchemaDerivedWorkDocumentModel, updateDocumentCustomPageMillimeters, DocumentUnderline, documentCommentDraftRange, documentParagraphTabStops, documentScriptFontSegments, documentUnderlineKeyboardShortcuts, documentParagraphIndent, documentFontNameFromCssFamily, DocumentScriptFontFormatting, canInsertDocumentComment, documentTableHorizontalAlignment, documentTextLayoutBatches, normalizeDocumentImageWrapSide, updateDocumentPageOrientation, documentCitationStyleDetails, validateDocumentBookmarkName, setCustomDocumentColumns, updateDocumentPageMarginMillimeters, documentCitationStyle, normalizeDocumentCharacterPositionHalfPoints, documentTransactionsOnlyHydrateChunks, normalizeDocumentKerningThresholdHalfPoints, DEFAULT_DOCUMENT_TABLE_CELL_MARGINS, DOCUMENT_TABLE_STYLE_OPTIONS, updateDocumentColumnWidth, updateDocumentPaperSizePreset, documentPageMetrics, documentStrikeStyle, createDocumentBibliography, DocumentSubscript, DocumentParagraphIdentity, updateDocumentPageChromeVariant, updateDocumentMirrorMargins, activeDocumentTableStyle, normalizeDocumentScriptFontSlot, measureDocumentLayoutBlocksIncrementally, documentWordLineHeightFactor, normalizeDocumentLegacyTextEffect, createWorkDocumentExtensions } from "./6282.js";
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";
19
19
  import { useOfficeTaskPaneEscape, normalizeOfficeFontFamily, OfficeCheckbox, useOfficeDialog, useOfficeTaskPaneModal, CommittedOfficeNumberField, officeFontFamilyLabel, officeFontFamilies, CollectionState, createOfficeKernelClient, OfficeNumberField } from "./0~7048.js";
20
20
  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
21
  import { WorkOfficePreviewBar, isWorkspaceContextMenuKeyboardEvent, Tabs, WorkOfficeZoomControls, WorkOfficeStatusBar, Dialog, WorkOfficeRibbon, workspaceContextMenuPosition, OfficeSelect, WorkspaceContextMenu, WorkOfficeRibbonGroup, WorkOfficeRibbonButton } from "./0~4595.js";
22
- import { createOfficeId as createWorkId } from "./5184.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
23
  import { normalizeDocumentHref, DOCUMENT_LINK_VALIDATION_MESSAGE } from "./0~work-document-links.js";
24
24
  import { OfficeColorPicker, isOfficeShortcutBlocked } from "./0~3635.js";
25
25
  import { OfficeTableInsertPopover, readOfficeFileAsDataUrl } from "./0~9445.js";
@@ -4087,6 +4087,9 @@ function createDocumentPageChromeEditorExtensions(placeholder = '输入页眉或
4087
4087
  DocumentFontFamily,
4088
4088
  font_size,
4089
4089
  extension_color,
4090
+ DocumentHighlight.configure({
4091
+ multicolor: true
4092
+ }),
4090
4093
  DocumentUnderline,
4091
4094
  DocumentStrike,
4092
4095
  DocumentCharacterFormatting,
@@ -4804,65 +4807,6 @@ function normalizeDocumentPageNumberInput(value) {
4804
4807
  const number = Number(value);
4805
4808
  return Number.isFinite(number) ? Math.min(9999, Math.max(1, Math.round(number))) : null;
4806
4809
  }
4807
- function collectWorkDocumentOutline(document1) {
4808
- const items = [];
4809
- const hierarchy = [];
4810
- document1.descendants((node, position)=>{
4811
- if ('heading' !== node.type.name) return;
4812
- const level = documentHeadingLevel(node.attrs.level);
4813
- while((hierarchy.at(-1)?.level ?? 0) >= level)hierarchy.pop();
4814
- const parent = hierarchy.at(-1);
4815
- const from = position + 1;
4816
- const item = {
4817
- id: `heading-${position}`,
4818
- text: normalizedDocumentHeadingText(node.textContent),
4819
- level,
4820
- depth: hierarchy.length,
4821
- from,
4822
- to: from + node.content.size,
4823
- ...parent ? {
4824
- parentId: parent.id
4825
- } : {}
4826
- };
4827
- items.push(item);
4828
- hierarchy.push(item);
4829
- });
4830
- return items.map((item, index)=>({
4831
- ...item,
4832
- hasChildren: (items[index + 1]?.depth ?? -1) > item.depth
4833
- }));
4834
- }
4835
- function currentWorkDocumentOutlineItem(items, position) {
4836
- let current = null;
4837
- for (const item of items){
4838
- if (item.from > position) break;
4839
- current = item;
4840
- }
4841
- return current;
4842
- }
4843
- function visibleWorkDocumentOutlineItems(items, collapsedIds, rawQuery) {
4844
- const query = normalizedDocumentOutlineQuery(rawQuery);
4845
- if (query) return items.filter((item)=>normalizedDocumentOutlineQuery(item.text).includes(query));
4846
- const visible = [];
4847
- let hiddenBelowDepth = null;
4848
- for (const item of items){
4849
- if (null !== hiddenBelowDepth && item.depth <= hiddenBelowDepth) hiddenBelowDepth = null;
4850
- if (null === hiddenBelowDepth) {
4851
- visible.push(item);
4852
- if (item.hasChildren && collapsedIds.has(item.id)) hiddenBelowDepth = item.depth;
4853
- }
4854
- }
4855
- return visible;
4856
- }
4857
- function documentHeadingLevel(value) {
4858
- return 'number' == typeof value && Number.isInteger(value) && value >= 1 && value <= 6 ? value : 1;
4859
- }
4860
- function normalizedDocumentHeadingText(value) {
4861
- return value.replace(/\s+/g, ' ').trim() || '未命名标题';
4862
- }
4863
- function normalizedDocumentOutlineQuery(value) {
4864
- return value.normalize('NFKC').trim().toLocaleLowerCase();
4865
- }
4866
4810
  const DOCUMENT_OUTLINE_ITEM_HEIGHT = 31;
4867
4811
  const DOCUMENT_OUTLINE_LIST_PADDING_TOP = 6;
4868
4812
  const document_outline_navigation_DocumentOutlineNavigation = /*#__PURE__*/ forwardRef(function({ activeItemId, collapsedIds, items, rovingId, onNavigate, onRovingIdChange, onToggleCollapsed }, ref) {
@@ -7222,6 +7166,15 @@ const documentCommandCatalog = {
7222
7166
  ]
7223
7167
  }
7224
7168
  },
7169
+ runBorder: {
7170
+ id: 'font.runBorder',
7171
+ label: '字符边框',
7172
+ location: {
7173
+ area: 'ribbon',
7174
+ tab: 'home',
7175
+ group: 'font'
7176
+ }
7177
+ },
7225
7178
  clearFormatting: {
7226
7179
  id: 'font.clearFormatting',
7227
7180
  label: '清除格式',
@@ -7512,6 +7465,24 @@ const documentCommandCatalog = {
7512
7465
  ]
7513
7466
  }
7514
7467
  },
7468
+ tableOfContents: {
7469
+ id: 'references.tableOfContents',
7470
+ label: '插入或自定义目录',
7471
+ location: {
7472
+ area: 'ribbon',
7473
+ tab: 'references',
7474
+ group: 'tableOfContents'
7475
+ }
7476
+ },
7477
+ refreshTableOfContents: {
7478
+ id: 'references.refreshTableOfContents',
7479
+ label: '更新目录',
7480
+ location: {
7481
+ area: 'ribbon',
7482
+ tab: 'references',
7483
+ group: 'tableOfContents'
7484
+ }
7485
+ },
7515
7486
  refreshFields: {
7516
7487
  id: 'references.refreshFields',
7517
7488
  label: '更新域',
@@ -8098,6 +8069,428 @@ function DocumentStatusBar({ bibliographyCount, citationCount, currentPage, page
8098
8069
  ]
8099
8070
  });
8100
8071
  }
8072
+ const proofingStateOptions = [
8073
+ {
8074
+ value: 'mixed',
8075
+ label: '混合(保持不变)',
8076
+ disabled: true
8077
+ },
8078
+ {
8079
+ value: 'inherit',
8080
+ label: '跟随样式'
8081
+ },
8082
+ {
8083
+ value: 'check',
8084
+ label: '检查拼写和语法'
8085
+ },
8086
+ {
8087
+ value: 'skip',
8088
+ label: '不检查拼写或语法'
8089
+ }
8090
+ ];
8091
+ const proofingLanguageSuggestions = [
8092
+ 'en-US',
8093
+ 'en-GB',
8094
+ 'zh-CN',
8095
+ 'zh-TW',
8096
+ 'ja-JP',
8097
+ 'ko-KR',
8098
+ 'ar-SA',
8099
+ 'he-IL',
8100
+ 'fr-FR',
8101
+ 'de-DE',
8102
+ 'es-ES',
8103
+ 'x-none'
8104
+ ];
8105
+ function DocumentProofingDialog({ source, restoreFocusTarget, onApply, onClose }) {
8106
+ const [draft, setDraft] = useState(()=>({
8107
+ latin: source.latin.value ?? '',
8108
+ eastAsia: source.eastAsia.value ?? '',
8109
+ bidi: source.bidi.value ?? '',
8110
+ noProof: proofingStateDraft(source)
8111
+ }));
8112
+ const [touched, setTouched] = useState({
8113
+ latin: false,
8114
+ eastAsia: false,
8115
+ bidi: false,
8116
+ noProof: false
8117
+ });
8118
+ const formId = useId();
8119
+ const datalistId = useId();
8120
+ const error = proofingDialogError(draft, touched);
8121
+ const patch = proofingDialogPatch(source, draft, touched);
8122
+ const hasChanges = Object.keys(patch).length > 0;
8123
+ const submit = (event)=>{
8124
+ event?.preventDefault();
8125
+ if (!hasChanges || error) return;
8126
+ if (onApply(patch)) onClose();
8127
+ };
8128
+ return /*#__PURE__*/ jsx(Dialog, {
8129
+ title: "设置校对语言",
8130
+ description: source.selectedCharacters ? `为选中的 ${source.selectedCharacters} 个字符分别设置拉丁、东亚和双向文字校对语言。` : '设置当前位置后续输入文字的校对语言。',
8131
+ className: "work-document-proofing-dialog",
8132
+ restoreFocusTarget: restoreFocusTarget,
8133
+ onClose: onClose,
8134
+ footer: /*#__PURE__*/ jsxs(Fragment, {
8135
+ children: [
8136
+ /*#__PURE__*/ jsx(button_Button, {
8137
+ tone: "quiet",
8138
+ onClick: onClose,
8139
+ children: "取消"
8140
+ }),
8141
+ /*#__PURE__*/ jsx(button_Button, {
8142
+ tone: "primary",
8143
+ type: "submit",
8144
+ form: formId,
8145
+ disabled: !hasChanges || Boolean(error),
8146
+ children: "应用"
8147
+ })
8148
+ ]
8149
+ }),
8150
+ children: /*#__PURE__*/ jsxs("form", {
8151
+ id: formId,
8152
+ onSubmit: submit,
8153
+ children: [
8154
+ /*#__PURE__*/ jsxs("fieldset", {
8155
+ children: [
8156
+ /*#__PURE__*/ jsx("legend", {
8157
+ children: "按文字系统设置语言"
8158
+ }),
8159
+ /*#__PURE__*/ jsx(ProofingLanguageField, {
8160
+ label: "拉丁文字",
8161
+ languageKey: "latin",
8162
+ value: draft.latin,
8163
+ mixed: source.latin.mixed && !touched.latin,
8164
+ list: datalistId,
8165
+ initialFocus: true,
8166
+ error: touched.latin && !validLanguageDraft(draft.latin),
8167
+ onChange: (value)=>{
8168
+ setDraft((current)=>({
8169
+ ...current,
8170
+ latin: value
8171
+ }));
8172
+ setTouched((current)=>({
8173
+ ...current,
8174
+ latin: true
8175
+ }));
8176
+ }
8177
+ }),
8178
+ /*#__PURE__*/ jsx(ProofingLanguageField, {
8179
+ label: "东亚文字",
8180
+ languageKey: "eastAsia",
8181
+ value: draft.eastAsia,
8182
+ mixed: source.eastAsia.mixed && !touched.eastAsia,
8183
+ list: datalistId,
8184
+ error: touched.eastAsia && !validLanguageDraft(draft.eastAsia),
8185
+ onChange: (value)=>{
8186
+ setDraft((current)=>({
8187
+ ...current,
8188
+ eastAsia: value
8189
+ }));
8190
+ setTouched((current)=>({
8191
+ ...current,
8192
+ eastAsia: true
8193
+ }));
8194
+ }
8195
+ }),
8196
+ /*#__PURE__*/ jsx(ProofingLanguageField, {
8197
+ label: "双向文字",
8198
+ languageKey: "bidi",
8199
+ value: draft.bidi,
8200
+ mixed: source.bidi.mixed && !touched.bidi,
8201
+ list: datalistId,
8202
+ error: touched.bidi && !validLanguageDraft(draft.bidi),
8203
+ onChange: (value)=>{
8204
+ setDraft((current)=>({
8205
+ ...current,
8206
+ bidi: value
8207
+ }));
8208
+ setTouched((current)=>({
8209
+ ...current,
8210
+ bidi: true
8211
+ }));
8212
+ }
8213
+ }),
8214
+ /*#__PURE__*/ jsx("datalist", {
8215
+ id: datalistId,
8216
+ children: proofingLanguageSuggestions.map((language)=>/*#__PURE__*/ jsx("option", {
8217
+ value: language
8218
+ }, language))
8219
+ })
8220
+ ]
8221
+ }),
8222
+ /*#__PURE__*/ jsxs("div", {
8223
+ className: "work-document-proofing-dialog-state",
8224
+ children: [
8225
+ /*#__PURE__*/ jsx("span", {
8226
+ className: "work-document-proofing-dialog-state-label",
8227
+ "aria-hidden": "true",
8228
+ children: "校对行为"
8229
+ }),
8230
+ /*#__PURE__*/ jsx(OfficeSelect, {
8231
+ ariaLabel: "校对行为",
8232
+ value: draft.noProof,
8233
+ options: proofingStateOptions,
8234
+ onValueChange: (noProof)=>{
8235
+ setDraft((current)=>({
8236
+ ...current,
8237
+ noProof
8238
+ }));
8239
+ setTouched((current)=>({
8240
+ ...current,
8241
+ noProof: true
8242
+ }));
8243
+ }
8244
+ }),
8245
+ source.noProof.mixed && !touched.noProof && /*#__PURE__*/ jsx("p", {
8246
+ role: "status",
8247
+ children: "当前选区包含不同的校对行为,保持不变。"
8248
+ })
8249
+ ]
8250
+ }),
8251
+ /*#__PURE__*/ jsxs("p", {
8252
+ className: "work-document-proofing-dialog-help",
8253
+ children: [
8254
+ "使用 BCP 47 语言标记,例如 ",
8255
+ /*#__PURE__*/ jsx("code", {
8256
+ children: "en-US"
8257
+ }),
8258
+ "、",
8259
+ /*#__PURE__*/ jsx("code", {
8260
+ children: "zh-CN"
8261
+ }),
8262
+ " 或",
8263
+ ' ',
8264
+ /*#__PURE__*/ jsx("code", {
8265
+ children: "ar-SA"
8266
+ }),
8267
+ "。留空或选择“跟随样式”会移除直接格式。"
8268
+ ]
8269
+ }),
8270
+ error && /*#__PURE__*/ jsx("p", {
8271
+ className: "work-document-proofing-dialog-error",
8272
+ role: "alert",
8273
+ children: error
8274
+ })
8275
+ ]
8276
+ })
8277
+ });
8278
+ }
8279
+ function ProofingLanguageField({ label, languageKey, value, mixed, list, initialFocus = false, error, onChange }) {
8280
+ return /*#__PURE__*/ jsxs("div", {
8281
+ className: "work-document-proofing-dialog-language",
8282
+ children: [
8283
+ /*#__PURE__*/ jsx("label", {
8284
+ htmlFor: `work-document-proofing-${languageKey}`,
8285
+ children: label
8286
+ }),
8287
+ /*#__PURE__*/ jsxs("div", {
8288
+ children: [
8289
+ /*#__PURE__*/ jsx(office_text_field_OfficeTextField, {
8290
+ id: `work-document-proofing-${languageKey}`,
8291
+ "aria-label": `${label}校对语言`,
8292
+ "aria-invalid": error || void 0,
8293
+ "data-autofocus": initialFocus ? 'true' : void 0,
8294
+ list: list,
8295
+ value: value,
8296
+ placeholder: mixed ? '混合(保持不变)' : '跟随样式',
8297
+ spellCheck: false,
8298
+ onChange: (event)=>onChange(event.currentTarget.value)
8299
+ }),
8300
+ /*#__PURE__*/ jsx(button_Button, {
8301
+ tone: "quiet",
8302
+ type: "button",
8303
+ "aria-label": `${label}跟随样式`,
8304
+ onClick: ()=>onChange(''),
8305
+ children: "跟随样式"
8306
+ })
8307
+ ]
8308
+ }),
8309
+ mixed && /*#__PURE__*/ jsxs("p", {
8310
+ role: "status",
8311
+ children: [
8312
+ "当前选区包含不同的",
8313
+ label,
8314
+ "校对语言,输入后才会统一修改。"
8315
+ ]
8316
+ })
8317
+ ]
8318
+ });
8319
+ }
8320
+ function proofingStateDraft(source) {
8321
+ if (source.noProof.mixed) return 'mixed';
8322
+ if (null === source.noProof.value) return 'inherit';
8323
+ return source.noProof.value ? 'skip' : 'check';
8324
+ }
8325
+ function proofingDialogPatch(source, draft, touched) {
8326
+ const languages = {};
8327
+ for (const key of [
8328
+ 'latin',
8329
+ 'eastAsia',
8330
+ 'bidi'
8331
+ ]){
8332
+ if (!touched[key] || !validLanguageDraft(draft[key])) continue;
8333
+ const value = draft[key] || null;
8334
+ if (source[key].mixed || source[key].value !== value) languages[key] = value;
8335
+ }
8336
+ const patch = {};
8337
+ if (Object.keys(languages).length) patch.languages = languages;
8338
+ if (touched.noProof && 'mixed' !== draft.noProof) {
8339
+ const noProof = 'inherit' === draft.noProof ? null : 'skip' === draft.noProof;
8340
+ if (source.noProof.mixed || source.noProof.value !== noProof) patch.noProof = noProof;
8341
+ }
8342
+ return patch;
8343
+ }
8344
+ function proofingDialogError(draft, touched) {
8345
+ for (const key of [
8346
+ 'latin',
8347
+ 'eastAsia',
8348
+ 'bidi'
8349
+ ])if (touched[key] && !validLanguageDraft(draft[key])) return '请输入有效的 BCP 47 语言标记;不能包含空格、下划线或控制字符。';
8350
+ return null;
8351
+ }
8352
+ function validLanguageDraft(value) {
8353
+ return '' === value || null !== normalizeDocumentLanguageTag(value);
8354
+ }
8355
+ const PROOFING_LANGUAGE_SLOTS = [
8356
+ 'latin',
8357
+ 'eastAsia',
8358
+ 'bidi'
8359
+ ];
8360
+ const PROOFING_LANGUAGE_SLOT_SET = new Set(PROOFING_LANGUAGE_SLOTS);
8361
+ function documentProofingDialogSource(editor) {
8362
+ const latin = new Map();
8363
+ const eastAsia = new Map();
8364
+ const bidi = new Map();
8365
+ const noProof = new Map();
8366
+ const { doc, selection } = editor.state;
8367
+ const addValues = (attributes)=>{
8368
+ const languages = parseDocumentProofingLanguages(attributes.proofingLanguages);
8369
+ addSelectedValue(latin, languages?.latin ?? null);
8370
+ addSelectedValue(eastAsia, languages?.eastAsia ?? null);
8371
+ addSelectedValue(bidi, languages?.bidi ?? null);
8372
+ addSelectedValue(noProof, normalizeDocumentNoProof(attributes.noProof));
8373
+ };
8374
+ if (selection.empty) addValues(editor.getAttributes('textStyle'));
8375
+ else doc.nodesBetween(selection.from, selection.to, (node, position)=>{
8376
+ if (!node.isText || position >= selection.to || position + node.nodeSize <= selection.from) return;
8377
+ const textStyle = node.marks.find((mark)=>'textStyle' === mark.type.name);
8378
+ addValues(textStyle?.attrs ?? {});
8379
+ });
8380
+ if (!latin.size) addValues(editor.getAttributes('textStyle'));
8381
+ const selectedText = selection.empty ? '' : doc.textBetween(selection.from, selection.to, '', '');
8382
+ return {
8383
+ latin: selectedValue(latin),
8384
+ eastAsia: selectedValue(eastAsia),
8385
+ bidi: selectedValue(bidi),
8386
+ noProof: selectedValue(noProof),
8387
+ selectedCharacters: Array.from(selectedText).length
8388
+ };
8389
+ }
8390
+ function applyDocumentProofingDialogPatch(editor, selection, patch) {
8391
+ if (!validDocumentProofingDialogPatch(patch) || editor.isDestroyed) return false;
8392
+ const maximum = editor.state.doc.content.size;
8393
+ if (!Number.isSafeInteger(selection.from) || !Number.isSafeInteger(selection.to) || selection.from < 0 || selection.to < selection.from || selection.to > maximum) return false;
8394
+ const textStyle = editor.state.schema.marks.textStyle;
8395
+ if (!textStyle) return false;
8396
+ const transaction = editor.state.tr;
8397
+ try {
8398
+ transaction.setSelection(TextSelection.create(transaction.doc, selection.from, selection.to));
8399
+ } catch {
8400
+ return false;
8401
+ }
8402
+ if (selection.from === selection.to) {
8403
+ const marks = transaction.selection.$from.marks();
8404
+ const current = marks.find((mark)=>mark.type === textStyle);
8405
+ const attributes = patchedProofingAttributes(current, patch);
8406
+ if (!attributes) return false;
8407
+ const retained = marks.filter((mark)=>mark.type !== textStyle);
8408
+ if (textStyleAttributesHaveValue(attributes)) retained.push(textStyle.create(attributes));
8409
+ transaction.setStoredMarks(retained);
8410
+ transaction.scrollIntoView();
8411
+ editor.view.dispatch(transaction);
8412
+ editor.view.focus();
8413
+ return true;
8414
+ }
8415
+ const updates = [];
8416
+ transaction.doc.nodesBetween(selection.from, selection.to, (node, position)=>{
8417
+ if (!node.isText || !node.text) return;
8418
+ const from = Math.max(selection.from, position);
8419
+ const to = Math.min(selection.to, position + node.nodeSize);
8420
+ if (to <= from) return;
8421
+ const current = node.marks.find((mark)=>mark.type === textStyle);
8422
+ const attributes = patchedProofingAttributes(current, patch);
8423
+ if (attributes) updates.push({
8424
+ from,
8425
+ to,
8426
+ current,
8427
+ attributes
8428
+ });
8429
+ });
8430
+ for (const update of updates){
8431
+ if (update.current) transaction.removeMark(update.from, update.to, update.current);
8432
+ if (textStyleAttributesHaveValue(update.attributes)) transaction.addMark(update.from, update.to, textStyle.create(update.attributes));
8433
+ }
8434
+ if (!transaction.docChanged) return false;
8435
+ editor.view.dispatch(transaction.scrollIntoView());
8436
+ editor.view.focus();
8437
+ return true;
8438
+ }
8439
+ function patchedProofingAttributes(current, patch) {
8440
+ const attributes = {
8441
+ ...current?.attrs ?? {}
8442
+ };
8443
+ let changed = false;
8444
+ if (patch.languages) {
8445
+ const currentLanguages = parseDocumentProofingLanguages(current?.attrs.proofingLanguages);
8446
+ const nextLanguages = patchDocumentProofingLanguages(currentLanguages, patch.languages);
8447
+ const currentSerialized = serializeDocumentProofingLanguages(currentLanguages) ?? null;
8448
+ const nextSerialized = serializeDocumentProofingLanguages(nextLanguages) ?? null;
8449
+ if (currentSerialized !== nextSerialized) {
8450
+ attributes.proofingLanguages = nextSerialized;
8451
+ changed = true;
8452
+ }
8453
+ }
8454
+ if (void 0 !== patch.noProof) {
8455
+ const currentNoProof = normalizeDocumentNoProof(current?.attrs.noProof);
8456
+ if (currentNoProof !== patch.noProof) {
8457
+ attributes.noProof = patch.noProof;
8458
+ changed = true;
8459
+ }
8460
+ }
8461
+ return changed ? attributes : null;
8462
+ }
8463
+ function validDocumentProofingDialogPatch(patch) {
8464
+ if (!isRecord(patch)) return false;
8465
+ const keys = Object.keys(patch);
8466
+ if (!keys.length || keys.some((key)=>'languages' !== key && 'noProof' !== key)) return false;
8467
+ if (void 0 !== patch.noProof && null !== patch.noProof && null === normalizeDocumentNoProof(patch.noProof)) return false;
8468
+ if (void 0 !== patch.languages) {
8469
+ if (!isRecord(patch.languages)) return false;
8470
+ const languageEntries = Object.entries(patch.languages);
8471
+ if (!languageEntries.length || languageEntries.some(([slot, language])=>!PROOFING_LANGUAGE_SLOT_SET.has(slot) || null !== language && null === normalizeDocumentLanguageTag(language))) return false;
8472
+ }
8473
+ return void 0 !== patch.languages || void 0 !== patch.noProof;
8474
+ }
8475
+ function addSelectedValue(values, value) {
8476
+ values.set(null === value ? 'inherited' : String(value), value);
8477
+ }
8478
+ function selectedValue(values) {
8479
+ if (1 !== values.size) return {
8480
+ mixed: true,
8481
+ value: null
8482
+ };
8483
+ return {
8484
+ mixed: false,
8485
+ value: values.values().next().value ?? null
8486
+ };
8487
+ }
8488
+ function textStyleAttributesHaveValue(attributes) {
8489
+ return Object.values(attributes).some((value)=>null != value && '' !== value);
8490
+ }
8491
+ function isRecord(source) {
8492
+ return 'object' == typeof source && null !== source && !Array.isArray(source);
8493
+ }
8101
8494
  function DocumentFormatTools({ editor }) {
8102
8495
  const hasFormat = useSyncExternalStore(subscribeDocumentFormatClipboard, hasDocumentFormatClipboard, hasDocumentFormatClipboard);
8103
8496
  const [formatPainterActive, setFormatPainterActive] = useState(false);
@@ -8949,6 +9342,14 @@ function DocumentHomeRibbon({ editor, findReplaceMode, layoutFonts = [], onFindT
8949
9342
  size: 16
8950
9343
  })
8951
9344
  }),
9345
+ /*#__PURE__*/ jsx(ToolbarButton, {
9346
+ label: "字符边框",
9347
+ active: documentRunBorderIsVisible(parseDocumentRunBorder(editor.getAttributes('textStyle').runBorder)),
9348
+ onClick: ()=>editor.commands.toggleDocumentRunBorder(),
9349
+ children: /*#__PURE__*/ jsx(Square, {
9350
+ size: 16
9351
+ })
9352
+ }),
8952
9353
  /*#__PURE__*/ jsx(ToolbarButton, {
8953
9354
  label: "清除格式",
8954
9355
  onClick: ()=>editor.commands.clearDocumentFormatting(),
@@ -9177,7 +9578,7 @@ function applyDocumentScriptFontPatch(editor, selection, scalarPatch, scriptFont
9177
9578
  const current = marks.find((mark)=>mark.type === textStyle);
9178
9579
  const attributes = patchedTextStyleAttributes(current, scalarPatch, scriptFontPatch, normalizeDocumentScriptFontSlot(current?.attrs.scriptFontSlot) ?? documentScriptFontSlotFromHint(parseDocumentScriptFonts(current?.attrs.scriptFonts)?.hint));
9179
9580
  const retained = marks.filter((mark)=>mark.type !== textStyle);
9180
- if (textStyleAttributesHaveValue(attributes)) retained.push(textStyle.create(attributes));
9581
+ if (document_font_dialog_script_font_model_textStyleAttributesHaveValue(attributes)) retained.push(textStyle.create(attributes));
9181
9582
  transaction.setStoredMarks(retained);
9182
9583
  transaction.scrollIntoView();
9183
9584
  editor.view.dispatch(transaction);
@@ -9204,7 +9605,7 @@ function applyDocumentScriptFontPatch(editor, selection, scalarPatch, scriptFont
9204
9605
  });
9205
9606
  for (const update of updates){
9206
9607
  if (update.current) transaction.removeMark(update.from, update.to, update.current);
9207
- if (textStyleAttributesHaveValue(update.attributes)) transaction.addMark(update.from, update.to, textStyle.create(update.attributes));
9608
+ if (document_font_dialog_script_font_model_textStyleAttributesHaveValue(update.attributes)) transaction.addMark(update.from, update.to, textStyle.create(update.attributes));
9208
9609
  }
9209
9610
  if (!transaction.docChanged) return false;
9210
9611
  editor.view.dispatch(transaction.scrollIntoView());
@@ -9233,7 +9634,7 @@ function patchedTextStyleAttributes(current, scalarPatch, scriptFontPatch, slot,
9233
9634
  attributes.wordLineHeightFactor = family ? documentWordLineHeightFactor(family) : null;
9234
9635
  return attributes;
9235
9636
  }
9236
- function textStyleAttributesHaveValue(attributes) {
9637
+ function document_font_dialog_script_font_model_textStyleAttributesHaveValue(attributes) {
9237
9638
  return Object.values(attributes).some((value)=>null != value && '' !== value);
9238
9639
  }
9239
9640
  function validScriptFontPatch(patch) {
@@ -9243,6 +9644,170 @@ function addFontValue(values, value) {
9243
9644
  const normalized = value?.trim() || null;
9244
9645
  values.set(normalized?.toLocaleLowerCase() ?? 'inherited', normalized);
9245
9646
  }
9647
+ function addDocumentFontDialogRunBorderValue(values, source) {
9648
+ const border = parseDocumentRunBorder(source);
9649
+ const serialized = serializeDocumentRunBorder(border);
9650
+ values.set(serialized ?? 'inherited', border);
9651
+ }
9652
+ function selectedDocumentFontDialogRunBorderValue(values) {
9653
+ return 1 === values.size ? {
9654
+ mixed: false,
9655
+ value: values.values().next().value ?? null
9656
+ } : {
9657
+ mixed: true,
9658
+ value: null
9659
+ };
9660
+ }
9661
+ function createDocumentFontDialogRunBorderDraft(source) {
9662
+ const border = source.value;
9663
+ const visible = border && 'nil' !== border.style && 'none' !== border.style;
9664
+ return {
9665
+ runBorderMode: source.mixed ? 'mixed' : border ? visible ? 'value' : 'none' : 'inherit',
9666
+ runBorderStyle: visible ? border.style : 'single',
9667
+ runBorderColor: border?.color?.value ?? 'auto',
9668
+ runBorderWidthPoints: formatPoints((border?.size ?? 4) / 8),
9669
+ runBorderSpacingPoints: String(border?.space ?? 1),
9670
+ runBorderShadow: border?.shadow ?? false,
9671
+ runBorderFrame: border?.frame ?? false
9672
+ };
9673
+ }
9674
+ function documentFontDialogRunBorderDraftError(draft) {
9675
+ if ('value' !== draft.runBorderMode) return null;
9676
+ if (!DOCUMENT_RUN_BORDER_STYLES.includes(draft.runBorderStyle) || 'nil' === draft.runBorderStyle || 'none' === draft.runBorderStyle) return '请选择有效的字符边框线型。';
9677
+ if ('auto' !== draft.runBorderColor && !/^#[0-9a-f]{6}$/i.test(draft.runBorderColor)) return '请选择有效的字符边框颜色。';
9678
+ if (null === runBorderSizeFromDraft(draft)) return '请输入 0.25 至 12 磅、以 0.125 磅递增的字符边框宽度。';
9679
+ if (null === runBorderSpaceFromDraft(draft)) return '请输入 0 至 31 磅的整数字符边框间距。';
9680
+ return null;
9681
+ }
9682
+ function documentFontDialogRunBorderPatch(source, draft, touched) {
9683
+ if (!touched || 'mixed' === draft.runBorderMode) return {};
9684
+ if ('inherit' === draft.runBorderMode) return source.mixed || source.value ? {
9685
+ runBorder: null
9686
+ } : {};
9687
+ const border = 'none' === draft.runBorderMode ? {
9688
+ style: 'nil'
9689
+ } : documentFontDialogRunBorderFromDraft(source, draft);
9690
+ if (!border) return {};
9691
+ return serializeDocumentRunBorder(source.value) === serializeDocumentRunBorder(border) ? {} : {
9692
+ runBorder: border
9693
+ };
9694
+ }
9695
+ function documentFontDialogRunBorderFromDraft(source, draft) {
9696
+ if ('none' === draft.runBorderMode) return {
9697
+ style: 'nil'
9698
+ };
9699
+ if ('value' !== draft.runBorderMode) return source.value;
9700
+ const size = runBorderSizeFromDraft(draft);
9701
+ const space = runBorderSpaceFromDraft(draft);
9702
+ if (null === size || null === space) return null;
9703
+ const sourceColor = source.value?.color;
9704
+ return normalizeDocumentRunBorder({
9705
+ style: draft.runBorderStyle,
9706
+ color: {
9707
+ value: draft.runBorderColor,
9708
+ ...sourceColor?.theme && sourceColor.value === draft.runBorderColor ? {
9709
+ theme: sourceColor.theme
9710
+ } : {}
9711
+ },
9712
+ size,
9713
+ space,
9714
+ shadow: draft.runBorderShadow,
9715
+ frame: draft.runBorderFrame
9716
+ });
9717
+ }
9718
+ function runBorderSizeFromDraft(draft) {
9719
+ const points = Number(draft.runBorderWidthPoints);
9720
+ const size = Math.round(8 * points);
9721
+ return Number.isFinite(points) && size >= 2 && size <= 96 && 8 * points === size ? size : null;
9722
+ }
9723
+ function runBorderSpaceFromDraft(draft) {
9724
+ const space = Number(draft.runBorderSpacingPoints);
9725
+ return Number.isSafeInteger(space) && space >= 0 && space <= 31 ? space : null;
9726
+ }
9727
+ function formatPoints(value) {
9728
+ return Number(value.toFixed(3)).toString();
9729
+ }
9730
+ function addDocumentFontDialogRunShadingValue(values, source) {
9731
+ const shading = parseDocumentRunShading(source);
9732
+ const serialized = serializeDocumentRunShading(shading);
9733
+ values.set(serialized ?? 'inherited', shading);
9734
+ }
9735
+ function selectedDocumentFontDialogRunShadingValue(values) {
9736
+ return 1 === values.size ? {
9737
+ mixed: false,
9738
+ value: values.values().next().value ?? null
9739
+ } : {
9740
+ mixed: true,
9741
+ value: null
9742
+ };
9743
+ }
9744
+ function createDocumentFontDialogRunShadingDraft(source) {
9745
+ const shading = source.value;
9746
+ return {
9747
+ runShadingMode: source.mixed ? 'mixed' : shading ? 'nil' === shading.pattern ? 'none' : 'value' : 'inherit',
9748
+ runShadingPattern: shading && 'nil' !== shading.pattern ? shading.pattern : 'clear',
9749
+ runShadingColor: shading?.color?.value ?? 'auto',
9750
+ runShadingFill: shading?.fill?.value ?? 'auto'
9751
+ };
9752
+ }
9753
+ function documentFontDialogRunShadingDraftError(draft) {
9754
+ if ('value' !== draft.runShadingMode) return null;
9755
+ if (!validColor(draft.runShadingColor)) return '请选择有效的字符底纹前景色。';
9756
+ if (!validColor(draft.runShadingFill)) return '请选择有效的字符底纹背景色。';
9757
+ return null;
9758
+ }
9759
+ function documentFontDialogRunShadingPatch(source, draft, touched) {
9760
+ if (!touched || 'mixed' === draft.runShadingMode) return {};
9761
+ if ('inherit' === draft.runShadingMode) return source.mixed || source.value ? {
9762
+ runShading: null
9763
+ } : {};
9764
+ const shading = 'none' === draft.runShadingMode ? {
9765
+ pattern: 'nil'
9766
+ } : documentFontDialogRunShadingFromDraft(source, draft);
9767
+ if (!shading) return {};
9768
+ return serializeDocumentRunShading(source.value) === serializeDocumentRunShading(shading) ? {} : {
9769
+ runShading: shading
9770
+ };
9771
+ }
9772
+ function documentFontDialogRunShadingFromDraft(source, draft) {
9773
+ if ('none' === draft.runShadingMode) return {
9774
+ pattern: 'nil'
9775
+ };
9776
+ if ('value' !== draft.runShadingMode) return source.value;
9777
+ return normalizeDocumentRunShading({
9778
+ pattern: draft.runShadingPattern,
9779
+ ...shadingColorPatch('color', draft.runShadingColor, source.value?.color),
9780
+ ...shadingColorPatch('fill', draft.runShadingFill, source.value?.fill)
9781
+ });
9782
+ }
9783
+ function documentFontDialogRunShadingPreviewStyle(source, draft) {
9784
+ const shading = documentFontDialogRunShadingFromDraft(source, draft);
9785
+ const cssText = documentRunShadingDomAttributes(shading).style;
9786
+ if (!cssText) return {};
9787
+ const probe = document.createElement('span');
9788
+ probe.style.cssText = cssText;
9789
+ return {
9790
+ backgroundColor: probe.style.backgroundColor || void 0,
9791
+ backgroundImage: probe.style.backgroundImage || void 0,
9792
+ backgroundSize: probe.style.backgroundSize || void 0,
9793
+ boxDecorationBreak: 'clone',
9794
+ WebkitBoxDecorationBreak: 'clone'
9795
+ };
9796
+ }
9797
+ function shadingColorPatch(name, value, source) {
9798
+ if (!source && 'auto' === value) return {};
9799
+ return {
9800
+ [name]: {
9801
+ value,
9802
+ ...source?.theme && source.value === value ? {
9803
+ theme: source.theme
9804
+ } : {}
9805
+ }
9806
+ };
9807
+ }
9808
+ function validColor(value) {
9809
+ return 'auto' === value || /^#[0-9a-f]{6}$/i.test(value);
9810
+ }
9246
9811
  function documentFontDialogSource(editor) {
9247
9812
  const { doc, selection } = editor.state;
9248
9813
  const scaleValues = new Map();
@@ -9255,6 +9820,8 @@ function documentFontDialogSource(editor) {
9255
9820
  const legacyTextShadowValues = new Map();
9256
9821
  const legacyTextEmbossValues = new Map();
9257
9822
  const legacyTextImprintValues = new Map();
9823
+ const runBorderValues = new Map();
9824
+ const runShadingValues = new Map();
9258
9825
  const latinFontValues = new Map();
9259
9826
  const eastAsiaFontValues = new Map();
9260
9827
  const complexScriptFontValues = new Map();
@@ -9269,6 +9836,8 @@ function documentFontDialogSource(editor) {
9269
9836
  addLegacyTextEffectValue(legacyTextShadowValues, attributes.legacyTextShadow);
9270
9837
  addLegacyTextEffectValue(legacyTextEmbossValues, attributes.legacyTextEmboss);
9271
9838
  addLegacyTextEffectValue(legacyTextImprintValues, attributes.legacyTextImprint);
9839
+ addDocumentFontDialogRunBorderValue(runBorderValues, attributes.runBorder);
9840
+ addDocumentFontDialogRunShadingValue(runShadingValues, attributes.runShading);
9272
9841
  addDocumentScriptFontValues(latinFontValues, eastAsiaFontValues, complexScriptFontValues, attributes);
9273
9842
  };
9274
9843
  if (selection.empty) addValues(editor.getAttributes('textStyle'));
@@ -9277,20 +9846,22 @@ function documentFontDialogSource(editor) {
9277
9846
  const textStyle = node.marks.find((mark)=>'textStyle' === mark.type.name);
9278
9847
  addValues(textStyle?.attrs ?? {});
9279
9848
  });
9280
- if (!scaleValues.size || !spacingValues.size || !positionValues.size || !kerningValues.size || !emphasisValues.size || !hiddenTextValues.size || !legacyTextOutlineValues.size || !legacyTextShadowValues.size || !legacyTextEmbossValues.size || !legacyTextImprintValues.size || !latinFontValues.size || !eastAsiaFontValues.size || !complexScriptFontValues.size) addValues(editor.getAttributes('textStyle'));
9281
- const characterScale = selectedValue(scaleValues);
9282
- const characterPosition = selectedValue(positionValues);
9283
- const characterSpacing = selectedValue(spacingValues);
9284
- const kerningThreshold = selectedValue(kerningValues);
9285
- const emphasisMark = selectedValue(emphasisValues);
9286
- const hiddenText = selectedValue(hiddenTextValues);
9287
- const legacyTextOutline = selectedValue(legacyTextOutlineValues);
9288
- const legacyTextShadow = selectedValue(legacyTextShadowValues);
9289
- const legacyTextEmboss = selectedValue(legacyTextEmbossValues);
9290
- const legacyTextImprint = selectedValue(legacyTextImprintValues);
9291
- const latinFont = selectedValue(latinFontValues);
9292
- const eastAsiaFont = selectedValue(eastAsiaFontValues);
9293
- const complexScriptFont = selectedValue(complexScriptFontValues);
9849
+ if (!scaleValues.size || !spacingValues.size || !positionValues.size || !kerningValues.size || !emphasisValues.size || !hiddenTextValues.size || !legacyTextOutlineValues.size || !legacyTextShadowValues.size || !legacyTextEmbossValues.size || !legacyTextImprintValues.size || !runBorderValues.size || !runShadingValues.size || !latinFontValues.size || !eastAsiaFontValues.size || !complexScriptFontValues.size) addValues(editor.getAttributes('textStyle'));
9850
+ const characterScale = document_font_dialog_model_selectedValue(scaleValues);
9851
+ const characterPosition = document_font_dialog_model_selectedValue(positionValues);
9852
+ const characterSpacing = document_font_dialog_model_selectedValue(spacingValues);
9853
+ const kerningThreshold = document_font_dialog_model_selectedValue(kerningValues);
9854
+ const emphasisMark = document_font_dialog_model_selectedValue(emphasisValues);
9855
+ const hiddenText = document_font_dialog_model_selectedValue(hiddenTextValues);
9856
+ const legacyTextOutline = document_font_dialog_model_selectedValue(legacyTextOutlineValues);
9857
+ const legacyTextShadow = document_font_dialog_model_selectedValue(legacyTextShadowValues);
9858
+ const legacyTextEmboss = document_font_dialog_model_selectedValue(legacyTextEmbossValues);
9859
+ const legacyTextImprint = document_font_dialog_model_selectedValue(legacyTextImprintValues);
9860
+ const runBorder = selectedDocumentFontDialogRunBorderValue(runBorderValues);
9861
+ const runShading = selectedDocumentFontDialogRunShadingValue(runShadingValues);
9862
+ const latinFont = document_font_dialog_model_selectedValue(latinFontValues);
9863
+ const eastAsiaFont = document_font_dialog_model_selectedValue(eastAsiaFontValues);
9864
+ const complexScriptFont = document_font_dialog_model_selectedValue(complexScriptFontValues);
9294
9865
  const textStyle = editor.getAttributes('textStyle');
9295
9866
  const selectedText = selection.empty ? '' : doc.textBetween(selection.from, selection.to, ' ', ' ').replace(/\s+/g, ' ').trim();
9296
9867
  return {
@@ -9304,6 +9875,8 @@ function documentFontDialogSource(editor) {
9304
9875
  legacyTextShadow,
9305
9876
  legacyTextEmboss,
9306
9877
  legacyTextImprint,
9878
+ runBorder,
9879
+ runShading,
9307
9880
  latinFont,
9308
9881
  eastAsiaFont,
9309
9882
  complexScriptFont,
@@ -9324,23 +9897,29 @@ function createDocumentFontDialogDraft(source) {
9324
9897
  characterScaleMode: source.characterScale.mixed ? 'mixed' : 'value',
9325
9898
  characterScalePercent: source.characterScale.mixed ? '' : String(scale ?? 100),
9326
9899
  characterPositionMode: source.characterPosition.mixed ? 'mixed' : null !== position && position < 0 ? 'lowered' : null !== position && position > 0 ? 'raised' : 'normal',
9327
- characterPositionPoints: formatPoints(positionPoints || 1),
9900
+ characterPositionPoints: document_font_dialog_model_formatPoints(positionPoints || 1),
9328
9901
  characterSpacingMode: source.characterSpacing.mixed ? 'mixed' : null !== spacing && spacing < 0 ? 'condensed' : null !== spacing && spacing > 0 ? 'expanded' : 'normal',
9329
- characterSpacingPoints: formatPoints(spacingPoints || 1),
9902
+ characterSpacingPoints: document_font_dialog_model_formatPoints(spacingPoints || 1),
9330
9903
  kerningEnabled: !source.kerningThreshold.mixed && null !== kerningThreshold,
9331
- kerningThresholdPoints: formatPoints(documentKerningThresholdPoints(kerningThreshold) ?? 12),
9904
+ kerningThresholdPoints: document_font_dialog_model_formatPoints(documentKerningThresholdPoints(kerningThreshold) ?? 12),
9332
9905
  emphasisMark: source.emphasisMark.mixed ? 'mixed' : source.emphasisMark.value ?? 'inherit',
9333
9906
  hiddenText: source.hiddenText.value ?? false,
9334
9907
  legacyTextOutline: source.legacyTextOutline.value ?? false,
9335
9908
  legacyTextShadow: source.legacyTextShadow.value ?? false,
9336
9909
  legacyTextEmboss: source.legacyTextEmboss.value ?? false,
9337
9910
  legacyTextImprint: source.legacyTextImprint.value ?? false,
9911
+ ...createDocumentFontDialogRunBorderDraft(source.runBorder),
9912
+ ...createDocumentFontDialogRunShadingDraft(source.runShading),
9338
9913
  latinFont: documentFontFamilyDraftValue(source.latinFont),
9339
9914
  eastAsiaFont: documentFontFamilyDraftValue(source.eastAsiaFont),
9340
9915
  complexScriptFont: documentFontFamilyDraftValue(source.complexScriptFont)
9341
9916
  };
9342
9917
  }
9343
9918
  function documentFontDialogDraftError(draft) {
9919
+ const runBorderError = documentFontDialogRunBorderDraftError(draft);
9920
+ if (runBorderError) return runBorderError;
9921
+ const runShadingError = documentFontDialogRunShadingDraftError(draft);
9922
+ if (runShadingError) return runShadingError;
9344
9923
  if ('mixed' !== draft.characterScaleMode && null === characterScalePercentFromDraft(draft)) return "请输入 1 至 600 的整数缩放比例。";
9345
9924
  if ('mixed' !== draft.characterSpacingMode && 'normal' !== draft.characterSpacingMode && null === characterSpacingTwipsFromDraft(draft)) return "请输入 0.05 至 1584 磅的间距。";
9346
9925
  if ('mixed' !== draft.characterPositionMode && 'normal' !== draft.characterPositionMode && null === characterPositionHalfPointsFromDraft(draft)) return "请输入 0.5 至 1584 磅、以 0.5 磅递增的位置值。";
@@ -9375,6 +9954,8 @@ function documentFontDialogPatch(source, draft, touched) {
9375
9954
  }
9376
9955
  if (touched.hiddenText && (source.hiddenText.mixed || source.hiddenText.value !== draft.hiddenText)) patch.hiddenText = draft.hiddenText;
9377
9956
  appendLegacyTextEffectPatch(patch, 'legacyTextOutline', source.legacyTextOutline, draft.legacyTextOutline, touched.legacyTextOutline);
9957
+ Object.assign(patch, documentFontDialogRunBorderPatch(source.runBorder, draft, touched.runBorder));
9958
+ Object.assign(patch, documentFontDialogRunShadingPatch(source.runShading, draft, touched.runShading));
9378
9959
  appendLegacyTextEffectPatch(patch, 'legacyTextShadow', source.legacyTextShadow, draft.legacyTextShadow, touched.legacyTextShadow);
9379
9960
  appendLegacyTextEffectPatch(patch, 'legacyTextEmboss', source.legacyTextEmboss, draft.legacyTextEmboss, touched.legacyTextEmboss);
9380
9961
  appendLegacyTextEffectPatch(patch, 'legacyTextImprint', source.legacyTextImprint, draft.legacyTextImprint, touched.legacyTextImprint);
@@ -9394,6 +9975,8 @@ function applyDocumentFontDialogPatch(editor, selection, patch) {
9394
9975
  const hasLegacyTextShadow = void 0 !== patch.legacyTextShadow;
9395
9976
  const hasLegacyTextEmboss = void 0 !== patch.legacyTextEmboss;
9396
9977
  const hasLegacyTextImprint = void 0 !== patch.legacyTextImprint;
9978
+ const hasRunBorder = void 0 !== patch.runBorder;
9979
+ const hasRunShading = void 0 !== patch.runShading;
9397
9980
  const hasLegacyTextEffects = hasLegacyTextOutline || hasLegacyTextShadow || hasLegacyTextEmboss || hasLegacyTextImprint;
9398
9981
  const scriptFontPatch = {
9399
9982
  ...void 0 !== patch.latinFont ? {
@@ -9412,8 +9995,10 @@ function applyDocumentFontDialogPatch(editor, selection, patch) {
9412
9995
  const spacing = hasSpacing ? normalizeDocumentCharacterSpacingTwips(patch.characterSpacingTwips) : null;
9413
9996
  const kerningThreshold = hasKerning && null !== patch.kerningThresholdHalfPoints ? normalizeDocumentKerningThresholdHalfPoints(patch.kerningThresholdHalfPoints) : null;
9414
9997
  const emphasisMark = hasEmphasis && null !== patch.emphasisMark ? normalizeDocumentEmphasisMark(patch.emphasisMark) : null;
9998
+ const runBorder = hasRunBorder && null !== patch.runBorder ? normalizeDocumentRunBorder(patch.runBorder) : null;
9999
+ const runShading = hasRunShading && null !== patch.runShading ? normalizeDocumentRunShading(patch.runShading) : null;
9415
10000
  const maximum = editor.state.doc.content.size;
9416
- if (editor.isDestroyed || !hasScale && !hasPosition && !hasSpacing && !hasKerning && !hasEmphasis && !hasHiddenText && !hasLegacyTextEffects && !hasScriptFonts || hasScale && null === scale || hasPosition && null === position || hasSpacing && null === spacing || hasKerning && null !== patch.kerningThresholdHalfPoints && null === kerningThreshold || hasEmphasis && null !== patch.emphasisMark && null === emphasisMark || hasHiddenText && 'boolean' != typeof patch.hiddenText || hasLegacyTextOutline && null === normalizeDocumentLegacyTextEffect(patch.legacyTextOutline) || hasLegacyTextShadow && null === normalizeDocumentLegacyTextEffect(patch.legacyTextShadow) || hasLegacyTextEmboss && null === normalizeDocumentLegacyTextEffect(patch.legacyTextEmboss) || hasLegacyTextImprint && null === normalizeDocumentLegacyTextEffect(patch.legacyTextImprint) || !Number.isSafeInteger(selection.from) || !Number.isSafeInteger(selection.to) || selection.from < 0 || selection.to < selection.from || selection.to > maximum || hasLegacyTextEffects && !documentFontDialogLegacyTextEffectsAreSafe(editor, selection, patch)) return false;
10001
+ if (editor.isDestroyed || !hasScale && !hasPosition && !hasSpacing && !hasKerning && !hasEmphasis && !hasHiddenText && !hasLegacyTextEffects && !hasRunBorder && !hasRunShading && !hasScriptFonts || hasScale && null === scale || hasPosition && null === position || hasSpacing && null === spacing || hasKerning && null !== patch.kerningThresholdHalfPoints && null === kerningThreshold || hasEmphasis && null !== patch.emphasisMark && null === emphasisMark || hasRunBorder && null !== patch.runBorder && null === runBorder || hasRunShading && null !== patch.runShading && null === runShading || hasHiddenText && 'boolean' != typeof patch.hiddenText || hasLegacyTextOutline && null === normalizeDocumentLegacyTextEffect(patch.legacyTextOutline) || hasLegacyTextShadow && null === normalizeDocumentLegacyTextEffect(patch.legacyTextShadow) || hasLegacyTextEmboss && null === normalizeDocumentLegacyTextEffect(patch.legacyTextEmboss) || hasLegacyTextImprint && null === normalizeDocumentLegacyTextEffect(patch.legacyTextImprint) || !Number.isSafeInteger(selection.from) || !Number.isSafeInteger(selection.to) || selection.from < 0 || selection.to < selection.from || selection.to > maximum || hasLegacyTextEffects && !documentFontDialogLegacyTextEffectsAreSafe(editor, selection, patch)) return false;
9417
10002
  const attributes = {};
9418
10003
  if (null !== scale) attributes.characterScalePercent = scale;
9419
10004
  if (null !== position) attributes.characterPositionHalfPoints = position;
@@ -9425,6 +10010,8 @@ function applyDocumentFontDialogPatch(editor, selection, patch) {
9425
10010
  if (hasLegacyTextShadow) attributes.legacyTextShadow = patch.legacyTextShadow ?? false;
9426
10011
  if (hasLegacyTextEmboss) attributes.legacyTextEmboss = patch.legacyTextEmboss ?? false;
9427
10012
  if (hasLegacyTextImprint) attributes.legacyTextImprint = patch.legacyTextImprint ?? false;
10013
+ if (hasRunBorder) attributes.runBorder = null === patch.runBorder ? null : serializeDocumentRunBorder(runBorder) ?? null;
10014
+ if (hasRunShading) attributes.runShading = null === patch.runShading ? null : serializeDocumentRunShading(runShading) ?? null;
9428
10015
  if (hasScriptFonts) return applyDocumentScriptFontPatch(editor, selection, attributes, scriptFontPatch);
9429
10016
  return editor.chain().setTextSelection(selection).setMark('textStyle', attributes).removeEmptyTextStyle().focus().scrollIntoView().run();
9430
10017
  }
@@ -9522,15 +10109,538 @@ function kerningThresholdHalfPointsFromDraft(draft) {
9522
10109
  if (!Number.isFinite(points) || points < 0 || points > 1638.5 || !Number.isSafeInteger(halfPoints)) return null;
9523
10110
  return normalizeDocumentKerningThresholdHalfPoints(halfPoints);
9524
10111
  }
9525
- function selectedValue(values) {
10112
+ function document_font_dialog_model_selectedValue(values) {
9526
10113
  const mixed = values.size > 1;
9527
10114
  return {
9528
10115
  mixed,
9529
10116
  value: mixed ? null : values.values().next().value ?? null
9530
10117
  };
9531
- }
9532
- function formatPoints(value) {
9533
- return String(Number(value.toFixed(2)));
10118
+ }
10119
+ function document_font_dialog_model_formatPoints(value) {
10120
+ return String(Number(value.toFixed(2)));
10121
+ }
10122
+ const runBorderModes = [
10123
+ {
10124
+ value: 'mixed',
10125
+ label: '混合(保持不变)',
10126
+ disabled: true
10127
+ },
10128
+ {
10129
+ value: 'inherit',
10130
+ label: '跟随样式'
10131
+ },
10132
+ {
10133
+ value: 'none',
10134
+ label: '无(显式重置)'
10135
+ },
10136
+ {
10137
+ value: 'value',
10138
+ label: '边框'
10139
+ }
10140
+ ];
10141
+ const runBorderStyles = [
10142
+ {
10143
+ value: 'single',
10144
+ label: '单实线'
10145
+ },
10146
+ {
10147
+ value: 'thick',
10148
+ label: '粗实线'
10149
+ },
10150
+ {
10151
+ value: 'double',
10152
+ label: '双线'
10153
+ },
10154
+ {
10155
+ value: 'dotted',
10156
+ label: '点线'
10157
+ },
10158
+ {
10159
+ value: 'dashed',
10160
+ label: '虚线'
10161
+ },
10162
+ {
10163
+ value: 'dotDash',
10164
+ label: '点划线'
10165
+ },
10166
+ {
10167
+ value: 'dotDotDash',
10168
+ label: '双点划线'
10169
+ },
10170
+ {
10171
+ value: 'triple',
10172
+ label: '三线'
10173
+ },
10174
+ {
10175
+ value: 'thinThickSmallGap',
10176
+ label: '细粗线(小间距)'
10177
+ },
10178
+ {
10179
+ value: 'thickThinSmallGap',
10180
+ label: '粗细线(小间距)'
10181
+ },
10182
+ {
10183
+ value: 'thinThickThinSmallGap',
10184
+ label: '细粗细线(小间距)'
10185
+ },
10186
+ {
10187
+ value: 'thinThickMediumGap',
10188
+ label: '细粗线(中间距)'
10189
+ },
10190
+ {
10191
+ value: 'thickThinMediumGap',
10192
+ label: '粗细线(中间距)'
10193
+ },
10194
+ {
10195
+ value: 'thinThickThinMediumGap',
10196
+ label: '细粗细线(中间距)'
10197
+ },
10198
+ {
10199
+ value: 'thinThickLargeGap',
10200
+ label: '细粗线(大间距)'
10201
+ },
10202
+ {
10203
+ value: 'thickThinLargeGap',
10204
+ label: '粗细线(大间距)'
10205
+ },
10206
+ {
10207
+ value: 'thinThickThinLargeGap',
10208
+ label: '细粗细线(大间距)'
10209
+ },
10210
+ {
10211
+ value: 'wave',
10212
+ label: '波浪线'
10213
+ },
10214
+ {
10215
+ value: 'doubleWave',
10216
+ label: '双波浪线'
10217
+ },
10218
+ {
10219
+ value: 'dashSmallGap',
10220
+ label: '短划线'
10221
+ },
10222
+ {
10223
+ value: 'dashDotStroked',
10224
+ label: '粗点划线'
10225
+ },
10226
+ {
10227
+ value: 'threeDEmboss',
10228
+ label: '三维浮雕'
10229
+ },
10230
+ {
10231
+ value: 'threeDEngrave',
10232
+ label: '三维凹刻'
10233
+ },
10234
+ {
10235
+ value: 'outset',
10236
+ label: '外凸'
10237
+ },
10238
+ {
10239
+ value: 'inset',
10240
+ label: '内凹'
10241
+ }
10242
+ ];
10243
+ function DocumentFontDialogRunBorderSection({ source, draft, touched, onDraftChange, onTouched }) {
10244
+ const enabled = 'value' === draft.runBorderMode;
10245
+ const update = (patch)=>{
10246
+ onDraftChange(patch);
10247
+ onTouched();
10248
+ };
10249
+ return /*#__PURE__*/ jsxs("fieldset", {
10250
+ className: "work-document-font-dialog-run-border",
10251
+ "aria-label": "字符边框设置",
10252
+ children: [
10253
+ /*#__PURE__*/ jsx("legend", {
10254
+ children: "字符边框"
10255
+ }),
10256
+ /*#__PURE__*/ jsxs("div", {
10257
+ className: "work-document-font-dialog-field",
10258
+ children: [
10259
+ /*#__PURE__*/ jsx("span", {
10260
+ children: "应用方式"
10261
+ }),
10262
+ /*#__PURE__*/ jsx(OfficeSelect, {
10263
+ ariaLabel: "字符边框",
10264
+ value: draft.runBorderMode,
10265
+ options: runBorderModes,
10266
+ onValueChange: (runBorderMode)=>update({
10267
+ runBorderMode
10268
+ })
10269
+ })
10270
+ ]
10271
+ }),
10272
+ /*#__PURE__*/ jsxs("div", {
10273
+ className: "work-document-font-dialog-field",
10274
+ children: [
10275
+ /*#__PURE__*/ jsx("span", {
10276
+ children: "线型"
10277
+ }),
10278
+ /*#__PURE__*/ jsx(OfficeSelect, {
10279
+ ariaLabel: "字符边框线型",
10280
+ value: draft.runBorderStyle,
10281
+ options: runBorderStyles,
10282
+ disabled: !enabled,
10283
+ onValueChange: (runBorderStyle)=>update({
10284
+ runBorderMode: 'value',
10285
+ runBorderStyle
10286
+ })
10287
+ })
10288
+ ]
10289
+ }),
10290
+ /*#__PURE__*/ jsxs("div", {
10291
+ className: "work-document-font-dialog-field",
10292
+ children: [
10293
+ /*#__PURE__*/ jsx("span", {
10294
+ children: "颜色"
10295
+ }),
10296
+ /*#__PURE__*/ jsx(OfficeColorPicker, {
10297
+ ariaLabel: "字符边框颜色",
10298
+ value: 'auto' === draft.runBorderColor ? '#000000' : draft.runBorderColor,
10299
+ disabled: !enabled,
10300
+ resetAction: {
10301
+ kind: 'automatic',
10302
+ label: '自动颜色',
10303
+ onSelect: ()=>update({
10304
+ runBorderMode: 'value',
10305
+ runBorderColor: 'auto'
10306
+ })
10307
+ },
10308
+ onValueChange: (runBorderColor)=>update({
10309
+ runBorderMode: 'value',
10310
+ runBorderColor: runBorderColor
10311
+ })
10312
+ })
10313
+ ]
10314
+ }),
10315
+ /*#__PURE__*/ jsxs("div", {
10316
+ className: "work-document-font-dialog-field",
10317
+ children: [
10318
+ /*#__PURE__*/ jsx("span", {
10319
+ children: "宽度"
10320
+ }),
10321
+ /*#__PURE__*/ jsxs("span", {
10322
+ className: "work-document-font-dialog-measure",
10323
+ children: [
10324
+ /*#__PURE__*/ jsx(OfficeNumberField, {
10325
+ ariaLabel: "字符边框宽度(磅)",
10326
+ value: draft.runBorderWidthPoints,
10327
+ min: 0.25,
10328
+ max: 12,
10329
+ step: 0.125,
10330
+ disabled: !enabled,
10331
+ onValueChange: (runBorderWidthPoints)=>update({
10332
+ runBorderMode: 'value',
10333
+ runBorderWidthPoints
10334
+ })
10335
+ }),
10336
+ /*#__PURE__*/ jsx("span", {
10337
+ "aria-hidden": "true",
10338
+ children: "磅"
10339
+ })
10340
+ ]
10341
+ })
10342
+ ]
10343
+ }),
10344
+ /*#__PURE__*/ jsxs("div", {
10345
+ className: "work-document-font-dialog-field",
10346
+ children: [
10347
+ /*#__PURE__*/ jsx("span", {
10348
+ children: "文字间距"
10349
+ }),
10350
+ /*#__PURE__*/ jsxs("span", {
10351
+ className: "work-document-font-dialog-measure",
10352
+ children: [
10353
+ /*#__PURE__*/ jsx(OfficeNumberField, {
10354
+ ariaLabel: "字符边框间距(磅)",
10355
+ value: draft.runBorderSpacingPoints,
10356
+ min: 0,
10357
+ max: 31,
10358
+ step: 1,
10359
+ disabled: !enabled,
10360
+ onValueChange: (runBorderSpacingPoints)=>update({
10361
+ runBorderMode: 'value',
10362
+ runBorderSpacingPoints
10363
+ })
10364
+ }),
10365
+ /*#__PURE__*/ jsx("span", {
10366
+ "aria-hidden": "true",
10367
+ children: "磅"
10368
+ })
10369
+ ]
10370
+ })
10371
+ ]
10372
+ }),
10373
+ /*#__PURE__*/ jsxs("div", {
10374
+ className: "work-document-font-dialog-run-border-effects",
10375
+ children: [
10376
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
10377
+ ariaLabel: "字符边框阴影",
10378
+ checked: draft.runBorderShadow,
10379
+ disabled: !enabled,
10380
+ onCheckedChange: (runBorderShadow)=>update({
10381
+ runBorderMode: 'value',
10382
+ runBorderShadow
10383
+ }),
10384
+ children: "阴影"
10385
+ }),
10386
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
10387
+ ariaLabel: "字符边框框架",
10388
+ checked: draft.runBorderFrame,
10389
+ disabled: !enabled,
10390
+ onCheckedChange: (runBorderFrame)=>update({
10391
+ runBorderMode: 'value',
10392
+ runBorderFrame
10393
+ }),
10394
+ children: "框架"
10395
+ })
10396
+ ]
10397
+ }),
10398
+ source.mixed && !touched ? /*#__PURE__*/ jsx("p", {
10399
+ className: "work-document-font-dialog-mixed",
10400
+ role: "status",
10401
+ children: "当前选区包含不同的字符边框。选择一种应用方式后才会统一修改。"
10402
+ }) : null
10403
+ ]
10404
+ });
10405
+ }
10406
+ function documentFontDialogRunBorderPreviewStyle(source, draft) {
10407
+ const border = documentFontDialogRunBorderFromDraft(source, draft);
10408
+ if (!border) return {};
10409
+ const presentation = documentBorderPresentation(border);
10410
+ return {
10411
+ border: `${presentation.width}px ${presentation.style} ${presentation.color}`,
10412
+ padding: `${(border.space ?? 0) * (96 / 72)}px`,
10413
+ boxDecorationBreak: 'clone',
10414
+ WebkitBoxDecorationBreak: 'clone',
10415
+ ...border.shadow && presentation.width > 0 ? {
10416
+ boxShadow: `2px 2px 0 ${presentation.color}`
10417
+ } : {}
10418
+ };
10419
+ }
10420
+ const shadingModes = [
10421
+ {
10422
+ value: 'mixed',
10423
+ label: '混合(保持不变)',
10424
+ disabled: true
10425
+ },
10426
+ {
10427
+ value: 'inherit',
10428
+ label: '跟随样式'
10429
+ },
10430
+ {
10431
+ value: 'none',
10432
+ label: '无(显式重置)'
10433
+ },
10434
+ {
10435
+ value: 'value',
10436
+ label: '底纹'
10437
+ }
10438
+ ];
10439
+ const shadingPatterns = [
10440
+ {
10441
+ value: 'clear',
10442
+ label: '清除(背景色)',
10443
+ group: '基本'
10444
+ },
10445
+ {
10446
+ value: 'solid',
10447
+ label: '实心(前景色)',
10448
+ group: '基本'
10449
+ },
10450
+ {
10451
+ value: 'horzStripe',
10452
+ label: '水平条纹',
10453
+ group: '条纹'
10454
+ },
10455
+ {
10456
+ value: 'vertStripe',
10457
+ label: '垂直条纹',
10458
+ group: '条纹'
10459
+ },
10460
+ {
10461
+ value: 'reverseDiagStripe',
10462
+ label: '反向对角条纹',
10463
+ group: '条纹'
10464
+ },
10465
+ {
10466
+ value: 'diagStripe',
10467
+ label: '对角条纹',
10468
+ group: '条纹'
10469
+ },
10470
+ {
10471
+ value: 'horzCross',
10472
+ label: '水平交叉',
10473
+ group: '交叉'
10474
+ },
10475
+ {
10476
+ value: 'diagCross',
10477
+ label: '对角交叉',
10478
+ group: '交叉'
10479
+ },
10480
+ {
10481
+ value: 'thinHorzStripe',
10482
+ label: '细水平条纹',
10483
+ group: '细线'
10484
+ },
10485
+ {
10486
+ value: 'thinVertStripe',
10487
+ label: '细垂直条纹',
10488
+ group: '细线'
10489
+ },
10490
+ {
10491
+ value: 'thinReverseDiagStripe',
10492
+ label: '细反向对角条纹',
10493
+ group: '细线'
10494
+ },
10495
+ {
10496
+ value: 'thinDiagStripe',
10497
+ label: '细对角条纹',
10498
+ group: '细线'
10499
+ },
10500
+ {
10501
+ value: 'thinHorzCross',
10502
+ label: '细水平交叉',
10503
+ group: '细线'
10504
+ },
10505
+ {
10506
+ value: 'thinDiagCross',
10507
+ label: '细对角交叉',
10508
+ group: '细线'
10509
+ },
10510
+ ...[
10511
+ 5,
10512
+ 10,
10513
+ 12,
10514
+ 15,
10515
+ 20,
10516
+ 25,
10517
+ 30,
10518
+ 35,
10519
+ 37,
10520
+ 40,
10521
+ 45,
10522
+ 50,
10523
+ 55,
10524
+ 60,
10525
+ 62,
10526
+ 65,
10527
+ 70,
10528
+ 75,
10529
+ 80,
10530
+ 85,
10531
+ 87,
10532
+ 90,
10533
+ 95
10534
+ ].map((percentage)=>({
10535
+ value: `pct${percentage}`,
10536
+ label: `${percentage}%`,
10537
+ group: '密度'
10538
+ }))
10539
+ ];
10540
+ function DocumentFontDialogRunShadingSection({ source, draft, touched, onDraftChange, onTouched }) {
10541
+ const enabled = 'value' === draft.runShadingMode;
10542
+ const update = (patch)=>{
10543
+ onDraftChange(patch);
10544
+ onTouched();
10545
+ };
10546
+ return /*#__PURE__*/ jsxs("fieldset", {
10547
+ className: "work-document-font-dialog-run-shading",
10548
+ "aria-label": "字符底纹设置",
10549
+ children: [
10550
+ /*#__PURE__*/ jsx("legend", {
10551
+ children: "字符底纹"
10552
+ }),
10553
+ /*#__PURE__*/ jsxs("div", {
10554
+ className: "work-document-font-dialog-field",
10555
+ children: [
10556
+ /*#__PURE__*/ jsx("span", {
10557
+ children: "应用方式"
10558
+ }),
10559
+ /*#__PURE__*/ jsx(OfficeSelect, {
10560
+ ariaLabel: "字符底纹",
10561
+ value: draft.runShadingMode,
10562
+ options: shadingModes,
10563
+ onValueChange: (runShadingMode)=>update({
10564
+ runShadingMode
10565
+ })
10566
+ })
10567
+ ]
10568
+ }),
10569
+ /*#__PURE__*/ jsxs("div", {
10570
+ className: "work-document-font-dialog-field",
10571
+ children: [
10572
+ /*#__PURE__*/ jsx("span", {
10573
+ children: "图案"
10574
+ }),
10575
+ /*#__PURE__*/ jsx(OfficeSelect, {
10576
+ ariaLabel: "字符底纹图案",
10577
+ value: draft.runShadingPattern,
10578
+ options: shadingPatterns,
10579
+ disabled: !enabled,
10580
+ onValueChange: (runShadingPattern)=>update({
10581
+ runShadingMode: 'value',
10582
+ runShadingPattern
10583
+ })
10584
+ })
10585
+ ]
10586
+ }),
10587
+ /*#__PURE__*/ jsxs("div", {
10588
+ className: "work-document-font-dialog-field",
10589
+ children: [
10590
+ /*#__PURE__*/ jsx("span", {
10591
+ children: "前景色"
10592
+ }),
10593
+ /*#__PURE__*/ jsx(OfficeColorPicker, {
10594
+ ariaLabel: "字符底纹前景色",
10595
+ value: 'auto' === draft.runShadingColor ? '#000000' : draft.runShadingColor,
10596
+ disabled: !enabled,
10597
+ resetAction: {
10598
+ kind: 'automatic',
10599
+ label: '自动前景色',
10600
+ onSelect: ()=>update({
10601
+ runShadingMode: 'value',
10602
+ runShadingColor: 'auto'
10603
+ })
10604
+ },
10605
+ onValueChange: (runShadingColor)=>update({
10606
+ runShadingMode: 'value',
10607
+ runShadingColor: runShadingColor
10608
+ })
10609
+ })
10610
+ ]
10611
+ }),
10612
+ /*#__PURE__*/ jsxs("div", {
10613
+ className: "work-document-font-dialog-field",
10614
+ children: [
10615
+ /*#__PURE__*/ jsx("span", {
10616
+ children: "背景色"
10617
+ }),
10618
+ /*#__PURE__*/ jsx(OfficeColorPicker, {
10619
+ ariaLabel: "字符底纹背景色",
10620
+ value: 'auto' === draft.runShadingFill ? '#ffffff' : draft.runShadingFill,
10621
+ disabled: !enabled,
10622
+ resetAction: {
10623
+ kind: 'automatic',
10624
+ label: '自动背景色',
10625
+ onSelect: ()=>update({
10626
+ runShadingMode: 'value',
10627
+ runShadingFill: 'auto'
10628
+ })
10629
+ },
10630
+ onValueChange: (runShadingFill)=>update({
10631
+ runShadingMode: 'value',
10632
+ runShadingFill: runShadingFill
10633
+ })
10634
+ })
10635
+ ]
10636
+ }),
10637
+ source.mixed && !touched ? /*#__PURE__*/ jsx("p", {
10638
+ className: "work-document-font-dialog-mixed",
10639
+ role: "status",
10640
+ children: "当前选区包含不同的字符底纹。选择一种应用方式后才会统一修改。"
10641
+ }) : null
10642
+ ]
10643
+ });
9534
10644
  }
9535
10645
  const characterSpacingModes = [
9536
10646
  {
@@ -9613,6 +10723,8 @@ function DocumentFontDialog({ source, layoutFonts = [], restoreFocusTarget, onAp
9613
10723
  const [legacyTextShadowTouched, setLegacyTextShadowTouched] = useState(false);
9614
10724
  const [legacyTextEmbossTouched, setLegacyTextEmbossTouched] = useState(false);
9615
10725
  const [legacyTextImprintTouched, setLegacyTextImprintTouched] = useState(false);
10726
+ const [runBorderTouched, setRunBorderTouched] = useState(false);
10727
+ const [runShadingTouched, setRunShadingTouched] = useState(false);
9616
10728
  const [latinFontTouched, setLatinFontTouched] = useState(false);
9617
10729
  const [eastAsiaFontTouched, setEastAsiaFontTouched] = useState(false);
9618
10730
  const [complexScriptFontTouched, setComplexScriptFontTouched] = useState(false);
@@ -9630,6 +10742,8 @@ function DocumentFontDialog({ source, layoutFonts = [], restoreFocusTarget, onAp
9630
10742
  legacyTextShadow: legacyTextShadowTouched,
9631
10743
  legacyTextEmboss: legacyTextEmbossTouched,
9632
10744
  legacyTextImprint: legacyTextImprintTouched,
10745
+ runBorder: runBorderTouched,
10746
+ runShading: runShadingTouched,
9633
10747
  kerning: kerningTouched,
9634
10748
  latinFont: latinFontTouched
9635
10749
  });
@@ -9645,6 +10759,8 @@ function DocumentFontDialog({ source, layoutFonts = [], restoreFocusTarget, onAp
9645
10759
  emboss: draft.legacyTextEmboss,
9646
10760
  imprint: draft.legacyTextImprint
9647
10761
  });
10762
+ const previewRunBorder = documentFontDialogRunBorderPreviewStyle(source.runBorder, draft);
10763
+ const previewRunShading = documentFontDialogRunShadingPreviewStyle(source.runShading, draft);
9648
10764
  const submit = (event)=>{
9649
10765
  event?.preventDefault();
9650
10766
  if (!hasChanges || error) return;
@@ -10084,6 +11200,26 @@ function DocumentFontDialog({ source, layoutFonts = [], restoreFocusTarget, onAp
10084
11200
  })
10085
11201
  ]
10086
11202
  }),
11203
+ /*#__PURE__*/ jsx(DocumentFontDialogRunBorderSection, {
11204
+ source: source.runBorder,
11205
+ draft: draft,
11206
+ touched: runBorderTouched,
11207
+ onDraftChange: (runBorderDraft)=>setDraft((current)=>({
11208
+ ...current,
11209
+ ...runBorderDraft
11210
+ })),
11211
+ onTouched: ()=>setRunBorderTouched(true)
11212
+ }),
11213
+ /*#__PURE__*/ jsx(DocumentFontDialogRunShadingSection, {
11214
+ source: source.runShading,
11215
+ draft: draft,
11216
+ touched: runShadingTouched,
11217
+ onDraftChange: (runShadingDraft)=>setDraft((current)=>({
11218
+ ...current,
11219
+ ...runShadingDraft
11220
+ })),
11221
+ onTouched: ()=>setRunShadingTouched(true)
11222
+ }),
10087
11223
  source.characterScale.mixed && !characterScaleTouched && /*#__PURE__*/ jsx("p", {
10088
11224
  className: "work-document-font-dialog-mixed",
10089
11225
  role: "status",
@@ -10119,7 +11255,7 @@ function DocumentFontDialog({ source, layoutFonts = [], restoreFocusTarget, onAp
10119
11255
  role: "status",
10120
11256
  children: "当前选区包含不同的空心、阴影、阳文或阴文设置。勾选或取消对应选项后才会统一修改。"
10121
11257
  }),
10122
- (characterScaleTouched || characterSpacingTouched || characterPositionTouched || kerningTouched || emphasisTouched || hiddenTextTouched || legacyTextOutlineTouched || legacyTextShadowTouched || legacyTextEmbossTouched || legacyTextImprintTouched) && error && /*#__PURE__*/ jsx("p", {
11258
+ (characterScaleTouched || characterSpacingTouched || characterPositionTouched || kerningTouched || emphasisTouched || hiddenTextTouched || legacyTextOutlineTouched || legacyTextShadowTouched || legacyTextEmbossTouched || legacyTextImprintTouched || runBorderTouched || runShadingTouched) && error && /*#__PURE__*/ jsx("p", {
10123
11259
  className: "work-document-font-dialog-error",
10124
11260
  role: "alert",
10125
11261
  children: error
@@ -10146,6 +11282,8 @@ function DocumentFontDialog({ source, layoutFonts = [], restoreFocusTarget, onAp
10146
11282
  verticalAlign: `${previewPosition}pt`,
10147
11283
  ...previewEmphasis,
10148
11284
  ...previewLegacyTextEffects,
11285
+ ...previewRunShading,
11286
+ ...previewRunBorder,
10149
11287
  ...draft.hiddenText ? {
10150
11288
  textDecorationColor: 'currentColor',
10151
11289
  textDecorationLine: 'underline',
@@ -10225,7 +11363,7 @@ function previewScriptFontFamily(value, fallback) {
10225
11363
  function fontDialogDescription(source) {
10226
11364
  return source.selectedCharacters ? `分别设置当前选中内容的拉丁、东亚和复杂文字字体,以及原生字符缩放、间距、字距调整阈值、位置、着重号、隐藏文字、空心、阴影、阳文和阴文(${source.selectedCharacters} 个字符)。` : '分别设置当前位置后续输入文字的拉丁、东亚和复杂文字字体,以及原生字符缩放、间距、字距调整阈值、位置、着重号、隐藏文字、空心、阴影、阳文和阴文。';
10227
11365
  }
10228
- function DocumentPageChromeRibbon({ editor, editingPart, showPageNumber, onEditingPartChange, onTogglePageNumber, onOpenFontDialog, onClose }) {
11366
+ function DocumentPageChromeRibbon({ editor, editingPart, showPageNumber, onEditingPartChange, onTogglePageNumber, onOpenFontDialog, onOpenProofingDialog, onClose }) {
10229
11367
  const imageInputRef = useRef(null);
10230
11368
  const [, setRevision] = useState(0);
10231
11369
  const officeDialog = useOfficeDialog();
@@ -10383,6 +11521,13 @@ function DocumentPageChromeRibbon({ editor, editingPart, showPageNumber, onEditi
10383
11521
  ariaLabel: "页眉页脚文字颜色",
10384
11522
  value: pickerColor(state.color),
10385
11523
  onValueChange: (color)=>editor.chain().focus().setColor(color).run()
11524
+ }),
11525
+ onOpenProofingDialog && /*#__PURE__*/ jsx(PageChromeRibbonButton, {
11526
+ label: "页眉页脚校对语言",
11527
+ onClick: onOpenProofingDialog,
11528
+ children: /*#__PURE__*/ jsx(Languages, {
11529
+ size: 16
11530
+ })
10386
11531
  })
10387
11532
  ]
10388
11533
  }),
@@ -13797,15 +14942,17 @@ function headingLevelForShortcut(code, key) {
13797
14942
  if ('3' === value) return 3;
13798
14943
  return null;
13799
14944
  }
13800
- 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, citationsOpen, citationSourceCount, onToggleCitations, onInsertField, onRefreshFields, canInsertComment, onInsertComment, commentsOpen, commentCount, onToggleComments, trackChanges, changesOpen, changeCount, findReplaceMode, fileActions, onRibbonTabChange, onToggleTrackChanges, onToggleChanges, onDecideChange, onOpenWordCount, onOpenFindReplace }) {
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 }) {
13801
14946
  const [activeTab, setActiveTab] = useState(reviewOnly ? 'review' : 'home');
13802
14947
  const [fontDialogRequest, setFontDialogRequest] = useState(null);
14948
+ const [proofingDialogRequest, setProofingDialogRequest] = useState(null);
13803
14949
  const officeDialog = useOfficeDialog();
13804
14950
  const prompt = officeDialog.prompt;
13805
14951
  const imageSelected = editor.isActive('image');
13806
14952
  const tableSelected = editor.isActive('table');
13807
14953
  const activeBookmark = activeDocumentBookmark(editor);
13808
14954
  const hasRefreshableFields = documentHasRefreshableFields(editor);
14955
+ const hasTableOfContents = documentHasTableOfContents(editor);
13809
14956
  const documentChanges = collectDocumentChanges(editor.state.doc);
13810
14957
  const previousChangeIndex = adjacentDocumentChangeIndex(documentChanges, editor.state.selection, -1);
13811
14958
  const nextChangeIndex = adjacentDocumentChangeIndex(documentChanges, editor.state.selection, 1);
@@ -13828,6 +14975,18 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly =
13828
14975
  source: documentFontDialogSource(target)
13829
14976
  });
13830
14977
  }, []);
14978
+ const openProofingDialog = useCallback((target)=>{
14979
+ if (target.isDestroyed) return;
14980
+ const { from, to } = target.state.selection;
14981
+ setProofingDialogRequest({
14982
+ editor: target,
14983
+ selection: {
14984
+ from,
14985
+ to
14986
+ },
14987
+ source: documentProofingDialogSource(target)
14988
+ });
14989
+ }, []);
13831
14990
  const ribbonTabs = reviewOnly ? documentRibbonTabs.filter(({ id })=>'review' === id || 'view' === id) : pageChromeEditor ? [
13832
14991
  ...documentRibbonTabs,
13833
14992
  documentPageChromeRibbonTab
@@ -14203,6 +15362,30 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly =
14203
15362
  }),
14204
15363
  references: reviewOnly ? null : /*#__PURE__*/ jsxs(Fragment, {
14205
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
+ }),
14206
15389
  /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
14207
15390
  label: "脚注",
14208
15391
  priority: "high",
@@ -14289,20 +15472,30 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly =
14289
15472
  }),
14290
15473
  review: /*#__PURE__*/ jsxs(Fragment, {
14291
15474
  children: [
14292
- !reviewOnly && /*#__PURE__*/ jsx(document_toolbar_RibbonGroup, {
15475
+ !reviewOnly && /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
14293
15476
  label: "校对",
14294
15477
  priority: "high",
14295
- children: /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
14296
- label: "拼写检查",
14297
- displayLabel: true,
14298
- shortcut: spellingCommand.shortcut?.label,
14299
- ariaKeyShortcuts: spellingCommand.shortcut?.aria,
14300
- active: spellcheckEnabled,
14301
- onClick: onToggleSpellcheck,
14302
- children: /*#__PURE__*/ jsx(CheckCheck, {
14303
- size: 19
15478
+ children: [
15479
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15480
+ label: "拼写检查",
15481
+ displayLabel: true,
15482
+ shortcut: spellingCommand.shortcut?.label,
15483
+ ariaKeyShortcuts: spellingCommand.shortcut?.aria,
15484
+ active: spellcheckEnabled,
15485
+ onClick: onToggleSpellcheck,
15486
+ children: /*#__PURE__*/ jsx(CheckCheck, {
15487
+ size: 19
15488
+ })
15489
+ }),
15490
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
15491
+ label: "设置校对语言",
15492
+ displayLabel: true,
15493
+ onClick: ()=>openProofingDialog(editor),
15494
+ children: /*#__PURE__*/ jsx(Languages, {
15495
+ size: 19
15496
+ })
14304
15497
  })
14305
- })
15498
+ ]
14306
15499
  }),
14307
15500
  !suggestionOnly && /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
14308
15501
  label: "批注",
@@ -14533,6 +15726,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly =
14533
15726
  onEditingPartChange: onPageChromeEditingPartChange,
14534
15727
  onTogglePageNumber: onTogglePageChromePageNumber,
14535
15728
  onOpenFontDialog: ()=>openFontDialog(pageChromeEditor),
15729
+ onOpenProofingDialog: ()=>openProofingDialog(pageChromeEditor),
14536
15730
  onClose: onClosePageChrome
14537
15731
  }) : null
14538
15732
  }
@@ -14553,6 +15747,21 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly =
14553
15747
  });
14554
15748
  }
14555
15749
  }),
15750
+ proofingDialogRequest && /*#__PURE__*/ jsx(DocumentProofingDialog, {
15751
+ source: proofingDialogRequest.source,
15752
+ restoreFocusTarget: ()=>proofingDialogRequest.editor.isDestroyed ? null : proofingDialogRequest.editor.view.dom,
15753
+ onApply: (patch)=>applyDocumentProofingDialogPatch(proofingDialogRequest.editor, proofingDialogRequest.selection, patch),
15754
+ onClose: ()=>{
15755
+ const { editor: dialogEditor, selection } = proofingDialogRequest;
15756
+ setProofingDialogRequest(null);
15757
+ requestAnimationFrame(()=>{
15758
+ if (dialogEditor.isDestroyed) return;
15759
+ dialogEditor.chain().setTextSelection(selection).focus(null, {
15760
+ scrollIntoView: false
15761
+ }).run();
15762
+ });
15763
+ }
15764
+ }),
14556
15765
  officeDialog.dialog
14557
15766
  ]
14558
15767
  });
@@ -14937,6 +16146,182 @@ function commentActor(actor) {
14937
16146
  author: CURRENT_COMMENT_AUTHOR
14938
16147
  };
14939
16148
  }
16149
+ function DocumentTableOfContentsDialog({ editing, options, restoreFocusTarget, onCancel, onOptionsChange, onSubmit }) {
16150
+ const updateOptions = (update)=>onOptionsChange({
16151
+ ...options,
16152
+ ...update
16153
+ });
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: [
16202
+ /*#__PURE__*/ jsx("span", {
16203
+ children: "结束标题级别"
16204
+ }),
16205
+ /*#__PURE__*/ jsx(OfficeSelect, {
16206
+ ariaLabel: "结束标题级别",
16207
+ value: String(options.maxLevel),
16208
+ options: tableOfContentsLevelOptions.map((option)=>({
16209
+ ...option,
16210
+ disabled: Number(option.value) < options.minLevel
16211
+ })),
16212
+ onValueChange: (value)=>updateOptions({
16213
+ maxLevel: Number(value)
16214
+ })
16215
+ })
16216
+ ]
16217
+ })
16218
+ ]
16219
+ }),
16220
+ /*#__PURE__*/ jsxs("div", {
16221
+ className: "work-document-table-of-contents-dialog-options",
16222
+ children: [
16223
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
16224
+ ariaLabel: "目录项使用超链接",
16225
+ checked: options.hyperlinks,
16226
+ onCheckedChange: (hyperlinks)=>updateOptions({
16227
+ hyperlinks
16228
+ }),
16229
+ children: "目录项使用超链接"
16230
+ }),
16231
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
16232
+ ariaLabel: "显示页码",
16233
+ checked: options.showPageNumbers,
16234
+ onCheckedChange: (showPageNumbers)=>updateOptions({
16235
+ showPageNumbers,
16236
+ rightAlignPageNumbers: showPageNumbers && options.rightAlignPageNumbers
16237
+ }),
16238
+ children: "显示页码"
16239
+ }),
16240
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
16241
+ ariaLabel: "页码右对齐",
16242
+ checked: options.rightAlignPageNumbers,
16243
+ disabled: !options.showPageNumbers,
16244
+ onCheckedChange: (rightAlignPageNumbers)=>updateOptions({
16245
+ rightAlignPageNumbers
16246
+ }),
16247
+ children: "页码右对齐"
16248
+ })
16249
+ ]
16250
+ }),
16251
+ /*#__PURE__*/ jsxs("div", {
16252
+ className: "work-document-table-of-contents-dialog-leader",
16253
+ children: [
16254
+ /*#__PURE__*/ jsx("span", {
16255
+ children: "前导符"
16256
+ }),
16257
+ /*#__PURE__*/ jsx(OfficeSelect, {
16258
+ ariaLabel: "目录前导符",
16259
+ value: options.leader,
16260
+ options: tableOfContentsLeaderOptions,
16261
+ disabled: !options.showPageNumbers || !options.rightAlignPageNumbers,
16262
+ onValueChange: (leader)=>updateOptions({
16263
+ leader
16264
+ })
16265
+ })
16266
+ ]
16267
+ }),
16268
+ /*#__PURE__*/ jsxs("fieldset", {
16269
+ className: "work-document-table-of-contents-dialog-preview",
16270
+ "aria-label": "目录预览",
16271
+ "data-toc-leader": options.leader,
16272
+ "data-toc-right-align-page-numbers": String(options.rightAlignPageNumbers),
16273
+ children: [
16274
+ /*#__PURE__*/ jsx("legend", {
16275
+ children: "目录"
16276
+ }),
16277
+ /*#__PURE__*/ jsxs("span", {
16278
+ children: [
16279
+ "项目概述 ",
16280
+ /*#__PURE__*/ jsx("i", {
16281
+ "aria-hidden": "true"
16282
+ }),
16283
+ options.showPageNumbers ? '1' : ''
16284
+ ]
16285
+ }),
16286
+ /*#__PURE__*/ jsxs("span", {
16287
+ className: "nested",
16288
+ children: [
16289
+ "实施计划 ",
16290
+ /*#__PURE__*/ jsx("i", {
16291
+ "aria-hidden": "true"
16292
+ }),
16293
+ options.showPageNumbers ? '3' : ''
16294
+ ]
16295
+ })
16296
+ ]
16297
+ })
16298
+ ]
16299
+ });
16300
+ }
16301
+ const tableOfContentsLevelOptions = Array.from({
16302
+ length: 9
16303
+ }, (_, index)=>({
16304
+ value: String(index + 1),
16305
+ label: `标题 ${index + 1}`
16306
+ }));
16307
+ const tableOfContentsLeaderOptions = [
16308
+ {
16309
+ value: 'dot',
16310
+ label: '点线(……)'
16311
+ },
16312
+ {
16313
+ value: 'dash',
16314
+ label: '短横线(----)'
16315
+ },
16316
+ {
16317
+ value: 'underline',
16318
+ label: '下划线(____)'
16319
+ },
16320
+ {
16321
+ value: 'none',
16322
+ label: '无'
16323
+ }
16324
+ ];
14940
16325
  function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext }) {
14941
16326
  const officeDialog = useOfficeDialog();
14942
16327
  const [insertDialog, setInsertDialog] = useState(null);
@@ -15030,6 +16415,19 @@ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext })
15030
16415
  editor,
15031
16416
  resolveFieldContext
15032
16417
  ]);
16418
+ const openTableOfContents = useCallback(()=>{
16419
+ if (!editor) return;
16420
+ rememberInvoker();
16421
+ const selected = selectedDocumentTableOfContentsOptions(editor);
16422
+ setInsertDialog({
16423
+ kind: 'tableOfContents',
16424
+ editing: Boolean(selected),
16425
+ options: selected ?? DEFAULT_DOCUMENT_TABLE_OF_CONTENTS_OPTIONS
16426
+ });
16427
+ }, [
16428
+ editor,
16429
+ rememberInvoker
16430
+ ]);
15033
16431
  const refreshFields = useCallback(()=>editor?.commands.refreshDocumentFields(contentRef.current, {
15034
16432
  resolveContext: resolveFieldContext ?? void 0
15035
16433
  }) ?? false, [
@@ -15037,6 +16435,12 @@ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext })
15037
16435
  editor,
15038
16436
  resolveFieldContext
15039
16437
  ]);
16438
+ const refreshTableOfContents = useCallback(()=>editor?.commands.refreshDocumentTablesOfContents({
16439
+ resolveContext: resolveFieldContext ?? void 0
16440
+ }) ?? false, [
16441
+ editor,
16442
+ resolveFieldContext
16443
+ ]);
15040
16444
  const submitCaption = ()=>{
15041
16445
  if (!editor || insertDialog?.kind !== 'caption') return;
15042
16446
  const inserted = editor.chain().focus().insertDocumentCaption(insertDialog.captionKind, insertDialog.title.trim()).run();
@@ -15053,6 +16457,16 @@ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext })
15053
16457
  invokerRef.current = editor.view.dom;
15054
16458
  setInsertDialog(null);
15055
16459
  };
16460
+ const submitTableOfContents = ()=>{
16461
+ if (!editor || insertDialog?.kind !== 'tableOfContents') return;
16462
+ const buildOptions = {
16463
+ resolveContext: resolveFieldContext ?? void 0
16464
+ };
16465
+ const applied = insertDialog.editing ? editor.commands.updateDocumentTableOfContents(insertDialog.options, buildOptions) : editor.commands.insertDocumentTableOfContents(insertDialog.options, buildOptions);
16466
+ if (!applied) return;
16467
+ invokerRef.current = editor.view.dom;
16468
+ setInsertDialog(null);
16469
+ };
15056
16470
  const moveReferenceSelection = (event, currentIndex)=>{
15057
16471
  if (insertDialog?.kind !== 'crossReference') return;
15058
16472
  let nextIndex = currentIndex;
@@ -15183,6 +16597,17 @@ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext })
15183
16597
  }, targetKey);
15184
16598
  })
15185
16599
  })
16600
+ }),
16601
+ insertDialog?.kind === 'tableOfContents' && /*#__PURE__*/ jsx(DocumentTableOfContentsDialog, {
16602
+ editing: insertDialog.editing,
16603
+ options: insertDialog.options,
16604
+ restoreFocusTarget: ()=>invokerRef.current,
16605
+ onCancel: ()=>setInsertDialog(null),
16606
+ onOptionsChange: (options)=>setInsertDialog({
16607
+ ...insertDialog,
16608
+ options
16609
+ }),
16610
+ onSubmit: submitTableOfContents
15186
16611
  })
15187
16612
  ]
15188
16613
  });
@@ -15193,7 +16618,9 @@ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext })
15193
16618
  insertField,
15194
16619
  insertImage,
15195
16620
  insertNote,
15196
- refreshFields
16621
+ openTableOfContents,
16622
+ refreshFields,
16623
+ refreshTableOfContents
15197
16624
  };
15198
16625
  }
15199
16626
  function referenceTargetKey(target) {
@@ -17258,6 +18685,10 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
17258
18685
  documentInsert.refreshFields();
17259
18686
  restoreDocumentBodyFocus();
17260
18687
  };
18688
+ const refreshDocumentTableOfContents = ()=>{
18689
+ documentInsert.refreshTableOfContents();
18690
+ restoreDocumentBodyFocus();
18691
+ };
17261
18692
  const openSelectionContextMenu = (event)=>{
17262
18693
  if (!canEditDocument || !getSelectionMenuItems && !onAgentRequest) return false;
17263
18694
  const snapshot = createWorkDocumentSelectionSnapshot(editor, contentRef.current);
@@ -17354,11 +18785,13 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
17354
18785
  onInsertNote: documentInsert.insertNote,
17355
18786
  onInsertCaption: documentInsert.insertCaption,
17356
18787
  onInsertCrossReference: documentInsert.insertCrossReference,
18788
+ onOpenTableOfContents: documentInsert.openTableOfContents,
17357
18789
  citationsOpen: citationsOpen,
17358
18790
  citationSourceCount: currentContent.bibliography?.sources.length ?? 0,
17359
18791
  onToggleCitations: ()=>void toggleTaskPane('citations'),
17360
18792
  onInsertField: documentInsert.insertField,
17361
18793
  onRefreshFields: refreshDocumentFields,
18794
+ onRefreshTableOfContents: refreshDocumentTableOfContents,
17362
18795
  canInsertComment: documentComments.canInsert,
17363
18796
  onInsertComment: ()=>void startCommentDraft(),
17364
18797
  commentsOpen: documentComments.open,