@a3s-lab/office 0.3.0 → 0.4.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 (262) hide show
  1. package/COLLABORATION_ROADMAP.md +426 -0
  2. package/README.md +635 -9
  3. package/dist/0~1403.js +1 -1
  4. package/dist/0~2805.js +1279 -0
  5. package/dist/{0~use-office-draft.js → 0~345.js} +30 -1
  6. package/dist/0~3539.js +1 -1
  7. package/dist/0~3557.js +1 -1
  8. package/dist/0~3635.js +1 -1
  9. package/dist/0~4595.js +7 -2
  10. package/dist/0~4980.js +154 -0
  11. package/dist/{0~9512.js → 0~6090.js} +539 -3
  12. package/dist/0~7048.js +92 -66
  13. package/dist/0~7614.js +2 -1
  14. package/dist/0~7754.js +5653 -0
  15. package/dist/0~9445.js +1 -1
  16. package/dist/0~document-editor.js +1815 -1553
  17. package/dist/0~markdown-editor.js +250 -44
  18. package/dist/0~pdf-viewer.js +1016 -24
  19. package/dist/0~presentation-editor.js +179 -8
  20. package/dist/0~spreadsheet-editor.js +3371 -578
  21. package/dist/0~work-docx-export.js +6801 -658
  22. package/dist/0~work-docx-import.js +1388 -795
  23. package/dist/0~work-office-diagnostics.js +524 -30
  24. package/dist/0~work-pptx-export.js +2 -1
  25. package/dist/0~work-pptx-import.js +1 -2
  26. package/dist/0~work-presentation-charts.js +1 -1
  27. package/dist/2591.js +197 -0
  28. package/dist/3818.js +1090 -0
  29. package/dist/4104.js +970 -60
  30. package/dist/432.js +171 -14
  31. package/dist/4476.js +2008 -0
  32. package/dist/4560.js +674 -0
  33. package/dist/4650.js +70 -0
  34. package/dist/{1134.js → 5184.js} +183 -1
  35. package/dist/6489.js +18 -18
  36. package/dist/7060.js +132 -0
  37. package/dist/8715.js +2 -136
  38. package/dist/8928.js +16778 -0
  39. package/dist/9787.js +209 -0
  40. package/dist/core.d.ts +29 -9
  41. package/dist/core.js +10 -2
  42. package/dist/index.js +13 -3
  43. package/dist/internal/collaboration/office-collaboration-flat-json.d.ts +13 -0
  44. package/dist/internal/collaboration/office-collaboration-json.d.ts +4 -0
  45. package/dist/internal/collaboration/office-collaboration-presence.d.ts +86 -0
  46. package/dist/internal/collaboration/office-collaboration-transport.d.ts +42 -0
  47. package/dist/internal/collaboration/office-collaboration.d.ts +85 -0
  48. package/dist/internal/collaboration/office-document-collaboration-bibliography.d.ts +7 -0
  49. package/dist/internal/collaboration/office-document-collaboration-claims.d.ts +9 -0
  50. package/dist/internal/collaboration/office-document-collaboration-comments.d.ts +7 -0
  51. package/dist/internal/collaboration/office-document-collaboration-sidecar-utils.d.ts +27 -0
  52. package/dist/internal/collaboration/office-document-collaboration-sidecars.d.ts +16 -0
  53. package/dist/internal/collaboration/office-document-collaboration.d.ts +45 -0
  54. package/dist/internal/collaboration/office-markdown-collaboration.d.ts +31 -0
  55. package/dist/internal/collaboration/office-pdf-collaboration-claims.d.ts +4 -0
  56. package/dist/internal/collaboration/office-pdf-collaboration-conflicts.d.ts +2 -0
  57. package/dist/internal/collaboration/office-pdf-collaboration-model.d.ts +26 -0
  58. package/dist/internal/collaboration/office-pdf-collaboration-records.d.ts +21 -0
  59. package/dist/internal/collaboration/office-pdf-collaboration-types.d.ts +91 -0
  60. package/dist/internal/collaboration/office-pdf-collaboration-validation.d.ts +4 -0
  61. package/dist/internal/collaboration/office-pdf-collaboration.d.ts +35 -0
  62. package/dist/internal/collaboration/office-presentation-collaboration-model.d.ts +25 -0
  63. package/dist/internal/collaboration/office-presentation-collaboration-records.d.ts +22 -0
  64. package/dist/internal/collaboration/office-presentation-collaboration-validation.d.ts +4 -0
  65. package/dist/internal/collaboration/office-presentation-collaboration.d.ts +32 -0
  66. package/dist/internal/collaboration/office-spreadsheet-collaboration-cells.d.ts +13 -0
  67. package/dist/internal/collaboration/office-spreadsheet-collaboration-claims.d.ts +4 -0
  68. package/dist/internal/collaboration/office-spreadsheet-collaboration-conflicts.d.ts +2 -0
  69. package/dist/internal/collaboration/office-spreadsheet-collaboration-flat-json.d.ts +10 -0
  70. package/dist/internal/collaboration/office-spreadsheet-collaboration-model.d.ts +31 -0
  71. package/dist/internal/collaboration/office-spreadsheet-collaboration-records.d.ts +17 -0
  72. package/dist/internal/collaboration/office-spreadsheet-collaboration-validation.d.ts +4 -0
  73. package/dist/internal/collaboration/office-spreadsheet-collaboration.d.ts +32 -0
  74. package/dist/internal/features/work/components/work-document-page-border.d.ts +6 -0
  75. package/dist/internal/features/work/editors/document-command-catalog.d.ts +9 -0
  76. package/dist/internal/features/work/editors/document-editor-focus.d.ts +1 -1
  77. package/dist/internal/features/work/editors/document-editor.d.ts +7 -1
  78. package/dist/internal/features/work/editors/document-external-content.d.ts +14 -0
  79. package/dist/internal/features/work/editors/document-page-stack.d.ts +10 -1
  80. package/dist/internal/features/work/editors/document-page-thumbnail.d.ts +2 -0
  81. package/dist/internal/features/work/editors/document-pagination-run-coordinator.d.ts +26 -0
  82. package/dist/internal/features/work/editors/document-picture-properties-dialog-model.d.ts +20 -1
  83. package/dist/internal/features/work/editors/document-table-properties-dialog-model.d.ts +4 -1
  84. package/dist/internal/features/work/editors/document-toolbar.d.ts +8 -1
  85. package/dist/internal/features/work/editors/markdown-editor-focus.d.ts +1 -1
  86. package/dist/internal/features/work/editors/markdown-editor.d.ts +5 -1
  87. package/dist/internal/features/work/editors/markdown-toolbar.d.ts +2 -1
  88. package/dist/internal/features/work/editors/office-collaboration-participants.d.ts +3 -0
  89. package/dist/internal/features/work/editors/office-collaboration-presence-context.d.ts +17 -0
  90. package/dist/internal/features/work/editors/office-collaboration-presence-ui.d.ts +25 -0
  91. package/dist/internal/features/work/editors/office-editor-focus-handoff.d.ts +27 -0
  92. package/dist/internal/features/work/editors/pdf-annotation-controller.d.ts +5 -0
  93. package/dist/internal/features/work/editors/pdf-collaboration-presence.d.ts +3 -0
  94. package/dist/internal/features/work/editors/pdf-collaboration-projection.d.ts +10 -0
  95. package/dist/internal/features/work/editors/pdf-evidence-contract.d.ts +36 -0
  96. package/dist/internal/features/work/editors/pdf-evidence-overlay.d.ts +37 -0
  97. package/dist/internal/features/work/editors/pdf-toolbar.d.ts +2 -1
  98. package/dist/internal/features/work/editors/pdf-viewer-controller.d.ts +7 -1
  99. package/dist/internal/features/work/editors/pdf-viewer.d.ts +11 -1
  100. package/dist/internal/features/work/editors/presentation-collaboration-presence.d.ts +5 -0
  101. package/dist/internal/features/work/editors/presentation-editor-focus.d.ts +3 -2
  102. package/dist/internal/features/work/editors/presentation-editor-types.d.ts +3 -0
  103. package/dist/internal/features/work/editors/presentation-editor.d.ts +1 -1
  104. package/dist/internal/features/work/editors/spreadsheet-auto-filter.d.ts +9 -0
  105. package/dist/internal/features/work/editors/spreadsheet-cell-clear.d.ts +3 -0
  106. package/dist/internal/features/work/editors/spreadsheet-cell-merge.d.ts +12 -0
  107. package/dist/internal/features/work/editors/spreadsheet-clipboard-shortcuts.d.ts +3 -7
  108. package/dist/internal/features/work/editors/spreadsheet-collaboration-presence.d.ts +9 -0
  109. package/dist/internal/features/work/editors/spreadsheet-command-catalog.d.ts +464 -0
  110. package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +45 -9
  111. package/dist/internal/features/work/editors/spreadsheet-context-menu.d.ts +2 -0
  112. package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +9 -28
  113. package/dist/internal/features/work/editors/spreadsheet-editor.d.ts +8 -2
  114. package/dist/internal/features/work/editors/spreadsheet-format-painter.d.ts +27 -0
  115. package/dist/internal/features/work/editors/spreadsheet-freeze-panes.d.ts +14 -0
  116. package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +3 -2
  117. package/dist/internal/features/work/editors/use-document-pagination.d.ts +6 -1
  118. package/dist/internal/features/work/editors/use-document-review-conflicts.d.ts +28 -0
  119. package/dist/internal/features/work/editors/use-spreadsheet-auto-filter.d.ts +21 -0
  120. package/dist/internal/features/work/editors/use-spreadsheet-collaboration.d.ts +27 -0
  121. package/dist/internal/features/work/editors/use-spreadsheet-format-painter.d.ts +18 -0
  122. package/dist/internal/features/work/work-document-bookmark-references.d.ts +37 -0
  123. package/dist/internal/features/work/work-document-bookmarks.d.ts +29 -0
  124. package/dist/internal/features/work/work-document-caption-nodes.d.ts +4 -1
  125. package/dist/internal/features/work/work-document-comments.d.ts +1 -1
  126. package/dist/internal/features/work/work-document-equations.d.ts +513 -0
  127. package/dist/internal/features/work/work-document-extensions.d.ts +1 -0
  128. package/dist/internal/features/work/work-document-field-identity.d.ts +2 -0
  129. package/dist/internal/features/work/work-document-field-node.d.ts +2 -2
  130. package/dist/internal/features/work/work-document-fields.d.ts +7 -0
  131. package/dist/internal/features/work/work-document-image-identity.d.ts +30 -0
  132. package/dist/internal/features/work/work-document-image-layout.d.ts +46 -1
  133. package/dist/internal/features/work/work-document-image-wrap-contour.d.ts +16 -0
  134. package/dist/internal/features/work/work-document-incremental-layout.d.ts +2 -2
  135. package/dist/internal/features/work/work-document-model-codec.d.ts +4 -1
  136. package/dist/internal/features/work/work-document-note-integrity.d.ts +2 -0
  137. package/dist/internal/features/work/work-document-page-borders.d.ts +25 -0
  138. package/dist/internal/features/work/work-document-page-capture.d.ts +8 -0
  139. package/dist/internal/features/work/work-document-page-frames.d.ts +15 -0
  140. package/dist/internal/features/work/work-document-page-margins.d.ts +56 -0
  141. package/dist/internal/features/work/work-document-page-size.d.ts +47 -0
  142. package/dist/internal/features/work/work-document-pagination-measurement.d.ts +1 -2
  143. package/dist/internal/features/work/work-document-pagination-types.d.ts +7 -2
  144. package/dist/internal/features/work/work-document-pagination.d.ts +2 -2
  145. package/dist/internal/features/work/work-document-paragraph-borders.d.ts +32 -0
  146. package/dist/internal/features/work/work-document-paragraph-identity.d.ts +26 -0
  147. package/dist/internal/features/work/work-document-paragraph-shading.d.ts +18 -0
  148. package/dist/internal/features/work/work-document-review-conflicts.d.ts +35 -0
  149. package/dist/internal/features/work/work-document-section.d.ts +4 -0
  150. package/dist/internal/features/work/work-document-snapshot.d.ts +19 -0
  151. package/dist/internal/features/work/work-document-source.d.ts +23 -0
  152. package/dist/internal/features/work/work-document-table-column-widths.d.ts +7 -0
  153. package/dist/internal/features/work/work-document-table-property-changes.d.ts +3 -0
  154. package/dist/internal/features/work/work-document-table-row-identity.d.ts +15 -0
  155. package/dist/internal/features/work/work-document-table-sizing.d.ts +3 -0
  156. package/dist/internal/features/work/work-document-text-layout.d.ts +3 -1
  157. package/dist/internal/features/work/work-document-word-line-metrics.d.ts +2 -0
  158. package/dist/internal/features/work/work-docx-bookmark-diagnostics.d.ts +3 -0
  159. package/dist/internal/features/work/work-docx-bookmark-import.d.ts +24 -0
  160. package/dist/internal/features/work/work-docx-bookmarks.d.ts +17 -0
  161. package/dist/internal/features/work/work-docx-comment-durable-id-preservation.d.ts +2 -0
  162. package/dist/internal/features/work/work-docx-comment-export.d.ts +3 -0
  163. package/dist/internal/features/work/work-docx-comment-metadata.d.ts +2 -0
  164. package/dist/internal/features/work/work-docx-drawing-extension-preservation.d.ts +4 -0
  165. package/dist/internal/features/work/work-docx-equation-diagnostics.d.ts +3 -0
  166. package/dist/internal/features/work/work-docx-equation-export.d.ts +14 -0
  167. package/dist/internal/features/work/work-docx-equation-import.d.ts +30 -0
  168. package/dist/internal/features/work/work-docx-equation-story.d.ts +9 -0
  169. package/dist/internal/features/work/work-docx-export-formatting.d.ts +3 -1
  170. package/dist/internal/features/work/work-docx-export-image.d.ts +5 -1
  171. package/dist/internal/features/work/work-docx-export.d.ts +1 -1
  172. package/dist/internal/features/work/work-docx-field-instructions.d.ts +8 -0
  173. package/dist/internal/features/work/work-docx-font-table-preservation.d.ts +3 -0
  174. package/dist/internal/features/work/work-docx-ignorable-extension-preservation.d.ts +15 -0
  175. package/dist/internal/features/work/work-docx-image-crop.d.ts +12 -0
  176. package/dist/internal/features/work/work-docx-image-identity.d.ts +17 -0
  177. package/dist/internal/features/work/work-docx-image-layer.d.ts +12 -0
  178. package/dist/internal/features/work/work-docx-image-layout-import.d.ts +14 -2
  179. package/dist/internal/features/work/work-docx-image-wrap.d.ts +15 -0
  180. package/dist/internal/features/work/work-docx-import.d.ts +13 -3
  181. package/dist/internal/features/work/work-docx-list-export.d.ts +18 -0
  182. package/dist/internal/features/work/work-docx-list-import.d.ts +14 -0
  183. package/dist/internal/features/work/work-docx-note-comment-content-control-block-preservation.d.ts +18 -0
  184. package/dist/internal/features/work/work-docx-note-comment-content-control-model.d.ts +47 -0
  185. package/dist/internal/features/work/work-docx-note-comment-content-control-preservation.d.ts +4 -0
  186. package/dist/internal/features/work/work-docx-note-comment-content-control-properties.d.ts +17 -0
  187. package/dist/internal/features/work/work-docx-note-comment-content-control-table-model.d.ts +17 -0
  188. package/dist/internal/features/work/work-docx-note-comment-content-control-xml.d.ts +14 -0
  189. package/dist/internal/features/work/work-docx-note-comment-extension-preservation.d.ts +2 -0
  190. package/dist/internal/features/work/work-docx-note-comment-hyperlink-content.d.ts +40 -0
  191. package/dist/internal/features/work/work-docx-note-comment-hyperlink-preservation.d.ts +5 -0
  192. package/dist/internal/features/work/work-docx-note-comment-hyperlink-relationships.d.ts +35 -0
  193. package/dist/internal/features/work/work-docx-note-comment-identity.d.ts +15 -0
  194. package/dist/internal/features/work/work-docx-note-comment-run-preservation.d.ts +4 -0
  195. package/dist/internal/features/work/work-docx-note-comment-run-properties.d.ts +2 -0
  196. package/dist/internal/features/work/work-docx-note-image-import.d.ts +12 -0
  197. package/dist/internal/features/work/work-docx-note-image-relationships.d.ts +1 -0
  198. package/dist/internal/features/work/work-docx-numbering-extension-preservation.d.ts +6 -0
  199. package/dist/internal/features/work/work-docx-numbering.d.ts +1 -0
  200. package/dist/internal/features/work/work-docx-page-borders-diagnostics.d.ts +3 -0
  201. package/dist/internal/features/work/work-docx-page-borders-import.d.ts +10 -0
  202. package/dist/internal/features/work/work-docx-page-margins-diagnostics.d.ts +3 -0
  203. package/dist/internal/features/work/work-docx-page-margins-import.d.ts +18 -0
  204. package/dist/internal/features/work/work-docx-page-size-diagnostics.d.ts +2 -0
  205. package/dist/internal/features/work/work-docx-page-size-import.d.ts +17 -0
  206. package/dist/internal/features/work/work-docx-paragraph-borders-diagnostics.d.ts +3 -0
  207. package/dist/internal/features/work/work-docx-paragraph-borders-export.d.ts +18 -0
  208. package/dist/internal/features/work/work-docx-paragraph-borders-import.d.ts +24 -0
  209. package/dist/internal/features/work/work-docx-paragraph-default-collapsed.d.ts +19 -0
  210. package/dist/internal/features/work/work-docx-paragraph-extension-preservation.d.ts +2 -0
  211. package/dist/internal/features/work/work-docx-paragraph-identity-import.d.ts +10 -0
  212. package/dist/internal/features/work/work-docx-paragraph-identity.d.ts +18 -0
  213. package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +6 -1
  214. package/dist/internal/features/work/work-docx-paragraph-shading-diagnostics.d.ts +3 -0
  215. package/dist/internal/features/work/work-docx-paragraph-shading-export.d.ts +5 -0
  216. package/dist/internal/features/work/work-docx-paragraph-shading-import.d.ts +18 -0
  217. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +3 -0
  218. package/dist/internal/features/work/work-docx-settings-preservation.d.ts +10 -0
  219. package/dist/internal/features/work/work-docx-settings-xml.d.ts +16 -0
  220. package/dist/internal/features/work/work-docx-style-extension-preservation.d.ts +2 -0
  221. package/dist/internal/features/work/work-docx-table-cell-export.d.ts +2 -1
  222. package/dist/internal/features/work/work-docx-table-cell-import.d.ts +3 -0
  223. package/dist/internal/features/work/work-docx-table-extension-preservation.d.ts +2 -0
  224. package/dist/internal/features/work/work-docx-table-row-import.d.ts +2 -0
  225. package/dist/internal/features/work/work-docx-table-sizing-import.d.ts +1 -0
  226. package/dist/internal/features/work/work-docx-theme-reference.d.ts +23 -0
  227. package/dist/internal/features/work/work-docx-theme.d.ts +4 -1
  228. package/dist/internal/features/work/work-docx-twips.d.ts +12 -0
  229. package/dist/internal/features/work/work-file-contract.d.ts +1 -0
  230. package/dist/internal/features/work/work-file-io.d.ts +1 -1
  231. package/dist/internal/features/work/work-markdown-extensions.d.ts +4 -1
  232. package/dist/internal/features/work/work-ooxml-package-preservation.d.ts +12 -0
  233. package/dist/internal/features/work/work-ooxml-package-security.d.ts +6 -0
  234. package/dist/internal/features/work/work-ooxml-relationship-preservation.d.ts +10 -0
  235. package/dist/internal/features/work/work-ooxml-xml.d.ts +2 -0
  236. package/dist/internal/features/work/work-pdf-export.d.ts +10 -0
  237. package/dist/internal/features/work/work-repository.d.ts +2 -0
  238. package/dist/internal/features/work/work-types.d.ts +15 -2
  239. package/dist/internal/kernel/office-kernel-client-types.d.ts +1 -0
  240. package/dist/internal/kernel/office-kernel-protocol.d.ts +7 -0
  241. package/dist/internal/kernel/office-kernel-version.d.ts +1 -1
  242. package/dist/office-kernel.wasm +0 -0
  243. package/dist/office-kernel.worker.js +93 -41
  244. package/dist/react.d.ts +18 -12
  245. package/dist/react.js +1 -1
  246. package/dist/styles.css +1153 -14
  247. package/dist/vue.d.ts +48 -5
  248. package/dist/vue.js +42 -3
  249. package/dist/web-component.d.ts +24 -2
  250. package/dist/web-component.js +101 -2
  251. package/docs/latest/en/browser-editor-architecture.md +231 -49
  252. package/package.json +19 -4
  253. package/dist/0~152.js +0 -94
  254. package/dist/0~2582.js +0 -485
  255. package/dist/0~4750.js +0 -939
  256. package/dist/0~5861.js +0 -596
  257. package/dist/0~work-document-extensions.js +0 -3045
  258. package/dist/0~work-document-model-codec.js +0 -42
  259. package/dist/0~work-docx-bibliography.js +0 -347
  260. package/dist/0~work-docx-page-color.js +0 -42
  261. package/dist/2881.js +0 -1455
  262. package/dist/8728.js +0 -723
@@ -3,12 +3,12 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import extension_placeholder from "@tiptap/extension-placeholder";
4
4
  import { EditorContent, useEditor } from "@tiptap/react";
5
5
  import { Fragment as external_react_Fragment, forwardRef, useCallback, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
6
- import { Extension } from "@tiptap/core";
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, AlignCenter, AlignJustify, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowUp, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, BookMarked, BookOpen, Check, CheckCheck, CheckCircle2, ChevronDown, ChevronRight, ChevronUp, ClipboardCopy, ClipboardPaste, Cloud, Columns3, Eraser, 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, Underline, Undo2, Unlink, X, XCircle, ZoomIn, ZoomOut } from "lucide-react";
9
+ import { AArrowDown, AArrowUp, AlignCenter, AlignJustify, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowUp, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, BookMarked, BookOpen, Bookmark, Check, CheckCheck, CheckCircle2, ChevronDown, ChevronRight, ChevronUp, ClipboardCopy, ClipboardPaste, Cloud, Columns3, Eraser, 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, Underline, Undo2, Unlink, X, XCircle, ZoomIn, ZoomOut } from "lucide-react";
10
+ import { Node as model_Node } from "@tiptap/pm/model";
10
11
  import extension_color from "@tiptap/extension-color";
11
- import extension_image from "@tiptap/extension-image";
12
12
  import { TableKit } from "@tiptap/extension-table";
13
13
  import extension_text_align from "@tiptap/extension-text-align";
14
14
  import { TextStyle } from "@tiptap/extension-text-style";
@@ -16,27 +16,49 @@ import extension_underline from "@tiptap/extension-underline";
16
16
  import starter_kit from "@tiptap/starter-kit";
17
17
  import { BubbleMenu } from "@tiptap/react/menus";
18
18
  import { TableMap, isInTable, selectedRect } from "@tiptap/pm/tables";
19
- import { nextDocumentTabAlignment, normalizedTabPosition, documentParagraphTabStops, DEFAULT_DOCUMENT_TABLE_CELL_MARGINS, documentImageLayoutOptions, documentImageProperties, normalizeDocumentTabStops } from "./0~4750.js";
20
- import { documentTableRowRepeats, documentTableRowOptions, documentTableCellFormat, activeDocumentTableStyle, DEFAULT_DOCUMENT_TABLE_CELL_FORMAT, documentTableRowCantSplit, documentTableHorizontalAlignment, DOCUMENT_TABLE_STYLE_OPTIONS, canSetDocumentTableRowRepeatHeader } from "./0~2582.js";
21
- import { normalizeDocumentHtml, documentInitialSectionLayout, isValidDocumentCitationTag, sanitizeDocumentPageChromeHtml, mountWorkLiveDocumentCapture, resolveWorkDocumentEditorInput, createDocumentBibliography, syncDocumentContentFromHtml, clampDocumentMargin, documentMargins, millimetersToPixels as work_document_layout_millimetersToPixels, documentCitationStyleDetails, updateDocumentPageChromeVariant, normalizeDocumentColumns, documentPageChromeLegacyFields, resolveDocumentPageChrome, setCustomDocumentColumns, documentCitationStyle, normalizeDocumentPageChrome, createWorkDocumentModel, documentSectionLayoutFromNodeAttributes, updateDocumentColumnWidth } from "./2881.js";
22
19
  import { useOfficeTaskPaneEscape, normalizeOfficeFontFamily, OfficeCheckbox, useOfficeDialog, useOfficeTaskPaneModal, CommittedOfficeNumberField, officeFontFamilyLabel, officeFontFamilies, CollectionState, createOfficeKernelClient, OfficeNumberField } from "./0~7048.js";
23
- import { office_text_field_OfficeTextArea, Popover, officeOverlayPortalRoot, button_Button, icon_button_IconButton, office_text_field_OfficeTextField, useDialogFocusScope, useOfficeEditorWheelZoom, office_text_field_OfficeFileInput } from "./0~9512.js";
20
+ import { 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";
24
21
  import { WorkOfficePreviewBar, isWorkspaceContextMenuKeyboardEvent, Tabs, WorkOfficeZoomControls, WorkOfficeStatusBar, Dialog, WorkOfficeRibbon, workspaceContextMenuPosition, OfficeSelect, WorkspaceContextMenu, WorkOfficeRibbonGroup, WorkOfficeRibbonButton } from "./0~4595.js";
25
- import { createOfficeId as createWorkId } from "./1134.js";
26
- import { canInsertDocumentComment, documentTableSizing, activeDocumentSection, documentCommentViews, retainAnchoredDocumentComments, DocumentSubscript, documentCitationCount, documentBulletListStyle, collectDocumentChanges, editorDocumentCaptionTargets, collectDocumentCommentAnchors, documentTabLeaderLabel, documentOrderedListState, MAX_DOCUMENT_NUMBERING_START, documentCommentDraftRange, DocumentSuperscript, documentSectionById, createWorkDocumentExtensions } from "./0~work-document-extensions.js";
22
+ import { documentTableSizing, documentTableRowOptions, documentCommentViews, documentParagraphSpacing, documentPageBordersVisible, DOCUMENT_PAGE_BORDER_EDGES, normalizeDocumentColumns, clampDocumentMargin, normalizeDocumentTabStops, DOCUMENT_BOOKMARK_DUPLICATE_MESSAGE, createWorkDocumentModel, nextDocumentTabAlignment, documentBookmarkNameExists, normalizedTabPosition, DocumentImage, collectDocumentChanges, documentCitationCount, isDocumentParagraphArtBorderStyle, resolveDocumentPageMargins, updateDocumentPageMarginMode, resolveDocumentPageBorders, canSetDocumentTableRowRepeatHeader, MAX_DOCUMENT_IMAGE_RELATIVE_HEIGHT, retainAnchoredDocumentComments, measureDocumentLayoutBlocks, DocumentTableRowIdentity, work_document_page_margins_twipsToMillimeters, DEFAULT_DOCUMENT_TABLE_CELL_FORMAT, resolveWorkDocumentEditorInput, collectDocumentTextLayoutParagraphs, millimetersToPixels as work_document_layout_millimetersToPixels, documentBulletListStyle, readOfficeDocumentCollaboration as readWorkOfficeDocumentCollaboration, documentTabLeaderLabel, documentOrderedListState, normalizeDocumentPageChrome, documentPageMarginsForLayout, documentTableCellFormat, updateDocumentCustomPageMillimeters, documentCommentDraftRange, normalizeDocumentPageGeometry, documentParagraphTabStops, normalizeDocumentImageLayer, documentImageProperties, documentParagraphIndent, documentTableHorizontalAlignment, isValidDocumentCitationTag, canInsertDocumentComment, sanitizeDocumentPageChromeHtml, pageTwipsToMillimeters, normalizeDocumentPageBorders, DocumentParagraphFormatting, activeDocumentSection, documentTextLayoutBatches, normalizeDocumentImageWrapSide, normalizeDocumentHtml, documentParagraphDirection, documentParagraphPagination, updateDocumentPageOrientation, documentCitationStyleDetails, documentInitialSectionLayout, documentPageChromeLegacyFields, setCustomDocumentColumns, collectDocumentCommentAnchors, documentCitationStyle, updateDocumentPageMarginMillimeters, canChangeDocumentIndent, validateDocumentBookmarkName, DEFAULT_DOCUMENT_TABLE_CELL_MARGINS, DocumentSuperscript, documentSectionById, DOCUMENT_TABLE_STYLE_OPTIONS, updateDocumentColumnWidth, updateDocumentPaperSizePreset, DocumentEquation, documentPageMetrics, normalizeDocumentParagraphIndent, updateDocumentGutterPosition, mountWorkLiveDocumentCapture, activeDocumentBookmark, createDocumentBibliography, DocumentSubscript, DocumentParagraphIdentity, syncDocumentContentFromHtml, updateDocumentPageChromeVariant, editorDocumentCaptionTargets, resolveDocumentPageChrome, updateDocumentMirrorMargins, documentImageLayoutOptions, MAX_DOCUMENT_NUMBERING_START, resolveDocumentPageSize, activeDocumentTableStyle, editorDocumentBookmarkReferenceTargets, createOfficeDocumentCollaborationBinding as createWorkOfficeDocumentCollaborationBinding, createWorkDocumentExtensions } from "./8928.js";
23
+ import { createOfficeId as createWorkId } from "./5184.js";
27
24
  import { normalizeDocumentHref, DOCUMENT_LINK_VALIDATION_MESSAGE } from "./0~work-document-links.js";
28
25
  import { OfficeColorPicker, isOfficeShortcutBlocked } from "./0~3635.js";
29
26
  import { OfficeTableInsertPopover, readOfficeFileAsDataUrl } from "./0~9445.js";
30
- import { documentParagraphSpacing, normalizeDocumentParagraphIndent, documentParagraphDirection, documentParagraphPagination, documentParagraphIndent, canChangeDocumentIndent } from "./0~5861.js";
31
27
  import { mergeOfficeTiptapExtensions, documentTextStatistics, documentCurrentPage, documentPageCount, documentAgentMenuItems, createWorkDocumentSelectionSnapshot, documentPlainTextAsHtml, createWorkDocumentSelectionAction, documentCustomSelectionMenuItems } from "./0~5050.js";
32
- import { OFFICE_DOCUMENT_LAYOUT_HEBREW_FONT_ID, WorkEditorLoadingState, OFFICE_DOCUMENT_LAYOUT_FONT_ID, OFFICE_DOCUMENT_LAYOUT_ARABIC_FONT_ID, documentLayoutFontKey, OFFICE_DOCUMENT_LAYOUT_LATIN_FONT_ID } from "./432.js";
33
- import { useOfficeDraft } from "./0~use-office-draft.js";
28
+ import { WorkEditorLoadingState, documentLayoutFontKey, useOfficeCollaborationLocationNavigator, OFFICE_DOCUMENT_LAYOUT_HEBREW_FONT_ID, useOfficeEditorInitialFocus, OFFICE_DOCUMENT_LAYOUT_FONT_ID, OFFICE_DOCUMENT_LAYOUT_ARABIC_FONT_ID, OFFICE_DOCUMENT_LAYOUT_LATIN_FONT_ID } from "./432.js";
29
+ import { useOfficeDraft, InlineNotice } from "./0~345.js";
34
30
  import { documentPageColor, normalizeDocumentPageColor } from "./0~work-document-page-color.js";
35
31
  var document_editor_namespaceObject = {};
36
32
  __webpack_require__.r(document_editor_namespaceObject);
37
33
  __webpack_require__.d(document_editor_namespaceObject, {
38
34
  DocumentEditor: ()=>DocumentEditor
39
35
  });
36
+ function documentPageSurfaceGeometry(pages, fallback, requestedPageCount = pages.length) {
37
+ const pageCount = Math.max(1, Math.trunc(requestedPageCount));
38
+ const metrics = Array.from({
39
+ length: pageCount
40
+ }, (_, pageIndex)=>pages[pageIndex] ?? fallback);
41
+ const width = Math.max(1, ...metrics.map((page)=>page.width));
42
+ let top = 0;
43
+ const frames = metrics.map((page, pageIndex)=>{
44
+ const frame = {
45
+ height: page.height,
46
+ left: (width - page.width) / 2,
47
+ page,
48
+ pageIndex,
49
+ top,
50
+ width: page.width
51
+ };
52
+ top += page.height;
53
+ if (pageIndex + 1 < metrics.length) top += page.pageGap;
54
+ return frame;
55
+ });
56
+ return {
57
+ frames,
58
+ height: Math.max(1, top),
59
+ width
60
+ };
61
+ }
40
62
  function pageBreakDecorations(pageBreak) {
41
63
  const tableBreak = pageBreak.tableBreak;
42
64
  if (tableBreak?.cellBreaks?.length) {
@@ -149,14 +171,19 @@ function documentTableRepeatedHeaderElement(tableBreak, extraClassName = '') {
149
171
  }
150
172
  function applyDocumentPageBreakVariables(element, pageBreak) {
151
173
  element.style.setProperty('--work-document-page-spacer-height', `${pageBreak.spacerHeight}px`);
152
- element.style.setProperty('--work-document-page-gap-offset', `${pageBreak.remainingBodyHeight + pageBreak.page.marginBottom}px`);
153
- element.style.setProperty('--work-document-page-gap-height', `${pageBreak.page.pageGap}px`);
174
+ element.style.setProperty('--work-document-page-gap-offset', `${pageBreak.remainingBodyHeight + pageBreak.previousPage.marginBottom}px`);
175
+ element.style.setProperty('--work-document-page-gap-height', `${pageBreak.previousPage.pageGap}px`);
154
176
  element.style.setProperty('--work-document-previous-page-footer-top', `${pageBreak.remainingBodyHeight}px`);
155
- element.style.setProperty('--work-document-next-page-header-top', `${pageBreak.remainingBodyHeight + pageBreak.page.marginBottom + pageBreak.page.pageGap}px`);
156
- element.style.setProperty('--work-document-page-header-height', `${pageBreak.page.headerHeight}px`);
157
- element.style.setProperty('--work-document-page-footer-height', `${pageBreak.page.footerHeight}px`);
158
- element.style.setProperty('--work-document-page-margin-left', `${pageBreak.page.marginLeft}px`);
159
- element.style.setProperty('--work-document-page-margin-right', `${pageBreak.page.marginRight}px`);
177
+ element.style.setProperty('--work-document-next-page-header-top', `${pageBreak.remainingBodyHeight + pageBreak.previousPage.marginBottom + pageBreak.previousPage.pageGap}px`);
178
+ element.style.setProperty('--work-document-page-header-height', `${pageBreak.nextPage.headerHeight}px`);
179
+ element.style.setProperty('--work-document-page-footer-height', `${pageBreak.previousPage.footerHeight}px`);
180
+ element.style.setProperty('--work-document-page-margin-left', `${pageBreak.nextPage.marginLeft}px`);
181
+ element.style.setProperty('--work-document-page-margin-right', `${pageBreak.nextPage.marginRight}px`);
182
+ const previousPageOffset = (pageBreak.nextPage.width - pageBreak.previousPage.width) / 2;
183
+ element.style.setProperty('--work-document-previous-page-margin-left', `${previousPageOffset + pageBreak.previousPage.marginLeft}px`);
184
+ element.style.setProperty('--work-document-previous-page-margin-right', `${previousPageOffset + pageBreak.previousPage.marginRight}px`);
185
+ element.style.setProperty('--work-document-next-page-margin-left', `${pageBreak.nextPage.marginLeft}px`);
186
+ element.style.setProperty('--work-document-next-page-margin-right', `${pageBreak.nextPage.marginRight}px`);
160
187
  element.style.setProperty('--work-document-break-inline-offset-left', `${pageBreak.inlineOffsetLeft}px`);
161
188
  element.style.setProperty('--work-document-break-inline-offset-right', `${pageBreak.inlineOffsetRight}px`);
162
189
  }
@@ -263,1273 +290,6 @@ function documentPaginationState(state) {
263
290
  revision: 0
264
291
  };
265
292
  }
266
- function reusableDocumentLayoutBlocks(previous, blockId, element, nodeTo, dirtyFrom) {
267
- if (nodeTo > dirtyFrom) return [];
268
- return previous.filter((candidate)=>candidate.block.id === blockId || candidate.block.flowId === blockId).map((candidate)=>({
269
- ...candidate,
270
- block: {
271
- ...candidate.block
272
- },
273
- element
274
- }));
275
- }
276
- function isDocumentListNode(node) {
277
- return 'bulletList' === node.type.name || 'orderedList' === node.type.name;
278
- }
279
- function documentListChildId(listId, itemIndex, node, childIndex, nestedList = isDocumentListNode(node)) {
280
- const kind = nestedList ? 'list' : node.type.name;
281
- return `${listId}-item-${itemIndex}-${kind}-${childIndex}`;
282
- }
283
- function verticalBlockStart(style) {
284
- return nonNegativePixels(style.marginTop) + nonNegativePixels(style.paddingTop) + nonNegativePixels(style.borderTopWidth);
285
- }
286
- function verticalBlockEnd(style) {
287
- return nonNegativePixels(style.marginBottom) + nonNegativePixels(style.paddingBottom) + nonNegativePixels(style.borderBottomWidth);
288
- }
289
- function documentInlineOffsets(editor, element) {
290
- const elementRect = element.getBoundingClientRect();
291
- const editorRect = editor.view.dom.getBoundingClientRect();
292
- const scaleX = editor.view.dom.offsetWidth > 0 ? editorRect.width / editor.view.dom.offsetWidth : 1;
293
- return {
294
- inlineOffsetLeft: Math.max(0, (elementRect.left - editorRect.left) / Math.max(scaleX, 0.01)),
295
- inlineOffsetRight: Math.max(0, (editorRect.right - elementRect.right) / Math.max(scaleX, 0.01))
296
- };
297
- }
298
- function documentNodeParagraphPagination(node) {
299
- return {
300
- keepLines: directNodeBoolean(node, 'keepLines') ?? false,
301
- keepWithNext: directNodeBoolean(node, 'keepWithNext') ?? 'heading' === node.type.name,
302
- pageBreakBefore: directNodeBoolean(node, 'pageBreakBefore') ?? false,
303
- widowControl: directNodeBoolean(node, 'widowControl') ?? true
304
- };
305
- }
306
- function directNodeBoolean(node, attribute) {
307
- const value = node.attrs[attribute];
308
- return 'boolean' == typeof value ? value : null;
309
- }
310
- function measuredDocumentBlock({ block, element, from, to }) {
311
- return {
312
- block,
313
- element,
314
- from,
315
- to,
316
- inlineOffsetLeft: 0,
317
- inlineOffsetRight: 0,
318
- observeResize: true
319
- };
320
- }
321
- function shouldKeepDocumentBlockTogether(node) {
322
- return 'table' === node.type.name || 'blockquote' === node.type.name || 'codeBlock' === node.type.name || 'image' === node.type.name || 'documentNote' === node.type.name;
323
- }
324
- function documentPaginationLayoutKey(layout) {
325
- return JSON.stringify({
326
- margins: layout.margins,
327
- orientation: layout.orientation,
328
- pageSize: layout.pageSize
329
- });
330
- }
331
- function documentBlockId(sectionPosition, index, position) {
332
- return `section-${sectionPosition}-block-${index}-${position}`;
333
- }
334
- function work_document_pagination_dom_outerHeight(element) {
335
- if (!element) return 0;
336
- const style = getComputedStyle(element);
337
- return element.offsetHeight + nonNegativePixels(style.marginTop) + nonNegativePixels(style.marginBottom);
338
- }
339
- function nonNegativePixels(value) {
340
- const parsed = Number.parseFloat(value);
341
- return Number.isFinite(parsed) ? Math.max(0, parsed) : 0;
342
- }
343
- function positivePixels(value) {
344
- const parsed = Number.parseFloat(value);
345
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
346
- }
347
- function finitePixels(value) {
348
- const parsed = Number.parseFloat(value);
349
- return Number.isFinite(parsed) ? parsed : 0;
350
- }
351
- function elementForNode(editor, position) {
352
- const node = editor.view.nodeDOM(position);
353
- if (!node || node.nodeType !== Node.ELEMENT_NODE) return null;
354
- return node;
355
- }
356
- const MAX_DOCUMENT_TEXT_LAYOUT_PARAGRAPHS = 1024;
357
- const MAX_DOCUMENT_TEXT_LAYOUT_RUNS = 16384;
358
- const MAX_DOCUMENT_TEXT_LAYOUT_BYTES = 1048576;
359
- const MAX_DOCUMENT_TEXT_LAYOUT_RUNS_PER_PARAGRAPH = 4096;
360
- const MAX_DOCUMENT_TEXT_LAYOUT_FONTS_PER_RUN = 8;
361
- function collectDocumentTextLayoutParagraphs(editor, fonts, loadedFontIds, previous = null, dirtyFrom = 0) {
362
- const paragraphs = [];
363
- if (!fonts.length || 0 === loadedFontIds.size) return {
364
- paragraphs
365
- };
366
- let runCount = 0;
367
- let textBytes = 0;
368
- const collectParagraph = (node, element, id, position)=>{
369
- if (paragraphs.length >= MAX_DOCUMENT_TEXT_LAYOUT_PARAGRAPHS) return;
370
- if (reusableDocumentLayoutBlocks(previous?.blocks ?? [], id, element, position + node.nodeSize, dirtyFrom).length) return;
371
- const paragraph = documentTextLayoutParagraph(node, element, id, fonts, loadedFontIds);
372
- if (!paragraph) return;
373
- const paragraphBytes = utf8ByteLength(paragraph.text);
374
- if (runCount + paragraph.runs.length > MAX_DOCUMENT_TEXT_LAYOUT_RUNS || textBytes + paragraphBytes > MAX_DOCUMENT_TEXT_LAYOUT_BYTES) return;
375
- runCount += paragraph.runs.length;
376
- textBytes += paragraphBytes;
377
- paragraphs.push(paragraph);
378
- };
379
- editor.state.doc.forEach((section, sectionPosition)=>{
380
- if ('documentSection' !== section.type.name) return;
381
- let requiresBrowserLineMeasurement = false;
382
- section.forEach((node, offset, index)=>{
383
- if (paragraphs.length >= MAX_DOCUMENT_TEXT_LAYOUT_PARAGRAPHS) return;
384
- if (isFloatingSquareDocumentImage(node)) {
385
- requiresBrowserLineMeasurement = true;
386
- return;
387
- }
388
- if (requiresBrowserLineMeasurement) return;
389
- const position = sectionPosition + offset + 1;
390
- const element = elementForNode(editor, position);
391
- if (!element) return;
392
- const id = documentBlockId(sectionPosition, index, position);
393
- if (isDocumentListNode(node)) return void collectDocumentListTextLayoutParagraphs(editor, node, position, id, collectParagraph);
394
- collectParagraph(node, element, id, position);
395
- });
396
- });
397
- return {
398
- paragraphs
399
- };
400
- }
401
- function isFloatingSquareDocumentImage(node) {
402
- return 'image' === node.type.name && 'square' === node.attrs.layout && ('left' === node.attrs.alignment || 'right' === node.attrs.alignment);
403
- }
404
- function collectDocumentListTextLayoutParagraphs(editor, list, listPosition, listId, collectParagraph) {
405
- list.forEach((item, itemOffset, itemIndex)=>{
406
- if ('listItem' !== item.type.name) return;
407
- const itemPosition = listPosition + itemOffset + 1;
408
- item.forEach((node, offset, index)=>{
409
- const position = itemPosition + offset + 1;
410
- const nestedList = isDocumentListNode(node);
411
- const id = documentListChildId(listId, itemIndex, node, index, nestedList);
412
- if (nestedList) return void collectDocumentListTextLayoutParagraphs(editor, node, position, id, collectParagraph);
413
- const element = elementForNode(editor, position);
414
- if (element) collectParagraph(node, element, id, position);
415
- });
416
- });
417
- }
418
- function utf8ByteLength(value) {
419
- let bytes = 0;
420
- for(let index = 0; index < value.length; index += 1){
421
- const code = value.charCodeAt(index);
422
- if (code <= 0x7f) bytes += 1;
423
- else if (code <= 0x7ff) bytes += 2;
424
- else if (code >= 0xd800 && code <= 0xdbff && index + 1 < value.length && value.charCodeAt(index + 1) >= 0xdc00 && value.charCodeAt(index + 1) <= 0xdfff) {
425
- bytes += 4;
426
- index += 1;
427
- } else bytes += 3;
428
- }
429
- return bytes;
430
- }
431
- function documentTextLayoutParagraph(node, element, id, fonts, loadedFontIds) {
432
- const text = documentTextLayoutContent(node);
433
- if (!isDocumentLineFragmentNode(node) || null === text || text.length < 2 || text.includes('\u00ad') || element.querySelector('img,br,table,[data-document-note],[contenteditable="false"]:not(.work-document-auto-page-break):not([data-document-tab])')) return null;
434
- const style = getComputedStyle(element);
435
- const paragraphStyle = documentTextParagraphStyle(style);
436
- const hasTabs = text.includes('\t');
437
- if (!paragraphStyle || hasTabs && 'ltr' !== paragraphStyle.direction) return null;
438
- const runs = collectDocumentTextLayoutRuns(element, text, fonts, loadedFontIds, paragraphStyle);
439
- if (!runs) return null;
440
- const width = element.clientWidth || element.offsetWidth || positivePixels(style.width);
441
- const maxWidth = width - nonNegativePixels(style.paddingLeft) - nonNegativePixels(style.paddingRight);
442
- if (maxWidth <= 1) return null;
443
- const textIndent = finitePixels(style.textIndent);
444
- return {
445
- id,
446
- text,
447
- runs,
448
- maxWidth,
449
- firstLineMaxWidth: Math.max(1, maxWidth - textIndent),
450
- direction: paragraphStyle.direction,
451
- whiteSpace: paragraphStyle.whiteSpace,
452
- ...hasTabs ? {
453
- tabLayout: {
454
- origin: nonNegativePixels(style.marginLeft),
455
- firstLineIndent: textIndent,
456
- defaultInterval: 48,
457
- stops: normalizeDocumentTabStops(node.attrs.tabStops).map(({ position, alignment })=>({
458
- position,
459
- alignment
460
- }))
461
- }
462
- } : {}
463
- };
464
- }
465
- function isDocumentLineFragmentNode(node) {
466
- return 'paragraph' === node.type.name || 'heading' === node.type.name;
467
- }
468
- function documentTextLayoutContent(node) {
469
- const content = [];
470
- let supported = true;
471
- node.forEach((child)=>{
472
- if (child.isText) content.push(child.text ?? '');
473
- else if ('documentTab' === child.type.name) content.push('\t');
474
- else supported = false;
475
- });
476
- return supported ? content.join('') : null;
477
- }
478
- function collectDocumentTextLayoutRuns(element, expectedText, fonts, loadedFontIds, paragraphStyle = documentTextParagraphStyle(getComputedStyle(element))) {
479
- if (!paragraphStyle) return null;
480
- const tokens = documentTextLayoutDomTokens(element);
481
- if (!tokens) return null;
482
- const text = [];
483
- const runs = [];
484
- let utf16Offset = 0;
485
- for (const token of tokens){
486
- const runStyle = documentTextLayoutRunStyle(getComputedStyle(token.styleElement), fonts, loadedFontIds, paragraphStyle, token.styleElement === element);
487
- if (!runStyle) return null;
488
- const startUtf16 = utf16Offset;
489
- utf16Offset += token.text.length;
490
- text.push(token.text);
491
- const previous = runs.at(-1);
492
- if (previous && previous.endUtf16 === startUtf16 && documentTextLayoutRunStyleKey(previous) === documentTextLayoutRunStyleKey(runStyle)) previous.endUtf16 = utf16Offset;
493
- else runs.push({
494
- startUtf16,
495
- endUtf16: utf16Offset,
496
- ...runStyle
497
- });
498
- if (runs.length > MAX_DOCUMENT_TEXT_LAYOUT_RUNS_PER_PARAGRAPH) return null;
499
- }
500
- return text.join('') === expectedText && runs.length ? runs : null;
501
- }
502
- function documentTextLayoutDomTokens(element) {
503
- const tokens = [];
504
- const visit = (node)=>{
505
- if (node.nodeType === Node.TEXT_NODE) {
506
- const textNode = node;
507
- if (!textNode.data.length) return true;
508
- const parent = textNode.parentElement;
509
- if (!parent) return false;
510
- tokens.push({
511
- text: textNode.data,
512
- styleElement: parent
513
- });
514
- return true;
515
- }
516
- if (node.nodeType !== Node.ELEMENT_NODE) return true;
517
- const child = node;
518
- if (child.classList.contains('work-document-auto-page-break')) return true;
519
- if (child.matches('[data-document-tab]')) {
520
- tokens.push({
521
- text: '\t',
522
- styleElement: child.parentElement ?? element
523
- });
524
- return true;
525
- }
526
- if ('false' === child.contentEditable) return true;
527
- return Array.from(child.childNodes).every(visit);
528
- };
529
- return Array.from(element.childNodes).every(visit) ? tokens : null;
530
- }
531
- function documentTextParagraphStyle(style) {
532
- if (!cssValueIs(style.whiteSpace, 'normal', 'break-spaces') || !cssValueIs(style.overflowWrap, 'normal', 'anywhere', 'break-word') || !cssValueIs(style.wordBreak, 'normal') || !cssValueIs(style.hyphens, 'manual', 'none') || !cssValueIs(style.textTransform, 'none') || !cssValueIs(style.unicodeBidi, 'normal', 'isolate')) return null;
533
- return {
534
- direction: 'rtl' === style.direction ? 'rtl' : 'ltr',
535
- whiteSpace: 'break-spaces' === style.whiteSpace ? 'breakSpaces' : 'normal'
536
- };
537
- }
538
- function documentTextLayoutRunStyle(style, fonts, loadedFontIds, paragraphStyle, paragraphRoot) {
539
- const direction = 'rtl' === style.direction ? 'rtl' : 'ltr';
540
- const whiteSpace = 'break-spaces' === style.whiteSpace ? 'breakSpaces' : 'normal';
541
- if (direction !== paragraphStyle.direction || whiteSpace !== paragraphStyle.whiteSpace || !cssValueIs(style.whiteSpace, 'normal', 'break-spaces') || !cssValueIs(style.overflowWrap, 'normal', 'anywhere', 'break-word') || !cssValueIs(style.wordBreak, 'normal') || !cssValueIs(style.hyphens, 'manual', 'none') || !cssValueIs(style.wordSpacing, 'normal', '0', '0px') || !cssValueIs(style.textTransform, 'none') || !cssValueIs(style.fontVariationSettings, 'normal') || !cssValueIs(style.fontVariantCaps, 'normal') || !cssValueIs(style.fontVariantEastAsian, 'normal') || !cssValueIs(style.fontVariantNumeric, 'normal') || !cssValueIs(style.fontVariantPosition, 'normal') || !cssValueIs(style.fontStretch, 'normal', '100%') || !cssValueIs(style.verticalAlign, 'baseline') || !cssValueIs(style.unicodeBidi, 'normal') && !(paragraphRoot && 'isolate' === style.unicodeBidi)) return null;
542
- const fontStyle = style.fontStyle && 'normal' !== style.fontStyle ? 'italic' === style.fontStyle ? 'italic' : null : 'normal';
543
- if (!fontStyle) return null;
544
- const families = cssFontFamilies(style.fontFamily);
545
- if (!families?.length) return null;
546
- const weight = cssFontWeight(style.fontWeight);
547
- const matchedFonts = [];
548
- for (const family of families){
549
- if (isGenericCssFontFamily(family)) break;
550
- const font = matchingDocumentLayoutFont(fonts, loadedFontIds, family, weight, fontStyle);
551
- if (!font) return null;
552
- if (!matchedFonts.some((candidate)=>candidate.id === font.id)) matchedFonts.push(font);
553
- if (matchedFonts.length > MAX_DOCUMENT_TEXT_LAYOUT_FONTS_PER_RUN) return null;
554
- }
555
- const [font, ...fallbackFonts] = matchedFonts;
556
- if (!font) return null;
557
- const fontSize = positivePixels(style.fontSize);
558
- const lineHeight = positivePixels(style.lineHeight) || Math.max(1, 1.2 * fontSize);
559
- if (fontSize <= 0 || fontSize > 512 || lineHeight <= 0 || lineHeight > 2048) return null;
560
- const ligatures = cssLigatures(style);
561
- const kerning = cssKerning(style.fontKerning);
562
- if (null === ligatures || null === kerning) return null;
563
- const letterSpacing = style.letterSpacing && 'normal' !== style.letterSpacing ? finitePixels(style.letterSpacing) : 0;
564
- if (letterSpacing < -100 || letterSpacing > 100) return null;
565
- return {
566
- fontId: font.id,
567
- ...fallbackFonts.length ? {
568
- fallbackFontIds: fallbackFonts.map((candidate)=>candidate.id)
569
- } : {},
570
- fontSize,
571
- lineHeight,
572
- letterSpacing,
573
- ligatures,
574
- kerning
575
- };
576
- }
577
- function matchingDocumentLayoutFont(fonts, loadedFontIds, family, requestedWeight, requestedStyle) {
578
- const candidates = fonts.filter((font)=>loadedFontIds.has(font.id) && font.family === family && (font.style ?? 'normal') === requestedStyle);
579
- const exact = candidates.find((font)=>(font.weight ?? 400) === requestedWeight);
580
- if (exact) return exact;
581
- const weights = Array.from(new Set(candidates.map((font)=>font.weight ?? 400))).sort((left, right)=>left - right);
582
- const matchedWeight = cssFontWeightSearchOrder(weights, requestedWeight)[0];
583
- return void 0 === matchedWeight ? void 0 : candidates.find((font)=>(font.weight ?? 400) === matchedWeight);
584
- }
585
- function cssFontWeightSearchOrder(weights, requestedWeight) {
586
- if (requestedWeight < 400) return [
587
- ...weights.filter((weight)=>weight <= requestedWeight).sort((left, right)=>right - left),
588
- ...weights.filter((weight)=>weight > requestedWeight).sort((left, right)=>left - right)
589
- ];
590
- if (requestedWeight <= 500) return [
591
- ...weights.filter((weight)=>weight >= requestedWeight && weight <= 500).sort((left, right)=>left - right),
592
- ...weights.filter((weight)=>weight < requestedWeight).sort((left, right)=>right - left),
593
- ...weights.filter((weight)=>weight > 500).sort((left, right)=>left - right)
594
- ];
595
- return [
596
- ...weights.filter((weight)=>weight >= requestedWeight).sort((left, right)=>left - right),
597
- ...weights.filter((weight)=>weight < requestedWeight).sort((left, right)=>right - left)
598
- ];
599
- }
600
- function documentTextLayoutRunStyleKey(style) {
601
- return [
602
- style.fontId,
603
- (style.fallbackFontIds ?? []).join('\u0002'),
604
- style.fontSize,
605
- style.lineHeight,
606
- style.letterSpacing ?? 0,
607
- style.ligatures ?? true,
608
- style.kerning ?? true
609
- ].join('\u0000');
610
- }
611
- function cssValueIs(value, ...supported) {
612
- return !value || supported.includes(value);
613
- }
614
- function cssLigatures(style) {
615
- const variant = style.fontVariantLigatures;
616
- if (variant && 'normal' !== variant && 'none' !== variant) return null;
617
- const features = style.fontFeatureSettings;
618
- if (!features || 'normal' === features) return 'none' !== variant;
619
- if (/^["']?liga["']?\s+(?:0|off)$/i.test(features.trim())) return false;
620
- return null;
621
- }
622
- function cssKerning(value) {
623
- if (!value || 'auto' === value || 'normal' === value) return true;
624
- if ('none' === value) return false;
625
- return null;
626
- }
627
- function cssFontFamilies(value) {
628
- const families = [];
629
- let current = '';
630
- let quote = null;
631
- let escaped = false;
632
- const pushCurrent = ()=>{
633
- const family = current.trim();
634
- current = '';
635
- if (!family) return false;
636
- families.push(family);
637
- return true;
638
- };
639
- for (const character of value){
640
- if (escaped) {
641
- current += character;
642
- escaped = false;
643
- continue;
644
- }
645
- if ('\\' === character) {
646
- escaped = true;
647
- continue;
648
- }
649
- if (quote) {
650
- if (character === quote) quote = null;
651
- else current += character;
652
- continue;
653
- }
654
- if ('"' === character || "'" === character) {
655
- if (current.trim()) return null;
656
- quote = character;
657
- continue;
658
- }
659
- if (',' === character) {
660
- if (!pushCurrent()) return null;
661
- continue;
662
- }
663
- current += character;
664
- }
665
- if (escaped || quote || !pushCurrent()) return null;
666
- return families;
667
- }
668
- function isGenericCssFontFamily(family) {
669
- return CSS_GENERIC_FONT_FAMILIES.has(family.toLowerCase());
670
- }
671
- const CSS_GENERIC_FONT_FAMILIES = new Set([
672
- 'cursive',
673
- 'emoji',
674
- 'fangsong',
675
- 'fantasy',
676
- 'math',
677
- 'monospace',
678
- 'sans-serif',
679
- 'serif',
680
- 'system-ui',
681
- 'ui-monospace',
682
- 'ui-rounded',
683
- 'ui-sans-serif',
684
- 'ui-serif'
685
- ]);
686
- function cssFontWeight(value) {
687
- if ('normal' === value) return 400;
688
- if ('bold' === value) return 700;
689
- const parsed = Number.parseInt(value, 10);
690
- return Number.isFinite(parsed) ? parsed : 400;
691
- }
692
- function createDocumentLineFragments(lines, outerTop, outerBottom, scale = 1) {
693
- const normalizedScale = Number.isFinite(scale) && scale > 0 ? scale : 1;
694
- return lines.flatMap((line, index)=>{
695
- const next = lines[index + 1];
696
- const start = 0 === index ? outerTop : line.top;
697
- const end = next?.top ?? outerBottom;
698
- const from = line.position;
699
- const to = next?.position ?? from + 1;
700
- if (end <= start || to <= from) return [];
701
- return [
702
- {
703
- from,
704
- to,
705
- height: Math.max(1, (end - start) / normalizedScale)
706
- }
707
- ];
708
- });
709
- }
710
- function measureParagraphLineFragments(editor, node, element, id, from, to, pagination, shapedLayout) {
711
- const text = documentTextLayoutContent(node);
712
- if (!isDocumentLineFragmentNode(node) || (text ?? node.textContent).length < 2 || element.querySelector('img,table,[data-document-note],[contenteditable="false"]:not(.work-document-auto-page-break):not([data-document-tab])')) return [];
713
- if (null !== text && shapedLayout?.missingGlyphCount === 0) {
714
- const shaped = measureShapedParagraphLineFragments(editor, element, id, from, to, pagination, shapedLayout);
715
- if (shaped.length > 1) return shaped;
716
- }
717
- const range = element.ownerDocument.createRange();
718
- if ('function' != typeof range.getClientRects) return [];
719
- range.selectNodeContents(element);
720
- const rects = groupDocumentLineRects(Array.from(range.getClientRects()).filter((rect)=>rect.height > 0 && rect.width > 0).map((rect)=>({
721
- top: rect.top,
722
- right: rect.right,
723
- bottom: rect.bottom,
724
- left: rect.left
725
- })));
726
- range.detach();
727
- if (rects.length < 2) return [];
728
- const textNodes = measureDocumentTextNodes(element);
729
- const lines = rects.flatMap((rect, index)=>{
730
- if (0 === index) return [
731
- {
732
- ...rect,
733
- position: from
734
- }
735
- ];
736
- const position = documentPositionAtMeasuredLineStart(editor, textNodes, rect) ?? documentPositionAtPoint(editor, element, rect);
737
- const previous = rects[index - 1];
738
- if (null === position || position <= from || position >= to || rect.top <= previous.top) return [];
739
- return [
740
- {
741
- ...rect,
742
- position
743
- }
744
- ];
745
- });
746
- const uniqueLines = lines.filter((line, index)=>0 === index || line.position > lines[index - 1].position);
747
- if (uniqueLines.length !== rects.length) return [];
748
- const elementRect = element.getBoundingClientRect();
749
- const style = getComputedStyle(element);
750
- const scaleY = element.offsetHeight > 0 ? elementRect.height / element.offsetHeight : 1;
751
- const outerTop = elementRect.top - nonNegativePixels(style.marginTop) * scaleY;
752
- const outerBottom = elementRect.bottom + nonNegativePixels(style.marginBottom) * scaleY;
753
- const fragments = createDocumentLineFragments(uniqueLines, outerTop, outerBottom, scaleY);
754
- if (fragments.length !== uniqueLines.length) return [];
755
- const editorRect = editor.view.dom.getBoundingClientRect();
756
- const scaleX = editor.view.dom.offsetWidth > 0 ? editorRect.width / editor.view.dom.offsetWidth : 1;
757
- const inlineOffsetLeft = Math.max(0, (elementRect.left - editorRect.left) / Math.max(scaleX, 0.01));
758
- const inlineOffsetRight = Math.max(0, (editorRect.right - elementRect.right) / Math.max(scaleX, 0.01));
759
- const minimumFragmentsPerPage = pagination.widowControl ? Math.min(2, fragments.length) : 1;
760
- return fragments.map((fragment, flowIndex)=>({
761
- block: {
762
- id: `${id}-line-${flowIndex}`,
763
- height: fragment.height,
764
- breakBefore: 0 === flowIndex && pagination.pageBreakBefore,
765
- keepTogether: 0 === flowIndex && pagination.keepLines,
766
- keepWithNext: flowIndex + 1 === fragments.length && pagination.keepWithNext,
767
- flowId: id,
768
- flowIndex,
769
- flowCount: fragments.length,
770
- minimumFragmentsPerPage
771
- },
772
- element,
773
- from: fragment.from,
774
- to: flowIndex + 1 < fragments.length ? fragment.to : to,
775
- inlineOffsetLeft: 0 === flowIndex ? 0 : inlineOffsetLeft,
776
- inlineOffsetRight: 0 === flowIndex ? 0 : inlineOffsetRight,
777
- observeResize: false
778
- }));
779
- }
780
- function measureShapedParagraphLineFragments(editor, element, id, from, to, pagination, layout) {
781
- if (layout.lines.length < 2) return [];
782
- const style = getComputedStyle(element);
783
- const fragments = createShapedDocumentLineFragments(layout.lines, from, to, verticalBlockStart(style), verticalBlockEnd(style));
784
- if (fragments.length !== layout.lines.length) return [];
785
- const { inlineOffsetLeft, inlineOffsetRight } = documentInlineOffsets(editor, element);
786
- const minimumFragmentsPerPage = pagination.widowControl ? Math.min(2, fragments.length) : 1;
787
- return fragments.map((fragment, flowIndex)=>({
788
- block: {
789
- id: `${id}-line-${flowIndex}`,
790
- height: fragment.height,
791
- breakBefore: 0 === flowIndex && pagination.pageBreakBefore,
792
- keepTogether: 0 === flowIndex && pagination.keepLines,
793
- keepWithNext: flowIndex + 1 === fragments.length && pagination.keepWithNext,
794
- flowId: id,
795
- flowIndex,
796
- flowCount: fragments.length,
797
- minimumFragmentsPerPage
798
- },
799
- element,
800
- from: fragment.from,
801
- to: fragment.to,
802
- inlineOffsetLeft: 0 === flowIndex ? 0 : inlineOffsetLeft,
803
- inlineOffsetRight: 0 === flowIndex ? 0 : inlineOffsetRight,
804
- observeResize: false
805
- }));
806
- }
807
- function createShapedDocumentLineFragments(lines, nodeFrom, nodeTo, blockStart, blockEnd) {
808
- const fragments = [];
809
- for (const [index, line] of lines.entries()){
810
- const next = lines[index + 1];
811
- const from = 0 === index ? nodeFrom : nodeFrom + 1 + Math.max(0, line.startUtf16);
812
- const to = next ? nodeFrom + 1 + Math.max(0, next.startUtf16) : nodeTo;
813
- if (line.endUtf16 < line.startUtf16 || next && next.startUtf16 < line.endUtf16 || !Number.isSafeInteger(from) || !Number.isSafeInteger(to) || to <= from || from < nodeFrom || to > nodeTo) return [];
814
- fragments.push({
815
- from,
816
- to,
817
- height: line.height + (0 === index ? blockStart : 0) + (index + 1 === lines.length ? blockEnd : 0)
818
- });
819
- }
820
- return fragments;
821
- }
822
- function groupDocumentLineRects(rects) {
823
- const lines = [];
824
- for (const rect of [
825
- ...rects
826
- ].sort((left, right)=>left.top - right.top || left.left - right.left)){
827
- const line = lines.find((candidate)=>verticallyOverlaps(candidate, rect));
828
- if (line) {
829
- line.top = Math.min(line.top, rect.top);
830
- line.right = Math.max(line.right, rect.right);
831
- line.bottom = Math.max(line.bottom, rect.bottom);
832
- line.left = Math.min(line.left, rect.left);
833
- } else lines.push({
834
- ...rect
835
- });
836
- }
837
- return lines.sort((left, right)=>left.top - right.top);
838
- }
839
- function verticallyOverlaps(left, right) {
840
- const overlap = Math.min(left.bottom, right.bottom) - Math.max(left.top, right.top);
841
- const minimumHeight = Math.min(left.bottom - left.top, right.bottom - right.top);
842
- return overlap > Math.max(1, 0.2 * minimumHeight);
843
- }
844
- function measureDocumentTextNodes(element) {
845
- const measurements = [];
846
- const showText = element.ownerDocument.defaultView?.NodeFilter.SHOW_TEXT ?? 4;
847
- const walker = element.ownerDocument.createTreeWalker(element, showText);
848
- let current = walker.nextNode();
849
- while(current){
850
- const node = current;
851
- if (node.data.length > 0 && !node.parentElement?.closest('.work-document-auto-page-break, [contenteditable="false"]')) {
852
- const range = element.ownerDocument.createRange();
853
- range.selectNodeContents(node);
854
- const rects = Array.from(range.getClientRects()).filter((rect)=>rect.height > 0 && rect.width > 0).map((rect)=>({
855
- top: rect.top,
856
- right: rect.right,
857
- bottom: rect.bottom,
858
- left: rect.left
859
- }));
860
- range.detach();
861
- if (rects.length) measurements.push({
862
- node,
863
- rects
864
- });
865
- }
866
- current = walker.nextNode();
867
- }
868
- return measurements;
869
- }
870
- function documentPositionAtMeasuredLineStart(editor, textNodes, line) {
871
- for (const measurement of textNodes){
872
- if (!measurement.rects.some((rect)=>verticallyOverlaps(rect, line))) continue;
873
- const range = measurement.node.ownerDocument.createRange();
874
- const offset = findDocumentLineStartOffset(measurement.node.data.length, line, (candidate)=>{
875
- range.setStart(measurement.node, candidate);
876
- range.setEnd(measurement.node, candidate + 1);
877
- const rectangle = Array.from(range.getClientRects()).find((rect)=>rect.height > 0 && rect.width > 0);
878
- return rectangle ? {
879
- top: rectangle.top,
880
- bottom: rectangle.bottom
881
- } : null;
882
- });
883
- range.detach();
884
- if (null !== offset) try {
885
- return editor.view.posAtDOM(measurement.node, offset);
886
- } catch {}
887
- }
888
- return null;
889
- }
890
- function findDocumentLineStartOffset(textLength, line, rectangleAt) {
891
- if (!Number.isSafeInteger(textLength) || textLength <= 0) return null;
892
- let lower = 0;
893
- let upper = textLength;
894
- while(lower < upper){
895
- const middle = Math.floor((lower + upper) / 2);
896
- const rectangle = rectangleAt(middle);
897
- if (!rectangle || rectangle.bottom <= line.top) lower = middle + 1;
898
- else upper = middle;
899
- }
900
- const searchStart = Math.max(0, lower - 4);
901
- const searchEnd = Math.min(textLength, lower + 5);
902
- for(let offset = searchStart; offset < searchEnd; offset += 1){
903
- const rectangle = rectangleAt(offset);
904
- if (rectangle && rectangle.bottom > line.top && rectangle.top < line.bottom) return offset;
905
- }
906
- return null;
907
- }
908
- function documentPositionAtPoint(editor, element, line) {
909
- const ownerDocument = element.ownerDocument;
910
- const style = getComputedStyle(element);
911
- const x = 'rtl' === style.direction ? line.right - 0.5 : line.left + 0.5;
912
- const y = line.top + (line.bottom - line.top) / 2;
913
- const caretPosition = ownerDocument.caretPositionFromPoint?.(x, y);
914
- const offsetNode = caretPosition?.offsetNode ?? ownerDocument.caretRangeFromPoint?.(x, y)?.startContainer;
915
- const offset = caretPosition?.offset ?? ownerDocument.caretRangeFromPoint?.(x, y)?.startOffset;
916
- if (!offsetNode || void 0 === offset || offsetNode !== element && !element.contains(offsetNode)) return null;
917
- try {
918
- return editor.view.posAtDOM(offsetNode, offset);
919
- } catch {
920
- return null;
921
- }
922
- }
923
- const MAX_DOCUMENT_TABLE_ROW_FRAGMENTS = 256;
924
- function createDocumentTableRowFragmentPlan(cells, rowHeight, maximumFragmentedRowHeight) {
925
- const height = Math.max(1, rowHeight);
926
- const atomic = ()=>[
927
- {
928
- height,
929
- cellRanges: cells.flatMap((cell)=>cell.to > cell.from ? [
930
- {
931
- from: cell.from,
932
- to: cell.to
933
- }
934
- ] : [])
935
- }
936
- ];
937
- if (!cells.length || !Number.isFinite(maximumFragmentedRowHeight) || maximumFragmentedRowHeight <= 0) return atomic();
938
- const candidates = normalizedDocumentTableBreakCoordinates(cells.flatMap((cell)=>cell.boundaries.slice(1, -1).map((boundary)=>boundary.y).filter((y)=>y > 0.5 && y < height - 0.5))).slice(0, MAX_DOCUMENT_TABLE_ROW_FRAGMENTS - 1);
939
- if (!candidates.length) return atomic();
940
- const coordinates = [
941
- 0,
942
- ...candidates,
943
- height
944
- ];
945
- const positions = coordinates.map((coordinate, coordinateIndex)=>cells.map((cell)=>{
946
- if (0 === coordinateIndex) return {
947
- position: cell.from,
948
- y: 0
949
- };
950
- if (coordinateIndex + 1 === coordinates.length) return {
951
- position: cell.to,
952
- y: height
953
- };
954
- return documentTableBoundaryAtOrBefore(cell, coordinate);
955
- }));
956
- const fragments = coordinates.slice(0, -1).flatMap((coordinate, index)=>{
957
- const nextCoordinate = coordinates[index + 1];
958
- const cellRanges = cells.flatMap((_cell, cellIndex)=>{
959
- const from = positions[index]?.[cellIndex]?.position;
960
- const to = positions[index + 1]?.[cellIndex]?.position;
961
- return void 0 !== from && void 0 !== to && to > from ? [
962
- {
963
- from,
964
- to
965
- }
966
- ] : [];
967
- });
968
- if (!cellRanges.length) return [];
969
- const cellBreaks = 0 === index ? void 0 : cells.map((cell, cellIndex)=>{
970
- const boundary = positions[index]?.[cellIndex] ?? {
971
- position: cell.from,
972
- y: 0
973
- };
974
- return {
975
- cellIndex: cell.cellIndex,
976
- position: boundary.position,
977
- alignmentOffset: Math.max(0, coordinate - boundary.y),
978
- ...void 0 === cell.tableOffsetLeft ? {} : {
979
- tableOffsetLeft: cell.tableOffsetLeft
980
- },
981
- ...void 0 === cell.outerWidth ? {} : {
982
- outerWidth: cell.outerWidth
983
- },
984
- ...void 0 === cell.contentOffsetLeft ? {} : {
985
- contentOffsetLeft: cell.contentOffsetLeft
986
- }
987
- };
988
- });
989
- return [
990
- {
991
- height: Math.max(1, nextCoordinate - coordinate),
992
- cellRanges,
993
- ...cellBreaks ? {
994
- cellBreaks
995
- } : {}
996
- }
997
- ];
998
- });
999
- return fragments.length > 1 ? fragments : atomic();
1000
- }
1001
- function normalizedDocumentTableBreakCoordinates(coordinates) {
1002
- const result = [];
1003
- for (const coordinate of [
1004
- ...coordinates
1005
- ].sort((left, right)=>left - right)){
1006
- if (!Number.isFinite(coordinate)) continue;
1007
- const normalized = Math.round(4 * coordinate) / 4;
1008
- if (0 === result.length || normalized - result.at(-1) >= 1) result.push(normalized);
1009
- }
1010
- return result;
1011
- }
1012
- function documentTableBoundaryAtOrBefore(cell, coordinate) {
1013
- let result = cell.boundaries[0] ?? {
1014
- position: cell.from,
1015
- y: 0
1016
- };
1017
- for (const boundary of cell.boundaries){
1018
- if (boundary.y > coordinate + 0.5) break;
1019
- result = boundary;
1020
- }
1021
- return result;
1022
- }
1023
- function measureDocumentTableRows(editor, tableNode, tableElement, tableId, tableFrom, maximumFragmentedRowHeight) {
1024
- const rows = [];
1025
- let complete = true;
1026
- tableNode.forEach((row, offset)=>{
1027
- const from = tableFrom + offset + 1;
1028
- const element = elementForNode(editor, from);
1029
- if ('tableRow' !== row.type.name || !element || 'tr' !== element.tagName.toLowerCase()) {
1030
- complete = false;
1031
- return;
1032
- }
1033
- rows.push({
1034
- node: row,
1035
- element,
1036
- from,
1037
- to: from + row.nodeSize,
1038
- height: Math.max(1, work_document_pagination_dom_outerHeight(element))
1039
- });
1040
- });
1041
- if (!complete || rows.length !== tableNode.childCount || !rows.length) return [];
1042
- let leadingHeaderRowCount = 0;
1043
- for (const row of rows){
1044
- if (!documentTableRowRepeats(row.node)) break;
1045
- leadingHeaderRowCount += 1;
1046
- }
1047
- const repeatHeaderCount = leadingHeaderRowCount < rows.length ? leadingHeaderRowCount : 0;
1048
- const rowHeight = rows.reduce((sum, row)=>sum + row.height, 0);
1049
- const tableStyle = getComputedStyle(tableElement);
1050
- const blockStart = verticalBlockStart(tableStyle);
1051
- const blockEnd = verticalBlockEnd(tableStyle);
1052
- const unassignedHeight = Math.max(0, work_document_pagination_dom_outerHeight(tableElement) - rowHeight - blockStart - blockEnd);
1053
- const repeatHeaderHeight = repeatHeaderCount > 0 ? rows.slice(0, repeatHeaderCount).reduce((sum, row)=>sum + row.height, 0) : 0;
1054
- const tableBreak = documentTablePaginationBreak(tableElement, tableNode, tableId, rows, repeatHeaderCount, repeatHeaderHeight);
1055
- const { inlineOffsetLeft, inlineOffsetRight } = documentInlineOffsets(editor, tableElement);
1056
- const continuationRowHeight = Math.max(1, maximumFragmentedRowHeight - repeatHeaderHeight);
1057
- const rowPlans = rows.map((row, rowIndex)=>{
1058
- const cells = measureDocumentTableCellFragments(editor, row);
1059
- const canSplit = rowIndex >= leadingHeaderRowCount && !documentTableRowCantSplit(row.node);
1060
- return createDocumentTableRowFragmentPlan(cells, row.height, canSplit ? continuationRowHeight : 0);
1061
- });
1062
- const flowCount = rowPlans.reduce((count, fragments)=>count + fragments.length, 0);
1063
- const repeatHeaderFragmentCount = repeatHeaderCount > 0 ? rowPlans.slice(0, repeatHeaderCount).reduce((count, fragments)=>count + fragments.length, 0) : 0;
1064
- const result = [];
1065
- let flowIndex = 0;
1066
- for (const [rowIndex, row] of rows.entries()){
1067
- const fragments = rowPlans[rowIndex] ?? [];
1068
- for (const [fragmentIndex, fragment] of fragments.entries()){
1069
- const first = 0 === flowIndex;
1070
- const last = flowIndex + 1 === flowCount;
1071
- const repeatsHeader = repeatHeaderFragmentCount > 0 && flowIndex >= repeatHeaderFragmentCount;
1072
- result.push({
1073
- block: {
1074
- id: fragments.length > 1 ? `${tableId}-row-${rowIndex}-fragment-${fragmentIndex}` : `${tableId}-row-${rowIndex}`,
1075
- height: fragment.height + (first ? blockStart : 0) + (last ? blockEnd + unassignedHeight : 0),
1076
- ...flowCount > 1 ? {
1077
- flowId: tableId,
1078
- flowIndex,
1079
- flowCount,
1080
- minimumFragmentsPerPage: 1
1081
- } : {},
1082
- ...flowCount > 1 && repeatHeaderFragmentCount > 0 ? {
1083
- repeatHeaderCount: repeatHeaderFragmentCount,
1084
- repeatHeaderHeight
1085
- } : {}
1086
- },
1087
- element: tableElement,
1088
- from: row.from,
1089
- to: row.to,
1090
- inlineOffsetLeft,
1091
- inlineOffsetRight,
1092
- observeResize: false,
1093
- selectionRanges: fragment.cellRanges,
1094
- tableBreak: {
1095
- ...tableBreak,
1096
- repeatedHeaderRowsHtml: repeatsHeader ? tableBreak.repeatedHeaderRowsHtml : [],
1097
- repeatedHeaderOverlayHtml: repeatsHeader ? tableBreak.repeatedHeaderOverlayHtml : '',
1098
- ...fragment.cellBreaks ? {
1099
- cellBreaks: fragment.cellBreaks
1100
- } : {}
1101
- }
1102
- });
1103
- flowIndex += 1;
1104
- }
1105
- }
1106
- return result;
1107
- }
1108
- function measureDocumentTableCellFragments(editor, row) {
1109
- const rowRect = row.element.getBoundingClientRect();
1110
- const tableElement = row.element.closest('table');
1111
- const tableRect = tableElement?.getBoundingClientRect();
1112
- const scaleX = tableElement && tableElement.offsetWidth > 0 && tableRect ? tableRect.width / tableElement.offsetWidth : 1;
1113
- const scaleY = row.element.offsetHeight > 0 ? rowRect.height / row.element.offsetHeight : 1;
1114
- const result = [];
1115
- row.node.forEach((cell, cellOffset, cellIndex)=>{
1116
- const from = row.from + cellOffset + 2;
1117
- const to = row.from + cellOffset + cell.nodeSize;
1118
- const element = elementForNode(editor, from - 1);
1119
- const boundaries = [];
1120
- cell.forEach((_block, blockOffset, blockIndex)=>{
1121
- const position = from + blockOffset;
1122
- const blockElement = elementForNode(editor, position);
1123
- if (!blockElement) return;
1124
- const rect = blockElement.getBoundingClientRect();
1125
- const style = getComputedStyle(blockElement);
1126
- const y = Math.max(0, (rect.top - rowRect.top) / Math.max(scaleY, 0.01) - nonNegativePixels(style.marginTop));
1127
- if (0 === blockIndex) boundaries.push({
1128
- position: from,
1129
- y
1130
- });
1131
- else boundaries.push({
1132
- position,
1133
- y
1134
- });
1135
- });
1136
- const cellStyle = element ? getComputedStyle(element) : null;
1137
- const cellRect = element?.getBoundingClientRect();
1138
- const contentStart = boundaries[0]?.y ?? (cellRect ? (cellRect.top - rowRect.top) / Math.max(scaleY, 0.01) + nonNegativePixels(cellStyle?.borderTopWidth ?? '') + nonNegativePixels(cellStyle?.paddingTop ?? '') : 0);
1139
- if (!boundaries.length) boundaries.push({
1140
- position: from,
1141
- y: contentStart
1142
- });
1143
- boundaries[0] = {
1144
- position: from,
1145
- y: contentStart
1146
- };
1147
- const contentEnd = cellRect ? (cellRect.bottom - rowRect.top) / Math.max(scaleY, 0.01) - nonNegativePixels(cellStyle?.borderBottomWidth ?? '') - nonNegativePixels(cellStyle?.paddingBottom ?? '') : row.height;
1148
- boundaries.push({
1149
- position: to,
1150
- y: Math.max(contentStart, Math.min(row.height, contentEnd))
1151
- });
1152
- result.push({
1153
- cellIndex,
1154
- from,
1155
- to,
1156
- boundaries,
1157
- ...cellRect && tableRect ? {
1158
- tableOffsetLeft: Math.max(0, (cellRect.left - tableRect.left) / Math.max(scaleX, 0.01)),
1159
- outerWidth: Math.max(1, element?.offsetWidth || cellRect.width / Math.max(scaleX, 0.01)),
1160
- contentOffsetLeft: nonNegativePixels(cellStyle?.borderLeftWidth ?? '') + nonNegativePixels(cellStyle?.paddingLeft ?? '')
1161
- } : {}
1162
- });
1163
- });
1164
- return result;
1165
- }
1166
- function documentTablePaginationBreak(tableElement, tableNode, tableId, rows, repeatHeaderCount, repeatHeaderHeight) {
1167
- const table = 'table' === tableElement.tagName.toLowerCase() ? tableElement : Array.from(tableElement.children).find((child)=>'table' === child.tagName.toLowerCase());
1168
- const colgroup = Array.from(table?.children ?? []).find((child)=>'colgroup' === child.tagName.toLowerCase());
1169
- const tableRect = table?.getBoundingClientRect();
1170
- const scaleX = table instanceof HTMLElement && table.offsetWidth > 0 && tableRect ? tableRect.width / table.offsetWidth : 1;
1171
- const scaleY = table instanceof HTMLElement && table.offsetHeight > 0 && tableRect ? tableRect.height / table.offsetHeight : 1;
1172
- const columnCount = logicalDocumentTableColumnCount(tableNode);
1173
- const tableWidth = table instanceof HTMLElement ? table.offsetWidth || (tableRect?.width ?? 0) / Math.max(scaleX, 0.01) : tableElement.offsetWidth;
1174
- const repeatedHeaderRows = rows.slice(0, repeatHeaderCount);
1175
- const leadingCell = rows[0]?.element.querySelector(':scope > th:first-child, :scope > td:first-child');
1176
- const leadingCellRect = leadingCell?.getBoundingClientRect();
1177
- const leadingCellStyle = leadingCell ? getComputedStyle(leadingCell) : null;
1178
- return {
1179
- tableId,
1180
- columnCount,
1181
- colgroupHtml: colgroup instanceof HTMLElement ? colgroup.outerHTML : '',
1182
- repeatHeaderHeight,
1183
- tableWidth,
1184
- leadingCellOffsetLeft: leadingCellRect && tableRect ? (leadingCellRect.left - tableRect.left) / Math.max(scaleX, 0.01) + nonNegativePixels(leadingCellStyle?.borderLeftWidth ?? '') + nonNegativePixels(leadingCellStyle?.paddingLeft ?? '') : 0,
1185
- repeatedHeaderRowsHtml: repeatedHeaderRows.map((row)=>repeatedDocumentTableRowHtml(row.element)),
1186
- repeatedHeaderOverlayHtml: repeatedDocumentTableHeaderOverlayHtml(repeatedHeaderRows, tableRect, scaleX, scaleY, tableWidth, columnCount)
1187
- };
1188
- }
1189
- function logicalDocumentTableColumnCount(table) {
1190
- let maximum = 1;
1191
- table.forEach((row)=>{
1192
- let columns = 0;
1193
- row.forEach((cell)=>{
1194
- const colspan = Number(cell.attrs.colspan);
1195
- columns += Number.isSafeInteger(colspan) && colspan > 0 ? colspan : 1;
1196
- });
1197
- maximum = Math.max(maximum, columns);
1198
- });
1199
- return maximum;
1200
- }
1201
- function repeatedDocumentTableRowHtml(row) {
1202
- const clone = row.cloneNode(true);
1203
- sanitizeRepeatedDocumentTableElement(clone);
1204
- return clone.outerHTML;
1205
- }
1206
- function repeatedDocumentTableHeaderOverlayHtml(rows, tableRect, scaleX, scaleY, tableWidth, columnCount) {
1207
- const container = document.createElement('div');
1208
- const originTop = rows[0]?.element.getBoundingClientRect().top ?? 0;
1209
- let fallbackTop = 0;
1210
- for (const row of rows){
1211
- const rowRect = row.element.getBoundingClientRect();
1212
- const rowTop = rowRect.height > 0 ? Math.max(0, (rowRect.top - originTop) / Math.max(scaleY, 0.01)) : fallbackTop;
1213
- const cells = Array.from(row.element.children).filter((element)=>element instanceof HTMLElement && [
1214
- 'td',
1215
- 'th'
1216
- ].includes(element.tagName.toLowerCase()));
1217
- let fallbackColumn = 0;
1218
- for (const source of cells){
1219
- const colspan = Math.max(1, Number.parseInt(source.getAttribute('colspan') ?? '1', 10) || 1);
1220
- const cellRect = source.getBoundingClientRect();
1221
- const fallbackLeft = fallbackColumn / Math.max(1, columnCount) * tableWidth;
1222
- const fallbackWidth = colspan / Math.max(1, columnCount) * tableWidth;
1223
- const left = tableRect && cellRect.width > 0 ? (cellRect.left - tableRect.left) / Math.max(scaleX, 0.01) : fallbackLeft;
1224
- const width = cellRect.width > 0 ? source.offsetWidth || cellRect.width / Math.max(scaleX, 0.01) : fallbackWidth;
1225
- const height = cellRect.height > 0 ? source.offsetHeight || cellRect.height / Math.max(scaleY, 0.01) : row.height;
1226
- container.append(repeatedDocumentTableHeaderCellElement(source, left, rowTop, width, height));
1227
- fallbackColumn += colspan;
1228
- }
1229
- fallbackTop += row.height;
1230
- }
1231
- return container.innerHTML;
1232
- }
1233
- function repeatedDocumentTableHeaderCellElement(source, left, top, width, height) {
1234
- const sourceClone = source.cloneNode(true);
1235
- sanitizeRepeatedDocumentTableElement(sourceClone);
1236
- const cell = document.createElement('div');
1237
- cell.className = 'work-document-table-repeated-header-cell';
1238
- cell.dataset.sourceCell = source.tagName.toLowerCase();
1239
- cell.innerHTML = sourceClone.innerHTML;
1240
- const computed = getComputedStyle(source);
1241
- for (const property of REPEATED_TABLE_HEADER_STYLE_PROPERTIES){
1242
- const value = computed.getPropertyValue(property);
1243
- if (value) cell.style.setProperty(property, value);
1244
- }
1245
- cell.style.left = `${left}px`;
1246
- cell.style.top = `${top}px`;
1247
- cell.style.width = `${Math.max(1, width)}px`;
1248
- cell.style.height = `${Math.max(1, height)}px`;
1249
- return cell;
1250
- }
1251
- const REPEATED_TABLE_HEADER_STYLE_PROPERTIES = [
1252
- 'background-color',
1253
- 'border-top',
1254
- 'border-right',
1255
- 'border-bottom',
1256
- 'border-left',
1257
- 'color',
1258
- 'font-family',
1259
- 'font-size',
1260
- 'font-style',
1261
- 'font-weight',
1262
- 'letter-spacing',
1263
- 'line-height',
1264
- 'padding-top',
1265
- 'padding-right',
1266
- 'padding-bottom',
1267
- 'padding-left',
1268
- 'text-align',
1269
- 'white-space'
1270
- ];
1271
- function sanitizeRepeatedDocumentTableElement(clone) {
1272
- clone.removeAttribute('contenteditable');
1273
- clone.removeAttribute('data-node-view-content-react');
1274
- clone.classList.remove('selectedCell');
1275
- for (const descendant of clone.querySelectorAll('*')){
1276
- descendant.removeAttribute('contenteditable');
1277
- descendant.removeAttribute('data-node-view-content-react');
1278
- descendant.classList.remove('selectedCell');
1279
- descendant.classList.remove('ProseMirror-selectednode');
1280
- }
1281
- clone.querySelectorAll(".work-document-auto-page-break,.work-document-table-page-break,.work-document-table-cell-page-break,.work-document-table-repeated-header").forEach((pageBreak)=>{
1282
- pageBreak.remove();
1283
- });
1284
- }
1285
- function measureDocumentLayoutBlocks(editor, previous = null, dirtyFrom = 0, textLayouts = new Map(), maximumFragmentedTableRowHeight = 1000000) {
1286
- const blocks = [];
1287
- const counts = {
1288
- measured: 0,
1289
- reused: 0
1290
- };
1291
- let unsupportedLayout = false;
1292
- let firstLayoutKey = null;
1293
- editor.state.doc.forEach((section, sectionPosition, sectionIndex)=>{
1294
- if ('documentSection' !== section.type.name) return;
1295
- const layout = documentSectionLayoutFromNodeAttributes(section.attrs);
1296
- const layoutKey = documentPaginationLayoutKey(layout);
1297
- firstLayoutKey ??= layoutKey;
1298
- if (layout.columns.count > 1 || layoutKey !== firstLayoutKey) unsupportedLayout = true;
1299
- measureSectionBlocks(editor, section, sectionPosition, sectionIndex, layout, previous?.blocks ?? [], dirtyFrom, textLayouts, maximumFragmentedTableRowHeight, counts, blocks);
1300
- });
1301
- return {
1302
- blocks,
1303
- measuredBlockCount: counts.measured,
1304
- reusedBlockCount: counts.reused,
1305
- unsupportedLayout
1306
- };
1307
- }
1308
- function documentPageMetrics(layout) {
1309
- const portrait = 'letter' === layout.pageSize ? {
1310
- width: 816,
1311
- height: 1056
1312
- } : {
1313
- width: work_document_layout_millimetersToPixels(210),
1314
- height: work_document_layout_millimetersToPixels(297)
1315
- };
1316
- const landscape = 'landscape' === layout.orientation;
1317
- return {
1318
- width: landscape ? portrait.height : portrait.width,
1319
- height: landscape ? portrait.width : portrait.height,
1320
- marginTop: work_document_layout_millimetersToPixels(layout.margins.top),
1321
- marginRight: work_document_layout_millimetersToPixels(layout.margins.right),
1322
- marginBottom: work_document_layout_millimetersToPixels(layout.margins.bottom),
1323
- marginLeft: work_document_layout_millimetersToPixels(layout.margins.left),
1324
- headerHeight: 0,
1325
- footerHeight: 0,
1326
- pageGap: 28
1327
- };
1328
- }
1329
- function documentPageBodyHeight(page) {
1330
- return Math.max(1, page.height - page.marginTop - page.marginBottom);
1331
- }
1332
- function documentPaginationSurfaceHeight(pageCount, page) {
1333
- const count = Math.max(1, Math.trunc(pageCount));
1334
- return page.height * count + page.pageGap * (count - 1);
1335
- }
1336
- function documentPageChromeHeights(editor) {
1337
- const page = editor.view.dom.closest('.work-document-page');
1338
- return {
1339
- headerHeight: work_document_pagination_dom_outerHeight(page?.querySelector('.work-document-page-header') ?? null),
1340
- footerHeight: work_document_pagination_dom_outerHeight(page?.querySelector('.work-document-page-footer') ?? null)
1341
- };
1342
- }
1343
- function measureSectionBlocks(editor, section, sectionPosition, sectionIndex, layout, previous, dirtyFrom, textLayouts, maximumFragmentedTableRowHeight, counts, result) {
1344
- const sectionBlocks = [];
1345
- section.forEach((node, offset, index)=>{
1346
- const position = sectionPosition + offset + 1;
1347
- const element = elementForNode(editor, position);
1348
- if (!element) return;
1349
- const id = documentBlockId(sectionPosition, index, position);
1350
- const paragraphPagination = documentNodeParagraphPagination(node);
1351
- if (isDocumentListNode(node)) {
1352
- const reused = reusableDocumentListLayoutBlocks(previous, id, position + node.nodeSize, dirtyFrom);
1353
- if (reused.length) {
1354
- counts.reused += reused.length;
1355
- sectionBlocks.push(...reused);
1356
- return;
1357
- }
1358
- const listBlocks = measureDocumentListBlocks(editor, node, element, id, position, textLayouts, maximumFragmentedTableRowHeight);
1359
- if (listBlocks.length) {
1360
- counts.measured += listBlocks.length;
1361
- sectionBlocks.push(...listBlocks);
1362
- return;
1363
- }
1364
- }
1365
- const reused = reusableDocumentLayoutBlocks(previous, id, element, position + node.nodeSize, dirtyFrom);
1366
- if (reused.length) {
1367
- counts.reused += reused.length;
1368
- sectionBlocks.push(...reused);
1369
- return;
1370
- }
1371
- if ('table' === node.type.name) {
1372
- const tableRows = measureDocumentTableRows(editor, node, element, id, position, maximumFragmentedTableRowHeight);
1373
- if (tableRows.length) {
1374
- counts.measured += tableRows.length;
1375
- sectionBlocks.push(...tableRows);
1376
- return;
1377
- }
1378
- }
1379
- const lineFragments = measureParagraphLineFragments(editor, node, element, id, position, position + node.nodeSize, paragraphPagination, textLayouts.get(id));
1380
- if (lineFragments.length > 1) {
1381
- counts.measured += lineFragments.length;
1382
- sectionBlocks.push(...lineFragments);
1383
- return;
1384
- }
1385
- counts.measured += 1;
1386
- sectionBlocks.push(measuredDocumentBlock({
1387
- block: {
1388
- id,
1389
- height: Math.max(1, work_document_pagination_dom_outerHeight(element)),
1390
- breakBefore: paragraphPagination.pageBreakBefore,
1391
- breakAfter: 'pageBreak' === node.type.name,
1392
- keepTogether: paragraphPagination.keepLines || shouldKeepDocumentBlockTogether(node),
1393
- keepWithNext: paragraphPagination.keepWithNext
1394
- },
1395
- element,
1396
- from: position,
1397
- to: position + node.nodeSize
1398
- }));
1399
- });
1400
- const last = sectionBlocks.at(-1);
1401
- if (last && 'continuous' !== layout.breakAfter && 'nextColumn' !== layout.breakAfter) last.block.breakAfter = true;
1402
- const sectionMetadata = {
1403
- id: 'string' == typeof section.attrs.id && section.attrs.id ? section.attrs.id : `document-section-${sectionIndex + 1}`,
1404
- index: sectionIndex,
1405
- position: sectionPosition,
1406
- layout
1407
- };
1408
- for (const block of sectionBlocks)block.section = sectionMetadata;
1409
- result.push(...sectionBlocks);
1410
- }
1411
- function measureDocumentListBlocks(editor, list, listElement, listId, listPosition, textLayouts, maximumFragmentedTableRowHeight) {
1412
- const blocks = [];
1413
- let complete = true;
1414
- list.forEach((item, itemOffset, itemIndex)=>{
1415
- if ('listItem' !== item.type.name) {
1416
- complete = false;
1417
- return;
1418
- }
1419
- const itemPosition = listPosition + itemOffset + 1;
1420
- const itemElement = elementForNode(editor, itemPosition);
1421
- if (!itemElement || 'li' !== itemElement.tagName.toLowerCase()) {
1422
- complete = false;
1423
- return;
1424
- }
1425
- const itemBlocks = [];
1426
- item.forEach((node, offset, index)=>{
1427
- const position = itemPosition + offset + 1;
1428
- const element = elementForNode(editor, position);
1429
- if (!element) {
1430
- complete = false;
1431
- return;
1432
- }
1433
- const nestedList = isDocumentListNode(node);
1434
- const id = documentListChildId(listId, itemIndex, node, index, nestedList);
1435
- if (nestedList) {
1436
- const nestedBlocks = measureDocumentListBlocks(editor, node, element, id, position, textLayouts, maximumFragmentedTableRowHeight);
1437
- if (!nestedBlocks.length) {
1438
- complete = false;
1439
- return;
1440
- }
1441
- itemBlocks.push(...nestedBlocks);
1442
- return;
1443
- }
1444
- itemBlocks.push(...measureDocumentListItemBlock(editor, node, element, id, position, textLayouts.get(id), maximumFragmentedTableRowHeight));
1445
- });
1446
- if (!itemBlocks.length) {
1447
- complete = false;
1448
- return;
1449
- }
1450
- fitDocumentBlocksToContainer(itemBlocks, itemElement);
1451
- blocks.push(...itemBlocks);
1452
- });
1453
- if (!complete || !blocks.length) return [];
1454
- fitDocumentBlocksToContainer(blocks, listElement);
1455
- return blocks;
1456
- }
1457
- function measureDocumentListItemBlock(editor, node, element, id, position, shapedLayout, maximumFragmentedTableRowHeight) {
1458
- if ('table' === node.type.name) {
1459
- const rows = measureDocumentTableRows(editor, node, element, id, position, maximumFragmentedTableRowHeight);
1460
- if (rows.length) return rows;
1461
- }
1462
- const pagination = documentNodeParagraphPagination(node);
1463
- const lineFragments = measureParagraphLineFragments(editor, node, element, id, position, position + node.nodeSize, pagination, shapedLayout);
1464
- const { inlineOffsetLeft, inlineOffsetRight } = documentInlineOffsets(editor, element);
1465
- if (lineFragments.length > 1) return lineFragments.map((fragment)=>({
1466
- ...fragment,
1467
- inlineOffsetLeft,
1468
- inlineOffsetRight
1469
- }));
1470
- return [
1471
- {
1472
- ...measuredDocumentBlock({
1473
- block: {
1474
- id,
1475
- height: Math.max(1, work_document_pagination_dom_outerHeight(element)),
1476
- breakBefore: pagination.pageBreakBefore,
1477
- breakAfter: 'pageBreak' === node.type.name,
1478
- keepTogether: pagination.keepLines || shouldKeepDocumentBlockTogether(node),
1479
- keepWithNext: pagination.keepWithNext
1480
- },
1481
- element,
1482
- from: position,
1483
- to: position + node.nodeSize
1484
- }),
1485
- inlineOffsetLeft,
1486
- inlineOffsetRight
1487
- }
1488
- ];
1489
- }
1490
- function fitDocumentBlocksToContainer(blocks, container) {
1491
- const target = work_document_pagination_dom_outerHeight(container);
1492
- if (!(target > 0) || 0 === blocks.length) return;
1493
- const measured = blocks.reduce((height, candidate)=>height + candidate.block.height, 0);
1494
- let difference = target - measured;
1495
- if (Math.abs(difference) < 0.01) return;
1496
- const first = blocks[0];
1497
- const last = blocks.at(-1);
1498
- if (difference > 0) {
1499
- const style = getComputedStyle(container);
1500
- const start = Math.min(difference, verticalBlockStart(style));
1501
- first.block.height += start;
1502
- difference -= start;
1503
- const end = Math.min(difference, verticalBlockEnd(style));
1504
- last.block.height += end;
1505
- difference -= end;
1506
- last.block.height += difference;
1507
- return;
1508
- }
1509
- let remaining = -difference;
1510
- const candidates = Array.from(new Set([
1511
- last,
1512
- first,
1513
- ...blocks.slice(1, -1).reverse()
1514
- ]));
1515
- for (const candidate of candidates){
1516
- if (remaining <= 0) break;
1517
- const removable = Math.max(0, candidate.block.height - 1);
1518
- const amount = Math.min(removable, remaining);
1519
- candidate.block.height -= amount;
1520
- remaining -= amount;
1521
- }
1522
- }
1523
- function reusableDocumentListLayoutBlocks(previous, listId, nodeTo, dirtyFrom) {
1524
- if (nodeTo > dirtyFrom) return [];
1525
- const prefix = `${listId}-item-`;
1526
- return previous.filter(({ block })=>block.id.startsWith(prefix) || block.flowId?.startsWith(prefix)).map((candidate)=>({
1527
- ...candidate,
1528
- block: {
1529
- ...candidate.block
1530
- }
1531
- }));
1532
- }
1533
293
  const DOCUMENT_NAVIGATION_COLLECTION_WINDOW_THRESHOLD = 48;
1534
294
  const DOCUMENT_NAVIGATION_COLLECTION_WINDOW_LIMIT = 32;
1535
295
  const DOCUMENT_NAVIGATION_COLLECTION_WINDOW_OVERSCAN = 4;
@@ -3872,8 +2632,8 @@ function formatCommentDate(value) {
3872
2632
  }).format(time);
3873
2633
  }
3874
2634
  const DOCUMENT_EDITOR_FOCUS_RETRY_FRAMES = 4;
3875
- function restoreDocumentEditorFocus(getTarget) {
3876
- const commandTrigger = document.activeElement;
2635
+ function restoreDocumentEditorFocus(getTarget, focusOrigin = document.activeElement) {
2636
+ const commandTrigger = focusOrigin;
3877
2637
  let remainingFrames = DOCUMENT_EDITOR_FOCUS_RETRY_FRAMES;
3878
2638
  const restore = ()=>{
3879
2639
  if (remainingFrames <= 0) return;
@@ -3900,9 +2660,43 @@ function fallbackPaginationPageDescriptor(sectionId, sectionIndex, layout, physi
3900
2660
  sectionId: sectionId ?? 'document-section-1',
3901
2661
  sectionIndex: sectionIndex ?? 0,
3902
2662
  layout,
3903
- pageChrome: resolveDocumentPageChrome(layout, sectionPage, sectionPage)
2663
+ pageChrome: resolveDocumentPageChrome(layout, sectionPage, sectionPage),
2664
+ page: documentPageMetrics(layout)
3904
2665
  };
3905
2666
  }
2667
+ function shouldPublishDocumentUpdate(transaction) {
2668
+ return !transaction.getMeta('preventUpdate') && false !== transaction.getMeta('addToHistory');
2669
+ }
2670
+ function applyExternalDocumentContent(editor, content) {
2671
+ let nextDocument;
2672
+ try {
2673
+ nextDocument = createNodeFromContent(content, editor.schema, {
2674
+ errorOnInvalidContent: true,
2675
+ slice: false
2676
+ });
2677
+ } catch {
2678
+ return 'unsupported';
2679
+ }
2680
+ if (!(nextDocument instanceof model_Node)) return 'unsupported';
2681
+ const currentDocument = editor.state.doc;
2682
+ const start = currentDocument.content.findDiffStart(nextDocument.content);
2683
+ if (null === start) return 'unchanged';
2684
+ const end = currentDocument.content.findDiffEnd(nextDocument.content);
2685
+ let currentEnd = end?.a ?? currentDocument.content.size;
2686
+ let nextEnd = end?.b ?? nextDocument.content.size;
2687
+ const overlap = start - Math.min(currentEnd, nextEnd);
2688
+ if (overlap > 0) {
2689
+ currentEnd += overlap;
2690
+ nextEnd += overlap;
2691
+ }
2692
+ try {
2693
+ const transaction = editor.state.tr.replace(start, currentEnd, nextDocument.slice(start, nextEnd)).setMeta('addToHistory', false).setMeta('preventUpdate', true);
2694
+ editor.view.dispatch(transaction);
2695
+ return 'applied';
2696
+ } catch {
2697
+ return 'unsupported';
2698
+ }
2699
+ }
3906
2700
  const SEARCH_CONTEXT_LENGTH = 34;
3907
2701
  function documentTextMatches(document1, rawQuery) {
3908
2702
  const query = rawQuery.toLocaleLowerCase();
@@ -4520,7 +3314,11 @@ const documentFormatBlockAttributeNames = [
4520
3314
  'keepWithNext',
4521
3315
  'pageBreakBefore',
4522
3316
  'widowControl',
3317
+ 'contextualSpacing',
3318
+ 'outlineLevel',
4523
3319
  'paragraphDirection',
3320
+ 'paragraphBorders',
3321
+ 'paragraphShading',
4524
3322
  'tabStops'
4525
3323
  ];
4526
3324
  function documentFormatBlockAtSelection(editor) {
@@ -4890,21 +3688,29 @@ function createDocumentPageChromeEditorExtensions(placeholder = '输入页眉或
4890
3688
  resizable: false
4891
3689
  }
4892
3690
  }),
4893
- extension_image.configure({
3691
+ DocumentImage.configure({
4894
3692
  allowBase64: true,
4895
3693
  inline: true
4896
3694
  }),
3695
+ DocumentEquation,
4897
3696
  TextStyle,
4898
3697
  extension_color,
4899
3698
  extension_underline,
4900
3699
  DocumentSubscript,
4901
3700
  DocumentSuperscript,
4902
3701
  DocumentPageChromeCommands,
3702
+ DocumentParagraphFormatting,
4903
3703
  extension_text_align.configure({
4904
3704
  types: [
4905
3705
  'paragraph'
4906
3706
  ]
4907
3707
  }),
3708
+ DocumentParagraphIdentity.configure({
3709
+ types: [
3710
+ 'paragraph'
3711
+ ]
3712
+ }),
3713
+ DocumentTableRowIdentity,
4908
3714
  DocumentPageChromeWpsShortcuts,
4909
3715
  extension_placeholder.configure({
4910
3716
  placeholder
@@ -5080,6 +3886,7 @@ function emptyPageChrome(content) {
5080
3886
  function DocumentLayoutPanel({ layout, activeTab: controlledActiveTab, sectionIndex, sectionCount, onActiveTabChange, onChange, onInsertSection, onMergeSection, onClose }) {
5081
3887
  const tabsId = useId();
5082
3888
  const [internalActiveTab, setInternalActiveTab] = useState('page');
3889
+ const [customPaperSelected, setCustomPaperSelected] = useState('custom' === layout.pageSize);
5083
3890
  const activeTab = controlledActiveTab ?? internalActiveTab;
5084
3891
  const changeActiveTab = (tab)=>{
5085
3892
  if (void 0 === controlledActiveTab) setInternalActiveTab(tab);
@@ -5103,6 +3910,17 @@ function DocumentLayoutPanel({ layout, activeTab: controlledActiveTab, sectionIn
5103
3910
  '左'
5104
3911
  ]
5105
3912
  ];
3913
+ const pageMargins = documentPageMarginsForLayout(layout);
3914
+ const exactPageGeometry = normalizeDocumentPageGeometry(layout.pageGeometry);
3915
+ const resolvedPageSize = resolveDocumentPageSize(layout);
3916
+ const customPageDimensions = exactPageGeometry ? {
3917
+ width: pageTwipsToMillimeters(exactPageGeometry.width),
3918
+ height: pageTwipsToMillimeters(exactPageGeometry.height)
3919
+ } : {
3920
+ width: resolvedPageSize.width,
3921
+ height: resolvedPageSize.height
3922
+ };
3923
+ const gutterPosition = pageMargins.gutterAtTop ? 'top' : pageMargins.gutterOnRight ? 'right' : 'left';
5106
3924
  const update = (patch)=>onChange({
5107
3925
  ...layout,
5108
3926
  ...patch
@@ -5169,20 +3987,42 @@ function DocumentLayoutPanel({ layout, activeTab: controlledActiveTab, sectionIn
5169
3987
  }),
5170
3988
  /*#__PURE__*/ jsx(OfficeSelect, {
5171
3989
  ariaLabel: "纸张大小",
5172
- value: layout.pageSize,
3990
+ value: customPaperSelected ? 'custom' : layout.pageSize,
5173
3991
  options: [
3992
+ {
3993
+ value: 'a3',
3994
+ label: 'A3'
3995
+ },
5174
3996
  {
5175
3997
  value: 'a4',
5176
3998
  label: 'A4'
5177
3999
  },
4000
+ {
4001
+ value: 'a5',
4002
+ label: 'A5'
4003
+ },
5178
4004
  {
5179
4005
  value: 'letter',
5180
4006
  label: 'Letter'
4007
+ },
4008
+ {
4009
+ value: 'legal',
4010
+ label: 'Legal'
4011
+ },
4012
+ {
4013
+ value: 'tabloid',
4014
+ label: 'Tabloid'
4015
+ },
4016
+ {
4017
+ value: 'custom',
4018
+ label: '自定义'
5181
4019
  }
5182
4020
  ],
5183
- onValueChange: (pageSize)=>update({
5184
- pageSize
5185
- })
4021
+ onValueChange: (pageSize)=>{
4022
+ if ('custom' === pageSize) return void setCustomPaperSelected(true);
4023
+ setCustomPaperSelected(false);
4024
+ onChange(updateDocumentPaperSizePreset(layout, pageSize));
4025
+ }
5186
4026
  })
5187
4027
  ]
5188
4028
  }),
@@ -5205,13 +4045,46 @@ function DocumentLayoutPanel({ layout, activeTab: controlledActiveTab, sectionIn
5205
4045
  label: '横向'
5206
4046
  }
5207
4047
  ],
5208
- onValueChange: (orientation)=>update({
5209
- orientation
5210
- })
4048
+ onValueChange: (orientation)=>onChange(updateDocumentPageOrientation(layout, orientation))
5211
4049
  })
5212
4050
  ]
5213
4051
  })
5214
4052
  ]
4053
+ }),
4054
+ (customPaperSelected || 'custom' === layout.pageSize) && /*#__PURE__*/ jsx("div", {
4055
+ className: "work-document-layout-paired-fields",
4056
+ children: [
4057
+ [
4058
+ 'width',
4059
+ '宽度'
4060
+ ],
4061
+ [
4062
+ 'height',
4063
+ '高度'
4064
+ ]
4065
+ ].map(([dimension, label])=>/*#__PURE__*/ jsxs("div", {
4066
+ className: "work-office-field",
4067
+ children: [
4068
+ /*#__PURE__*/ jsxs("span", {
4069
+ children: [
4070
+ label,
4071
+ "(毫米)"
4072
+ ]
4073
+ }),
4074
+ /*#__PURE__*/ jsx(CommittedOfficeNumberField, {
4075
+ min: 25.4,
4076
+ max: 558.8,
4077
+ step: 0.1,
4078
+ ariaLabel: `纸张${label}`,
4079
+ value: customPageDimensions[dimension],
4080
+ normalizeValue: normalizeDocumentPageDimensionInput,
4081
+ onValueCommit: (value)=>{
4082
+ setCustomPaperSelected(false);
4083
+ onChange(updateDocumentCustomPageMillimeters(layout, dimension, value));
4084
+ }
4085
+ })
4086
+ ]
4087
+ }, dimension))
5215
4088
  })
5216
4089
  ]
5217
4090
  }),
@@ -5227,24 +4100,167 @@ function DocumentLayoutPanel({ layout, activeTab: controlledActiveTab, sectionIn
5227
4100
  className: "work-office-field",
5228
4101
  children: [
5229
4102
  /*#__PURE__*/ jsx("span", {
5230
- children: label
4103
+ children: label
4104
+ }),
4105
+ /*#__PURE__*/ jsx(CommittedOfficeNumberField, {
4106
+ min: 5,
4107
+ max: 60,
4108
+ step: 1,
4109
+ ariaLabel: `${label}页边距`,
4110
+ value: layout.margins[side],
4111
+ normalizeValue: normalizeDocumentMarginInput,
4112
+ onValueCommit: (value)=>onChange(updateDocumentPageMarginMillimeters(layout, side, value))
4113
+ })
4114
+ ]
4115
+ }, side))
4116
+ })
4117
+ ]
4118
+ }),
4119
+ /*#__PURE__*/ jsxs("section", {
4120
+ className: "work-document-layout-group",
4121
+ "aria-label": "高级页边距",
4122
+ children: [
4123
+ /*#__PURE__*/ jsx("h3", {
4124
+ children: "高级页边距"
4125
+ }),
4126
+ /*#__PURE__*/ jsx("div", {
4127
+ className: "work-document-layout-margin-grid",
4128
+ children: [
4129
+ [
4130
+ 'header',
4131
+ '页眉距顶端'
4132
+ ],
4133
+ [
4134
+ 'footer',
4135
+ '页脚距底端'
4136
+ ],
4137
+ [
4138
+ 'gutter',
4139
+ '装订线'
4140
+ ]
4141
+ ].map(([key, label])=>/*#__PURE__*/ jsxs("div", {
4142
+ className: "work-office-field",
4143
+ children: [
4144
+ /*#__PURE__*/ jsx("span", {
4145
+ children: label
4146
+ }),
4147
+ /*#__PURE__*/ jsx(CommittedOfficeNumberField, {
4148
+ min: 0,
4149
+ max: 60,
4150
+ step: 0.1,
4151
+ ariaLabel: label,
4152
+ value: work_document_page_margins_twipsToMillimeters(pageMargins[key]),
4153
+ normalizeValue: normalizeDocumentPageOffsetInput,
4154
+ onValueCommit: (value)=>onChange(updateDocumentPageMarginMillimeters(layout, key, value))
4155
+ })
4156
+ ]
4157
+ }, key))
4158
+ }),
4159
+ /*#__PURE__*/ jsxs("div", {
4160
+ className: "work-document-layout-paired-fields",
4161
+ children: [
4162
+ /*#__PURE__*/ jsxs("div", {
4163
+ className: "work-office-field",
4164
+ children: [
4165
+ /*#__PURE__*/ jsx("span", {
4166
+ children: "上边距规则"
4167
+ }),
4168
+ /*#__PURE__*/ jsx(OfficeSelect, {
4169
+ ariaLabel: "上边距与页眉关系",
4170
+ value: pageMargins.top < 0 ? 'fromPageEdge' : 'clearChrome',
4171
+ options: [
4172
+ {
4173
+ value: 'clearChrome',
4174
+ label: '避让页眉'
4175
+ },
4176
+ {
4177
+ value: 'fromPageEdge',
4178
+ label: '允许重叠'
4179
+ }
4180
+ ],
4181
+ onValueChange: (mode)=>onChange(updateDocumentPageMarginMode(layout, 'top', mode))
4182
+ })
4183
+ ]
4184
+ }),
4185
+ /*#__PURE__*/ jsxs("div", {
4186
+ className: "work-office-field",
4187
+ children: [
4188
+ /*#__PURE__*/ jsx("span", {
4189
+ children: "下边距规则"
4190
+ }),
4191
+ /*#__PURE__*/ jsx(OfficeSelect, {
4192
+ ariaLabel: "下边距与页脚关系",
4193
+ value: pageMargins.bottom < 0 ? 'fromPageEdge' : 'clearChrome',
4194
+ options: [
4195
+ {
4196
+ value: 'clearChrome',
4197
+ label: '避让页脚'
4198
+ },
4199
+ {
4200
+ value: 'fromPageEdge',
4201
+ label: '允许重叠'
4202
+ }
4203
+ ],
4204
+ onValueChange: (mode)=>onChange(updateDocumentPageMarginMode(layout, 'bottom', mode))
4205
+ })
4206
+ ]
4207
+ })
4208
+ ]
4209
+ }),
4210
+ /*#__PURE__*/ jsxs("div", {
4211
+ className: "work-document-layout-paired-fields",
4212
+ children: [
4213
+ /*#__PURE__*/ jsxs("div", {
4214
+ className: "work-office-field",
4215
+ children: [
4216
+ /*#__PURE__*/ jsx("span", {
4217
+ children: "多页方式"
4218
+ }),
4219
+ /*#__PURE__*/ jsx(OfficeSelect, {
4220
+ ariaLabel: "镜像页边距",
4221
+ value: pageMargins.mirrorMargins ? 'mirrored' : 'normal',
4222
+ options: [
4223
+ {
4224
+ value: 'normal',
4225
+ label: '普通'
4226
+ },
4227
+ {
4228
+ value: 'mirrored',
4229
+ label: '镜像页边距'
4230
+ }
4231
+ ],
4232
+ onValueChange: (value)=>onChange(updateDocumentMirrorMargins(layout, 'mirrored' === value))
4233
+ })
4234
+ ]
4235
+ }),
4236
+ /*#__PURE__*/ jsxs("div", {
4237
+ className: "work-office-field",
4238
+ children: [
4239
+ /*#__PURE__*/ jsx("span", {
4240
+ children: "装订线位置"
5231
4241
  }),
5232
- /*#__PURE__*/ jsx(CommittedOfficeNumberField, {
5233
- min: 5,
5234
- max: 60,
5235
- step: 1,
5236
- ariaLabel: `${label}页边距`,
5237
- value: layout.margins[side],
5238
- normalizeValue: normalizeDocumentMarginInput,
5239
- onValueCommit: (value)=>update({
5240
- margins: {
5241
- ...layout.margins,
5242
- [side]: value
5243
- }
5244
- })
4242
+ /*#__PURE__*/ jsx(OfficeSelect, {
4243
+ ariaLabel: "装订线位置",
4244
+ value: gutterPosition,
4245
+ options: [
4246
+ {
4247
+ value: 'left',
4248
+ label: '左侧'
4249
+ },
4250
+ {
4251
+ value: 'right',
4252
+ label: '右侧'
4253
+ },
4254
+ {
4255
+ value: 'top',
4256
+ label: '顶部'
4257
+ }
4258
+ ],
4259
+ onValueChange: (position)=>onChange(updateDocumentGutterPosition(layout, position))
5245
4260
  })
5246
4261
  ]
5247
- }, side))
4262
+ })
4263
+ ]
5248
4264
  })
5249
4265
  ]
5250
4266
  })
@@ -5373,6 +4389,16 @@ function normalizeDocumentMarginInput(value) {
5373
4389
  const number = Number(value);
5374
4390
  return Number.isFinite(number) ? clampDocumentMargin(number) : null;
5375
4391
  }
4392
+ function normalizeDocumentPageOffsetInput(value) {
4393
+ if (!value.trim()) return null;
4394
+ const number = Number(value);
4395
+ return Number.isFinite(number) ? Math.min(60, Math.max(0, Math.round(10 * number) / 10)) : null;
4396
+ }
4397
+ function normalizeDocumentPageDimensionInput(value) {
4398
+ if (!value.trim()) return null;
4399
+ const number = Number(value);
4400
+ return Number.isFinite(number) ? Math.min(558.8, Math.max(25.4, Math.round(10 * number) / 10)) : null;
4401
+ }
5376
4402
  function normalizeDocumentPageNumberInput(value) {
5377
4403
  if (!value.trim()) return null;
5378
4404
  const number = Number(value);
@@ -5564,6 +4590,7 @@ function DocumentPageThumbnail({ backgroundColor, fallbackText, orientation, pag
5564
4590
  const sourceMutation = useThumbnailSourceMutation(source, captureActive);
5565
4591
  const [image, setImage] = useState(null);
5566
4592
  const [state, setState] = useState(source ? 'loading' : 'idle');
4593
+ const sourcePage = source ? thumbnailSourcePage(source, pageIndex) : null;
5567
4594
  useEffect(()=>{
5568
4595
  if (!source || !captureActive) {
5569
4596
  setImage(null);
@@ -5605,6 +4632,10 @@ function DocumentPageThumbnail({ backgroundColor, fallbackText, orientation, pag
5605
4632
  source?.pageGap,
5606
4633
  source?.pageHeight,
5607
4634
  source?.pageWidth,
4635
+ sourcePage?.height,
4636
+ sourcePage?.left,
4637
+ sourcePage?.top,
4638
+ sourcePage?.width,
5608
4639
  source?.revision,
5609
4640
  sourceMutation
5610
4641
  ]);
@@ -5616,7 +4647,7 @@ function DocumentPageThumbnail({ backgroundColor, fallbackText, orientation, pag
5616
4647
  "data-thumbnail-state": state,
5617
4648
  "aria-hidden": "true",
5618
4649
  style: {
5619
- aspectRatio: source ? `${source.pageWidth} / ${source.pageHeight}` : void 0,
4650
+ aspectRatio: source ? `${sourcePage?.width ?? source.pageWidth} / ${sourcePage?.height ?? source.pageHeight}` : void 0,
5620
4651
  backgroundColor
5621
4652
  },
5622
4653
  children: image ? /*#__PURE__*/ jsx("img", {
@@ -5634,23 +4665,32 @@ async function renderDocumentPageThumbnail(source, pageIndex, targetPixelWidth,
5634
4665
  if (!Number.isSafeInteger(pageIndex) || pageIndex < 0 || pageIndex >= source.pageCount) throw new Error('The requested document thumbnail page is out of range.');
5635
4666
  const capture = mountWorkLiveDocumentCapture(source, pageIndex);
5636
4667
  try {
5637
- const scale = Math.max(0.1, Math.min(1, targetPixelWidth / source.pageWidth));
4668
+ const page = thumbnailSourcePage(source, pageIndex);
4669
+ const scale = Math.max(0.1, Math.min(1, targetPixelWidth / page.width));
5638
4670
  const canvas = await renderCanvas(capture.viewport, {
5639
4671
  backgroundColor: capture.backgroundColor,
5640
4672
  fontReadyTimeout: 0,
5641
- height: source.pageHeight,
4673
+ height: page.height,
5642
4674
  logging: false,
5643
4675
  scale,
5644
4676
  useCORS: true,
5645
- width: source.pageWidth,
5646
- windowHeight: Math.ceil(source.pageHeight),
5647
- windowWidth: Math.ceil(source.pageWidth)
4677
+ width: page.width,
4678
+ windowHeight: Math.ceil(page.height),
4679
+ windowWidth: Math.ceil(page.width)
5648
4680
  });
5649
4681
  return canvas.toDataURL('image/png');
5650
4682
  } finally{
5651
4683
  capture.host.remove();
5652
4684
  }
5653
4685
  }
4686
+ function thumbnailSourcePage(source, pageIndex) {
4687
+ return source.pages?.[pageIndex] ?? {
4688
+ height: source.pageHeight,
4689
+ left: 0,
4690
+ top: pageIndex * (source.pageHeight + source.pageGap),
4691
+ width: source.pageWidth
4692
+ };
4693
+ }
5654
4694
  async function renderDocumentPageCanvas(element, options) {
5655
4695
  const { default: html2canvas } = await import("html2canvas");
5656
4696
  return html2canvas(element, options);
@@ -5762,7 +4802,8 @@ function DocumentPageNavigation({ currentPage, pages, thumbnailSource, onSelectP
5762
4802
  const itemHeights = useMemo(()=>pages.map((page)=>estimatedDocumentPageItemHeight(page, thumbnailSource)), [
5763
4803
  pages,
5764
4804
  thumbnailSource?.pageHeight,
5765
- thumbnailSource?.pageWidth
4805
+ thumbnailSource?.pageWidth,
4806
+ thumbnailSource?.pages
5766
4807
  ]);
5767
4808
  const pageStarts = useMemo(()=>documentNavigationItemStarts(itemHeights, DOCUMENT_PAGE_ITEM_GAP), [
5768
4809
  itemHeights
@@ -5953,7 +4994,8 @@ function documentPageIndex(pages, physicalPage) {
5953
4994
  }
5954
4995
  function estimatedDocumentPageItemHeight(page, source) {
5955
4996
  const width = 'landscape' === page.orientation ? DOCUMENT_PAGE_LANDSCAPE_WIDTH : DOCUMENT_PAGE_PORTRAIT_WIDTH;
5956
- const sourceRatio = source && source.pageWidth > 0 && source.pageHeight > 0 ? source.pageHeight / source.pageWidth : null;
4997
+ const sourcePage = source?.pages?.[page.physicalPage - 1];
4998
+ const sourceRatio = sourcePage && sourcePage.width > 0 && sourcePage.height > 0 ? sourcePage.height / sourcePage.width : source && source.pageWidth > 0 && source.pageHeight > 0 ? source.pageHeight / source.pageWidth : null;
5957
4999
  const fallbackRatio = 'landscape' === page.orientation ? 210 / 297 : 297 / 210;
5958
5000
  return Math.ceil(width * (sourceRatio ?? fallbackRatio) + DOCUMENT_PAGE_BUTTON_CHROME_HEIGHT);
5959
5001
  }
@@ -6323,25 +5365,135 @@ function runWithInstantDocumentScroll(editor, run) {
6323
5365
  else restore();
6324
5366
  }
6325
5367
  }
6326
- function DocumentPageStack({ pageColor, pageCount, pageGap, pageHeight }) {
5368
+ function WorkDocumentPageBorder({ layout, physicalPage = 1, sectionPage }) {
5369
+ const source = normalizeDocumentPageBorders(layout.pageBorders);
5370
+ if (!source || !documentPageBordersVisible(source, sectionPage)) return null;
5371
+ const resolved = resolveDocumentPageBorders(source, resolveDocumentPageMargins(layout, physicalPage).body);
5372
+ if (!resolved) return null;
5373
+ const visibleEdges = DOCUMENT_PAGE_BORDER_EDGES.filter((edge)=>(resolved.edges[edge]?.width ?? 0) > 0);
5374
+ if (!visibleEdges.length) return null;
5375
+ const shadow = visibleEdges.map((edge)=>source.edges[edge]).find((border)=>border?.shadow);
5376
+ const shadowColor = shadow ? resolved.edges[visibleEdges.find((edge)=>source.edges[edge] === shadow) ?? 'top']?.color ?? '#000000' : null;
5377
+ const art = visibleEdges.some((edge)=>{
5378
+ const border = source.edges[edge];
5379
+ return border && isDocumentParagraphArtBorderStyle(border.style);
5380
+ });
5381
+ const frame = visibleEdges.some((edge)=>source.edges[edge]?.frame);
5382
+ return /*#__PURE__*/ jsx("div", {
5383
+ className: `work-document-page-border ${resolved.zOrder}`,
5384
+ "data-document-page-border-art": String(art),
5385
+ "data-document-page-border-display": resolved.display,
5386
+ "data-document-page-border-frame": String(frame),
5387
+ "data-document-page-border-offset-from": resolved.offsetFrom,
5388
+ "data-document-page-border-z-order": resolved.zOrder,
5389
+ "aria-hidden": "true",
5390
+ style: {
5391
+ top: resolved.insets.top,
5392
+ right: resolved.insets.right,
5393
+ bottom: resolved.insets.bottom,
5394
+ left: resolved.insets.left,
5395
+ ...Object.fromEntries(DOCUMENT_PAGE_BORDER_EDGES.flatMap((edge)=>{
5396
+ const border = resolved.edges[edge];
5397
+ if (!border || border.width <= 0) return [];
5398
+ const property = `border${edge[0]?.toUpperCase()}${edge.slice(1)}`;
5399
+ return [
5400
+ [
5401
+ property,
5402
+ `${formatPixels(border.width)}px ${border.style} ${border.color}`
5403
+ ]
5404
+ ];
5405
+ })),
5406
+ ...shadowColor ? {
5407
+ filter: `drop-shadow(2px 2px 0 ${shadowColor})`
5408
+ } : {}
5409
+ }
5410
+ });
5411
+ }
5412
+ function formatPixels(value) {
5413
+ return Number(value.toFixed(3)).toString();
5414
+ }
5415
+ function DocumentPageStack({ pageColor, pageCount, pageGap, pageHeight, pageWidth = 794, pages = [] }) {
6327
5416
  const count = Math.max(1, Math.trunc(pageCount));
6328
5417
  const gap = Math.max(0, pageGap);
6329
5418
  const height = Math.max(1, pageHeight);
6330
- return /*#__PURE__*/ jsx("div", {
6331
- className: "work-document-page-stack",
6332
- "data-page-count": count,
6333
- "aria-hidden": "true",
6334
- children: Array.from({
6335
- length: count
6336
- }, (_, pageIndex)=>/*#__PURE__*/ jsx("div", {
6337
- className: "work-document-page-sheet",
6338
- "data-page-index": pageIndex + 1,
6339
- style: {
6340
- backgroundColor: pageColor,
6341
- height,
6342
- top: pageIndex * (height + gap)
6343
- }
6344
- }, pageIndex))
5419
+ const fallbackPage = {
5420
+ width: Math.max(1, pageWidth),
5421
+ height,
5422
+ marginTop: 0,
5423
+ marginRight: 0,
5424
+ marginBottom: 0,
5425
+ marginLeft: 0,
5426
+ headerHeight: 0,
5427
+ footerHeight: 0,
5428
+ pageGap: gap
5429
+ };
5430
+ const geometry = documentPageSurfaceGeometry(pages.map((page)=>page.page ?? fallbackPage), fallbackPage, count);
5431
+ const descriptors = geometry.frames.map((frame)=>({
5432
+ frame,
5433
+ page: pages[frame.pageIndex]
5434
+ }));
5435
+ return /*#__PURE__*/ jsxs(Fragment, {
5436
+ children: [
5437
+ /*#__PURE__*/ jsx("div", {
5438
+ className: "work-document-page-stack",
5439
+ "data-page-count": count,
5440
+ "data-page-surface-height": geometry.height,
5441
+ "data-page-surface-width": geometry.width,
5442
+ "aria-hidden": "true",
5443
+ children: descriptors.map(({ frame, page })=>{
5444
+ const resolvedPageSize = page ? resolveDocumentPageSize(page.layout) : null;
5445
+ return /*#__PURE__*/ jsx("div", {
5446
+ className: "work-document-page-sheet",
5447
+ "data-work-document-page-sheet": "",
5448
+ "data-page-index": frame.pageIndex + 1,
5449
+ "data-page-top": frame.top,
5450
+ "data-page-left": frame.left,
5451
+ "data-page-width": frame.width,
5452
+ "data-page-height": frame.height,
5453
+ "data-pdf-orientation": resolvedPageSize?.orientation,
5454
+ "data-pdf-page-size": resolvedPageSize?.preset,
5455
+ "data-pdf-page-width-points": resolvedPageSize?.widthPoints ?? 72 * frame.width / 96,
5456
+ "data-pdf-page-height-points": resolvedPageSize?.heightPoints ?? 72 * frame.height / 96,
5457
+ style: {
5458
+ backgroundColor: pageColor,
5459
+ height: frame.height,
5460
+ left: frame.left,
5461
+ top: frame.top,
5462
+ width: frame.width
5463
+ }
5464
+ }, frame.pageIndex);
5465
+ })
5466
+ }),
5467
+ [
5468
+ 'back',
5469
+ 'front'
5470
+ ].map((zOrder)=>/*#__PURE__*/ jsx("div", {
5471
+ className: `work-document-page-border-stack ${zOrder}`,
5472
+ "data-document-page-border-stack": zOrder,
5473
+ "aria-hidden": "true",
5474
+ children: descriptors.flatMap(({ frame, page })=>{
5475
+ const resolved = page ? resolveDocumentPageBorders(page.layout.pageBorders, resolveDocumentPageMargins(page.layout, page.physicalPage ?? frame.pageIndex + 1).body) : null;
5476
+ if (!page || resolved?.zOrder !== zOrder) return [];
5477
+ return [
5478
+ /*#__PURE__*/ jsx("div", {
5479
+ className: "work-document-page-border-surface",
5480
+ "data-page-index": frame.pageIndex + 1,
5481
+ style: {
5482
+ height: frame.height,
5483
+ left: frame.left,
5484
+ top: frame.top,
5485
+ width: frame.width
5486
+ },
5487
+ children: /*#__PURE__*/ jsx(WorkDocumentPageBorder, {
5488
+ layout: page.layout,
5489
+ physicalPage: page.physicalPage ?? frame.pageIndex + 1,
5490
+ sectionPage: page.sectionPage
5491
+ })
5492
+ }, frame.pageIndex)
5493
+ ];
5494
+ })
5495
+ }, zOrder))
5496
+ ]
6345
5497
  });
6346
5498
  }
6347
5499
  function DocumentRulerTabStops({ marginLeft, pageWidth, rulerRef, tabStops, textWidth, onChange }) {
@@ -6751,21 +5903,10 @@ function DocumentRulerHandle({ kind, label, maximum, minimum, position, value, v
6751
5903
  });
6752
5904
  }
6753
5905
  function documentRulerPageWidth(layout) {
6754
- const portrait = documentRulerPortraitPage(layout);
6755
- return 'landscape' === layout.orientation ? portrait.height : portrait.width;
5906
+ return resolveDocumentPageSize(layout).widthPixels;
6756
5907
  }
6757
5908
  function documentRulerPageHeight(layout) {
6758
- const portrait = documentRulerPortraitPage(layout);
6759
- return 'landscape' === layout.orientation ? portrait.width : portrait.height;
6760
- }
6761
- function documentRulerPortraitPage(layout) {
6762
- return 'letter' === layout.pageSize ? {
6763
- width: 816,
6764
- height: 1056
6765
- } : {
6766
- width: work_document_layout_millimetersToPixels(210),
6767
- height: work_document_layout_millimetersToPixels(297)
6768
- };
5909
+ return resolveDocumentPageSize(layout).heightPixels;
6769
5910
  }
6770
5911
  function documentRulerTicks(layout, pageWidth) {
6771
5912
  const left = work_document_layout_millimetersToPixels(layout.margins.left);
@@ -7780,6 +6921,15 @@ const documentCommandCatalog = {
7780
6921
  ]
7781
6922
  }
7782
6923
  },
6924
+ bookmark: {
6925
+ id: 'insert.bookmark',
6926
+ label: '添加书签',
6927
+ location: {
6928
+ area: 'ribbon',
6929
+ tab: 'insert',
6930
+ group: 'links'
6931
+ }
6932
+ },
7783
6933
  spelling: {
7784
6934
  id: 'review.spelling',
7785
6935
  label: '拼写检查',
@@ -9604,9 +8754,7 @@ function DocumentPageLayoutRibbon({ editor, layout, layoutOpen, pageColor, onLay
9604
8754
  label: '横向'
9605
8755
  }
9606
8756
  ],
9607
- onValueChange: (orientation)=>update({
9608
- orientation
9609
- })
8757
+ onValueChange: (orientation)=>onLayoutChange(updateDocumentPageOrientation(layout, orientation))
9610
8758
  })
9611
8759
  ]
9612
8760
  }),
@@ -9620,18 +8768,39 @@ function DocumentPageLayoutRibbon({ editor, layout, layoutOpen, pageColor, onLay
9620
8768
  ariaLabel: "纸张大小",
9621
8769
  value: layout.pageSize,
9622
8770
  options: [
8771
+ {
8772
+ value: 'a3',
8773
+ label: 'A3'
8774
+ },
9623
8775
  {
9624
8776
  value: 'a4',
9625
8777
  label: 'A4'
9626
8778
  },
8779
+ {
8780
+ value: 'a5',
8781
+ label: 'A5'
8782
+ },
9627
8783
  {
9628
8784
  value: 'letter',
9629
8785
  label: 'Letter'
8786
+ },
8787
+ {
8788
+ value: 'legal',
8789
+ label: 'Legal'
8790
+ },
8791
+ {
8792
+ value: 'tabloid',
8793
+ label: 'Tabloid'
8794
+ },
8795
+ {
8796
+ value: 'custom',
8797
+ label: '自定义'
9630
8798
  }
9631
8799
  ],
9632
- onValueChange: (pageSize)=>update({
9633
- pageSize
9634
- })
8800
+ onValueChange: (pageSize)=>{
8801
+ if ('custom' === pageSize) return void onOpenLayout('page');
8802
+ onLayoutChange(updateDocumentPaperSizePreset(layout, pageSize));
8803
+ }
9635
8804
  })
9636
8805
  ]
9637
8806
  }),
@@ -9742,9 +8911,11 @@ const FALLBACK_IMAGE_HEIGHT_PIXELS = 180;
9742
8911
  const MINIMUM_IMAGE_SIZE_CENTIMETERS = 0.01;
9743
8912
  const MAXIMUM_IMAGE_SIZE_CENTIMETERS = 55.87;
9744
8913
  const MAXIMUM_WRAP_DISTANCE_MILLIMETERS = 25;
8914
+ const MAXIMUM_IMAGE_OFFSET_MILLIMETERS = 558.7;
9745
8915
  function createDocumentPicturePropertiesDraft(source) {
9746
8916
  const width = effectiveDimension(source.properties.width, source.renderedWidth, FALLBACK_IMAGE_WIDTH_PIXELS);
9747
8917
  const height = effectiveDimension(source.properties.height, source.renderedHeight, FALLBACK_IMAGE_HEIGHT_PIXELS);
8918
+ const layer = normalizeDocumentImageLayer(source.properties.layer ?? {});
9748
8919
  return {
9749
8920
  width: document_picture_properties_dialog_model_centimeters(width),
9750
8921
  height: document_picture_properties_dialog_model_centimeters(height),
@@ -9753,7 +8924,22 @@ function createDocumentPicturePropertiesDraft(source) {
9753
8924
  layout: source.properties.layout,
9754
8925
  alignment: source.properties.alignment,
9755
8926
  wrapDistance: formatNumber(source.properties.wrapDistance),
9756
- alternativeText: source.properties.alternativeText
8927
+ wrapSide: normalizeDocumentImageWrapSide(source.properties.wrapSide),
8928
+ alternativeText: source.properties.alternativeText,
8929
+ precisePosition: Boolean(source.properties.position),
8930
+ horizontalOffset: formatNumber(source.properties.position?.horizontalOffset ?? 0),
8931
+ verticalOffset: formatNumber(source.properties.position?.verticalOffset ?? 0),
8932
+ horizontalReference: source.properties.position?.horizontalReference ?? 'column',
8933
+ verticalReference: source.properties.position?.verticalReference ?? 'paragraph',
8934
+ cropTop: formatNumber(source.properties.crop?.top ?? 0),
8935
+ cropRight: formatNumber(source.properties.crop?.right ?? 0),
8936
+ cropBottom: formatNumber(source.properties.crop?.bottom ?? 0),
8937
+ cropLeft: formatNumber(source.properties.crop?.left ?? 0),
8938
+ relativeHeight: String(layer.relativeHeight),
8939
+ behindDocument: layer.behindDocument,
8940
+ allowOverlap: layer.allowOverlap,
8941
+ layoutInCell: layer.layoutInCell,
8942
+ lockAnchor: layer.lockAnchor
9757
8943
  };
9758
8944
  }
9759
8945
  function withDocumentPictureDimension(draft, dimension, value) {
@@ -9789,11 +8975,15 @@ function documentPicturePropertiesErrors(draft) {
9789
8975
  return {
9790
8976
  width: imageDimensionError(draft.width),
9791
8977
  height: imageDimensionError(draft.height),
9792
- wrapDistance: 'inline' === draft.layout || validNumber(draft.wrapDistance, 0, MAXIMUM_WRAP_DISTANCE_MILLIMETERS) ? null : '请输入 0 到 25 之间的毫米数。'
8978
+ wrapDistance: 'inline' === draft.layout || 'none' === draft.layout || validNumber(draft.wrapDistance, 0, MAXIMUM_WRAP_DISTANCE_MILLIMETERS) ? null : '请输入 0 到 25 之间的毫米数。',
8979
+ horizontalOffset: !draft.precisePosition || validSignedNumber(draft.horizontalOffset, MAXIMUM_IMAGE_OFFSET_MILLIMETERS) ? null : '请输入 -558.7 到 558.7 之间的毫米数。',
8980
+ verticalOffset: !draft.precisePosition || validSignedNumber(draft.verticalOffset, MAXIMUM_IMAGE_OFFSET_MILLIMETERS) ? null : '请输入 -558.7 到 558.7 之间的毫米数。',
8981
+ crop: imageCropError(draft),
8982
+ relativeHeight: 'inline' === draft.layout || validInteger(draft.relativeHeight, 0, MAX_DOCUMENT_IMAGE_RELATIVE_HEIGHT) ? null : `请输入 0 到 ${MAX_DOCUMENT_IMAGE_RELATIVE_HEIGHT} 之间的整数。`
9793
8983
  };
9794
8984
  }
9795
8985
  function hasDocumentPicturePropertiesErrors(errors) {
9796
- return Boolean(errors.width || errors.height || errors.wrapDistance);
8986
+ return Boolean(errors.width || errors.height || errors.wrapDistance || errors.horizontalOffset || errors.verticalOffset || errors.crop || errors.relativeHeight);
9797
8987
  }
9798
8988
  function documentPicturePropertyChanges(initial, current) {
9799
8989
  const errors = documentPicturePropertiesErrors(current);
@@ -9805,8 +8995,31 @@ function documentPicturePropertyChanges(initial, current) {
9805
8995
  if (initial.layout !== current.layout) changes.layout = current.layout;
9806
8996
  if (initial.alignment !== current.alignment) changes.alignment = current.alignment;
9807
8997
  if (!sameDraftNumber(initial.wrapDistance, current.wrapDistance)) changes.wrapDistance = Number(current.wrapDistance);
8998
+ if (initial.wrapSide !== current.wrapSide) changes.wrapSide = current.wrapSide;
9808
8999
  const alternativeText = current.alternativeText.trim();
9809
9000
  if (initial.alternativeText.trim() !== alternativeText) changes.alternativeText = alternativeText;
9001
+ if (initial.precisePosition !== current.precisePosition || current.precisePosition && (!sameDraftNumber(initial.horizontalOffset, current.horizontalOffset) || !sameDraftNumber(initial.verticalOffset, current.verticalOffset) || initial.horizontalReference !== current.horizontalReference || initial.verticalReference !== current.verticalReference)) changes.position = current.precisePosition ? {
9002
+ horizontalOffset: Number(current.horizontalOffset),
9003
+ verticalOffset: Number(current.verticalOffset),
9004
+ horizontalReference: current.horizontalReference,
9005
+ verticalReference: current.verticalReference
9006
+ } : null;
9007
+ if (!sameDraftNumber(initial.cropTop, current.cropTop) || !sameDraftNumber(initial.cropRight, current.cropRight) || !sameDraftNumber(initial.cropBottom, current.cropBottom) || !sameDraftNumber(initial.cropLeft, current.cropLeft)) {
9008
+ const crop = {
9009
+ top: Number(current.cropTop),
9010
+ right: Number(current.cropRight),
9011
+ bottom: Number(current.cropBottom),
9012
+ left: Number(current.cropLeft)
9013
+ };
9014
+ changes.crop = Object.values(crop).some((edge)=>edge > 0) ? crop : null;
9015
+ }
9016
+ if (!sameDraftNumber(initial.relativeHeight, current.relativeHeight) || initial.behindDocument !== current.behindDocument || initial.allowOverlap !== current.allowOverlap || initial.layoutInCell !== current.layoutInCell || initial.lockAnchor !== current.lockAnchor) changes.layer = normalizeDocumentImageLayer({
9017
+ relativeHeight: current.relativeHeight,
9018
+ behindDocument: current.behindDocument,
9019
+ allowOverlap: current.allowOverlap,
9020
+ layoutInCell: current.layoutInCell,
9021
+ lockAnchor: current.lockAnchor
9022
+ });
9810
9023
  return Object.keys(changes).length ? changes : null;
9811
9024
  }
9812
9025
  function effectiveDimension(explicit, rendered, fallback) {
@@ -9821,6 +9034,27 @@ function validNumber(value, minimum, maximum) {
9821
9034
  const number = numericDraft(value);
9822
9035
  return null !== number && number >= minimum && number <= maximum;
9823
9036
  }
9037
+ function validSignedNumber(value, maximumMagnitude) {
9038
+ if (!value.trim()) return false;
9039
+ const number = Number(value);
9040
+ return Number.isFinite(number) && Math.abs(number) <= maximumMagnitude;
9041
+ }
9042
+ function validInteger(value, minimum, maximum) {
9043
+ if (!value.trim()) return false;
9044
+ const number = Number(value);
9045
+ return Number.isInteger(number) && number >= minimum && number <= maximum;
9046
+ }
9047
+ function imageCropError(draft) {
9048
+ const edges = [
9049
+ draft.cropTop,
9050
+ draft.cropRight,
9051
+ draft.cropBottom,
9052
+ draft.cropLeft
9053
+ ].map(numericDraft);
9054
+ if (edges.some((edge)=>null === edge || edge > 99.99)) return '请输入 0 到 99.99 之间的裁剪百分比。';
9055
+ const [top = 0, right = 0, bottom = 0, left = 0] = edges;
9056
+ return left + right < 100 && top + bottom < 100 ? null : '相对两边的裁剪量之和必须小于 100%。';
9057
+ }
9824
9058
  function numericDraft(value) {
9825
9059
  if (!value.trim()) return null;
9826
9060
  const number = Number(value);
@@ -9854,10 +9088,25 @@ const document_picture_properties_dialog_layoutOptions = [
9854
9088
  label: '四周环绕',
9855
9089
  icon: TextWrap
9856
9090
  },
9091
+ {
9092
+ value: 'tight',
9093
+ label: '紧密环绕',
9094
+ icon: TextWrap
9095
+ },
9096
+ {
9097
+ value: 'through',
9098
+ label: '穿越环绕',
9099
+ icon: TextWrap
9100
+ },
9857
9101
  {
9858
9102
  value: 'topBottom',
9859
9103
  label: '上下环绕',
9860
9104
  icon: Rows3
9105
+ },
9106
+ {
9107
+ value: 'none',
9108
+ label: '自由浮动',
9109
+ icon: TextWrap
9861
9110
  }
9862
9111
  ];
9863
9112
  const alignmentOptions = [
@@ -9877,6 +9126,52 @@ const alignmentOptions = [
9877
9126
  icon: AlignRight
9878
9127
  }
9879
9128
  ];
9129
+ const wrapSideOptions = [
9130
+ {
9131
+ value: 'bothSides',
9132
+ label: '两侧'
9133
+ },
9134
+ {
9135
+ value: 'left',
9136
+ label: '仅左侧'
9137
+ },
9138
+ {
9139
+ value: 'right',
9140
+ label: '仅右侧'
9141
+ },
9142
+ {
9143
+ value: 'largest',
9144
+ label: '较宽一侧'
9145
+ }
9146
+ ];
9147
+ const horizontalReferenceOptions = [
9148
+ {
9149
+ value: 'column',
9150
+ label: '栏'
9151
+ },
9152
+ {
9153
+ value: 'margin',
9154
+ label: '页边距'
9155
+ },
9156
+ {
9157
+ value: 'page',
9158
+ label: '页面'
9159
+ }
9160
+ ];
9161
+ const verticalReferenceOptions = [
9162
+ {
9163
+ value: 'paragraph',
9164
+ label: '段落'
9165
+ },
9166
+ {
9167
+ value: 'margin',
9168
+ label: '页边距'
9169
+ },
9170
+ {
9171
+ value: 'page',
9172
+ label: '页面'
9173
+ }
9174
+ ];
9880
9175
  function DocumentPicturePropertiesControl({ editor }) {
9881
9176
  const [source, setSource] = useState(null);
9882
9177
  const triggerRef = useRef(null);
@@ -9967,6 +9262,17 @@ function DocumentPicturePropertiesDialog({ editor, source, restoreFocusTarget, o
9967
9262
  }),
9968
9263
  /*#__PURE__*/ jsx(PictureAlignmentSection, {
9969
9264
  draft: draft,
9265
+ errors: errors,
9266
+ onDraftChange: setDraft
9267
+ }),
9268
+ /*#__PURE__*/ jsx(PictureLayerSection, {
9269
+ draft: draft,
9270
+ errors: errors,
9271
+ onDraftChange: setDraft
9272
+ }),
9273
+ /*#__PURE__*/ jsx(PictureCropSection, {
9274
+ draft: draft,
9275
+ errors: errors,
9970
9276
  onDraftChange: setDraft
9971
9277
  }),
9972
9278
  /*#__PURE__*/ jsxs("label", {
@@ -10082,6 +9388,24 @@ function PictureLayoutSection({ draft, onDraftChange }) {
10082
9388
  }, option.value);
10083
9389
  })
10084
9390
  }),
9391
+ /*#__PURE__*/ jsxs("div", {
9392
+ className: "work-document-picture-properties-wrap-side",
9393
+ children: [
9394
+ /*#__PURE__*/ jsx("span", {
9395
+ children: "环绕侧"
9396
+ }),
9397
+ /*#__PURE__*/ jsx(OfficeSelect, {
9398
+ ariaLabel: "图片文字环绕侧",
9399
+ value: draft.wrapSide,
9400
+ options: wrapSideOptions,
9401
+ disabled: 'inline' === draft.layout || 'topBottom' === draft.layout || 'none' === draft.layout,
9402
+ onValueChange: (wrapSide)=>onDraftChange((current)=>({
9403
+ ...current,
9404
+ wrapSide
9405
+ }))
9406
+ })
9407
+ ]
9408
+ }),
10085
9409
  /*#__PURE__*/ jsx(PictureNumberRow, {
10086
9410
  label: "文字距离",
10087
9411
  ariaLabel: "图片与文字距离(毫米)",
@@ -10090,7 +9414,7 @@ function PictureLayoutSection({ draft, onDraftChange }) {
10090
9414
  min: 0,
10091
9415
  max: 25,
10092
9416
  step: 0.5,
10093
- disabled: 'inline' === draft.layout,
9417
+ disabled: 'inline' === draft.layout || 'none' === draft.layout,
10094
9418
  invalid: Boolean(errors.wrapDistance),
10095
9419
  onValueChange: (wrapDistance)=>onDraftChange((current)=>({
10096
9420
  ...current,
@@ -10104,7 +9428,7 @@ function PictureLayoutSection({ draft, onDraftChange }) {
10104
9428
  ]
10105
9429
  });
10106
9430
  }
10107
- function PictureAlignmentSection({ draft, onDraftChange }) {
9431
+ function PictureAlignmentSection({ draft, errors, onDraftChange }) {
10108
9432
  return /*#__PURE__*/ jsxs("fieldset", {
10109
9433
  className: "work-document-picture-properties-section position",
10110
9434
  children: [
@@ -10139,6 +9463,203 @@ function PictureAlignmentSection({ draft, onDraftChange }) {
10139
9463
  ]
10140
9464
  }, option.value);
10141
9465
  })
9466
+ }),
9467
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
9468
+ ariaLabel: "使用精确图片位置",
9469
+ checked: draft.precisePosition,
9470
+ disabled: 'inline' === draft.layout,
9471
+ onCheckedChange: (precisePosition)=>onDraftChange((current)=>({
9472
+ ...current,
9473
+ precisePosition
9474
+ })),
9475
+ children: "使用精确位置"
9476
+ }),
9477
+ /*#__PURE__*/ jsxs("div", {
9478
+ className: "work-document-picture-properties-position-grid",
9479
+ children: [
9480
+ /*#__PURE__*/ jsx(PictureNumberRow, {
9481
+ label: "水平偏移",
9482
+ ariaLabel: "图片水平偏移(毫米)",
9483
+ value: draft.horizontalOffset,
9484
+ unit: "毫米",
9485
+ min: -558.7,
9486
+ max: 558.7,
9487
+ step: 0.5,
9488
+ disabled: 'inline' === draft.layout || !draft.precisePosition,
9489
+ invalid: Boolean(errors.horizontalOffset),
9490
+ onValueChange: (horizontalOffset)=>onDraftChange((current)=>({
9491
+ ...current,
9492
+ horizontalOffset
9493
+ }))
9494
+ }),
9495
+ /*#__PURE__*/ jsx(OfficeSelect, {
9496
+ ariaLabel: "水平相对于",
9497
+ value: draft.horizontalReference,
9498
+ options: horizontalReferenceOptions,
9499
+ disabled: 'inline' === draft.layout || !draft.precisePosition,
9500
+ onValueChange: (horizontalReference)=>onDraftChange((current)=>({
9501
+ ...current,
9502
+ horizontalReference
9503
+ }))
9504
+ }),
9505
+ /*#__PURE__*/ jsx(PictureNumberRow, {
9506
+ label: "垂直偏移",
9507
+ ariaLabel: "图片垂直偏移(毫米)",
9508
+ value: draft.verticalOffset,
9509
+ unit: "毫米",
9510
+ min: -558.7,
9511
+ max: 558.7,
9512
+ step: 0.5,
9513
+ disabled: 'inline' === draft.layout || !draft.precisePosition,
9514
+ invalid: Boolean(errors.verticalOffset),
9515
+ onValueChange: (verticalOffset)=>onDraftChange((current)=>({
9516
+ ...current,
9517
+ verticalOffset
9518
+ }))
9519
+ }),
9520
+ /*#__PURE__*/ jsx(OfficeSelect, {
9521
+ ariaLabel: "垂直相对于",
9522
+ value: draft.verticalReference,
9523
+ options: verticalReferenceOptions,
9524
+ disabled: 'inline' === draft.layout || !draft.precisePosition,
9525
+ onValueChange: (verticalReference)=>onDraftChange((current)=>({
9526
+ ...current,
9527
+ verticalReference
9528
+ }))
9529
+ })
9530
+ ]
9531
+ }),
9532
+ errors.horizontalOffset && /*#__PURE__*/ jsx("p", {
9533
+ role: "alert",
9534
+ children: errors.horizontalOffset
9535
+ }),
9536
+ errors.verticalOffset && /*#__PURE__*/ jsx("p", {
9537
+ role: "alert",
9538
+ children: errors.verticalOffset
9539
+ })
9540
+ ]
9541
+ });
9542
+ }
9543
+ function PictureLayerSection({ draft, errors, onDraftChange }) {
9544
+ const disabled = 'inline' === draft.layout;
9545
+ return /*#__PURE__*/ jsxs("fieldset", {
9546
+ className: "work-document-picture-properties-section layer",
9547
+ children: [
9548
+ /*#__PURE__*/ jsx("legend", {
9549
+ children: "图层与锚点"
9550
+ }),
9551
+ /*#__PURE__*/ jsx(PictureNumberRow, {
9552
+ label: "层级顺序",
9553
+ ariaLabel: "图片绘图层级顺序",
9554
+ value: draft.relativeHeight,
9555
+ unit: "序号",
9556
+ min: 0,
9557
+ max: MAX_DOCUMENT_IMAGE_RELATIVE_HEIGHT,
9558
+ step: 1,
9559
+ disabled: disabled,
9560
+ invalid: Boolean(errors.relativeHeight),
9561
+ onValueChange: (relativeHeight)=>onDraftChange((current)=>({
9562
+ ...current,
9563
+ relativeHeight
9564
+ }))
9565
+ }),
9566
+ errors.relativeHeight && /*#__PURE__*/ jsx("p", {
9567
+ role: "alert",
9568
+ children: errors.relativeHeight
9569
+ }),
9570
+ /*#__PURE__*/ jsxs("div", {
9571
+ className: "work-document-picture-properties-layer-options",
9572
+ children: [
9573
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
9574
+ ariaLabel: "图片衬于文字下方",
9575
+ checked: draft.behindDocument,
9576
+ disabled: disabled,
9577
+ onCheckedChange: (behindDocument)=>onDraftChange((current)=>({
9578
+ ...current,
9579
+ behindDocument
9580
+ })),
9581
+ children: "衬于文字下方"
9582
+ }),
9583
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
9584
+ ariaLabel: "允许图片重叠",
9585
+ checked: draft.allowOverlap,
9586
+ disabled: disabled,
9587
+ onCheckedChange: (allowOverlap)=>onDraftChange((current)=>({
9588
+ ...current,
9589
+ allowOverlap
9590
+ })),
9591
+ children: "允许对象重叠"
9592
+ }),
9593
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
9594
+ ariaLabel: "图片随表格单元格布局",
9595
+ checked: draft.layoutInCell,
9596
+ disabled: disabled,
9597
+ onCheckedChange: (layoutInCell)=>onDraftChange((current)=>({
9598
+ ...current,
9599
+ layoutInCell
9600
+ })),
9601
+ children: "随单元格布局"
9602
+ }),
9603
+ /*#__PURE__*/ jsx(OfficeCheckbox, {
9604
+ ariaLabel: "锁定图片锚点",
9605
+ checked: draft.lockAnchor,
9606
+ disabled: disabled,
9607
+ onCheckedChange: (lockAnchor)=>onDraftChange((current)=>({
9608
+ ...current,
9609
+ lockAnchor
9610
+ })),
9611
+ children: "锁定锚点"
9612
+ })
9613
+ ]
9614
+ })
9615
+ ]
9616
+ });
9617
+ }
9618
+ function PictureCropSection({ draft, errors, onDraftChange }) {
9619
+ const fields = [
9620
+ {
9621
+ key: 'cropTop',
9622
+ label: '上方裁剪'
9623
+ },
9624
+ {
9625
+ key: 'cropRight',
9626
+ label: '右侧裁剪'
9627
+ },
9628
+ {
9629
+ key: 'cropBottom',
9630
+ label: '下方裁剪'
9631
+ },
9632
+ {
9633
+ key: 'cropLeft',
9634
+ label: '左侧裁剪'
9635
+ }
9636
+ ];
9637
+ return /*#__PURE__*/ jsxs("fieldset", {
9638
+ className: "work-document-picture-properties-section crop",
9639
+ children: [
9640
+ /*#__PURE__*/ jsx("legend", {
9641
+ children: "裁剪"
9642
+ }),
9643
+ /*#__PURE__*/ jsx("div", {
9644
+ className: "work-document-picture-properties-crop-grid",
9645
+ children: fields.map((field)=>/*#__PURE__*/ jsx(PictureNumberRow, {
9646
+ label: field.label,
9647
+ ariaLabel: `图片${field.label}(百分比)`,
9648
+ value: draft[field.key],
9649
+ unit: "%",
9650
+ min: 0,
9651
+ max: 99.99,
9652
+ step: 1,
9653
+ invalid: Boolean(errors.crop),
9654
+ onValueChange: (value)=>onDraftChange((current)=>({
9655
+ ...current,
9656
+ [field.key]: value
9657
+ }))
9658
+ }, field.key))
9659
+ }),
9660
+ errors.crop && /*#__PURE__*/ jsx("p", {
9661
+ role: "alert",
9662
+ children: errors.crop
10142
9663
  })
10143
9664
  ]
10144
9665
  });
@@ -10229,10 +9750,28 @@ function DocumentPictureRibbon({ editor }) {
10229
9750
  })
10230
9751
  }),
10231
9752
  /*#__PURE__*/ jsx(PictureButton, {
10232
- label: "四周环绕",
10233
- active: 'square' === image.layout,
9753
+ label: "四周环绕",
9754
+ active: 'square' === image.layout,
9755
+ disabled: !imageSelected,
9756
+ onClick: ()=>updateLayout('square'),
9757
+ children: /*#__PURE__*/ jsx(TextWrap, {
9758
+ size: 18
9759
+ })
9760
+ }),
9761
+ /*#__PURE__*/ jsx(PictureButton, {
9762
+ label: "紧密环绕",
9763
+ active: 'tight' === image.layout,
9764
+ disabled: !imageSelected,
9765
+ onClick: ()=>updateLayout('tight'),
9766
+ children: /*#__PURE__*/ jsx(TextWrap, {
9767
+ size: 18
9768
+ })
9769
+ }),
9770
+ /*#__PURE__*/ jsx(PictureButton, {
9771
+ label: "穿越环绕",
9772
+ active: 'through' === image.layout,
10234
9773
  disabled: !imageSelected,
10235
- onClick: ()=>updateLayout('square'),
9774
+ onClick: ()=>updateLayout('through'),
10236
9775
  children: /*#__PURE__*/ jsx(TextWrap, {
10237
9776
  size: 18
10238
9777
  })
@@ -10245,6 +9784,15 @@ function DocumentPictureRibbon({ editor }) {
10245
9784
  children: /*#__PURE__*/ jsx(Rows3, {
10246
9785
  size: 18
10247
9786
  })
9787
+ }),
9788
+ /*#__PURE__*/ jsx(PictureButton, {
9789
+ label: "自由浮动",
9790
+ active: 'none' === image.layout,
9791
+ disabled: !imageSelected,
9792
+ onClick: ()=>updateLayout('none'),
9793
+ children: /*#__PURE__*/ jsx(TextWrap, {
9794
+ size: 18
9795
+ })
10248
9796
  })
10249
9797
  ]
10250
9798
  }),
@@ -10286,7 +9834,7 @@ function DocumentPictureRibbon({ editor }) {
10286
9834
  ariaLabel: "图片与文字距离",
10287
9835
  value: wrapDistanceValue,
10288
9836
  options: imageWrapDistanceOptionsForValue(wrapDistanceValue),
10289
- disabled: !imageSelected || 'inline' === image.layout,
9837
+ disabled: !imageSelected || 'inline' === image.layout || 'none' === image.layout,
10290
9838
  onValueChange: (value)=>editor.commands.setDocumentImageLayoutOptions({
10291
9839
  wrapDistance: Number(value)
10292
9840
  })
@@ -10529,7 +10077,8 @@ function createDocumentTablePropertiesDraft(source) {
10529
10077
  repeatHeader: source.rowOptions.repeatHeader
10530
10078
  },
10531
10079
  column: {
10532
- width: document_table_properties_dialog_model_centimeters(source.renderedColumnWidth ?? source.sizing.columnWidth ?? FALLBACK_COLUMN_WIDTH_CENTIMETERS * document_table_properties_dialog_model_PIXELS_PER_CENTIMETER)
10080
+ widthType: source.sizing.columnWidthType,
10081
+ width: 'percent' === source.sizing.columnWidthType ? document_table_properties_dialog_model_formatNumber(source.sizing.columnWidth ?? 100) : document_table_properties_dialog_model_centimeters(source.renderedColumnWidth ?? source.sizing.columnWidth ?? FALLBACK_COLUMN_WIDTH_CENTIMETERS * document_table_properties_dialog_model_PIXELS_PER_CENTIMETER)
10533
10082
  },
10534
10083
  cell: {
10535
10084
  verticalAlign: source.cellFormat.verticalAlign,
@@ -10547,7 +10096,7 @@ function documentTablePropertiesErrors(draft) {
10547
10096
  tableWidth: tableWidthError(draft),
10548
10097
  tableIndent: tableIndentError(draft),
10549
10098
  rowHeight: draft.row.heightEnabled ? dimensionError(draft.row.height) : null,
10550
- columnWidth: dimensionError(draft.column.width),
10099
+ columnWidth: 'percent' === draft.column.widthType ? percentageError(draft.column.width) : dimensionError(draft.column.width),
10551
10100
  cellMargins
10552
10101
  };
10553
10102
  }
@@ -10566,6 +10115,17 @@ function draftForTableWidthType(current, widthType, renderedTableWidth) {
10566
10115
  }
10567
10116
  };
10568
10117
  }
10118
+ function draftForColumnWidthType(current, widthType, source) {
10119
+ if (widthType === current.column.widthType) return current;
10120
+ const width = 'percent' === widthType ? document_table_properties_dialog_model_formatNumber('percent' === source.sizing.columnWidthType ? source.sizing.columnWidth ?? 100 : source.renderedColumnWidth && source.renderedTableWidth ? source.renderedColumnWidth / source.renderedTableWidth * 100 : 100 / Math.max(1, source.sizing.selectedColumnCount)) : document_table_properties_dialog_model_centimeters(source.renderedColumnWidth ?? ('pixels' === source.sizing.columnWidthType ? source.sizing.columnWidth : null) ?? FALLBACK_COLUMN_WIDTH_CENTIMETERS * document_table_properties_dialog_model_PIXELS_PER_CENTIMETER);
10121
+ return {
10122
+ ...current,
10123
+ column: {
10124
+ widthType,
10125
+ width
10126
+ }
10127
+ };
10128
+ }
10569
10129
  function documentTablePropertyChanges(initial, current, source) {
10570
10130
  const errors = documentTablePropertiesErrors(current);
10571
10131
  if (hasDocumentTablePropertiesErrors(errors)) return null;
@@ -10581,9 +10141,11 @@ function documentTablePropertyChanges(initial, current, source) {
10581
10141
  repeatHeader: current.row.repeatHeader
10582
10142
  } : {}
10583
10143
  };
10584
- if (!document_table_properties_dialog_model_sameDraftNumber(initial.column.width, current.column.width)) changes.column = {
10585
- width: document_table_properties_dialog_model_rounded(Number(current.column.width) * document_table_properties_dialog_model_PIXELS_PER_CENTIMETER),
10586
- renderedColumnWidths: source.renderedColumnWidths
10144
+ if (initial.column.widthType !== current.column.widthType || !document_table_properties_dialog_model_sameDraftNumber(initial.column.width, current.column.width)) changes.column = {
10145
+ type: current.column.widthType,
10146
+ width: 'percent' === current.column.widthType ? document_table_properties_dialog_model_rounded(Number(current.column.width)) : document_table_properties_dialog_model_rounded(Number(current.column.width) * document_table_properties_dialog_model_PIXELS_PER_CENTIMETER),
10147
+ renderedColumnWidths: source.renderedColumnWidths,
10148
+ renderedTableWidth: source.renderedTableWidth
10587
10149
  };
10588
10150
  const marginsChanged = initial.cell.useTableMargins !== current.cell.useTableMargins || !current.cell.useTableMargins && marginSides.some((side)=>!document_table_properties_dialog_model_sameDraftNumber(initial.cell.margins[side], current.cell.margins[side]));
10589
10151
  if (initial.cell.verticalAlign !== current.cell.verticalAlign || marginsChanged) changes.cell = {
@@ -10648,6 +10210,9 @@ function tableIndentError(draft) {
10648
10210
  function dimensionError(value) {
10649
10211
  return document_table_properties_dialog_model_validNumber(value, 0.5, 30) ? null : '请输入 0.5 到 30 之间的厘米数。';
10650
10212
  }
10213
+ function percentageError(value) {
10214
+ return document_table_properties_dialog_model_validNumber(value, 1, 100) ? null : '请输入 1 到 100 之间的百分比。';
10215
+ }
10651
10216
  function document_table_properties_dialog_model_validNumber(value, minimum, maximum) {
10652
10217
  if (!value.trim()) return false;
10653
10218
  const numeric = Number(value);
@@ -10698,6 +10263,16 @@ const widthOptions = [
10698
10263
  label: '厘米'
10699
10264
  }
10700
10265
  ];
10266
+ const columnWidthOptions = [
10267
+ {
10268
+ value: 'pixels',
10269
+ label: '厘米'
10270
+ },
10271
+ {
10272
+ value: 'percent',
10273
+ label: '百分比'
10274
+ }
10275
+ ];
10701
10276
  const document_table_properties_dialog_sections_alignmentOptions = [
10702
10277
  {
10703
10278
  value: 'left',
@@ -11026,25 +10601,43 @@ function RowSection({ draft, setDraft, errors, source }) {
11026
10601
  ]
11027
10602
  });
11028
10603
  }
11029
- function ColumnSection({ draft, setDraft, errors }) {
10604
+ function ColumnSection({ draft, setDraft, errors, source }) {
11030
10605
  return /*#__PURE__*/ jsxs("fieldset", {
11031
10606
  className: "work-document-table-properties-section",
11032
10607
  children: [
11033
10608
  /*#__PURE__*/ jsx("legend", {
11034
10609
  children: "当前列尺寸"
11035
10610
  }),
10611
+ /*#__PURE__*/ jsx("div", {
10612
+ className: "work-document-table-properties-choice-grid width",
10613
+ children: columnWidthOptions.map((option)=>/*#__PURE__*/ jsxs("label", {
10614
+ children: [
10615
+ /*#__PURE__*/ jsx("input", {
10616
+ type: "radio",
10617
+ name: "table-properties-column-width-type",
10618
+ value: option.value,
10619
+ checked: draft.column.widthType === option.value,
10620
+ onChange: ()=>setDraft((current)=>draftForColumnWidthType(current, option.value, source))
10621
+ }),
10622
+ /*#__PURE__*/ jsx("span", {
10623
+ children: option.label
10624
+ })
10625
+ ]
10626
+ }, option.value))
10627
+ }),
11036
10628
  /*#__PURE__*/ jsx(NumberRow, {
11037
10629
  label: "列宽",
11038
- ariaLabel: "当前列宽(厘米)",
10630
+ ariaLabel: 'percent' === draft.column.widthType ? '当前列宽(百分比)' : '当前列宽(厘米)',
11039
10631
  value: draft.column.width,
11040
- unit: "厘米",
11041
- min: 0.5,
11042
- max: 30,
11043
- step: 0.1,
10632
+ unit: 'percent' === draft.column.widthType ? '%' : '厘米',
10633
+ min: 'percent' === draft.column.widthType ? 1 : 0.5,
10634
+ max: 'percent' === draft.column.widthType ? 100 : 30,
10635
+ step: 'percent' === draft.column.widthType ? 1 : 0.1,
11044
10636
  invalid: Boolean(errors.columnWidth),
11045
10637
  onValueChange: (width)=>setDraft((current)=>({
11046
10638
  ...current,
11047
10639
  column: {
10640
+ ...current.column,
11048
10641
  width
11049
10642
  }
11050
10643
  }))
@@ -12067,12 +11660,13 @@ function headingLevelForShortcut(code, key) {
12067
11660
  if ('3' === value) return 3;
12068
11661
  return null;
12069
11662
  }
12070
- function DocumentToolbar({ editor, layoutOpen, layout, layoutFonts = [], navigationOpen, pageColor, showPageNumbers, showRulers, spellcheckEnabled, viewMode, zoom, pageChromeEditor, pageChromeEditingPart, pageChromeShowPageNumber, onRequestImage, onPageChromeEditingPartChange, onClosePageChrome, onTogglePageChromePageNumber, onToggleLayout, onLayoutChange, onOpenLayout, onToggleNavigation, onTogglePageNumbers, 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, onOpenWordCount, onOpenFindReplace }) {
11663
+ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layoutOpen, layout, layoutFonts = [], navigationOpen, pageColor, showPageNumbers, showRulers, spellcheckEnabled, viewMode, zoom, pageChromeEditor, pageChromeEditingPart, pageChromeShowPageNumber, onRequestImage, onPageChromeEditingPartChange, onClosePageChrome, onTogglePageChromePageNumber, onToggleLayout, onLayoutChange, onOpenLayout, onToggleNavigation, onTogglePageNumbers, 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, onOpenWordCount, onOpenFindReplace }) {
12071
11664
  const [activeTab, setActiveTab] = useState('home');
12072
11665
  const officeDialog = useOfficeDialog();
12073
11666
  const prompt = officeDialog.prompt;
12074
11667
  const imageSelected = editor.isActive('image');
12075
11668
  const tableSelected = editor.isActive('table');
11669
+ const activeBookmark = activeDocumentBookmark(editor);
12076
11670
  const hasRefreshableFields = documentHasRefreshableFields(editor);
12077
11671
  const documentChanges = collectDocumentChanges(editor.state.doc);
12078
11672
  const previousChangeIndex = adjacentDocumentChangeIndex(documentChanges, editor.state.selection, -1);
@@ -12098,7 +11692,7 @@ function DocumentToolbar({ editor, layoutOpen, layout, layoutFonts = [], navigat
12098
11692
  if (editor.isActive('link')) return void editor.chain().focus().unsetLink().run();
12099
11693
  const href = await prompt({
12100
11694
  title: '添加链接',
12101
- description: '为当前选中的文字设置跳转地址。',
11695
+ description: '输入网页、邮箱地址,或使用 #书签名称 跳转到文档内位置。',
12102
11696
  fieldLabel: '链接地址',
12103
11697
  initialValue: editor.getAttributes('link').href ?? 'https://',
12104
11698
  placeholder: 'https://',
@@ -12117,6 +11711,26 @@ function DocumentToolbar({ editor, layoutOpen, layout, layoutFonts = [], navigat
12117
11711
  editor,
12118
11712
  prompt
12119
11713
  ]);
11714
+ const toggleBookmark = useCallback(async ()=>{
11715
+ if (activeBookmark) return void editor.chain().focus().deleteDocumentBookmark(activeBookmark.id).run();
11716
+ const name = await prompt({
11717
+ title: '添加书签',
11718
+ description: '为当前光标位置或选中内容创建文档内链接目标。',
11719
+ fieldLabel: '书签名称',
11720
+ initialValue: '',
11721
+ placeholder: '例如 Architecture_2',
11722
+ confirmLabel: '添加书签',
11723
+ required: '请输入书签名称。',
11724
+ validate: (value)=>validateDocumentBookmarkName(value) ?? (documentBookmarkNameExists(editor, value) ? DOCUMENT_BOOKMARK_DUPLICATE_MESSAGE : null),
11725
+ restoreFocusTarget: ()=>editor.view.dom
11726
+ });
11727
+ if (null === name || editor.isDestroyed) return;
11728
+ editor.chain().focus().insertDocumentBookmark(name.trim()).run();
11729
+ }, [
11730
+ activeBookmark,
11731
+ editor,
11732
+ prompt
11733
+ ]);
12120
11734
  useEffect(()=>{
12121
11735
  setActiveTab((current)=>{
12122
11736
  if (pageChromeEditor) return 'pageChrome';
@@ -12149,12 +11763,18 @@ function DocumentToolbar({ editor, layoutOpen, layout, layoutFonts = [], navigat
12149
11763
  const documentHistoryTarget = insideEditor || !isDocumentNativeTextUndoTarget(event.target);
12150
11764
  if (documentHistoryTarget && 'z' === key) {
12151
11765
  event.preventDefault();
11766
+ if (history) {
11767
+ if (event.shiftKey) history.redo();
11768
+ else history.undo();
11769
+ return;
11770
+ }
12152
11771
  const command = event.shiftKey ? editor.chain().focus().redo() : editor.chain().focus().undo();
12153
11772
  command.run();
12154
11773
  return;
12155
11774
  }
12156
11775
  if (documentHistoryTarget && 'y' === key && !event.shiftKey) {
12157
11776
  event.preventDefault();
11777
+ if (history) return void history.redo();
12158
11778
  editor.chain().focus().redo().run();
12159
11779
  return;
12160
11780
  }
@@ -12205,6 +11825,7 @@ function DocumentToolbar({ editor, layoutOpen, layout, layoutFonts = [], navigat
12205
11825
  canInsertComment,
12206
11826
  editor,
12207
11827
  hasRefreshableFields,
11828
+ history,
12208
11829
  onInsertComment,
12209
11830
  onOpenFindReplace,
12210
11831
  onOpenWordCount,
@@ -12267,8 +11888,9 @@ function DocumentToolbar({ editor, layoutOpen, layout, layoutFonts = [], navigat
12267
11888
  }),
12268
11889
  shortcut: undoCommand.shortcut?.label,
12269
11890
  ariaKeyShortcuts: undoCommand.shortcut?.aria,
12270
- disabled: editor.isDestroyed || !editor.can().chain().undo().run(),
11891
+ disabled: editor.isDestroyed || (history ? !history.canUndo : !editor.can().chain().undo().run()),
12271
11892
  onSelect: ()=>{
11893
+ if (history) return void history.undo();
12272
11894
  editor.chain().focus().undo().run();
12273
11895
  }
12274
11896
  },
@@ -12280,12 +11902,14 @@ function DocumentToolbar({ editor, layoutOpen, layout, layoutFonts = [], navigat
12280
11902
  }),
12281
11903
  shortcut: redoCommand.shortcut?.label,
12282
11904
  ariaKeyShortcuts: redoCommand.shortcut?.aria,
12283
- disabled: editor.isDestroyed || !editor.can().chain().redo().run(),
11905
+ disabled: editor.isDestroyed || (history ? !history.canRedo : !editor.can().chain().redo().run()),
12284
11906
  onSelect: ()=>{
11907
+ if (history) return void history.redo();
12285
11908
  editor.chain().focus().redo().run();
12286
11909
  }
12287
11910
  }
12288
11911
  ],
11912
+ defaultCollapsed: defaultRibbonCollapsed,
12289
11913
  className: "work-document-ribbon",
12290
11914
  toolbarClassName: "document-toolbar",
12291
11915
  panels: {
@@ -12331,20 +11955,31 @@ function DocumentToolbar({ editor, layoutOpen, layout, layoutFonts = [], navigat
12331
11955
  })
12332
11956
  })
12333
11957
  }),
12334
- /*#__PURE__*/ jsx(document_toolbar_RibbonGroup, {
11958
+ /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
12335
11959
  label: "链接",
12336
11960
  priority: "low",
12337
- children: /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12338
- label: editor.isActive('link') ? '取消链接' : '添加链接',
12339
- shortcut: "Cmd/Ctrl+K",
12340
- ariaKeyShortcuts: "Control+K Meta+K",
12341
- displayLabel: true,
12342
- active: editor.isActive('link'),
12343
- onClick: ()=>void toggleLink(),
12344
- children: /*#__PURE__*/ jsx(Link2, {
12345
- size: 19
11961
+ children: [
11962
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
11963
+ label: editor.isActive('link') ? '取消链接' : '添加链接',
11964
+ shortcut: "Cmd/Ctrl+K",
11965
+ ariaKeyShortcuts: "Control+K Meta+K",
11966
+ displayLabel: true,
11967
+ active: editor.isActive('link'),
11968
+ onClick: ()=>void toggleLink(),
11969
+ children: /*#__PURE__*/ jsx(Link2, {
11970
+ size: 19
11971
+ })
11972
+ }),
11973
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
11974
+ label: activeBookmark ? '删除书签' : '添加书签',
11975
+ displayLabel: true,
11976
+ active: Boolean(activeBookmark),
11977
+ onClick: ()=>void toggleBookmark(),
11978
+ children: /*#__PURE__*/ jsx(Bookmark, {
11979
+ size: 19
11980
+ })
12346
11981
  })
12347
- })
11982
+ ]
12348
11983
  }),
12349
11984
  /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
12350
11985
  label: "页眉和页脚",
@@ -13070,7 +12705,7 @@ function useDocumentComments({ contentRef, editor, onBeforeDraft }) {
13070
12705
  toggleResolved
13071
12706
  };
13072
12707
  }
13073
- function useDocumentInsertCommands({ contentRef, editor }) {
12708
+ function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext }) {
13074
12709
  const officeDialog = useOfficeDialog();
13075
12710
  const [insertDialog, setInsertDialog] = useState(null);
13076
12711
  const invokerRef = useRef(null);
@@ -13129,15 +12764,21 @@ function useDocumentInsertCommands({ contentRef, editor }) {
13129
12764
  ]);
13130
12765
  const insertCrossReference = useCallback(()=>{
13131
12766
  if (!editor) return;
13132
- const targets = editorDocumentCaptionTargets(editor);
12767
+ const targets = [
12768
+ ...editorDocumentCaptionTargets(editor).map((target)=>({
12769
+ ...target,
12770
+ type: 'caption'
12771
+ })),
12772
+ ...editorDocumentBookmarkReferenceTargets(editor)
12773
+ ];
13133
12774
  if (!targets.length) return void officeDialog.notice({
13134
- title: '还没有题注',
13135
- description: '请先插入图片或表格题注。'
12775
+ title: '还没有可引用目标',
12776
+ description: '请先插入图片或表格题注,或在正文中添加书签。'
13136
12777
  });
13137
12778
  rememberInvoker();
13138
12779
  setInsertDialog({
13139
12780
  kind: 'crossReference',
13140
- selectedId: targets[0]?.id ?? '',
12781
+ selectedKey: targets[0] ? referenceTargetKey(targets[0]) : '',
13141
12782
  targets
13142
12783
  });
13143
12784
  }, [
@@ -13148,14 +12789,21 @@ function useDocumentInsertCommands({ contentRef, editor }) {
13148
12789
  const insertField = useCallback((kind)=>{
13149
12790
  if (!editor) return;
13150
12791
  if (!editor.chain().focus().insertDocumentField(kind).run()) return;
13151
- editor.commands.refreshDocumentFields(contentRef.current);
12792
+ editor.commands.refreshDocumentFields(contentRef.current, {
12793
+ resolveContext: resolveFieldContext ?? void 0,
12794
+ addToHistory: false
12795
+ });
13152
12796
  }, [
13153
12797
  contentRef,
13154
- editor
12798
+ editor,
12799
+ resolveFieldContext
13155
12800
  ]);
13156
- const refreshFields = useCallback(()=>editor?.commands.refreshDocumentFields(contentRef.current) ?? false, [
12801
+ const refreshFields = useCallback(()=>editor?.commands.refreshDocumentFields(contentRef.current, {
12802
+ resolveContext: resolveFieldContext ?? void 0
12803
+ }) ?? false, [
13157
12804
  contentRef,
13158
- editor
12805
+ editor,
12806
+ resolveFieldContext
13159
12807
  ]);
13160
12808
  const submitCaption = ()=>{
13161
12809
  if (!editor || insertDialog?.kind !== 'caption') return;
@@ -13166,7 +12814,7 @@ function useDocumentInsertCommands({ contentRef, editor }) {
13166
12814
  };
13167
12815
  const submitCrossReference = ()=>{
13168
12816
  if (!editor || insertDialog?.kind !== 'crossReference') return;
13169
- const target = insertDialog.targets.find((candidate)=>candidate.id === insertDialog.selectedId);
12817
+ const target = insertDialog.targets.find((candidate)=>referenceTargetKey(candidate) === insertDialog.selectedKey);
13170
12818
  if (!target) return;
13171
12819
  const inserted = editor.chain().focus().insertDocumentCrossReference(target).run();
13172
12820
  if (!inserted) return;
@@ -13187,11 +12835,12 @@ function useDocumentInsertCommands({ contentRef, editor }) {
13187
12835
  event.stopPropagation();
13188
12836
  const target = insertDialog.targets[nextIndex];
13189
12837
  if (!target) return;
12838
+ const targetKey = referenceTargetKey(target);
13190
12839
  setInsertDialog({
13191
12840
  ...insertDialog,
13192
- selectedId: target.id
12841
+ selectedKey: targetKey
13193
12842
  });
13194
- requestAnimationFrame(()=>referenceInputRefs.current.get(target.id)?.focus({
12843
+ requestAnimationFrame(()=>referenceInputRefs.current.get(targetKey)?.focus({
13195
12844
  preventScroll: true
13196
12845
  }));
13197
12846
  };
@@ -13247,7 +12896,7 @@ function useDocumentInsertCommands({ contentRef, editor }) {
13247
12896
  }),
13248
12897
  insertDialog?.kind === 'crossReference' && /*#__PURE__*/ jsx(Dialog, {
13249
12898
  title: "插入交叉引用",
13250
- description: "选择正文中已有的图片或表格题注。",
12899
+ description: "选择正文中已有的图片、表格题注或书签。",
13251
12900
  className: "work-document-reference-dialog",
13252
12901
  restoreFocusTarget: ()=>invokerRef.current,
13253
12902
  onClose: ()=>setInsertDialog(null),
@@ -13260,7 +12909,7 @@ function useDocumentInsertCommands({ contentRef, editor }) {
13260
12909
  }),
13261
12910
  /*#__PURE__*/ jsx(button_Button, {
13262
12911
  tone: "primary",
13263
- disabled: !insertDialog.selectedId,
12912
+ disabled: !insertDialog.selectedKey,
13264
12913
  onClick: submitCrossReference,
13265
12914
  children: "插入引用"
13266
12915
  })
@@ -13269,35 +12918,38 @@ function useDocumentInsertCommands({ contentRef, editor }) {
13269
12918
  children: /*#__PURE__*/ jsx("div", {
13270
12919
  className: "work-document-reference-list",
13271
12920
  role: "radiogroup",
13272
- "aria-label": "可引用题注",
13273
- children: insertDialog.targets.map((target, index)=>/*#__PURE__*/ jsxs("label", {
12921
+ "aria-label": "可引用目标",
12922
+ children: insertDialog.targets.map((target, index)=>{
12923
+ const targetKey = referenceTargetKey(target);
12924
+ return /*#__PURE__*/ jsxs("label", {
13274
12925
  onDoubleClick: submitCrossReference,
13275
12926
  children: [
13276
12927
  /*#__PURE__*/ jsx("input", {
13277
12928
  ref: (element)=>{
13278
- if (element) referenceInputRefs.current.set(target.id, element);
13279
- else referenceInputRefs.current.delete(target.id);
12929
+ if (element) referenceInputRefs.current.set(targetKey, element);
12930
+ else referenceInputRefs.current.delete(targetKey);
13280
12931
  },
13281
12932
  type: "radio",
13282
12933
  name: "document-cross-reference",
13283
- value: target.id,
13284
- checked: target.id === insertDialog.selectedId,
13285
- "data-autofocus": target.id === insertDialog.selectedId ? '' : void 0,
13286
- tabIndex: target.id === insertDialog.selectedId ? 0 : -1,
12934
+ value: targetKey,
12935
+ checked: targetKey === insertDialog.selectedKey,
12936
+ "data-autofocus": targetKey === insertDialog.selectedKey ? '' : void 0,
12937
+ tabIndex: targetKey === insertDialog.selectedKey ? 0 : -1,
13287
12938
  onChange: ()=>setInsertDialog({
13288
12939
  ...insertDialog,
13289
- selectedId: target.id
12940
+ selectedKey: targetKey
13290
12941
  }),
13291
12942
  onKeyDown: (event)=>moveReferenceSelection(event, index)
13292
12943
  }),
13293
12944
  /*#__PURE__*/ jsx("strong", {
13294
- children: target.display
12945
+ children: 'bookmark' === target.type ? `书签 ${target.name}` : target.display
13295
12946
  }),
13296
12947
  /*#__PURE__*/ jsx("span", {
13297
- children: target.title || '(无题注文字)'
12948
+ children: target.title || ('bookmark' === target.type ? '(空书签)' : '(无题注文字)')
13298
12949
  })
13299
12950
  ]
13300
- }, target.id))
12951
+ }, targetKey);
12952
+ })
13301
12953
  })
13302
12954
  })
13303
12955
  ]
@@ -13312,6 +12964,9 @@ function useDocumentInsertCommands({ contentRef, editor }) {
13312
12964
  refreshFields
13313
12965
  };
13314
12966
  }
12967
+ function referenceTargetKey(target) {
12968
+ return `${target.type}:${target.id}`;
12969
+ }
13315
12970
  const browserFontRegistrations = new Map();
13316
12971
  function useDocumentLayoutFonts(fonts) {
13317
12972
  const key = documentLayoutFontKey(fonts);
@@ -13533,13 +13188,13 @@ function planIncrementalDocumentLayout(previous, next, dirtyFrom) {
13533
13188
  }
13534
13189
  return fullLayout();
13535
13190
  }
13536
- function mergeIncrementalDocumentLayout(previous, partial, page) {
13191
+ function mergeIncrementalDocumentLayout(previous, partial) {
13537
13192
  if (0 === partial.startPageIndex) return partial;
13538
13193
  const startPageIndex = partial.startPageIndex;
13539
13194
  const prefixPages = previous.pages.slice(0, startPageIndex);
13540
13195
  if (prefixPages.length !== startPageIndex || partial.pages[0]?.index !== startPageIndex) throw new Error('Incremental document layout has an invalid page prefix.');
13541
13196
  const prefixBreaks = previous.breaks.filter((pageBreak)=>pageBreak.pageIndex < startPageIndex);
13542
- const boundaryBreak = incrementalBoundaryBreak(prefixPages.at(-1), partial.pages[0], page);
13197
+ const boundaryBreak = incrementalBoundaryBreak(prefixPages.at(-1), partial.pages[0]);
13543
13198
  return {
13544
13199
  ...partial,
13545
13200
  startPageIndex: 0,
@@ -13583,9 +13238,9 @@ function prefixMatches(pages, startPageIndex, previousBlockById, next, startBloc
13583
13238
  });
13584
13239
  }
13585
13240
  function sameLayoutBlock(left, right) {
13586
- return left.id === right.id && left.height === right.height && left.breakBefore === right.breakBefore && left.breakAfter === right.breakAfter && left.keepTogether === right.keepTogether && left.keepWithNext === right.keepWithNext && left.flowId === right.flowId && left.flowIndex === right.flowIndex && left.flowCount === right.flowCount && left.minimumFragmentsPerPage === right.minimumFragmentsPerPage && left.repeatHeaderCount === right.repeatHeaderCount && left.repeatHeaderHeight === right.repeatHeaderHeight;
13241
+ return left.id === right.id && left.height === right.height && left.pageStyleId === right.pageStyleId && left.breakBefore === right.breakBefore && left.breakAfter === right.breakAfter && left.keepTogether === right.keepTogether && left.keepWithNext === right.keepWithNext && left.flowId === right.flowId && left.flowIndex === right.flowIndex && left.flowCount === right.flowCount && left.minimumFragmentsPerPage === right.minimumFragmentsPerPage && left.repeatHeaderCount === right.repeatHeaderCount && left.repeatHeaderHeight === right.repeatHeaderHeight;
13587
13242
  }
13588
- function incrementalBoundaryBreak(previous, next, page) {
13243
+ function incrementalBoundaryBreak(previous, next) {
13589
13244
  const beforeBlockId = next?.placements[0]?.blockId;
13590
13245
  if (!previous || !next || !beforeBlockId) return null;
13591
13246
  const remainingBodyHeight = Math.max(0, previous.availableHeight - previous.usedHeight);
@@ -13593,14 +13248,78 @@ function incrementalBoundaryBreak(previous, next, page) {
13593
13248
  beforeBlockId,
13594
13249
  pageIndex: next.index,
13595
13250
  remainingBodyHeight,
13596
- spacerHeight: remainingBodyHeight + page.marginBottom + page.pageGap + page.marginTop
13251
+ spacerHeight: remainingBodyHeight + previous.page.marginBottom + previous.page.pageGap + next.page.marginTop
13252
+ };
13253
+ }
13254
+ function createDocumentPaginationRunCoordinator({ cancelFrame, onAbort, onCoalescedRequest, onError, onRunFinish, onRunStart, requestFrame, run }) {
13255
+ let activeController = null;
13256
+ let disposed = false;
13257
+ let frame = 0;
13258
+ let rerunRequested = false;
13259
+ let running = false;
13260
+ const queue = ()=>{
13261
+ if (disposed || frame || running) return;
13262
+ frame = requestFrame(()=>{
13263
+ if (disposed) return;
13264
+ frame = requestFrame(()=>{
13265
+ frame = 0;
13266
+ if (!disposed) execute();
13267
+ });
13268
+ });
13269
+ };
13270
+ const execute = async ()=>{
13271
+ if (disposed || running) return;
13272
+ running = true;
13273
+ const controller = new AbortController();
13274
+ activeController = controller;
13275
+ onRunStart?.();
13276
+ try {
13277
+ await run(controller.signal);
13278
+ } catch (error) {
13279
+ if (!controller.signal.aborted) onError(error);
13280
+ } finally{
13281
+ if (activeController === controller) activeController = null;
13282
+ running = false;
13283
+ onRunFinish?.(controller.signal.aborted);
13284
+ if (!disposed && rerunRequested) {
13285
+ rerunRequested = false;
13286
+ queue();
13287
+ }
13288
+ }
13289
+ };
13290
+ const request = ({ invalidateActive = false } = {})=>{
13291
+ if (disposed) return;
13292
+ if (!running) return void queue();
13293
+ rerunRequested = true;
13294
+ onCoalescedRequest?.();
13295
+ if (invalidateActive && activeController && !activeController.signal.aborted) {
13296
+ onAbort?.('invalidation');
13297
+ activeController.abort();
13298
+ }
13299
+ };
13300
+ const dispose = ()=>{
13301
+ if (disposed) return;
13302
+ disposed = true;
13303
+ rerunRequested = false;
13304
+ if (frame) {
13305
+ cancelFrame(frame);
13306
+ frame = 0;
13307
+ }
13308
+ if (activeController && !activeController.signal.aborted) {
13309
+ onAbort?.('disposal');
13310
+ activeController.abort();
13311
+ }
13312
+ activeController = null;
13313
+ };
13314
+ return {
13315
+ dispose,
13316
+ request
13597
13317
  };
13598
13318
  }
13599
13319
  function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, page, selectionVersion, wasmUrl, layoutFonts, loadedLayoutFontIds }) {
13600
13320
  const client = useOfficeKernelClient(wasmUrl, layoutFonts);
13601
13321
  const editorMounted = useEditorMounted(editor);
13602
13322
  const revision = useRef(0);
13603
- const activeRequest = useRef(null);
13604
13323
  const measurementCache = useRef(null);
13605
13324
  const paginationCache = useRef(null);
13606
13325
  const measurementEditor = useRef(null);
@@ -13628,8 +13347,6 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13628
13347
  }
13629
13348
  if (!editor || !editorMounted || editor.isDestroyed || !enabled || !client) {
13630
13349
  const nextRevision = ++revision.current;
13631
- activeRequest.current?.abort();
13632
- activeRequest.current = null;
13633
13350
  if (editor && editorMounted && !editor.isDestroyed) {
13634
13351
  const editorDom = editor.view.dom;
13635
13352
  editor.commands.clearDocumentPagination(nextRevision);
@@ -13654,6 +13371,17 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13654
13371
  delete editorDom.dataset.paginationTextEngine;
13655
13372
  delete editorDom.dataset.paginationTextRuns;
13656
13373
  delete editorDom.dataset.paginationUnsupportedText;
13374
+ delete editorDom.dataset.paginationAborts;
13375
+ delete editorDom.dataset.paginationActive;
13376
+ delete editorDom.dataset.paginationAssetTriggers;
13377
+ delete editorDom.dataset.paginationCoalescedRequests;
13378
+ delete editorDom.dataset.paginationDocumentTriggers;
13379
+ delete editorDom.dataset.paginationFontTriggers;
13380
+ delete editorDom.dataset.paginationResizeTriggers;
13381
+ delete editorDom.dataset.paginationRuns;
13382
+ delete editorDom.dataset.paginationTextBatch;
13383
+ delete editorDom.dataset.paginationTextBatches;
13384
+ delete editorDom.dataset.paginationViewportTriggers;
13657
13385
  }
13658
13386
  }
13659
13387
  if (!enabled) {
@@ -13666,10 +13394,26 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13666
13394
  }
13667
13395
  const editorDom = editor.view.dom;
13668
13396
  let disposed = false;
13669
- let frame = 0;
13670
13397
  let observedElements = [];
13671
13398
  const observedHeights = new Map();
13399
+ let observedBlockPositions = new Map();
13400
+ let observingExternalLayout = false;
13401
+ const diagnosticCounts = {
13402
+ aborts: 0,
13403
+ assetTriggers: 0,
13404
+ coalescedRequests: 0,
13405
+ documentTriggers: 0,
13406
+ fontTriggers: 0,
13407
+ resizeTriggers: 0,
13408
+ runs: 0,
13409
+ viewportTriggers: 0
13410
+ };
13411
+ const updateDiagnostic = (name, datasetName)=>{
13412
+ diagnosticCounts[name] += 1;
13413
+ editorDom.dataset[datasetName] = String(diagnosticCounts[name]);
13414
+ };
13672
13415
  const observer = "u" < typeof ResizeObserver ? null : new ResizeObserver((entries)=>{
13416
+ if (!observingExternalLayout) return;
13673
13417
  let earliestChangedBlock = 1 / 0;
13674
13418
  let changed = false;
13675
13419
  for (const entry of entries){
@@ -13680,27 +13424,32 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13680
13424
  const resized = void 0 !== previous && previous !== next;
13681
13425
  changed ||= resized;
13682
13426
  if (resized) {
13683
- for (const block of measurementCache.current?.blocks ?? [])if (block.element === element) earliestChangedBlock = Math.min(earliestChangedBlock, block.from);
13427
+ const blockPosition = observedBlockPositions.get(element);
13428
+ if (void 0 !== blockPosition) earliestChangedBlock = Math.min(earliestChangedBlock, blockPosition);
13684
13429
  }
13685
13430
  }
13686
13431
  if (changed) {
13432
+ updateDiagnostic('resizeTriggers', 'paginationResizeTriggers');
13687
13433
  markDirty(Number.isFinite(earliestChangedBlock) ? earliestChangedBlock : 0);
13688
13434
  schedule();
13689
13435
  }
13690
13436
  });
13691
- const observeBlocks = (blocks)=>{
13437
+ const stopObservingBlocks = ()=>{
13438
+ observingExternalLayout = false;
13692
13439
  observer?.disconnect();
13693
- observedElements = Array.from(new Set(blocks.filter((block)=>block.observeResize).map((block)=>block.element)));
13440
+ };
13441
+ const observeBlocks = (blocks)=>{
13442
+ stopObservingBlocks();
13443
+ observedBlockPositions = documentResizeObservationPositions(blocks);
13444
+ observedElements = Array.from(observedBlockPositions.keys());
13694
13445
  observedHeights.clear();
13695
- for (const element of observedElements)observer?.observe(element);
13696
13446
  for (const element of observedElements)observedHeights.set(element, element.offsetHeight);
13447
+ observingExternalLayout = true;
13448
+ for (const element of observedElements)observer?.observe(element);
13697
13449
  };
13698
- const run = async ()=>{
13699
- frame = 0;
13450
+ const run = async (signal)=>{
13451
+ stopObservingBlocks();
13700
13452
  const nextRevision = ++revision.current;
13701
- activeRequest.current?.abort();
13702
- const controller = new AbortController();
13703
- activeRequest.current = controller;
13704
13453
  editor.commands.clearDocumentPagination(nextRevision);
13705
13454
  editorDom.dataset.paginationState = 'measuring';
13706
13455
  editorDom.dataset.paginationReusedPageChrome = '0';
@@ -13716,36 +13465,44 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13716
13465
  if (textLayoutCollection.paragraphs.length) {
13717
13466
  editorDom.dataset.paginationState = 'shaping';
13718
13467
  try {
13719
- const textLayout = await client.textLayout({
13720
- revision: nextRevision,
13721
- documentRevision,
13722
- paragraphs: textLayoutCollection.paragraphs
13723
- }, controller.signal);
13724
- if (disposed || controller.signal.aborted || nextRevision !== revision.current || editor.isDestroyed) return;
13725
- for (const layout of textLayout.layouts)if (0 === layout.missingGlyphCount) {
13726
- textLayouts.set(layout.id, layout);
13727
- fallbackGlyphCount += layout.fallbackGlyphCount;
13468
+ let textLayoutEngine = '';
13469
+ let unsupportedTextLayoutCount = 0;
13470
+ const textLayoutBatches = documentTextLayoutBatches(textLayoutCollection.paragraphs);
13471
+ editorDom.dataset.paginationTextBatches = String(textLayoutBatches.length);
13472
+ for (const [batchIndex, paragraphs] of textLayoutBatches.entries()){
13473
+ editorDom.dataset.paginationTextBatch = String(batchIndex + 1);
13474
+ const textLayout = await client.textLayout({
13475
+ revision: nextRevision,
13476
+ documentRevision,
13477
+ paragraphs
13478
+ }, signal);
13479
+ if (disposed || signal.aborted || nextRevision !== revision.current || editor.isDestroyed) return;
13480
+ textLayoutEngine ||= textLayout.engine;
13481
+ unsupportedTextLayoutCount += textLayout.unsupportedParagraphIds.length + textLayout.layouts.filter((layout)=>layout.missingGlyphCount > 0).length;
13482
+ for (const layout of textLayout.layouts)if (0 === layout.missingGlyphCount) {
13483
+ textLayouts.set(layout.id, layout);
13484
+ fallbackGlyphCount += layout.fallbackGlyphCount;
13485
+ }
13728
13486
  }
13729
- editorDom.dataset.paginationTextEngine = textLayout.engine;
13730
- editorDom.dataset.paginationUnsupportedText = String(textLayout.unsupportedParagraphIds.length + textLayout.layouts.filter((layout)=>layout.missingGlyphCount > 0).length);
13487
+ editorDom.dataset.paginationTextEngine = textLayoutEngine;
13488
+ editorDom.dataset.paginationUnsupportedText = String(unsupportedTextLayoutCount);
13731
13489
  } catch (error) {
13732
- if (controller.signal.aborted || error instanceof DOMException && 'AbortError' === error.name) return;
13490
+ if (signal.aborted || error instanceof DOMException && 'AbortError' === error.name) return;
13491
+ textLayouts.clear();
13492
+ fallbackGlyphCount = 0;
13733
13493
  editorDom.dataset.paginationTextEngine = 'dom';
13734
13494
  editorDom.dataset.paginationUnsupportedText = String(textLayoutCollection.paragraphs.length);
13735
13495
  }
13736
13496
  } else {
13737
13497
  editorDom.dataset.paginationTextEngine = 'dom';
13738
13498
  editorDom.dataset.paginationUnsupportedText = '0';
13499
+ editorDom.dataset.paginationTextBatch = '0';
13500
+ editorDom.dataset.paginationTextBatches = '0';
13739
13501
  }
13740
13502
  editorDom.dataset.paginationShapedParagraphs = String(textLayouts.size);
13741
13503
  editorDom.dataset.paginationFallbackGlyphs = String(fallbackGlyphCount);
13742
13504
  editorDom.dataset.paginationShapedRuns = String(textLayoutCollection.paragraphs.reduce((count, paragraph)=>count + (textLayouts.has(paragraph.id) ? paragraph.runs.length : 0), 0));
13743
- const effectivePage = {
13744
- ...page,
13745
- ...documentPageChromeHeights(editor)
13746
- };
13747
- const availablePageHeight = documentPageBodyHeight(effectivePage);
13748
- const snapshot = measureDocumentLayoutBlocks(editor, measurementCache.current, measurementStart, textLayouts, availablePageHeight);
13505
+ const snapshot = measureDocumentLayoutBlocks(editor, measurementCache.current, measurementStart, textLayouts);
13749
13506
  measurementCache.current = snapshot;
13750
13507
  editorDom.dataset.paginationBlocks = String(snapshot.blocks.length);
13751
13508
  editorDom.dataset.paginationFlows = String(new Set(snapshot.blocks.flatMap((block)=>block.block.flowId ? [
@@ -13753,8 +13510,8 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13753
13510
  ] : [])).size);
13754
13511
  editorDom.dataset.paginationMeasuredBlocks = String(snapshot.measuredBlockCount);
13755
13512
  editorDom.dataset.paginationReusedBlocks = String(snapshot.reusedBlockCount);
13756
- observeBlocks(snapshot.blocks);
13757
13513
  if (snapshot.unsupportedLayout || !snapshot.blocks.length) {
13514
+ observeBlocks(snapshot.blocks);
13758
13515
  editorDom.dataset.paginationState = snapshot.unsupportedLayout ? 'unsupported' : 'empty';
13759
13516
  setPagination(null);
13760
13517
  paginationCache.current = null;
@@ -13770,11 +13527,12 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13770
13527
  revision: nextRevision,
13771
13528
  documentRevision,
13772
13529
  startPageIndex: layoutPlan.startPageIndex,
13773
- page: effectivePage,
13530
+ page,
13531
+ pageStyles: snapshot.pageStyles,
13774
13532
  blocks: layoutPlan.blocks
13775
- }, controller.signal);
13776
- if (disposed || controller.signal.aborted || nextRevision !== revision.current || editor.isDestroyed) return;
13777
- const layout = previousPagination && layoutPlan.startPageIndex > 0 ? mergeIncrementalDocumentLayout(previousPagination.layout, partialLayout, effectivePage) : partialLayout;
13533
+ }, signal);
13534
+ if (disposed || signal.aborted || nextRevision !== revision.current || editor.isDestroyed) return;
13535
+ const layout = previousPagination && layoutPlan.startPageIndex > 0 ? mergeIncrementalDocumentLayout(previousPagination.layout, partialLayout) : partialLayout;
13778
13536
  const blockById = new Map(snapshot.blocks.map((block)=>[
13779
13537
  block.block.id,
13780
13538
  block
@@ -13792,7 +13550,8 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13792
13550
  return block ? [
13793
13551
  {
13794
13552
  ...pageBreak,
13795
- page: effectivePage,
13553
+ previousPage: pageByIndex.get(pageBreak.pageIndex - 1)?.page ?? layout.pages[pageBreak.pageIndex - 1]?.page ?? page,
13554
+ nextPage: pageByIndex.get(pageBreak.pageIndex)?.page ?? layout.pages[pageBreak.pageIndex]?.page ?? page,
13796
13555
  position: block.from,
13797
13556
  inlineOffsetLeft: block.inlineOffsetLeft,
13798
13557
  inlineOffsetRight: block.inlineOffsetRight,
@@ -13802,6 +13561,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13802
13561
  }
13803
13562
  ] : [];
13804
13563
  }));
13564
+ observeBlocks(snapshot.blocks);
13805
13565
  editorDom.dataset.paginationEngine = layout.engine;
13806
13566
  editorDom.dataset.paginationDocumentRevision = String(layout.documentRevision);
13807
13567
  editorDom.dataset.paginationPages = String(layout.pages.length);
@@ -13819,7 +13579,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13819
13579
  paginationCache.current = nextPagination;
13820
13580
  setPagination(nextPagination);
13821
13581
  } catch (error) {
13822
- if (disposed || controller.signal.aborted || error instanceof DOMException && 'AbortError' === error.name) return;
13582
+ if (disposed || signal.aborted || error instanceof DOMException && 'AbortError' === error.name) return;
13823
13583
  editorDom.dataset.paginationState = 'error';
13824
13584
  editorDom.dataset.paginationError = error instanceof Error ? `${error.name}: ${error.message}` : 'UnknownError';
13825
13585
  editor.commands.clearDocumentPagination(nextRevision);
@@ -13827,30 +13587,54 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13827
13587
  setPagination(null);
13828
13588
  }
13829
13589
  };
13830
- const schedule = ()=>{
13831
- if (disposed || frame) return;
13832
- frame = requestAnimationFrame(()=>{
13833
- frame = requestAnimationFrame(()=>void run());
13590
+ const coordinator = createDocumentPaginationRunCoordinator({
13591
+ cancelFrame: cancelAnimationFrame,
13592
+ onAbort: ()=>updateDiagnostic('aborts', 'paginationAborts'),
13593
+ onCoalescedRequest: ()=>updateDiagnostic('coalescedRequests', 'paginationCoalescedRequests'),
13594
+ onError: (error)=>{
13595
+ if (disposed || editor.isDestroyed) return;
13596
+ editorDom.dataset.paginationState = 'error';
13597
+ editorDom.dataset.paginationError = error instanceof Error ? `${error.name}: ${error.message}` : 'UnknownError';
13598
+ paginationCache.current = null;
13599
+ setPagination(null);
13600
+ },
13601
+ onRunFinish: ()=>{
13602
+ if (!disposed) editorDom.dataset.paginationActive = 'false';
13603
+ },
13604
+ onRunStart: ()=>{
13605
+ editorDom.dataset.paginationActive = 'true';
13606
+ updateDiagnostic('runs', 'paginationRuns');
13607
+ },
13608
+ requestFrame: requestAnimationFrame,
13609
+ run
13610
+ });
13611
+ const schedule = (invalidateActive = false)=>{
13612
+ coordinator.request({
13613
+ invalidateActive
13834
13614
  });
13835
13615
  };
13836
13616
  const markDirty = (position)=>{
13837
13617
  dirtyMeasurementFrom.current = Math.min(dirtyMeasurementFrom.current, Math.max(0, position));
13838
13618
  };
13839
13619
  const handleDocumentUpdate = ({ transaction })=>{
13620
+ updateDiagnostic('documentTriggers', 'paginationDocumentTriggers');
13840
13621
  markDirty(earliestChangedPosition(transaction));
13841
- schedule();
13622
+ schedule(true);
13842
13623
  };
13843
13624
  const handleLoadedAsset = (event)=>{
13625
+ updateDiagnostic('assetTriggers', 'paginationAssetTriggers');
13844
13626
  const target = event.target;
13845
13627
  const block = measurementCache.current?.blocks.find((candidate)=>target instanceof Node && (candidate.element === target || candidate.element.contains(target)));
13846
13628
  markDirty(block?.from ?? 0);
13847
13629
  schedule();
13848
13630
  };
13849
13631
  const handleFontLoading = ()=>{
13632
+ updateDiagnostic('fontTriggers', 'paginationFontTriggers');
13850
13633
  markDirty(0);
13851
13634
  schedule();
13852
13635
  };
13853
13636
  const handleWindowResize = ()=>{
13637
+ updateDiagnostic('viewportTriggers', 'paginationViewportTriggers');
13854
13638
  markDirty(0);
13855
13639
  schedule();
13856
13640
  };
@@ -13862,14 +13646,12 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13862
13646
  schedule();
13863
13647
  return ()=>{
13864
13648
  disposed = true;
13865
- if (frame) cancelAnimationFrame(frame);
13866
- observer?.disconnect();
13649
+ stopObservingBlocks();
13867
13650
  editor.off('update', handleDocumentUpdate);
13868
13651
  editorDom.removeEventListener('load', handleLoadedAsset, true);
13869
13652
  fonts?.removeEventListener('loadingdone', handleFontLoading);
13870
13653
  window.removeEventListener('resize', handleWindowResize);
13871
- activeRequest.current?.abort();
13872
- activeRequest.current = null;
13654
+ coordinator.dispose();
13873
13655
  };
13874
13656
  }, [
13875
13657
  client,
@@ -13884,6 +13666,9 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13884
13666
  loadedLayoutFontIds,
13885
13667
  pageKey
13886
13668
  ]);
13669
+ const resolveFieldContext = useMemo(()=>pagination ? createDocumentFieldPaginationContextResolver(pagination) : null, [
13670
+ pagination
13671
+ ]);
13887
13672
  return useMemo(()=>{
13888
13673
  const currentPage = pagination ? pageForPosition(pagination, editor?.state.selection.from ?? 0) : null;
13889
13674
  return {
@@ -13891,15 +13676,54 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
13891
13676
  currentPageDescriptor: null === currentPage ? null : pagination?.pages.find((page)=>page.physicalPage === currentPage) ?? null,
13892
13677
  pageCount: pagination ? Math.max(1, pagination.layout.pages.length) : null,
13893
13678
  pages: pagination?.pages ?? [],
13894
- paginating: enabled && null === pagination
13679
+ paginating: enabled && null === pagination,
13680
+ resolveFieldContext
13895
13681
  };
13896
13682
  }, [
13897
13683
  editor,
13898
13684
  enabled,
13899
13685
  pagination,
13686
+ resolveFieldContext,
13900
13687
  selectionVersion
13901
13688
  ]);
13902
13689
  }
13690
+ function createDocumentFieldPaginationContextResolver(pagination) {
13691
+ const pagesByPhysicalNumber = new Map(pagination.pages.map((page)=>[
13692
+ page.physicalPage,
13693
+ page
13694
+ ]));
13695
+ const sectionPhysicalPages = new Map();
13696
+ for (const block of pagination.blocks){
13697
+ const sectionId = block.section?.id;
13698
+ const physicalPage = pagination.pageByBlockId.get(block.block.id);
13699
+ if (!sectionId || !physicalPage) continue;
13700
+ const pages = sectionPhysicalPages.get(sectionId) ?? new Set();
13701
+ pages.add(physicalPage);
13702
+ sectionPhysicalPages.set(sectionId, pages);
13703
+ }
13704
+ return (position)=>{
13705
+ const block = blockForPosition(pagination.blocks, position);
13706
+ const section = block?.section;
13707
+ const physicalPage = block ? pagination.pageByBlockId.get(block.block.id) : void 0;
13708
+ const page = physicalPage ? pagesByPhysicalNumber.get(physicalPage) : void 0;
13709
+ if (!section || !page) return null;
13710
+ return {
13711
+ pageNumber: page.pageNumber,
13712
+ totalPages: Math.max(1, pagination.layout.pages.length),
13713
+ sectionNumber: section.index + 1,
13714
+ sectionPages: Math.max(1, sectionPhysicalPages.get(section.id)?.size ?? 0)
13715
+ };
13716
+ };
13717
+ }
13718
+ function documentResizeObservationPositions(blocks) {
13719
+ const positions = new Map();
13720
+ for (const block of blocks){
13721
+ if (!block.observeResize) continue;
13722
+ const previous = positions.get(block.element);
13723
+ positions.set(block.element, void 0 === previous ? block.from : Math.min(previous, block.from));
13724
+ }
13725
+ return positions;
13726
+ }
13903
13727
  function deriveDocumentPaginationPageDescriptors(layout, blocks, documentNode, previousPages = [], requestedReusedPageCount = 0) {
13904
13728
  const blockById = new Map(blocks.map((candidate)=>[
13905
13729
  candidate.block.id,
@@ -13942,7 +13766,7 @@ function deriveDocumentPaginationPageDescriptors(layout, blocks, documentNode, p
13942
13766
  const firstSectionPage = 1 === sectionPage;
13943
13767
  const pageNumber = firstSectionPage && void 0 !== section.layout.pageNumberStart ? section.layout.pageNumberStart : Math.max(1, previousPageNumber + 1);
13944
13768
  const previousPage = previousPageByIndex.get(page.index);
13945
- if (reuseOpen && page.index < reusablePhysicalPageCount && previousPage && reusableDocumentPageDescriptor(previousPage, physicalPage, pageNumber, sectionPage, section.id, section.index, section.layout)) {
13769
+ if (reuseOpen && page.index < reusablePhysicalPageCount && previousPage && reusableDocumentPageDescriptor(previousPage, physicalPage, pageNumber, sectionPage, section.id, section.index, section.layout, page.page)) {
13946
13770
  descriptors.push(previousPage);
13947
13771
  reusedPageCount += 1;
13948
13772
  continue;
@@ -13958,7 +13782,8 @@ function deriveDocumentPaginationPageDescriptors(layout, blocks, documentNode, p
13958
13782
  sectionId: section.id,
13959
13783
  sectionIndex: section.index,
13960
13784
  layout: section.layout,
13961
- pageChrome: resolveDocumentPageChrome(section.layout, sectionPage, physicalPage)
13785
+ pageChrome: resolveDocumentPageChrome(section.layout, sectionPage, physicalPage),
13786
+ page: documentPageMetrics(section.layout, physicalPage)
13962
13787
  });
13963
13788
  }
13964
13789
  return {
@@ -13967,8 +13792,8 @@ function deriveDocumentPaginationPageDescriptors(layout, blocks, documentNode, p
13967
13792
  derivedPageCount: descriptors.length - reusedPageCount
13968
13793
  };
13969
13794
  }
13970
- function reusableDocumentPageDescriptor(candidate, physicalPage, pageNumber, sectionPage, sectionId, sectionIndex, layout) {
13971
- return candidate.pageIndex === physicalPage - 1 && candidate.physicalPage === physicalPage && candidate.pageNumber === pageNumber && candidate.sectionPage === sectionPage && candidate.sectionId === sectionId && candidate.sectionIndex === sectionIndex && JSON.stringify(candidate.layout) === JSON.stringify(layout);
13795
+ function reusableDocumentPageDescriptor(candidate, physicalPage, pageNumber, sectionPage, sectionId, sectionIndex, layout, page) {
13796
+ return candidate.pageIndex === physicalPage - 1 && candidate.physicalPage === physicalPage && candidate.pageNumber === pageNumber && candidate.sectionPage === sectionPage && candidate.sectionId === sectionId && candidate.sectionIndex === sectionIndex && pageMetricsKey(candidate.page) === pageMetricsKey(page) && JSON.stringify(candidate.layout) === JSON.stringify(layout);
13972
13797
  }
13973
13798
  function documentPagePreviewText(blocks, documentNode) {
13974
13799
  const measuredText = documentNode ? documentPageTextRanges(blocks).reduce((preview, range)=>{
@@ -14043,20 +13868,24 @@ function useOfficeKernelClient(wasmUrl, fonts) {
14043
13868
  return client;
14044
13869
  }
14045
13870
  function pageForPosition(pagination, position) {
14046
- const rangedBlock = pagination.blocks.find((block)=>block.selectionRanges?.some((range)=>range.from <= position && position < range.to));
14047
- if (rangedBlock) return pagination.pageByBlockId.get(rangedBlock.block.id) ?? 1;
13871
+ const block = blockForPosition(pagination.blocks, position);
13872
+ return block ? pagination.pageByBlockId.get(block.block.id) ?? 1 : 1;
13873
+ }
13874
+ function blockForPosition(blocks, position) {
13875
+ const rangedBlock = blocks.find((block)=>block.selectionRanges?.some((range)=>range.from <= position && position < range.to));
13876
+ if (rangedBlock) return rangedBlock;
14048
13877
  let lower = 0;
14049
- let upper = pagination.blocks.length - 1;
14050
- let containing = pagination.blocks[0];
13878
+ let upper = blocks.length - 1;
13879
+ let containing = blocks[0];
14051
13880
  while(lower <= upper){
14052
13881
  const middle = Math.floor((lower + upper) / 2);
14053
- const candidate = pagination.blocks[middle];
13882
+ const candidate = blocks[middle];
14054
13883
  if (candidate.from <= position) {
14055
13884
  containing = candidate;
14056
13885
  lower = middle + 1;
14057
13886
  } else upper = middle - 1;
14058
13887
  }
14059
- return containing ? pagination.pageByBlockId.get(containing.block.id) ?? 1 : 1;
13888
+ return containing;
14060
13889
  }
14061
13890
  function pageMetricsKey(page) {
14062
13891
  return [
@@ -14078,6 +13907,204 @@ function earliestChangedPosition(transaction) {
14078
13907
  });
14079
13908
  return Number.isFinite(earliest) ? earliest : 0;
14080
13909
  }
13910
+ function collectDocumentReviewSnapshot(document1) {
13911
+ return {
13912
+ comments: collectDocumentCommentAnchors(document1),
13913
+ changes: collectDocumentChanges(document1)
13914
+ };
13915
+ }
13916
+ function reconcileDocumentReviewConflicts({ active, before, after, comments }) {
13917
+ const commentIds = new Set(comments.map((comment)=>comment.id.trim()).filter(Boolean));
13918
+ const afterComments = new Map(after.comments.map((comment)=>[
13919
+ comment.id,
13920
+ comment
13921
+ ]));
13922
+ const afterChanges = new Map(after.changes.map((change)=>[
13923
+ work_document_review_conflicts_changeKey(change),
13924
+ change
13925
+ ]));
13926
+ const surviving = active.filter((conflict)=>conflictStillApplies(conflict, commentIds, afterComments, afterChanges));
13927
+ const detected = detectDocumentReviewConflicts(before, after, commentIds, afterComments, afterChanges);
13928
+ const activeKeys = new Set(active.map(documentReviewConflictKey));
13929
+ const newlyDetected = detected.filter((conflict)=>!activeKeys.has(documentReviewConflictKey(conflict)));
13930
+ const byKey = new Map(surviving.map((conflict)=>[
13931
+ documentReviewConflictKey(conflict),
13932
+ conflict
13933
+ ]));
13934
+ for (const conflict of detected){
13935
+ const key = documentReviewConflictKey(conflict);
13936
+ if (!activeKeys.has(key)) byKey.set(key, conflict);
13937
+ }
13938
+ return {
13939
+ active: Array.from(byKey.values()),
13940
+ detected: newlyDetected
13941
+ };
13942
+ }
13943
+ function detectDocumentReviewConflicts(before, after, commentIds, afterComments, afterChanges) {
13944
+ const conflicts = [];
13945
+ for (const previous of before.comments){
13946
+ const next = afterComments.get(previous.id);
13947
+ if (!commentIds.has(previous.id)) {
13948
+ if (next) conflicts.push({
13949
+ id: previous.id,
13950
+ kind: 'comment',
13951
+ reason: 'record-removed',
13952
+ previousText: previous.anchorText,
13953
+ nextText: next.anchorText
13954
+ });
13955
+ continue;
13956
+ }
13957
+ if (next) {
13958
+ if (next.anchorText !== previous.anchorText) conflicts.push({
13959
+ id: previous.id,
13960
+ kind: 'comment',
13961
+ reason: 'text-changed',
13962
+ previousText: previous.anchorText,
13963
+ nextText: next.anchorText
13964
+ });
13965
+ } else conflicts.push({
13966
+ id: previous.id,
13967
+ kind: 'comment',
13968
+ reason: 'removed',
13969
+ previousText: previous.anchorText
13970
+ });
13971
+ }
13972
+ const afterChangesById = new Map();
13973
+ for (const change of after.changes){
13974
+ const candidates = afterChangesById.get(change.id) ?? [];
13975
+ candidates.push(change);
13976
+ afterChangesById.set(change.id, candidates);
13977
+ }
13978
+ for (const previous of before.changes){
13979
+ const next = afterChanges.get(work_document_review_conflicts_changeKey(previous));
13980
+ if (next) {
13981
+ if (next.text !== previous.text) conflicts.push({
13982
+ id: previous.id,
13983
+ kind: previous.kind,
13984
+ reason: 'text-changed',
13985
+ previousText: previous.text,
13986
+ nextText: next.text
13987
+ });
13988
+ continue;
13989
+ }
13990
+ const changedKind = afterChangesById.get(previous.id)?.[0];
13991
+ conflicts.push(changedKind ? {
13992
+ id: previous.id,
13993
+ kind: previous.kind,
13994
+ reason: 'kind-changed',
13995
+ previousText: previous.text,
13996
+ nextKind: changedKind.kind,
13997
+ nextText: changedKind.text
13998
+ } : {
13999
+ id: previous.id,
14000
+ kind: previous.kind,
14001
+ reason: 'removed',
14002
+ previousText: previous.text
14003
+ });
14004
+ }
14005
+ return conflicts;
14006
+ }
14007
+ function conflictStillApplies(conflict, commentIds, comments, changes) {
14008
+ if ('comment' === conflict.kind) {
14009
+ if (!commentIds.has(conflict.id)) return comments.has(conflict.id);
14010
+ return comments.get(conflict.id)?.anchorText !== conflict.previousText;
14011
+ }
14012
+ return changes.get(`${conflict.kind}:${conflict.id}`)?.text !== conflict.previousText;
14013
+ }
14014
+ function work_document_review_conflicts_changeKey(change) {
14015
+ return `${change.kind}:${change.id}`;
14016
+ }
14017
+ function documentReviewConflictKey(conflict) {
14018
+ return 'comment' === conflict.kind ? `comment:${conflict.id}` : `change:${conflict.id}`;
14019
+ }
14020
+ function useDocumentReviewConflicts({ activeConflictsRef, appliedSourceKeyRef, artifactId, content, editor, editorInput, normalizedContent, onReviewConflict, publishedDocumentRef, reconcileControlledUpdates = true }) {
14021
+ const appliedArtifactIdRef = useRef(artifactId);
14022
+ const onReviewConflictRef = useRef(onReviewConflict);
14023
+ const [visibleConflicts, setVisibleConflicts] = useState([]);
14024
+ onReviewConflictRef.current = onReviewConflict;
14025
+ useEffect(()=>{
14026
+ if (!editor || !reconcileControlledUpdates) return;
14027
+ const artifactChanged = appliedArtifactIdRef.current !== artifactId;
14028
+ const sourceChanged = appliedSourceKeyRef.current !== editorInput.sourceKey;
14029
+ appliedArtifactIdRef.current = artifactId;
14030
+ if (artifactChanged) {
14031
+ activeConflictsRef.current = [];
14032
+ setVisibleConflicts([]);
14033
+ }
14034
+ const before = collectDocumentReviewSnapshot(editor.state.doc);
14035
+ if (sourceChanged) {
14036
+ const currentContent = normalizeDocumentHtml({
14037
+ ...content,
14038
+ html: editor.getHTML()
14039
+ });
14040
+ const canReuseCurrentDocument = 'string' == typeof editorInput.source && currentContent === normalizedContent;
14041
+ appliedSourceKeyRef.current = editorInput.sourceKey;
14042
+ if (!canReuseCurrentDocument) {
14043
+ const result = applyExternalDocumentContent(editor, editorInput.source);
14044
+ if ('unsupported' === result) editor.chain().setMeta('addToHistory', false).setContent(editorInput.source, {
14045
+ emitUpdate: false
14046
+ }).run();
14047
+ }
14048
+ publishedDocumentRef.current = editor.state.doc;
14049
+ }
14050
+ if (artifactChanged || !sourceChanged && 0 === activeConflictsRef.current.length) return;
14051
+ const reconciled = reconcileDocumentReviewConflicts({
14052
+ active: activeConflictsRef.current,
14053
+ before,
14054
+ after: collectDocumentReviewSnapshot(editor.state.doc),
14055
+ comments: content.comments ?? []
14056
+ });
14057
+ activeConflictsRef.current = reconciled.active;
14058
+ setVisibleConflicts((visible)=>{
14059
+ if (reconciled.detected.length) return reconciled.active;
14060
+ const visibleKeys = new Set(visible.map(documentReviewConflictKey));
14061
+ return reconciled.active.filter((conflict)=>visibleKeys.has(documentReviewConflictKey(conflict)));
14062
+ });
14063
+ if (reconciled.detected.length) onReviewConflictRef.current?.({
14064
+ artifactId,
14065
+ conflicts: reconciled.detected.map((conflict)=>({
14066
+ ...conflict
14067
+ }))
14068
+ });
14069
+ }, [
14070
+ activeConflictsRef,
14071
+ appliedSourceKeyRef,
14072
+ artifactId,
14073
+ content,
14074
+ editor,
14075
+ editorInput,
14076
+ normalizedContent,
14077
+ publishedDocumentRef,
14078
+ reconcileControlledUpdates
14079
+ ]);
14080
+ return {
14081
+ dismiss: ()=>setVisibleConflicts([]),
14082
+ visibleConflicts
14083
+ };
14084
+ }
14085
+ function DocumentReviewConflictNotice({ conflicts, onDismiss }) {
14086
+ if (!conflicts.length) return null;
14087
+ return /*#__PURE__*/ jsxs(InlineNotice, {
14088
+ className: "work-document-review-conflict",
14089
+ icon: /*#__PURE__*/ jsx(TriangleAlert, {}),
14090
+ role: "alert",
14091
+ title: "审阅内容与外部更新冲突",
14092
+ tone: "warning",
14093
+ actions: /*#__PURE__*/ jsx(button_Button, {
14094
+ "aria-label": "关闭审阅冲突提示",
14095
+ size: "compact",
14096
+ tone: "quiet",
14097
+ onClick: onDismiss,
14098
+ children: "关闭"
14099
+ }),
14100
+ children: [
14101
+ "外部版本已应用,其中 ",
14102
+ conflicts.length,
14103
+ ' ',
14104
+ "个批注或修订范围发生变化。请检查后再继续审阅。"
14105
+ ]
14106
+ });
14107
+ }
14081
14108
  const EMPTY_DOCUMENT_LAYOUT_FONTS = [];
14082
14109
  const EMPTY_DOCUMENT_EXTENSIONS = [];
14083
14110
  function createTrackedDocumentChange(_kind) {
@@ -14087,29 +14114,100 @@ function createTrackedDocumentChange(_kind) {
14087
14114
  date: new Date().toISOString()
14088
14115
  };
14089
14116
  }
14090
- function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTENSIONS, preview, saveStatus = '已自动保存', kernelWasmUrl, layoutFonts = EMPTY_DOCUMENT_LAYOUT_FONTS, fileActions, getSelectionMenuItems, onChange, onAgentRequest }) {
14117
+ function DocumentEditor(props) {
14118
+ if (!props.collaboration) return /*#__PURE__*/ jsx(DocumentEditorSurface, {
14119
+ ...props
14120
+ });
14121
+ return /*#__PURE__*/ jsx(CollaborativeDocumentEditor, {
14122
+ ...props,
14123
+ collaboration: props.collaboration
14124
+ });
14125
+ }
14126
+ function CollaborativeDocumentEditor(props) {
14127
+ const { collaboration, extensions = EMPTY_DOCUMENT_EXTENSIONS } = props;
14128
+ const collaborationRef = useRef(collaboration);
14129
+ const extensionsRef = useRef(extensions);
14130
+ if (collaborationRef.current !== collaboration) throw new Error('DocumentEditor collaboration sessions cannot be replaced while mounted. Remount the editor for another shared document.');
14131
+ if (extensionsRef.current !== extensions) throw new Error('DocumentEditor extensions cannot be replaced while a collaboration session is mounted. Remount the editor to change its schema.');
14132
+ const initial = useRef(null);
14133
+ if (!initial.current) initial.current = readWorkOfficeDocumentCollaboration(collaboration);
14134
+ const bridge = useRef({
14135
+ getContent: ()=>initial.current,
14136
+ isTracking: ()=>Boolean(initial.current?.trackChanges),
14137
+ onContentChange: ()=>void 0,
14138
+ onTrackingChange: ()=>void 0
14139
+ });
14140
+ const [binding, setBinding] = useState();
14141
+ const bindingRef = useRef(void 0);
14142
+ const disposeTokenRef = useRef(void 0);
14143
+ useEffect(()=>{
14144
+ disposeTokenRef.current = void 0;
14145
+ const current = bindingRef.current ?? createWorkOfficeDocumentCollaborationBinding(collaboration, {
14146
+ additionalExtensions: extensionsRef.current,
14147
+ workExtensions: {
14148
+ getContent: ()=>bridge.current.getContent(),
14149
+ isTracking: ()=>bridge.current.isTracking(),
14150
+ createChange: createTrackedDocumentChange,
14151
+ onContentChange: (content)=>bridge.current.onContentChange(content),
14152
+ onTrackingChange: (enabled)=>bridge.current.onTrackingChange(enabled)
14153
+ }
14154
+ });
14155
+ bindingRef.current = current;
14156
+ setBinding(current);
14157
+ return ()=>{
14158
+ const token = {};
14159
+ disposeTokenRef.current = token;
14160
+ queueMicrotask(()=>{
14161
+ if (disposeTokenRef.current !== token) return;
14162
+ if (bindingRef.current === current) bindingRef.current = void 0;
14163
+ current.destroy();
14164
+ });
14165
+ };
14166
+ }, [
14167
+ collaboration
14168
+ ]);
14169
+ if (!binding) return /*#__PURE__*/ jsx(WorkEditorLoadingState, {
14170
+ title: "正在准备协作文档"
14171
+ });
14172
+ return /*#__PURE__*/ jsx(DocumentEditorSurface, {
14173
+ ...props,
14174
+ collaborationBinding: binding,
14175
+ collaborationBridge: bridge,
14176
+ collaborationInitialContent: initial.current
14177
+ });
14178
+ }
14179
+ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, collaborationBinding, collaborationBridge, collaborationInitialContent, content, extensions = EMPTY_DOCUMENT_EXTENSIONS, preview: requestedPreview, defaultRibbonCollapsed = false, saveStatus = '已自动保存', kernelWasmUrl, layoutFonts = EMPTY_DOCUMENT_LAYOUT_FONTS, fileActions, getSelectionMenuItems, onChange, onAgentRequest, onReviewConflict }) {
14180
+ const collaborationEditable = !collaboration || 'edit' === collaboration.mode;
14181
+ const preview = requestedPreview || !collaborationEditable;
14182
+ const readOnly = preview;
14183
+ const initialContentRef = useRef(collaborationInitialContent ?? content);
14184
+ const effectiveContent = collaboration ? initialContentRef.current : content;
14091
14185
  const imageInputRef = useRef(null);
14092
14186
  const pageHeaderRef = useRef(null);
14093
14187
  const pageFooterRef = useRef(null);
14094
14188
  const workspaceRef = useRef(null);
14095
14189
  const reviewSurfaceRef = useRef(null);
14096
14190
  const pageSurfaceRef = useRef(null);
14191
+ const editableSurfaceRef = useRef(null);
14097
14192
  const taskPaneInvokerRef = useRef(null);
14098
14193
  const commentsDraftFocusRef = useRef(null);
14099
14194
  const citationsDraftFocusRef = useRef(null);
14100
14195
  const statisticsInvokerRef = useRef(null);
14101
- const contentRef = useRef(content);
14196
+ const contentRef = useRef(effectiveContent);
14102
14197
  const onChangeRef = useRef(onChange);
14103
- const trackChangesRef = useRef(Boolean(content.trackChanges));
14104
- const normalizedContent = useMemo(()=>normalizeDocumentHtml(content), [
14105
- content
14198
+ const trackChangesRef = useRef(Boolean(effectiveContent.trackChanges));
14199
+ const collaborationBindingRef = useRef(collaborationBinding);
14200
+ const normalizedContent = useMemo(()=>normalizeDocumentHtml(effectiveContent), [
14201
+ effectiveContent
14106
14202
  ]);
14107
- const editorInput = useMemo(()=>resolveWorkDocumentEditorInput(content, normalizedContent), [
14108
- content,
14203
+ const editorInput = useMemo(()=>resolveWorkDocumentEditorInput(effectiveContent, normalizedContent), [
14204
+ effectiveContent,
14109
14205
  normalizedContent
14110
14206
  ]);
14111
- const initialContentRef = useRef(editorInput.source);
14207
+ const initialEditorSourceRef = useRef(editorInput.source);
14112
14208
  const appliedSourceKeyRef = useRef(editorInput.sourceKey);
14209
+ const activeReviewConflictsRef = useRef([]);
14210
+ const publishedDocumentRef = useRef(null);
14113
14211
  const [taskPane, setTaskPane] = useState(null);
14114
14212
  const [layoutPanelTab, setLayoutPanelTab] = useState('page');
14115
14213
  const [findReplaceFocusRequest, setFindReplaceFocusRequest] = useState(0);
@@ -14130,17 +14228,36 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14130
14228
  const [selectionVersion, setSelectionVersion] = useState(0);
14131
14229
  const [statisticsOpen, setStatisticsOpen] = useState(false);
14132
14230
  const loadedLayoutFontIds = useDocumentLayoutFonts(layoutFonts);
14133
- contentRef.current = content;
14231
+ if (!collaboration) contentRef.current = content;
14134
14232
  onChangeRef.current = onChange;
14135
- trackChangesRef.current = Boolean(content.trackChanges);
14233
+ trackChangesRef.current = Boolean(contentRef.current.trackChanges);
14234
+ const commitContentChange = useCallback((next)=>{
14235
+ const previous = contentRef.current;
14236
+ const binding = collaborationBindingRef.current;
14237
+ binding?.updateSidecars(previous, next);
14238
+ contentRef.current = next;
14239
+ if (!binding) onChangeRef.current(next);
14240
+ }, []);
14241
+ collaborationBindingRef.current = collaborationBinding;
14242
+ if (collaborationBridge) collaborationBridge.current = {
14243
+ getContent: ()=>contentRef.current,
14244
+ isTracking: ()=>trackChangesRef.current,
14245
+ onContentChange: (next)=>commitContentChange(next),
14246
+ onTrackingChange: (trackChanges)=>{
14247
+ trackChangesRef.current = trackChanges;
14248
+ commitContentChange({
14249
+ ...contentRef.current,
14250
+ trackChanges
14251
+ });
14252
+ }
14253
+ };
14136
14254
  const editorExtensions = useMemo(()=>mergeOfficeTiptapExtensions('DocumentEditor', [
14137
- ...createWorkDocumentExtensions({
14255
+ ...collaborationBinding?.extensions ?? createWorkDocumentExtensions({
14138
14256
  getContent: ()=>contentRef.current,
14139
14257
  isTracking: ()=>trackChangesRef.current,
14140
14258
  createChange: createTrackedDocumentChange,
14141
14259
  onContentChange: (next)=>{
14142
- contentRef.current = next;
14143
- onChangeRef.current(next);
14260
+ commitContentChange(next);
14144
14261
  },
14145
14262
  onTrackingChange: (trackChanges)=>{
14146
14263
  trackChangesRef.current = trackChanges;
@@ -14148,15 +14265,17 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14148
14265
  ...contentRef.current,
14149
14266
  trackChanges
14150
14267
  };
14151
- contentRef.current = next;
14152
- onChangeRef.current(next);
14268
+ commitContentChange(next);
14153
14269
  }
14154
14270
  }),
14155
14271
  extension_placeholder.configure({
14156
14272
  placeholder: '在这里开始输入…'
14157
14273
  }),
14158
14274
  DocumentPagination
14159
- ], extensions), [
14275
+ ], collaboration ? EMPTY_DOCUMENT_EXTENSIONS : extensions), [
14276
+ collaboration,
14277
+ collaborationBinding,
14278
+ commitContentChange,
14160
14279
  extensions
14161
14280
  ]);
14162
14281
  const editorProps = useMemo(()=>({
@@ -14169,33 +14288,123 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14169
14288
  }), []);
14170
14289
  const editor = useEditor({
14171
14290
  extensions: editorExtensions,
14172
- content: initialContentRef.current,
14173
- editable: !preview,
14291
+ content: collaboration ? void 0 : initialEditorSourceRef.current,
14292
+ editable: !readOnly,
14174
14293
  editorProps,
14175
- onUpdate: ({ editor: current })=>{
14294
+ onCreate: ({ editor: current })=>{
14295
+ publishedDocumentRef.current = current.state.doc;
14296
+ },
14297
+ onUpdate: ({ editor: current, transaction })=>{
14298
+ if (!shouldPublishDocumentUpdate(transaction)) {
14299
+ publishedDocumentRef.current = current.state.doc;
14300
+ return;
14301
+ }
14302
+ if (!publishedDocumentRef.current) {
14303
+ publishedDocumentRef.current = current.state.doc;
14304
+ return;
14305
+ }
14306
+ if (publishedDocumentRef.current.eq(current.state.doc)) return;
14307
+ publishedDocumentRef.current = current.state.doc;
14176
14308
  const anchors = collectDocumentCommentAnchors(current.state.doc);
14309
+ const retainedCommentIds = new Set(activeReviewConflictsRef.current.filter((conflict)=>'comment' === conflict.kind).map((conflict)=>conflict.id));
14177
14310
  const synchronized = syncDocumentContentFromHtml(contentRef.current, current.getHTML());
14178
14311
  const model = createWorkDocumentModel(synchronized.html, current.getJSON(), contentRef.current.model);
14179
14312
  const next = {
14180
14313
  ...synchronized,
14181
14314
  model,
14182
- comments: retainAnchoredDocumentComments(contentRef.current.comments ?? [], anchors)
14315
+ comments: retainAnchoredDocumentComments(contentRef.current.comments ?? [], anchors, retainedCommentIds)
14183
14316
  };
14184
14317
  appliedSourceKeyRef.current = `model:${model.revision}:${model.htmlFingerprint}`;
14185
- contentRef.current = next;
14186
- onChange(next);
14318
+ if (!collaborationBinding) {
14319
+ contentRef.current = next;
14320
+ onChangeRef.current(next);
14321
+ }
14187
14322
  },
14188
14323
  onSelectionUpdate: ()=>setSelectionVersion((value)=>value + 1)
14189
14324
  });
14325
+ const documentPresenceLocation = useMemo(()=>{
14326
+ if (!editor || editor.isDestroyed) return null;
14327
+ const { anchor, head } = editor.state.selection;
14328
+ return {
14329
+ kind: 'document',
14330
+ anchor,
14331
+ head
14332
+ };
14333
+ }, [
14334
+ editor,
14335
+ selectionVersion
14336
+ ]);
14337
+ useOfficePublishPresenceLocation(documentPresenceLocation);
14338
+ const navigateToDocumentParticipant = useCallback((participant)=>{
14339
+ const location = participant.location;
14340
+ if (!editor || editor.isDestroyed || location?.kind !== 'document') return false;
14341
+ const maximum = editor.state.doc.content.size;
14342
+ if (location.anchor > maximum || location.head > maximum) return false;
14343
+ const from = Math.min(location.anchor, location.head);
14344
+ const to = Math.max(location.anchor, location.head);
14345
+ return editor.chain().focus().setTextSelection({
14346
+ from,
14347
+ to
14348
+ }).scrollIntoView().run();
14349
+ }, [
14350
+ editor
14351
+ ]);
14352
+ useOfficeCollaborationLocationNavigator(navigateToDocumentParticipant);
14353
+ const reviewConflicts = useDocumentReviewConflicts({
14354
+ activeConflictsRef: activeReviewConflictsRef,
14355
+ appliedSourceKeyRef,
14356
+ artifactId,
14357
+ content: collaboration ? contentRef.current : content,
14358
+ editor,
14359
+ editorInput,
14360
+ normalizedContent,
14361
+ onReviewConflict,
14362
+ publishedDocumentRef,
14363
+ reconcileControlledUpdates: !collaboration
14364
+ });
14190
14365
  const documentComments = useDocumentComments({
14191
14366
  contentRef,
14192
14367
  editor,
14193
14368
  onBeforeDraft: ()=>setTaskPane(null)
14194
14369
  });
14195
- const documentInsert = useDocumentInsertCommands({
14196
- contentRef,
14197
- editor
14198
- });
14370
+ const [collaborationVersion, setCollaborationVersion] = useState(0);
14371
+ const handledCollaborationVersionRef = useRef(0);
14372
+ const pendingCollaborationContentRef = useRef(void 0);
14373
+ useEffect(()=>{
14374
+ if (!collaborationBinding) return;
14375
+ const unsubscribeChange = collaborationBinding.subscribe((change)=>{
14376
+ pendingCollaborationContentRef.current = change.content;
14377
+ contentRef.current = change.content;
14378
+ trackChangesRef.current = Boolean(change.content.trackChanges);
14379
+ setCollaborationVersion((value)=>value + 1);
14380
+ });
14381
+ const unsubscribeError = collaborationBinding.subscribeError((error)=>{
14382
+ queueMicrotask(()=>{
14383
+ throw error;
14384
+ });
14385
+ });
14386
+ const unsubscribeHistory = collaborationBinding.subscribeHistory(()=>{
14387
+ setCollaborationVersion((value)=>value + 1);
14388
+ });
14389
+ return ()=>{
14390
+ unsubscribeChange();
14391
+ unsubscribeError();
14392
+ unsubscribeHistory();
14393
+ };
14394
+ }, [
14395
+ collaborationBinding
14396
+ ]);
14397
+ useEffect(()=>{
14398
+ if (collaborationVersion === handledCollaborationVersionRef.current || !pendingCollaborationContentRef.current) return;
14399
+ handledCollaborationVersionRef.current = collaborationVersion;
14400
+ const next = pendingCollaborationContentRef.current;
14401
+ pendingCollaborationContentRef.current = void 0;
14402
+ contentRef.current = next;
14403
+ trackChangesRef.current = Boolean(next.trackChanges);
14404
+ onChangeRef.current(next);
14405
+ }, [
14406
+ collaborationVersion
14407
+ ]);
14199
14408
  const rememberTaskPaneInvoker = useCallback(()=>{
14200
14409
  const active = document.activeElement;
14201
14410
  if (active instanceof HTMLElement && active.isConnected) taskPaneInvokerRef.current = active;
@@ -14370,6 +14579,11 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14370
14579
  }, [
14371
14580
  editor
14372
14581
  ]);
14582
+ useOfficeEditorInitialFocus({
14583
+ enabled: autoFocus && !readOnly && Boolean(editor && !editor.isDestroyed),
14584
+ getTarget: ()=>!editor || editor.isDestroyed ? null : editor.view.dom,
14585
+ isTargetReady: (target)=>'true' === target.getAttribute('contenteditable')
14586
+ });
14373
14587
  const replaceDocumentText = useCallback((from, to, replacement)=>{
14374
14588
  if (!editor) return false;
14375
14589
  if (trackChangesRef.current) return editor.commands.replaceDocumentTextWithTrackedChange(from, to, replacement);
@@ -14384,7 +14598,7 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14384
14598
  if (!editor) return;
14385
14599
  const applyEditableState = ()=>{
14386
14600
  if (editor.isDestroyed) return;
14387
- editor.setEditable(!preview);
14601
+ editor.setEditable(!readOnly);
14388
14602
  const editorDom = editor.view.dom;
14389
14603
  editorDom.setAttribute('role', preview ? 'document' : 'textbox');
14390
14604
  if (preview) editorDom.removeAttribute('aria-multiline');
@@ -14397,7 +14611,8 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14397
14611
  };
14398
14612
  }, [
14399
14613
  editor,
14400
- preview
14614
+ preview,
14615
+ readOnly
14401
14616
  ]);
14402
14617
  useEffect(()=>{
14403
14618
  if (!editor) return;
@@ -14413,33 +14628,12 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14413
14628
  editor,
14414
14629
  spellcheckEnabled
14415
14630
  ]);
14416
- useEffect(()=>{
14417
- if (!editor || appliedSourceKeyRef.current === editorInput.sourceKey) return;
14418
- const currentContent = normalizeDocumentHtml({
14419
- ...content,
14420
- html: editor.getHTML()
14421
- });
14422
- if ('string' == typeof editorInput.source && currentContent === normalizedContent) {
14423
- appliedSourceKeyRef.current = editorInput.sourceKey;
14424
- return;
14425
- }
14426
- appliedSourceKeyRef.current = editorInput.sourceKey;
14427
- editor.chain().setMeta('addToHistory', false).setContent(editorInput.source, {
14428
- emitUpdate: false
14429
- }).run();
14430
- }, [
14431
- content,
14432
- editor,
14433
- editorInput,
14434
- normalizedContent
14435
- ]);
14436
14631
  const section = editor ? activeDocumentSection(editor) : null;
14437
- const layout = section?.layout ?? documentInitialSectionLayout(content);
14438
- const margins = documentMargins({
14439
- ...content,
14440
- pageSize: layout.pageSize,
14441
- margins: layout.margins
14442
- });
14632
+ const currentContent = contentRef.current;
14633
+ const layout = section?.layout ?? documentInitialSectionLayout(currentContent);
14634
+ const resolvedPageSize = resolveDocumentPageSize(layout);
14635
+ const resolvedMargins = resolveDocumentPageMargins(layout, 1);
14636
+ const margins = resolvedMargins.body;
14443
14637
  const marginPixels = {
14444
14638
  top: work_document_layout_millimetersToPixels(margins.top),
14445
14639
  right: work_document_layout_millimetersToPixels(margins.right),
@@ -14453,6 +14647,8 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14453
14647
  layout.margins.right,
14454
14648
  layout.margins.top,
14455
14649
  layout.orientation,
14650
+ JSON.stringify(layout.pageGeometry),
14651
+ JSON.stringify(layout.pageMargins),
14456
14652
  layout.pageSize
14457
14653
  ]);
14458
14654
  const pagination = useDocumentPagination({
@@ -14465,7 +14661,9 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14465
14661
  layout.columns.spacing,
14466
14662
  layout.columns.separator,
14467
14663
  layoutOpen,
14468
- JSON.stringify(pageChrome)
14664
+ JSON.stringify(pageChrome),
14665
+ JSON.stringify(layout.pageBorders),
14666
+ JSON.stringify(layout.pageMargins)
14469
14667
  ].join(':'),
14470
14668
  page: kernelPage,
14471
14669
  selectionVersion,
@@ -14473,12 +14671,33 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14473
14671
  layoutFonts,
14474
14672
  loadedLayoutFontIds
14475
14673
  });
14674
+ const documentInsert = useDocumentInsertCommands({
14675
+ contentRef,
14676
+ editor,
14677
+ resolveFieldContext: pagination.resolveFieldContext
14678
+ });
14679
+ useEffect(()=>{
14680
+ if (!editor || !pagination.resolveFieldContext) return;
14681
+ editor.commands.refreshDocumentFields(contentRef.current, {
14682
+ resolveContext: pagination.resolveFieldContext,
14683
+ addToHistory: false,
14684
+ updateClock: false
14685
+ });
14686
+ }, [
14687
+ contentRef,
14688
+ editor,
14689
+ pagination.resolveFieldContext
14690
+ ]);
14476
14691
  const pageCount = editor ? pagination.pageCount ?? documentPageCount(editor) : 1;
14477
- const paginationSurfaceHeight = pagination.pageCount ? documentPaginationSurfaceHeight(pagination.pageCount, kernelPage) : void 0;
14692
+ const paginationGeometry = pagination.pageCount ? documentPageSurfaceGeometry(pagination.pages.map((page)=>page.page), kernelPage, pagination.pageCount) : null;
14478
14693
  const currentPage = editor ? Math.min(pageCount, pagination.currentPage ?? documentCurrentPage(editor)) : 1;
14479
14694
  const currentPageDescriptor = pagination.currentPageDescriptor ?? fallbackPaginationPageDescriptor(section?.id, section?.index, layout, currentPage);
14480
14695
  const firstPageDescriptor = pagination.pages[0] ?? fallbackPaginationPageDescriptor(section?.id, section?.index, layout, 1);
14481
14696
  const lastPageDescriptor = pagination.pages.at(-1) ?? currentPageDescriptor;
14697
+ const firstPageFrame = paginationGeometry?.frames[0];
14698
+ const lastPageFrame = paginationGeometry?.frames.at(-1);
14699
+ const firstPageMargins = resolveDocumentPageMargins(firstPageDescriptor.layout, firstPageDescriptor.physicalPage);
14700
+ const lastPageMargins = resolveDocumentPageMargins(lastPageDescriptor.layout, lastPageDescriptor.physicalPage);
14482
14701
  const { chromeEditor: pageChromeEditor, close: closePageChrome, edit: editPageChrome, editing: pageChromeEditing, footerChrome, headerChrome, reset: resetPageChrome, setChromeEditor: setPageChromeEditor, togglePageNumber: toggleVisiblePageNumber, update: updateVisiblePageChrome, visibleChrome } = useDocumentPageChrome({
14483
14702
  editor,
14484
14703
  enabled: !preview && 'page' === viewMode,
@@ -14542,8 +14761,7 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14542
14761
  ...contentRef.current,
14543
14762
  pageColor
14544
14763
  };
14545
- contentRef.current = next;
14546
- onChangeRef.current(next);
14764
+ commitContentChange(next);
14547
14765
  }
14548
14766
  restoreDocumentBodyFocus();
14549
14767
  };
@@ -14562,8 +14780,8 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14562
14780
  if (!viewport) return;
14563
14781
  const style = getComputedStyle(viewport);
14564
14782
  setZoom(documentZoomForFit(fit, {
14565
- pageHeight: kernelPage.height,
14566
- pageWidth: kernelPage.width,
14783
+ pageHeight: currentPageDescriptor.page.height,
14784
+ pageWidth: currentPageDescriptor.page.width,
14567
14785
  viewportHeight: viewport.clientHeight,
14568
14786
  viewportWidth: viewport.clientWidth,
14569
14787
  viewportPadding: {
@@ -14633,12 +14851,19 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14633
14851
  className: "work-document-ribbon"
14634
14852
  }) : /*#__PURE__*/ jsx(DocumentToolbar, {
14635
14853
  editor: editor,
14854
+ defaultRibbonCollapsed: defaultRibbonCollapsed,
14855
+ history: collaborationBinding ? {
14856
+ canRedo: collaborationBinding.canRedo(),
14857
+ canUndo: collaborationBinding.canUndo(),
14858
+ redo: ()=>collaborationBinding.redo(),
14859
+ undo: ()=>collaborationBinding.undo()
14860
+ } : void 0,
14636
14861
  fileActions: fileActions,
14637
14862
  layout: layout,
14638
14863
  layoutFonts: layoutFonts,
14639
14864
  layoutOpen: layoutOpen,
14640
14865
  navigationOpen: navigationOpen,
14641
- pageColor: documentPageColor(content.pageColor),
14866
+ pageColor: documentPageColor(currentContent.pageColor),
14642
14867
  showPageNumbers: visibleChrome.showPageNumber,
14643
14868
  showRulers: showRulers,
14644
14869
  spellcheckEnabled: spellcheckEnabled,
@@ -14670,7 +14895,7 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14670
14895
  onInsertCaption: documentInsert.insertCaption,
14671
14896
  onInsertCrossReference: documentInsert.insertCrossReference,
14672
14897
  citationsOpen: citationsOpen,
14673
- citationSourceCount: content.bibliography?.sources.length ?? 0,
14898
+ citationSourceCount: currentContent.bibliography?.sources.length ?? 0,
14674
14899
  onToggleCitations: ()=>void toggleTaskPane('citations'),
14675
14900
  onInsertField: documentInsert.insertField,
14676
14901
  onRefreshFields: refreshDocumentFields,
@@ -14679,7 +14904,7 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14679
14904
  commentsOpen: documentComments.open,
14680
14905
  commentCount: documentComments.comments.length,
14681
14906
  onToggleComments: ()=>void toggleCommentsPanel(),
14682
- trackChanges: Boolean(content.trackChanges),
14907
+ trackChanges: Boolean(currentContent.trackChanges),
14683
14908
  changesOpen: changesOpen,
14684
14909
  changeCount: changes.length,
14685
14910
  findReplaceMode: findReplaceMode,
@@ -14695,6 +14920,10 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14695
14920
  onOpenWordCount: openDocumentStatistics,
14696
14921
  onOpenFindReplace: openFindReplace
14697
14922
  }),
14923
+ !preview && /*#__PURE__*/ jsx(DocumentReviewConflictNotice, {
14924
+ conflicts: reviewConflicts.visibleConflicts,
14925
+ onDismiss: reviewConflicts.dismiss
14926
+ }),
14698
14927
  /*#__PURE__*/ jsxs("div", {
14699
14928
  ref: workspaceRef,
14700
14929
  className: `work-document-workspace${!preview && taskPane ? ' task-pane-open' : ''}`,
@@ -14710,12 +14939,13 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14710
14939
  pageGap: kernelPage.pageGap,
14711
14940
  pageHeight: kernelPage.height,
14712
14941
  pageWidth: kernelPage.width,
14942
+ pages: paginationGeometry?.frames,
14713
14943
  revision: editorInput.sourceKey
14714
14944
  } : void 0,
14715
14945
  pages: (pagination.pages.length ? pagination.pages : [
14716
14946
  firstPageDescriptor
14717
14947
  ]).map((page)=>({
14718
- backgroundColor: documentPageColor(content.pageColor),
14948
+ backgroundColor: documentPageColor(currentContent.pageColor),
14719
14949
  orientation: page.layout.orientation,
14720
14950
  pageNumber: page.pageNumber,
14721
14951
  physicalPage: page.physicalPage,
@@ -14765,29 +14995,49 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14765
14995
  "data-pdf-page-gap": artifactId && pagination.pageCount ? kernelPage.pageGap : void 0,
14766
14996
  "data-pdf-page-height": artifactId && pagination.pageCount ? kernelPage.height : void 0,
14767
14997
  "data-pdf-page-size": artifactId && pagination.pageCount ? layout.pageSize : void 0,
14998
+ "data-pdf-page-width-points": artifactId && pagination.pageCount ? resolvedPageSize.widthPoints : void 0,
14999
+ "data-pdf-page-height-points": artifactId && pagination.pageCount ? resolvedPageSize.heightPoints : void 0,
14768
15000
  "data-pdf-page-width": artifactId && pagination.pageCount ? kernelPage.width : void 0,
15001
+ "data-document-page-gutter-position": resolvedMargins.gutterPosition,
15002
+ "data-document-page-margins-bounded": String(resolvedMargins.bounded),
15003
+ "data-document-page-mirror-margins": String(resolvedMargins.mirrorMargins),
15004
+ "data-document-page-top-margin-mode": resolvedMargins.topMode,
15005
+ "data-document-page-bottom-margin-mode": resolvedMargins.bottomMode,
14769
15006
  "aria-label": preview ? '文字预览' : '文字页面',
14770
15007
  style: {
14771
- padding: `${marginPixels.top}px ${marginPixels.right}px ${marginPixels.bottom}px ${marginPixels.left}px`,
14772
- backgroundColor: documentPageColor(content.pageColor),
14773
- minHeight: paginationSurfaceHeight,
14774
- '--work-document-page-color': documentPageColor(content.pageColor),
15008
+ padding: paginationGeometry ? `${firstPageDescriptor.page.marginTop}px 0 ${lastPageDescriptor.page.marginBottom}px` : `${marginPixels.top}px ${marginPixels.right}px ${marginPixels.bottom}px ${marginPixels.left}px`,
15009
+ backgroundColor: documentPageColor(currentContent.pageColor),
15010
+ width: 'page' === viewMode ? paginationGeometry?.width ?? kernelPage.width : void 0,
15011
+ minHeight: 'page' === viewMode ? paginationGeometry?.height ?? kernelPage.height : void 0,
15012
+ '--work-document-page-color': documentPageColor(currentContent.pageColor),
14775
15013
  '--work-document-page-margin-left': `${marginPixels.left}px`,
14776
15014
  '--work-document-page-margin-right': `${marginPixels.right}px`,
14777
15015
  '--work-document-page-margin-top': `${marginPixels.top}px`,
14778
- '--work-document-page-margin-bottom': `${marginPixels.bottom}px`
15016
+ '--work-document-page-margin-bottom': `${marginPixels.bottom}px`,
15017
+ '--work-document-page-header-distance': `${work_document_layout_millimetersToPixels(resolvedMargins.headerDistance)}px`,
15018
+ '--work-document-page-footer-distance': `${work_document_layout_millimetersToPixels(resolvedMargins.footerDistance)}px`,
15019
+ '--work-document-page-header-height': `${Math.max(0, marginPixels.top - work_document_layout_millimetersToPixels(resolvedMargins.headerDistance))}px`,
15020
+ '--work-document-page-footer-height': `${Math.max(0, marginPixels.bottom - work_document_layout_millimetersToPixels(resolvedMargins.footerDistance))}px`
14779
15021
  },
14780
15022
  children: [
14781
15023
  'page' === viewMode && pagination.pageCount && /*#__PURE__*/ jsx(DocumentPageStack, {
14782
- pageColor: documentPageColor(content.pageColor),
15024
+ pageColor: documentPageColor(currentContent.pageColor),
14783
15025
  pageCount: pagination.pageCount,
14784
15026
  pageGap: kernelPage.pageGap,
14785
- pageHeight: kernelPage.height
15027
+ pageHeight: kernelPage.height,
15028
+ pageWidth: kernelPage.width,
15029
+ pages: pagination.pages
14786
15030
  }),
14787
15031
  'page' === viewMode && (!preview || Boolean(headerChrome.headerHtml)) && /*#__PURE__*/ jsx("header", {
14788
15032
  ref: pageHeaderRef,
14789
15033
  className: `work-document-page-header${!preview && pageChromeEditing?.part === 'header' ? ' editing' : ''}`,
14790
15034
  "data-document-page-chrome": firstPageDescriptor.pageChrome.variant,
15035
+ style: firstPageFrame && paginationGeometry ? {
15036
+ height: firstPageDescriptor.page.headerHeight,
15037
+ left: firstPageFrame.left + firstPageDescriptor.page.marginLeft,
15038
+ right: paginationGeometry.width - firstPageFrame.left - firstPageFrame.width + firstPageDescriptor.page.marginRight,
15039
+ top: work_document_layout_millimetersToPixels(firstPageMargins.headerDistance)
15040
+ } : void 0,
14791
15041
  onDoubleClick: preview ? void 0 : (event)=>{
14792
15042
  if (pageChromeEditing?.part === 'header') return;
14793
15043
  event.preventDefault();
@@ -14812,6 +15062,7 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14812
15062
  }, `${pageChromeEditing.sectionId}-${pageChromeEditing.variant}-header`)
14813
15063
  }),
14814
15064
  /*#__PURE__*/ jsxs("section", {
15065
+ ref: editableSurfaceRef,
14815
15066
  className: `work-document-editable ${viewMode}`,
14816
15067
  "aria-label": "文档内容编辑区域",
14817
15068
  onDoubleClick: ()=>{
@@ -14826,6 +15077,11 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14826
15077
  /*#__PURE__*/ jsx(EditorContent, {
14827
15078
  editor: editor
14828
15079
  }),
15080
+ /*#__PURE__*/ jsx(OfficeTiptapPresenceLayer, {
15081
+ containerRef: editableSurfaceRef,
15082
+ editor: editor,
15083
+ kind: "document"
15084
+ }),
14829
15085
  !preview && /*#__PURE__*/ jsx(DocumentSelectionToolbar, {
14830
15086
  editor: editor,
14831
15087
  canInsertComment: documentComments.canInsert,
@@ -14838,6 +15094,12 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14838
15094
  ref: pageFooterRef,
14839
15095
  className: `work-document-page-footer${!preview && pageChromeEditing?.part === 'footer' ? ' editing' : ''}`,
14840
15096
  "data-document-page-chrome": lastPageDescriptor.pageChrome.variant,
15097
+ style: lastPageFrame && paginationGeometry ? {
15098
+ bottom: work_document_layout_millimetersToPixels(lastPageMargins.footerDistance),
15099
+ height: lastPageDescriptor.page.footerHeight,
15100
+ left: lastPageFrame.left + lastPageDescriptor.page.marginLeft,
15101
+ right: paginationGeometry.width - lastPageFrame.left - lastPageFrame.width + lastPageDescriptor.page.marginRight
15102
+ } : void 0,
14841
15103
  onDoubleClick: preview ? void 0 : (event)=>{
14842
15104
  if (pageChromeEditing?.part === 'footer') return;
14843
15105
  event.preventDefault();
@@ -14898,14 +15160,14 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14898
15160
  }),
14899
15161
  !preview && citationsOpen && /*#__PURE__*/ jsx(DocumentCitationsPanel, {
14900
15162
  editor: editor,
14901
- content: content,
15163
+ content: currentContent,
14902
15164
  onClose: closeTaskPane,
14903
15165
  onDirtyChange: setCitationsDirty
14904
15166
  }),
14905
15167
  !preview && changesOpen && /*#__PURE__*/ jsx(DocumentChangesPanel, {
14906
15168
  editor: editor,
14907
15169
  changes: changes,
14908
- trackChanges: Boolean(content.trackChanges),
15170
+ trackChanges: Boolean(currentContent.trackChanges),
14909
15171
  onTrackChangesChange: (enabled)=>editor.commands.setDocumentTrackChanges(enabled),
14910
15172
  onClose: closeTaskPane
14911
15173
  }),
@@ -14949,7 +15211,7 @@ function DocumentEditor({ artifactId, content, extensions = EMPTY_DOCUMENT_EXTEN
14949
15211
  })
14950
15212
  ]
14951
15213
  }) : /*#__PURE__*/ jsx(DocumentStatusBar, {
14952
- bibliographyCount: content.bibliography?.sources.length ?? 0,
15214
+ bibliographyCount: currentContent.bibliography?.sources.length ?? 0,
14953
15215
  citationCount: citationCount,
14954
15216
  currentPage: currentPage,
14955
15217
  pageCount: pageCount,