@a3s-lab/office 0.2.2 → 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 (277) hide show
  1. package/COLLABORATION_ROADMAP.md +426 -0
  2. package/README.md +649 -10
  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 +123 -12
  10. package/dist/0~4980.js +154 -0
  11. package/dist/0~5050.js +124 -113
  12. package/dist/{0~9512.js → 0~6090.js} +540 -3
  13. package/dist/0~7048.js +92 -66
  14. package/dist/0~7614.js +2 -1
  15. package/dist/0~7754.js +5653 -0
  16. package/dist/0~9445.js +1 -1
  17. package/dist/0~document-editor.js +4992 -3323
  18. package/dist/0~markdown-editor.js +250 -44
  19. package/dist/0~pdf-viewer.js +1016 -24
  20. package/dist/0~presentation-editor.js +179 -8
  21. package/dist/0~spreadsheet-editor.js +3371 -578
  22. package/dist/0~work-docx-export.js +6801 -658
  23. package/dist/0~work-docx-import.js +1388 -795
  24. package/dist/0~work-office-diagnostics.js +524 -30
  25. package/dist/0~work-pptx-export.js +2 -1
  26. package/dist/0~work-pptx-import.js +1 -2
  27. package/dist/0~work-presentation-charts.js +1 -1
  28. package/dist/2591.js +197 -0
  29. package/dist/3818.js +1090 -0
  30. package/dist/4104.js +970 -60
  31. package/dist/432.js +171 -14
  32. package/dist/4476.js +2008 -0
  33. package/dist/4560.js +674 -0
  34. package/dist/4650.js +70 -0
  35. package/dist/{1134.js → 5184.js} +183 -1
  36. package/dist/6489.js +18 -18
  37. package/dist/7060.js +132 -0
  38. package/dist/8715.js +2 -136
  39. package/dist/8928.js +16778 -0
  40. package/dist/9787.js +209 -0
  41. package/dist/core.d.ts +29 -9
  42. package/dist/core.js +10 -2
  43. package/dist/index.js +13 -3
  44. package/dist/internal/collaboration/office-collaboration-flat-json.d.ts +13 -0
  45. package/dist/internal/collaboration/office-collaboration-json.d.ts +4 -0
  46. package/dist/internal/collaboration/office-collaboration-presence.d.ts +86 -0
  47. package/dist/internal/collaboration/office-collaboration-transport.d.ts +42 -0
  48. package/dist/internal/collaboration/office-collaboration.d.ts +85 -0
  49. package/dist/internal/collaboration/office-document-collaboration-bibliography.d.ts +7 -0
  50. package/dist/internal/collaboration/office-document-collaboration-claims.d.ts +9 -0
  51. package/dist/internal/collaboration/office-document-collaboration-comments.d.ts +7 -0
  52. package/dist/internal/collaboration/office-document-collaboration-sidecar-utils.d.ts +27 -0
  53. package/dist/internal/collaboration/office-document-collaboration-sidecars.d.ts +16 -0
  54. package/dist/internal/collaboration/office-document-collaboration.d.ts +45 -0
  55. package/dist/internal/collaboration/office-markdown-collaboration.d.ts +31 -0
  56. package/dist/internal/collaboration/office-pdf-collaboration-claims.d.ts +4 -0
  57. package/dist/internal/collaboration/office-pdf-collaboration-conflicts.d.ts +2 -0
  58. package/dist/internal/collaboration/office-pdf-collaboration-model.d.ts +26 -0
  59. package/dist/internal/collaboration/office-pdf-collaboration-records.d.ts +21 -0
  60. package/dist/internal/collaboration/office-pdf-collaboration-types.d.ts +91 -0
  61. package/dist/internal/collaboration/office-pdf-collaboration-validation.d.ts +4 -0
  62. package/dist/internal/collaboration/office-pdf-collaboration.d.ts +35 -0
  63. package/dist/internal/collaboration/office-presentation-collaboration-model.d.ts +25 -0
  64. package/dist/internal/collaboration/office-presentation-collaboration-records.d.ts +22 -0
  65. package/dist/internal/collaboration/office-presentation-collaboration-validation.d.ts +4 -0
  66. package/dist/internal/collaboration/office-presentation-collaboration.d.ts +32 -0
  67. package/dist/internal/collaboration/office-spreadsheet-collaboration-cells.d.ts +13 -0
  68. package/dist/internal/collaboration/office-spreadsheet-collaboration-claims.d.ts +4 -0
  69. package/dist/internal/collaboration/office-spreadsheet-collaboration-conflicts.d.ts +2 -0
  70. package/dist/internal/collaboration/office-spreadsheet-collaboration-flat-json.d.ts +10 -0
  71. package/dist/internal/collaboration/office-spreadsheet-collaboration-model.d.ts +31 -0
  72. package/dist/internal/collaboration/office-spreadsheet-collaboration-records.d.ts +17 -0
  73. package/dist/internal/collaboration/office-spreadsheet-collaboration-validation.d.ts +4 -0
  74. package/dist/internal/collaboration/office-spreadsheet-collaboration.d.ts +32 -0
  75. package/dist/internal/design-system/primitives/navigation/tabs.d.ts +3 -1
  76. package/dist/internal/features/work/components/work-document-page-border.d.ts +6 -0
  77. package/dist/internal/features/work/editors/document-command-catalog.d.ts +536 -0
  78. package/dist/internal/features/work/editors/document-editor-focus.d.ts +1 -1
  79. package/dist/internal/features/work/editors/document-editor-support.d.ts +7 -3
  80. package/dist/internal/features/work/editors/document-editor.d.ts +8 -2
  81. package/dist/internal/features/work/editors/document-external-content.d.ts +14 -0
  82. package/dist/internal/features/work/editors/document-format-clipboard.d.ts +6 -0
  83. package/dist/internal/features/work/editors/document-format-tools.d.ts +4 -0
  84. package/dist/internal/features/work/editors/document-layout-panel.d.ts +4 -1
  85. package/dist/internal/features/work/editors/document-page-chrome-wps-shortcuts.d.ts +2 -0
  86. package/dist/internal/features/work/editors/document-page-layout-ribbon.d.ts +14 -0
  87. package/dist/internal/features/work/editors/document-page-stack.d.ts +10 -1
  88. package/dist/internal/features/work/editors/document-page-thumbnail.d.ts +2 -0
  89. package/dist/internal/features/work/editors/document-pagination-run-coordinator.d.ts +26 -0
  90. package/dist/internal/features/work/editors/document-picture-properties-dialog-model.d.ts +20 -1
  91. package/dist/internal/features/work/editors/document-review-navigation.d.ts +9 -0
  92. package/dist/internal/features/work/editors/document-statistics-dialog.d.ts +7 -0
  93. package/dist/internal/features/work/editors/document-status-bar.d.ts +2 -1
  94. package/dist/internal/features/work/editors/document-table-properties-dialog-model.d.ts +4 -1
  95. package/dist/internal/features/work/editors/document-toolbar.d.ts +19 -42
  96. package/dist/internal/features/work/editors/document-wps-shortcuts.d.ts +12 -0
  97. package/dist/internal/features/work/editors/document-zoom.d.ts +17 -0
  98. package/dist/internal/features/work/editors/markdown-editor-focus.d.ts +1 -1
  99. package/dist/internal/features/work/editors/markdown-editor.d.ts +5 -1
  100. package/dist/internal/features/work/editors/markdown-toolbar.d.ts +2 -1
  101. package/dist/internal/features/work/editors/office-collaboration-participants.d.ts +3 -0
  102. package/dist/internal/features/work/editors/office-collaboration-presence-context.d.ts +17 -0
  103. package/dist/internal/features/work/editors/office-collaboration-presence-ui.d.ts +25 -0
  104. package/dist/internal/features/work/editors/office-editor-focus-handoff.d.ts +27 -0
  105. package/dist/internal/features/work/editors/pdf-annotation-controller.d.ts +5 -0
  106. package/dist/internal/features/work/editors/pdf-collaboration-presence.d.ts +3 -0
  107. package/dist/internal/features/work/editors/pdf-collaboration-projection.d.ts +10 -0
  108. package/dist/internal/features/work/editors/pdf-evidence-contract.d.ts +36 -0
  109. package/dist/internal/features/work/editors/pdf-evidence-overlay.d.ts +37 -0
  110. package/dist/internal/features/work/editors/pdf-toolbar.d.ts +2 -1
  111. package/dist/internal/features/work/editors/pdf-viewer-controller.d.ts +7 -1
  112. package/dist/internal/features/work/editors/pdf-viewer.d.ts +11 -1
  113. package/dist/internal/features/work/editors/presentation-collaboration-presence.d.ts +5 -0
  114. package/dist/internal/features/work/editors/presentation-editor-focus.d.ts +3 -2
  115. package/dist/internal/features/work/editors/presentation-editor-types.d.ts +3 -0
  116. package/dist/internal/features/work/editors/presentation-editor.d.ts +1 -1
  117. package/dist/internal/features/work/editors/spreadsheet-auto-filter.d.ts +9 -0
  118. package/dist/internal/features/work/editors/spreadsheet-cell-clear.d.ts +3 -0
  119. package/dist/internal/features/work/editors/spreadsheet-cell-merge.d.ts +12 -0
  120. package/dist/internal/features/work/editors/spreadsheet-clipboard-shortcuts.d.ts +3 -7
  121. package/dist/internal/features/work/editors/spreadsheet-collaboration-presence.d.ts +9 -0
  122. package/dist/internal/features/work/editors/spreadsheet-command-catalog.d.ts +464 -0
  123. package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +45 -9
  124. package/dist/internal/features/work/editors/spreadsheet-context-menu.d.ts +2 -0
  125. package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +9 -28
  126. package/dist/internal/features/work/editors/spreadsheet-editor.d.ts +8 -2
  127. package/dist/internal/features/work/editors/spreadsheet-format-painter.d.ts +27 -0
  128. package/dist/internal/features/work/editors/spreadsheet-freeze-panes.d.ts +14 -0
  129. package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +3 -2
  130. package/dist/internal/features/work/editors/use-document-pagination.d.ts +6 -1
  131. package/dist/internal/features/work/editors/use-document-review-conflicts.d.ts +28 -0
  132. package/dist/internal/features/work/editors/use-spreadsheet-auto-filter.d.ts +21 -0
  133. package/dist/internal/features/work/editors/use-spreadsheet-collaboration.d.ts +27 -0
  134. package/dist/internal/features/work/editors/use-spreadsheet-format-painter.d.ts +18 -0
  135. package/dist/internal/features/work/editors/work-office-chrome.d.ts +22 -3
  136. package/dist/internal/features/work/editors/work-office-ribbon-overflow.d.ts +2 -0
  137. package/dist/internal/features/work/work-document-bookmark-references.d.ts +37 -0
  138. package/dist/internal/features/work/work-document-bookmarks.d.ts +29 -0
  139. package/dist/internal/features/work/work-document-caption-nodes.d.ts +4 -1
  140. package/dist/internal/features/work/work-document-comments.d.ts +1 -1
  141. package/dist/internal/features/work/work-document-equations.d.ts +513 -0
  142. package/dist/internal/features/work/work-document-extensions.d.ts +1 -0
  143. package/dist/internal/features/work/work-document-field-identity.d.ts +2 -0
  144. package/dist/internal/features/work/work-document-field-node.d.ts +2 -2
  145. package/dist/internal/features/work/work-document-fields.d.ts +7 -0
  146. package/dist/internal/features/work/work-document-image-identity.d.ts +30 -0
  147. package/dist/internal/features/work/work-document-image-layout.d.ts +46 -1
  148. package/dist/internal/features/work/work-document-image-wrap-contour.d.ts +16 -0
  149. package/dist/internal/features/work/work-document-incremental-layout.d.ts +2 -2
  150. package/dist/internal/features/work/work-document-model-codec.d.ts +4 -1
  151. package/dist/internal/features/work/work-document-note-integrity.d.ts +2 -0
  152. package/dist/internal/features/work/work-document-page-borders.d.ts +25 -0
  153. package/dist/internal/features/work/work-document-page-capture.d.ts +8 -0
  154. package/dist/internal/features/work/work-document-page-frames.d.ts +15 -0
  155. package/dist/internal/features/work/work-document-page-margins.d.ts +56 -0
  156. package/dist/internal/features/work/work-document-page-size.d.ts +47 -0
  157. package/dist/internal/features/work/work-document-pagination-measurement.d.ts +1 -2
  158. package/dist/internal/features/work/work-document-pagination-types.d.ts +7 -2
  159. package/dist/internal/features/work/work-document-pagination.d.ts +2 -2
  160. package/dist/internal/features/work/work-document-paragraph-borders.d.ts +32 -0
  161. package/dist/internal/features/work/work-document-paragraph-identity.d.ts +26 -0
  162. package/dist/internal/features/work/work-document-paragraph-shading.d.ts +18 -0
  163. package/dist/internal/features/work/work-document-review-conflicts.d.ts +35 -0
  164. package/dist/internal/features/work/work-document-section.d.ts +4 -0
  165. package/dist/internal/features/work/work-document-snapshot.d.ts +19 -0
  166. package/dist/internal/features/work/work-document-source.d.ts +23 -0
  167. package/dist/internal/features/work/work-document-table-column-widths.d.ts +7 -0
  168. package/dist/internal/features/work/work-document-table-property-changes.d.ts +3 -0
  169. package/dist/internal/features/work/work-document-table-row-identity.d.ts +15 -0
  170. package/dist/internal/features/work/work-document-table-sizing.d.ts +3 -0
  171. package/dist/internal/features/work/work-document-text-layout.d.ts +3 -1
  172. package/dist/internal/features/work/work-document-word-line-metrics.d.ts +2 -0
  173. package/dist/internal/features/work/work-docx-bookmark-diagnostics.d.ts +3 -0
  174. package/dist/internal/features/work/work-docx-bookmark-import.d.ts +24 -0
  175. package/dist/internal/features/work/work-docx-bookmarks.d.ts +17 -0
  176. package/dist/internal/features/work/work-docx-comment-durable-id-preservation.d.ts +2 -0
  177. package/dist/internal/features/work/work-docx-comment-export.d.ts +3 -0
  178. package/dist/internal/features/work/work-docx-comment-metadata.d.ts +2 -0
  179. package/dist/internal/features/work/work-docx-drawing-extension-preservation.d.ts +4 -0
  180. package/dist/internal/features/work/work-docx-equation-diagnostics.d.ts +3 -0
  181. package/dist/internal/features/work/work-docx-equation-export.d.ts +14 -0
  182. package/dist/internal/features/work/work-docx-equation-import.d.ts +30 -0
  183. package/dist/internal/features/work/work-docx-equation-story.d.ts +9 -0
  184. package/dist/internal/features/work/work-docx-export-formatting.d.ts +3 -1
  185. package/dist/internal/features/work/work-docx-export-image.d.ts +5 -1
  186. package/dist/internal/features/work/work-docx-export.d.ts +1 -1
  187. package/dist/internal/features/work/work-docx-field-instructions.d.ts +8 -0
  188. package/dist/internal/features/work/work-docx-font-table-preservation.d.ts +3 -0
  189. package/dist/internal/features/work/work-docx-ignorable-extension-preservation.d.ts +15 -0
  190. package/dist/internal/features/work/work-docx-image-crop.d.ts +12 -0
  191. package/dist/internal/features/work/work-docx-image-identity.d.ts +17 -0
  192. package/dist/internal/features/work/work-docx-image-layer.d.ts +12 -0
  193. package/dist/internal/features/work/work-docx-image-layout-import.d.ts +14 -2
  194. package/dist/internal/features/work/work-docx-image-wrap.d.ts +15 -0
  195. package/dist/internal/features/work/work-docx-import.d.ts +13 -3
  196. package/dist/internal/features/work/work-docx-list-export.d.ts +18 -0
  197. package/dist/internal/features/work/work-docx-list-import.d.ts +14 -0
  198. package/dist/internal/features/work/work-docx-note-comment-content-control-block-preservation.d.ts +18 -0
  199. package/dist/internal/features/work/work-docx-note-comment-content-control-model.d.ts +47 -0
  200. package/dist/internal/features/work/work-docx-note-comment-content-control-preservation.d.ts +4 -0
  201. package/dist/internal/features/work/work-docx-note-comment-content-control-properties.d.ts +17 -0
  202. package/dist/internal/features/work/work-docx-note-comment-content-control-table-model.d.ts +17 -0
  203. package/dist/internal/features/work/work-docx-note-comment-content-control-xml.d.ts +14 -0
  204. package/dist/internal/features/work/work-docx-note-comment-extension-preservation.d.ts +2 -0
  205. package/dist/internal/features/work/work-docx-note-comment-hyperlink-content.d.ts +40 -0
  206. package/dist/internal/features/work/work-docx-note-comment-hyperlink-preservation.d.ts +5 -0
  207. package/dist/internal/features/work/work-docx-note-comment-hyperlink-relationships.d.ts +35 -0
  208. package/dist/internal/features/work/work-docx-note-comment-identity.d.ts +15 -0
  209. package/dist/internal/features/work/work-docx-note-comment-run-preservation.d.ts +4 -0
  210. package/dist/internal/features/work/work-docx-note-comment-run-properties.d.ts +2 -0
  211. package/dist/internal/features/work/work-docx-note-image-import.d.ts +12 -0
  212. package/dist/internal/features/work/work-docx-note-image-relationships.d.ts +1 -0
  213. package/dist/internal/features/work/work-docx-numbering-extension-preservation.d.ts +6 -0
  214. package/dist/internal/features/work/work-docx-numbering.d.ts +1 -0
  215. package/dist/internal/features/work/work-docx-page-borders-diagnostics.d.ts +3 -0
  216. package/dist/internal/features/work/work-docx-page-borders-import.d.ts +10 -0
  217. package/dist/internal/features/work/work-docx-page-margins-diagnostics.d.ts +3 -0
  218. package/dist/internal/features/work/work-docx-page-margins-import.d.ts +18 -0
  219. package/dist/internal/features/work/work-docx-page-size-diagnostics.d.ts +2 -0
  220. package/dist/internal/features/work/work-docx-page-size-import.d.ts +17 -0
  221. package/dist/internal/features/work/work-docx-paragraph-borders-diagnostics.d.ts +3 -0
  222. package/dist/internal/features/work/work-docx-paragraph-borders-export.d.ts +18 -0
  223. package/dist/internal/features/work/work-docx-paragraph-borders-import.d.ts +24 -0
  224. package/dist/internal/features/work/work-docx-paragraph-default-collapsed.d.ts +19 -0
  225. package/dist/internal/features/work/work-docx-paragraph-extension-preservation.d.ts +2 -0
  226. package/dist/internal/features/work/work-docx-paragraph-identity-import.d.ts +10 -0
  227. package/dist/internal/features/work/work-docx-paragraph-identity.d.ts +18 -0
  228. package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +6 -1
  229. package/dist/internal/features/work/work-docx-paragraph-shading-diagnostics.d.ts +3 -0
  230. package/dist/internal/features/work/work-docx-paragraph-shading-export.d.ts +5 -0
  231. package/dist/internal/features/work/work-docx-paragraph-shading-import.d.ts +18 -0
  232. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +3 -0
  233. package/dist/internal/features/work/work-docx-settings-preservation.d.ts +10 -0
  234. package/dist/internal/features/work/work-docx-settings-xml.d.ts +16 -0
  235. package/dist/internal/features/work/work-docx-style-extension-preservation.d.ts +2 -0
  236. package/dist/internal/features/work/work-docx-table-cell-export.d.ts +2 -1
  237. package/dist/internal/features/work/work-docx-table-cell-import.d.ts +3 -0
  238. package/dist/internal/features/work/work-docx-table-extension-preservation.d.ts +2 -0
  239. package/dist/internal/features/work/work-docx-table-row-import.d.ts +2 -0
  240. package/dist/internal/features/work/work-docx-table-sizing-import.d.ts +1 -0
  241. package/dist/internal/features/work/work-docx-theme-reference.d.ts +23 -0
  242. package/dist/internal/features/work/work-docx-theme.d.ts +4 -1
  243. package/dist/internal/features/work/work-docx-twips.d.ts +12 -0
  244. package/dist/internal/features/work/work-file-contract.d.ts +1 -0
  245. package/dist/internal/features/work/work-file-io.d.ts +1 -1
  246. package/dist/internal/features/work/work-markdown-extensions.d.ts +4 -1
  247. package/dist/internal/features/work/work-ooxml-package-preservation.d.ts +12 -0
  248. package/dist/internal/features/work/work-ooxml-package-security.d.ts +6 -0
  249. package/dist/internal/features/work/work-ooxml-relationship-preservation.d.ts +10 -0
  250. package/dist/internal/features/work/work-ooxml-xml.d.ts +2 -0
  251. package/dist/internal/features/work/work-pdf-export.d.ts +10 -0
  252. package/dist/internal/features/work/work-repository.d.ts +2 -0
  253. package/dist/internal/features/work/work-types.d.ts +15 -2
  254. package/dist/internal/kernel/office-kernel-client-types.d.ts +1 -0
  255. package/dist/internal/kernel/office-kernel-protocol.d.ts +7 -0
  256. package/dist/internal/kernel/office-kernel-version.d.ts +1 -1
  257. package/dist/office-kernel.wasm +0 -0
  258. package/dist/office-kernel.worker.js +93 -41
  259. package/dist/react.d.ts +18 -12
  260. package/dist/react.js +1 -1
  261. package/dist/styles.css +1367 -16
  262. package/dist/vue.d.ts +48 -5
  263. package/dist/vue.js +42 -3
  264. package/dist/web-component.d.ts +24 -2
  265. package/dist/web-component.js +101 -2
  266. package/docs/latest/en/browser-editor-architecture.md +262 -47
  267. package/package.json +29 -4
  268. package/dist/0~152.js +0 -94
  269. package/dist/0~2582.js +0 -485
  270. package/dist/0~4750.js +0 -939
  271. package/dist/0~5861.js +0 -596
  272. package/dist/0~work-document-extensions.js +0 -3045
  273. package/dist/0~work-document-model-codec.js +0 -42
  274. package/dist/0~work-docx-bibliography.js +0 -347
  275. package/dist/0~work-docx-page-color.js +0 -42
  276. package/dist/2881.js +0 -1455
  277. package/dist/8728.js +0 -723
@@ -1,12 +1,13 @@
1
1
  import { __webpack_require__ } from "./0~rslib-runtime.js";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { AnnotationPlugin, DocumentManagerPlugin, ExportPlugin, HistoryPlugin, PDFViewer, ScrollPlugin, SearchPlugin, ThumbnailPlugin, ZoomMode, ZoomPlugin } from "@embedpdf/react-pdf-viewer";
3
+ import { AnnotationPlugin, DocumentManagerPlugin, ExportPlugin, FormPlugin, HistoryPlugin, PDFViewer, PdfAnnotationSubtype, ScrollPlugin, SearchPlugin, ThumbnailPlugin, ViewportPlugin, ZoomMode, ZoomPlugin } from "@embedpdf/react-pdf-viewer";
4
4
  import { AlertCircle, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Ellipsis, GalleryVerticalEnd, Highlighter, Loader2, Minus, MousePointer2, MoveHorizontal, Pencil, Plus, Redo2, Save, Scan, Search, SlidersHorizontal, Strikethrough, Trash2, Type, Underline, Undo2, X } from "lucide-react";
5
5
  import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
6
+ import { readOfficePdfCollaborationSource as readWorkOfficePdfCollaborationSource, createOfficePdfCollaborationBinding as createWorkOfficePdfCollaborationBinding, assertPdfCollaborationSource as assertWorkOfficePdfCollaborationSource } from "./3818.js";
6
7
  import { createOfficeEditorExtension, useOfficeEditorRuntime, useOfficeEditorKeyboardShortcuts } from "./0~3557.js";
7
8
  import { isOfficeShortcutBlocked, OfficeColorPicker } from "./0~3635.js";
8
- import { button_Button, moveOfficeMenuFocus, icon_button_IconButton, office_text_field_OfficeTextField, Popover, useDialogFocusScope, useOfficeEditorWheelZoom } from "./0~9512.js";
9
- import { StateView } from "./432.js";
9
+ import { officePresenceColorStyle, moveOfficeMenuFocus, useOfficeRemoteParticipants, WorkOfficeCollaborationParticipants, Popover, OfficePresenceAvatar, useOfficePublishPresenceLocation, button_Button, icon_button_IconButton, office_text_field_OfficeTextField, useDialogFocusScope, useOfficeEditorWheelZoom } from "./0~6090.js";
10
+ import { StateView, useOfficeCollaborationLocationNavigator, PDF_EVIDENCE_COORDINATE_BASIS } from "./432.js";
10
11
  var pdf_viewer_namespaceObject = {};
11
12
  __webpack_require__.r(pdf_viewer_namespaceObject);
12
13
  __webpack_require__.d(pdf_viewer_namespaceObject, {
@@ -92,6 +93,25 @@ function usePdfAnnotationController(registry) {
92
93
  })));
93
94
  if (deletions.length > 0) scope.deleteAnnotations(deletions);
94
95
  }, []);
96
+ const getSelectionLocation = useCallback(()=>{
97
+ const scope = activeAnnotationScope(capabilitiesRef.current);
98
+ const selected = safely(()=>scope?.getSelectedAnnotations());
99
+ if (selected?.length !== 1 || !selected[0]) return null;
100
+ return {
101
+ annotationId: selected[0].object.id,
102
+ pageIndex: selected[0].object.pageIndex
103
+ };
104
+ }, []);
105
+ const locateAnnotation = useCallback((pageIndex, annotationId)=>{
106
+ const scope = activeAnnotationScope(capabilitiesRef.current);
107
+ if (!scope) return false;
108
+ const annotation = safely(()=>scope.getAnnotationById(annotationId));
109
+ if (!annotation || annotation.object.pageIndex !== pageIndex) return false;
110
+ return Boolean(safely(()=>{
111
+ scope.selectAnnotation(pageIndex, annotationId);
112
+ return true;
113
+ }));
114
+ }, []);
95
115
  const setAnnotationColor = useCallback((color)=>{
96
116
  const normalizedColor = normalizeAnnotationColor(color);
97
117
  const capabilities = capabilitiesRef.current;
@@ -133,12 +153,16 @@ function usePdfAnnotationController(registry) {
133
153
  return useMemo(()=>({
134
154
  state,
135
155
  deleteSelection,
156
+ getSelectionLocation,
157
+ locateAnnotation,
136
158
  setAnnotationColor,
137
159
  setAnnotationOpacity,
138
160
  setAnnotationStrokeWidth,
139
161
  selectTool
140
162
  }), [
141
163
  deleteSelection,
164
+ getSelectionLocation,
165
+ locateAnnotation,
142
166
  setAnnotationColor,
143
167
  setAnnotationOpacity,
144
168
  setAnnotationStrokeWidth,
@@ -238,6 +262,457 @@ function safely(action) {
238
262
  return;
239
263
  }
240
264
  }
265
+ const COLLABORATIVE_PDF_ANNOTATION_TYPES = new Set([
266
+ PdfAnnotationSubtype.FREETEXT,
267
+ PdfAnnotationSubtype.HIGHLIGHT,
268
+ PdfAnnotationSubtype.UNDERLINE,
269
+ PdfAnnotationSubtype.STRIKEOUT,
270
+ PdfAnnotationSubtype.INK
271
+ ]);
272
+ function createWorkPdfCollaborationProjection(registry, session) {
273
+ const binding = createWorkOfficePdfCollaborationBinding(session);
274
+ const initialization = new AbortController();
275
+ let destroyed = false;
276
+ let failed = false;
277
+ let failure;
278
+ let projecting = false;
279
+ let scopes = null;
280
+ let previousRecords = new Map();
281
+ const pendingAnnotations = new Map();
282
+ let pendingFormValues = null;
283
+ const errorListeners = new Set();
284
+ const stop = [];
285
+ const close = ()=>{
286
+ initialization.abort();
287
+ scopes = null;
288
+ pendingAnnotations.clear();
289
+ pendingFormValues = null;
290
+ for (const unsubscribe of stop.splice(0))unsubscribe();
291
+ try {
292
+ binding.destroy();
293
+ } catch {}
294
+ };
295
+ const reportError = (error)=>{
296
+ if (destroyed || failed) return;
297
+ failed = true;
298
+ failure = error;
299
+ close();
300
+ for (const listener of errorListeners)listener(error);
301
+ };
302
+ const project = (content)=>{
303
+ if (!scopes || destroyed || failed) return;
304
+ projecting = true;
305
+ try {
306
+ projectAnnotations(scopes.annotations, scopes.baseAnnotations, previousRecords, content.annotations, pendingAnnotations);
307
+ previousRecords = new Map(content.annotations.map((record)=>[
308
+ record.id,
309
+ record
310
+ ]));
311
+ const projectedFormValues = projectedPdfFormValues(scopes.form, scopes.baseFormValues, content.formValues);
312
+ if (projectedFormValues && scopes.form) {
313
+ pendingFormValues = projectedFormValues.expected;
314
+ applyPdfFormValues(scopes.form, projectedFormValues.updates, reportError);
315
+ }
316
+ purgeViewerHistory(scopes.history);
317
+ } finally{
318
+ projecting = false;
319
+ }
320
+ };
321
+ const ready = registry.pluginsReady().then(async ()=>{
322
+ if (destroyed || failed) return;
323
+ const plugins = pdfProjectionPlugins(registry);
324
+ const annotationsLoaded = await waitForPdfAnnotationsLoaded(registry, plugins, initialization.signal);
325
+ if (!annotationsLoaded || destroyed || failed) return;
326
+ scopes = pdfProjectionScopes(plugins);
327
+ const activeScopes = scopes;
328
+ stop.push(activeScopes.annotations.onAnnotationEvent((event)=>{
329
+ if (destroyed || projecting || 'loaded' === event.type) return;
330
+ if (!event.committed || event.documentId !== activeScopes.documentId) return;
331
+ try {
332
+ const pending = pendingAnnotations.get(event.annotation.id);
333
+ if (projectedAnnotationEventMatches(pending, event)) {
334
+ pendingAnnotations.delete(event.annotation.id);
335
+ purgeViewerHistory(activeScopes.history);
336
+ return;
337
+ }
338
+ commitAnnotationEvent(binding, event);
339
+ purgeViewerHistory(activeScopes.history);
340
+ } catch (error) {
341
+ reportError(error);
342
+ }
343
+ }));
344
+ if (activeScopes.form) {
345
+ const form = activeScopes.form;
346
+ stop.push(form.onFieldValueChange(()=>{
347
+ if (destroyed || projecting) return;
348
+ try {
349
+ const values = form.getFormValues();
350
+ if (pendingFormValues) {
351
+ if (pdfJsonEqual(values, pendingFormValues)) {
352
+ pendingFormValues = null;
353
+ purgeViewerHistory(activeScopes.history);
354
+ }
355
+ return;
356
+ }
357
+ commitFormValues(binding, activeScopes.baseFormValues, form, values);
358
+ purgeViewerHistory(activeScopes.history);
359
+ } catch (error) {
360
+ reportError(error);
361
+ }
362
+ }));
363
+ stop.push(form.onFormReady(()=>{
364
+ if (destroyed) return;
365
+ try {
366
+ const projectedFormValues = projectedPdfFormValues(form, activeScopes.baseFormValues, binding.content().formValues);
367
+ if (projectedFormValues) {
368
+ pendingFormValues = projectedFormValues.expected;
369
+ applyPdfFormValues(form, projectedFormValues.updates, reportError);
370
+ }
371
+ purgeViewerHistory(activeScopes.history);
372
+ } catch (error) {
373
+ reportError(error);
374
+ }
375
+ }));
376
+ }
377
+ project(binding.content());
378
+ }).catch((error)=>{
379
+ reportError(error);
380
+ throw error;
381
+ });
382
+ stop.push(binding.subscribe(({ content })=>{
383
+ project(content);
384
+ }));
385
+ stop.push(binding.subscribeError(reportError));
386
+ return {
387
+ binding,
388
+ ready,
389
+ subscribeError (listener) {
390
+ if (failed) {
391
+ queueMicrotask(()=>listener(failure));
392
+ return ()=>void 0;
393
+ }
394
+ errorListeners.add(listener);
395
+ return ()=>errorListeners.delete(listener);
396
+ },
397
+ destroy () {
398
+ if (destroyed) return;
399
+ destroyed = true;
400
+ close();
401
+ errorListeners.clear();
402
+ }
403
+ };
404
+ }
405
+ function pdfProjectionPlugins(registry) {
406
+ const annotation = registry.getPlugin(AnnotationPlugin.id)?.provides();
407
+ const documentId = registry.getPlugin(DocumentManagerPlugin.id)?.provides().getActiveDocumentId();
408
+ if (!annotation || !documentId) throw new Error('PDF collaboration requires an open EmbedPDF annotation document.');
409
+ const form = registry.getPlugin(FormPlugin.id)?.provides().forDocument(documentId) ?? null;
410
+ const history = registry.getPlugin(HistoryPlugin.id)?.provides().forDocument(documentId) ?? null;
411
+ return {
412
+ annotation,
413
+ documentId,
414
+ form,
415
+ history
416
+ };
417
+ }
418
+ function pdfProjectionScopes({ annotation, documentId, form, history }) {
419
+ const annotations = annotation.forDocument(documentId);
420
+ return {
421
+ annotations,
422
+ baseAnnotations: new Map(annotations.getAnnotations().map(({ object })=>[
423
+ object.id,
424
+ viewerAnnotation(portableViewerAnnotation(object))
425
+ ])),
426
+ baseFormValues: form?.getFormValues() ?? {},
427
+ documentId,
428
+ form,
429
+ history
430
+ };
431
+ }
432
+ async function waitForPdfAnnotationsLoaded(registry, { annotation, documentId }, signal) {
433
+ if (signal.aborted) return false;
434
+ const document1 = registry.getPlugin(DocumentManagerPlugin.id)?.provides().getDocument(documentId);
435
+ if (!document1) throw new Error('PDF collaboration requires an open EmbedPDF annotation document.');
436
+ const scope = annotation.forDocument(documentId);
437
+ let resolveLoaded = ()=>void 0;
438
+ const loaded = new Promise((resolve)=>{
439
+ resolveLoaded = resolve;
440
+ });
441
+ const stopLoaded = scope.onAnnotationEvent((event)=>{
442
+ if ('loaded' === event.type && event.documentId === documentId) resolveLoaded();
443
+ });
444
+ let resolveAborted = ()=>void 0;
445
+ const aborted = new Promise((resolve)=>{
446
+ resolveAborted = resolve;
447
+ });
448
+ const onAbort = ()=>resolveAborted();
449
+ signal.addEventListener('abort', onAbort, {
450
+ once: true
451
+ });
452
+ if (signal.aborted) onAbort();
453
+ try {
454
+ const expected = await Promise.race([
455
+ registry.getEngine().getAllAnnotations(document1).toPromise(),
456
+ aborted.then(()=>null)
457
+ ]);
458
+ if (!expected || signal.aborted) return false;
459
+ const expectedIds = new Set(Object.values(expected).flatMap((records)=>records.map(({ id })=>id)));
460
+ if (pdfAnnotationIdsMatch(scope, expectedIds)) return true;
461
+ await Promise.race([
462
+ loaded,
463
+ aborted
464
+ ]);
465
+ if (signal.aborted) return false;
466
+ if (pdfAnnotationIdsMatch(scope, expectedIds)) return true;
467
+ throw new Error('PDF collaboration could not capture the source baseline.');
468
+ } finally{
469
+ signal.removeEventListener('abort', onAbort);
470
+ stopLoaded();
471
+ }
472
+ }
473
+ function pdfAnnotationIdsMatch(scope, expectedIds) {
474
+ const actualIds = new Set(scope.getAnnotations().map(({ object })=>object.id));
475
+ return expectedIds.size === actualIds.size && [
476
+ ...expectedIds
477
+ ].every((id)=>actualIds.has(id));
478
+ }
479
+ function purgeViewerHistory(history) {
480
+ history?.purgeByMetadata(()=>true);
481
+ }
482
+ function commitAnnotationEvent(binding, event) {
483
+ const previous = binding.content();
484
+ const current = previous.annotations.find(({ id })=>id === event.annotation.id);
485
+ assertCollaborativePdfAnnotation(event.annotation);
486
+ let annotations = previous.annotations;
487
+ if ('delete' === event.type) {
488
+ if (current?.deleted) return;
489
+ annotations = current ? previous.annotations.map((record)=>record.id === current.id ? {
490
+ ...record,
491
+ deleted: true
492
+ } : record) : [
493
+ ...previous.annotations,
494
+ {
495
+ id: event.annotation.id,
496
+ pageIndex: event.pageIndex,
497
+ source: 'base',
498
+ annotation: portableViewerAnnotation(event.annotation),
499
+ deleted: true
500
+ }
501
+ ];
502
+ } else {
503
+ const annotation = portableViewerAnnotation(event.annotation);
504
+ annotations = current ? previous.annotations.map((record)=>record.id === current.id ? {
505
+ ...record,
506
+ annotation,
507
+ pageIndex: event.pageIndex
508
+ } : record) : [
509
+ ...previous.annotations,
510
+ {
511
+ id: event.annotation.id,
512
+ pageIndex: event.pageIndex,
513
+ source: 'create' === event.type ? 'created' : 'base',
514
+ annotation
515
+ }
516
+ ];
517
+ }
518
+ binding.replace(previous, {
519
+ ...previous,
520
+ annotations
521
+ });
522
+ }
523
+ function commitFormValues(binding, baseValues, scope, values) {
524
+ const previous = binding.content();
525
+ const fields = new Map(scope.getFormFields().map((field)=>[
526
+ field.name,
527
+ field
528
+ ]));
529
+ const formValues = Object.entries(values).filter(([id, value])=>{
530
+ const field = fields.get(id);
531
+ if (!field) throw new Error(`PDF collaboration form field '${id}' does not exist.`);
532
+ if (field.readOnly && value !== baseValues[id]) throw new Error(`PDF collaboration form field '${id}' is read-only.`);
533
+ return value !== baseValues[id];
534
+ }).map(([id, value])=>({
535
+ id,
536
+ value
537
+ })).sort((left, right)=>left.id.localeCompare(right.id));
538
+ binding.replace(previous, {
539
+ ...previous,
540
+ formValues
541
+ });
542
+ }
543
+ function projectAnnotations(scope, baseAnnotations, previous, records, pending) {
544
+ assertPdfAnnotationSources(baseAnnotations, records);
545
+ const current = new Map(scope.getAnnotations().map(({ object })=>[
546
+ object.id,
547
+ object
548
+ ]));
549
+ const nextIds = new Set(records.map(({ id })=>id));
550
+ for (const [id, record] of previous){
551
+ if (nextIds.has(id)) continue;
552
+ const existing = current.get(id);
553
+ if ('created' === record.source) {
554
+ if (existing) {
555
+ pending.set(id, {
556
+ expected: 'deleted',
557
+ previous: existing
558
+ });
559
+ scope.deleteAnnotation(existing.pageIndex, id);
560
+ }
561
+ continue;
562
+ }
563
+ const base = baseAnnotations.get(id);
564
+ if (!(!base || pdfJsonEqual(existing, base))) {
565
+ pending.set(id, {
566
+ expected: base,
567
+ previous: existing
568
+ });
569
+ if (existing) scope.updateAnnotation(existing.pageIndex, id, base);
570
+ else scope.importAnnotations([
571
+ {
572
+ annotation: base
573
+ }
574
+ ]);
575
+ }
576
+ }
577
+ for (const record of records){
578
+ const existing = current.get(record.id);
579
+ if (record.deleted) {
580
+ if (existing) {
581
+ pending.set(record.id, {
582
+ expected: 'deleted',
583
+ previous: existing
584
+ });
585
+ scope.deleteAnnotation(existing.pageIndex, existing.id);
586
+ }
587
+ continue;
588
+ }
589
+ const expected = viewerAnnotation(record.annotation);
590
+ assertCollaborativePdfAnnotation(expected);
591
+ if (!existing) {
592
+ pending.set(record.id, {
593
+ expected
594
+ });
595
+ scope.importAnnotations([
596
+ {
597
+ annotation: expected
598
+ }
599
+ ]);
600
+ continue;
601
+ }
602
+ if (!projectedPdfAnnotationEqual(existing, expected, 'base' === record.source ? baseAnnotations.get(record.id) : void 0)) {
603
+ pending.set(record.id, {
604
+ expected,
605
+ previous: existing
606
+ });
607
+ scope.updateAnnotation(existing.pageIndex, existing.id, {
608
+ ...expected,
609
+ id: existing.id,
610
+ pageIndex: existing.pageIndex
611
+ });
612
+ }
613
+ }
614
+ }
615
+ function assertPdfAnnotationSources(baseAnnotations, records) {
616
+ for (const record of records){
617
+ const inSource = baseAnnotations.has(record.id);
618
+ if ('base' === record.source && !inSource) throw new Error(`PDF collaboration base annotation '${record.id}' does not exist in the source PDF.`);
619
+ if ('created' === record.source && inSource) throw new Error(`PDF collaboration annotation '${record.id}' conflicts with a source annotation.`);
620
+ const base = baseAnnotations.get(record.id);
621
+ if (base && (base.pageIndex !== record.pageIndex || base.type !== record.annotation.type)) throw new Error(`PDF collaboration base annotation '${record.id}' has a different source identity.`);
622
+ }
623
+ }
624
+ function assertCollaborativePdfAnnotation(annotation) {
625
+ if (COLLABORATIVE_PDF_ANNOTATION_TYPES.has(annotation.type)) return;
626
+ throw new Error(`PDF collaboration does not support annotation type '${annotation.type}'.`);
627
+ }
628
+ function projectedPdfFormValues(scope, baseValues, records) {
629
+ if (!scope) return null;
630
+ const fields = new Map(scope.getFormFields().map((field)=>[
631
+ field.name,
632
+ field
633
+ ]));
634
+ for (const { id, value } of records){
635
+ const field = fields.get(id);
636
+ if (!field) throw new Error(`PDF collaboration form field '${id}' does not exist.`);
637
+ if (field.readOnly && value !== baseValues[id]) throw new Error(`PDF collaboration form field '${id}' is read-only.`);
638
+ }
639
+ const expected = {
640
+ ...baseValues,
641
+ ...Object.fromEntries(records.map(({ id, value })=>[
642
+ id,
643
+ value
644
+ ]))
645
+ };
646
+ const current = scope.getFormValues();
647
+ const updates = Object.fromEntries(Object.entries(expected).filter(([id, value])=>current[id] !== value));
648
+ if (0 === Object.keys(updates).length) return null;
649
+ return {
650
+ expected,
651
+ updates
652
+ };
653
+ }
654
+ function applyPdfFormValues(scope, values, onError) {
655
+ scope.setFormValues(values).toPromise().catch(onError);
656
+ }
657
+ function projectedAnnotationEventMatches(pending, event) {
658
+ if (void 0 === pending) return false;
659
+ if ('deleted' === pending.expected) return 'delete' === event.type;
660
+ return 'delete' !== event.type && projectedPdfAnnotationEqual(event.annotation, pending.expected, pending.previous);
661
+ }
662
+ function projectedPdfAnnotationEqual(actual, expected, previous) {
663
+ const normalizedActual = portableViewerAnnotation(actual);
664
+ const normalizedExpected = portableViewerAnnotation(expected);
665
+ const normalizedPrevious = previous ? portableViewerAnnotation(previous) : void 0;
666
+ for (const key of Object.keys(normalizedActual))if (!Object.hasOwn(normalizedExpected, key)) {
667
+ if ('author' === key || normalizedPrevious && Object.hasOwn(normalizedPrevious, key) && pdfJsonEqual(normalizedActual[key], normalizedPrevious[key])) delete normalizedActual[key];
668
+ }
669
+ return pdfJsonEqual(normalizedActual, normalizedExpected);
670
+ }
671
+ function portableViewerAnnotation(annotation) {
672
+ return clonePdfValue(annotation);
673
+ }
674
+ function viewerAnnotation(annotation) {
675
+ return revivePdfDates(annotation);
676
+ }
677
+ function clonePdfValue(value) {
678
+ if (value instanceof Date) return value.toISOString();
679
+ if (Array.isArray(value)) return value.map(clonePdfValue);
680
+ if (!value || 'object' != typeof value) return value;
681
+ return Object.fromEntries(Object.entries(value).flatMap(([key, child])=>void 0 === child ? [] : [
682
+ [
683
+ key,
684
+ clonePdfValue(child)
685
+ ]
686
+ ]));
687
+ }
688
+ function revivePdfDates(value, key) {
689
+ if (Array.isArray(value)) return value.map((child)=>revivePdfDates(child));
690
+ if (!value || 'object' != typeof value) {
691
+ if (('created' === key || 'modified' === key) && 'string' == typeof value) {
692
+ const timestamp = Date.parse(value);
693
+ if (Number.isFinite(timestamp)) return new Date(timestamp);
694
+ }
695
+ return value;
696
+ }
697
+ return Object.fromEntries(Object.entries(value).map(([childKey, child])=>[
698
+ childKey,
699
+ revivePdfDates(child, childKey)
700
+ ]));
701
+ }
702
+ function pdfJsonEqual(left, right) {
703
+ return canonicalPdfJson(left) === canonicalPdfJson(right);
704
+ }
705
+ function canonicalPdfJson(value) {
706
+ return JSON.stringify(sortPdfJson(clonePdfValue(value)));
707
+ }
708
+ function sortPdfJson(value) {
709
+ if (Array.isArray(value)) return value.map(sortPdfJson);
710
+ if (!value || 'object' != typeof value) return value;
711
+ return Object.fromEntries(Object.entries(value).sort(([left], [right])=>left.localeCompare(right)).map(([key, child])=>[
712
+ key,
713
+ sortPdfJson(child)
714
+ ]));
715
+ }
241
716
  function createPdfEditorExtensions() {
242
717
  return [
243
718
  createOfficeEditorExtension({
@@ -406,6 +881,353 @@ function canZoom(viewer) {
406
881
  function canSave({ editable, save, viewer }) {
407
882
  return editable && save.enabled && documentReady(viewer) && viewer.state.features.export;
408
883
  }
884
+ function PdfCollaborationPresenceLayer({ pageIndex }) {
885
+ const participants = useOfficeRemoteParticipants().filter((participant)=>participant.location?.kind === 'pdf' && participant.location.pageIndex === pageIndex);
886
+ if (!participants.length) return null;
887
+ return /*#__PURE__*/ jsx("div", {
888
+ className: "work-pdf-remote-presence-layer",
889
+ "data-pdf-presence-page": pageIndex,
890
+ "aria-hidden": "true",
891
+ children: participants.map((participant)=>{
892
+ const location = participant.location;
893
+ if (location?.kind !== 'pdf') return null;
894
+ return /*#__PURE__*/ jsxs("span", {
895
+ className: "work-pdf-remote-participant",
896
+ "data-activity": participant.activity,
897
+ "data-annotation-id": location.annotationId,
898
+ "data-participant-id": participant.actor.id,
899
+ style: officePresenceColorStyle(participant),
900
+ children: [
901
+ /*#__PURE__*/ jsx(OfficePresenceAvatar, {
902
+ compact: true,
903
+ participant: participant
904
+ }),
905
+ /*#__PURE__*/ jsxs("span", {
906
+ children: [
907
+ /*#__PURE__*/ jsx("strong", {
908
+ children: participant.actor.name
909
+ }),
910
+ /*#__PURE__*/ jsx("small", {
911
+ children: location.annotationId ? '正在查看批注' : '正在查看此页'
912
+ })
913
+ ]
914
+ })
915
+ ]
916
+ }, participant.presenceId);
917
+ })
918
+ });
919
+ }
920
+ const MAX_EVIDENCE_REGIONS_PER_PAGE = 2000;
921
+ const MAX_REGION_TARGETS = 128;
922
+ const MAX_ID_LENGTH = 512;
923
+ const PAGE_ASPECT_RATIO_TOLERANCE = 0.005;
924
+ function PdfEvidenceOverlayLayer({ currentPage, evidenceOverlay, onEvidenceRegionSelect, registry, selectedEvidenceRegionId, sourceKey }) {
925
+ const [runtime, setRuntime] = useState(null);
926
+ const [pageState, setPageState] = useState({
927
+ status: 'idle'
928
+ });
929
+ const [viewportRevision, setViewportRevision] = useState(0);
930
+ const lastNavigationRef = useRef(null);
931
+ useEffect(()=>{
932
+ let disposed = false;
933
+ registry.pluginsReady().then(()=>{
934
+ if (!disposed) setRuntime(readEvidenceRuntime(registry));
935
+ }).catch((error)=>{
936
+ if (!disposed) setPageState({
937
+ message: evidenceErrorMessage(error),
938
+ status: 'error'
939
+ });
940
+ });
941
+ return ()=>{
942
+ disposed = true;
943
+ };
944
+ }, [
945
+ registry
946
+ ]);
947
+ useEffect(()=>{
948
+ if (!runtime) return;
949
+ let frame = 0;
950
+ const update = ()=>{
951
+ if (frame) return;
952
+ frame = window.requestAnimationFrame(()=>{
953
+ frame = 0;
954
+ setViewportRevision((value)=>value + 1);
955
+ });
956
+ };
957
+ const unsubscribe = [
958
+ runtime.scroll.onScroll(()=>update()),
959
+ runtime.scroll.onLayoutChange(()=>update()),
960
+ runtime.viewport.onScrollChange(()=>update()),
961
+ runtime.viewport.onViewportResize(()=>update()),
962
+ runtime.zoom?.onStateChange(()=>update())
963
+ ].filter((stop)=>Boolean(stop));
964
+ return ()=>{
965
+ if (frame) window.cancelAnimationFrame(frame);
966
+ for (const stop of unsubscribe)stop();
967
+ };
968
+ }, [
969
+ runtime
970
+ ]);
971
+ useEffect(()=>{
972
+ const abort = new AbortController();
973
+ if (!runtime || currentPage < 1) {
974
+ setPageState({
975
+ status: 'idle'
976
+ });
977
+ return ()=>abort.abort();
978
+ }
979
+ const identityError = validateOverlayIdentity(evidenceOverlay, sourceKey);
980
+ if (identityError) {
981
+ setPageState({
982
+ message: identityError,
983
+ status: 'error'
984
+ });
985
+ return ()=>abort.abort();
986
+ }
987
+ setPageState({
988
+ status: 'loading'
989
+ });
990
+ evidenceOverlay.loadPage(currentPage, abort.signal).then((page)=>{
991
+ if (abort.signal.aborted) return;
992
+ if (page) validatePdfEvidencePage(page, evidenceOverlay, runtime, currentPage);
993
+ setPageState({
994
+ page,
995
+ status: 'ready'
996
+ });
997
+ }).catch((error)=>{
998
+ if (!abort.signal.aborted) setPageState({
999
+ message: evidenceErrorMessage(error),
1000
+ status: 'error'
1001
+ });
1002
+ });
1003
+ return ()=>abort.abort();
1004
+ }, [
1005
+ currentPage,
1006
+ evidenceOverlay,
1007
+ runtime,
1008
+ sourceKey
1009
+ ]);
1010
+ const page = 'ready' === pageState.status ? pageState.page : null;
1011
+ const regions = useMemo(()=>page && runtime ? positionEvidenceRegions(page, runtime, viewportRevision) : [], [
1012
+ page,
1013
+ runtime,
1014
+ viewportRevision
1015
+ ]);
1016
+ useEffect(()=>{
1017
+ if (!runtime || !page || !selectedEvidenceRegionId) return;
1018
+ const selected = page.regions.find((region)=>region.id === selectedEvidenceRegionId);
1019
+ if (!selected) return;
1020
+ const navigationKey = `${page.pageNumber}:${selected.id}`;
1021
+ if (lastNavigationRef.current === navigationKey) return;
1022
+ lastNavigationRef.current = navigationKey;
1023
+ scrollEvidenceRegionIntoView(runtime, page, selected);
1024
+ }, [
1025
+ page,
1026
+ runtime,
1027
+ selectedEvidenceRegionId
1028
+ ]);
1029
+ useEffect(()=>{
1030
+ if (!runtime || !selectedEvidenceRegionId || !evidenceOverlay.locateRegion || page?.regions.some((region)=>region.id === selectedEvidenceRegionId)) return;
1031
+ const abort = new AbortController();
1032
+ evidenceOverlay.locateRegion(selectedEvidenceRegionId, abort.signal).then((location)=>{
1033
+ if (!abort.signal.aborted && location && location.pageNumber !== currentPage) scrollToEvidencePage(runtime, location.pageNumber);
1034
+ }).catch(()=>void 0);
1035
+ return ()=>abort.abort();
1036
+ }, [
1037
+ currentPage,
1038
+ evidenceOverlay,
1039
+ page,
1040
+ runtime,
1041
+ selectedEvidenceRegionId
1042
+ ]);
1043
+ if ('error' === pageState.status) return /*#__PURE__*/ jsx("div", {
1044
+ className: "work-pdf-evidence-status",
1045
+ role: "status",
1046
+ title: pageState.message,
1047
+ children: "当前页定位不可用"
1048
+ });
1049
+ if ('ready' === pageState.status && !pageState.page) return /*#__PURE__*/ jsx("div", {
1050
+ className: "work-pdf-evidence-status",
1051
+ role: "status",
1052
+ children: "当前页暂无安全定位"
1053
+ });
1054
+ if (!page || 0 === regions.length) return null;
1055
+ return /*#__PURE__*/ jsx("fieldset", {
1056
+ className: "work-pdf-evidence-overlay",
1057
+ "aria-label": "解析证据定位",
1058
+ children: regions.map(({ region, style })=>{
1059
+ const selected = region.id === selectedEvidenceRegionId;
1060
+ return /*#__PURE__*/ jsx("button", {
1061
+ type: "button",
1062
+ className: "work-pdf-evidence-region",
1063
+ "data-selected": selected || void 0,
1064
+ style: style,
1065
+ "aria-label": region.label ?? `解析区域 ${region.id}`,
1066
+ "aria-pressed": selected,
1067
+ title: region.label,
1068
+ onClick: ()=>onEvidenceRegionSelect?.(region)
1069
+ }, region.id);
1070
+ })
1071
+ });
1072
+ }
1073
+ function readEvidenceRuntime(registry) {
1074
+ const documentManager = registry.getPlugin(DocumentManagerPlugin.id)?.provides();
1075
+ const scroll = registry.getPlugin(ScrollPlugin.id)?.provides();
1076
+ const viewport = registry.getPlugin(ViewportPlugin.id)?.provides();
1077
+ if (!documentManager || !scroll || !viewport) throw new Error('Required PDF evidence capabilities are unavailable.');
1078
+ return {
1079
+ documentManager,
1080
+ scroll,
1081
+ viewport,
1082
+ zoom: registry.getPlugin(ZoomPlugin.id)?.provides() ?? null
1083
+ };
1084
+ }
1085
+ function validateOverlayIdentity(overlay, sourceKey) {
1086
+ if (!sourceKey || sourceKey !== overlay.sourceSha256) return 'The evidence source identity does not match the PDF source.';
1087
+ if (!isSha256(overlay.sourceSha256) || !isSha256(overlay.renderProfileSha256)) return 'The evidence overlay contains an invalid SHA-256 identity.';
1088
+ if (overlay.coordinateBasis !== PDF_EVIDENCE_COORDINATE_BASIS) return 'The evidence overlay uses an unsupported coordinate basis.';
1089
+ return null;
1090
+ }
1091
+ function validatePdfEvidencePage(page, overlay, runtime, requestedPage) {
1092
+ if (page.pageNumber !== requestedPage || page.sourceSha256 !== overlay.sourceSha256 || page.renderProfileSha256 !== overlay.renderProfileSha256 || page.coordinateBasis !== PDF_EVIDENCE_COORDINATE_BASIS || 'source' !== page.canvasKind) throw new Error('The evidence page identity is invalid.');
1093
+ if (!boundedId(page.canvasId) || !positiveFinite(page.canvasWidth) || !positiveFinite(page.canvasHeight)) throw new Error('The evidence canvas identity or dimensions are invalid.');
1094
+ const documentPage = runtime.documentManager.getActiveDocument()?.pages[requestedPage - 1];
1095
+ if (!documentPage) throw new Error('The PDF page is unavailable.');
1096
+ const expectedRotation = 90 * documentPage.rotation;
1097
+ if (page.rotationDegrees !== expectedRotation) throw new Error('The evidence canvas rotation does not match the PDF page.');
1098
+ const rotatedWidth = documentPage.rotation % 2 ? documentPage.size.height : documentPage.size.width;
1099
+ const rotatedHeight = documentPage.rotation % 2 ? documentPage.size.width : documentPage.size.height;
1100
+ const expectedRatio = rotatedWidth / rotatedHeight;
1101
+ const evidenceRatio = page.canvasWidth / page.canvasHeight;
1102
+ if (Math.abs(evidenceRatio / expectedRatio - 1) > PAGE_ASPECT_RATIO_TOLERANCE) throw new Error('The evidence canvas aspect ratio does not match the PDF page.');
1103
+ if (page.regions.length > MAX_EVIDENCE_REGIONS_PER_PAGE) throw new Error('The evidence page exceeds the bounded region limit.');
1104
+ const ids = new Set();
1105
+ for (const region of page.regions){
1106
+ if (!boundedId(region.id) || ids.has(region.id)) throw new Error('The evidence page contains an invalid or duplicate region identity.');
1107
+ ids.add(region.id);
1108
+ if (region.targetIds.length > MAX_REGION_TARGETS || 0 === region.sourceRegionIds.length || region.sourceRegionIds.length > MAX_EVIDENCE_REGIONS_PER_PAGE || region.targetIds.some((id)=>!boundedId(id)) || region.sourceRegionIds.some((id)=>!boundedId(id)) || new Set(region.targetIds).size !== region.targetIds.length || new Set(region.sourceRegionIds).size !== region.sourceRegionIds.length) throw new Error('The evidence region identity receipts are invalid.');
1109
+ validateBounds(region.bounds);
1110
+ }
1111
+ }
1112
+ function validateBounds(bounds) {
1113
+ const values = [
1114
+ bounds.left,
1115
+ bounds.top,
1116
+ bounds.right,
1117
+ bounds.bottom
1118
+ ];
1119
+ if (values.some((value)=>!Number.isInteger(value) || value < 0 || value > PDF_EVIDENCE_COORDINATE_BASIS) || bounds.right <= bounds.left || bounds.bottom <= bounds.top) throw new Error('The evidence region bounds are invalid.');
1120
+ }
1121
+ function positionEvidenceRegions(page, runtime, _revision) {
1122
+ const documentId = runtime.documentManager.getActiveDocumentId();
1123
+ const documentPage = runtime.documentManager.getActiveDocument()?.pages[page.pageNumber - 1];
1124
+ if (!documentId || !documentPage) return [];
1125
+ const scroll = runtime.scroll.forDocument(documentId);
1126
+ const viewport = runtime.viewport.forDocument(documentId);
1127
+ const metrics = viewport.getMetrics();
1128
+ const viewportGap = runtime.viewport.getViewportGap();
1129
+ const zoom = runtime.zoom?.forDocument(documentId).getState().currentZoomLevel ?? 1;
1130
+ const contentWidth = scroll.getLayout().totalContentSize.width;
1131
+ const horizontalCenterOffset = pdfEvidenceHorizontalCenterOffset(metrics.clientWidth, viewportGap, contentWidth, zoom);
1132
+ const width = documentPage.rotation % 2 ? documentPage.size.height : documentPage.size.width;
1133
+ const height = documentPage.rotation % 2 ? documentPage.size.width : documentPage.size.height;
1134
+ return page.regions.flatMap((region)=>{
1135
+ const rect = pdfEvidenceBoundsToPageRect(region.bounds, width, height);
1136
+ const positioned = scroll.getRectPositionForPage(page.pageNumber - 1, rect, void 0, 0);
1137
+ if (!positioned) return [];
1138
+ return [
1139
+ {
1140
+ region,
1141
+ style: {
1142
+ height: positioned.size.height,
1143
+ left: positioned.origin.x + viewportGap - metrics.scrollLeft + horizontalCenterOffset + metrics.clientLeft,
1144
+ top: positioned.origin.y + viewportGap - metrics.scrollTop + metrics.clientTop,
1145
+ width: positioned.size.width
1146
+ }
1147
+ }
1148
+ ];
1149
+ });
1150
+ }
1151
+ function scrollEvidenceRegionIntoView(runtime, page, region) {
1152
+ const documentId = runtime.documentManager.getActiveDocumentId();
1153
+ const documentPage = runtime.documentManager.getActiveDocument()?.pages[page.pageNumber - 1];
1154
+ if (!documentId || !documentPage) return;
1155
+ const width = documentPage.rotation % 2 ? documentPage.size.height : documentPage.size.width;
1156
+ const height = documentPage.rotation % 2 ? documentPage.size.width : documentPage.size.height;
1157
+ const rect = pdfEvidenceBoundsToPageRect(region.bounds, width, height);
1158
+ const rotatedCenter = {
1159
+ x: rect.origin.x + rect.size.width / 2,
1160
+ y: rect.origin.y + rect.size.height / 2
1161
+ };
1162
+ const pageCoordinates = pdfEvidencePointToUnrotatedPage(rotatedCenter, documentPage.size.width, documentPage.size.height, documentPage.rotation);
1163
+ runtime.scroll.forDocument(documentId).scrollToPage({
1164
+ alignX: 50,
1165
+ alignY: 32,
1166
+ behavior: 'auto',
1167
+ pageCoordinates,
1168
+ pageNumber: page.pageNumber
1169
+ });
1170
+ }
1171
+ function scrollToEvidencePage(runtime, pageNumber) {
1172
+ const documentId = runtime.documentManager.getActiveDocumentId();
1173
+ if (!documentId || !Number.isInteger(pageNumber) || pageNumber < 1) return;
1174
+ const scroll = runtime.scroll.forDocument(documentId);
1175
+ if (pageNumber > scroll.getTotalPages()) return;
1176
+ scroll.scrollToPage({
1177
+ behavior: 'auto',
1178
+ pageNumber
1179
+ });
1180
+ }
1181
+ function pdfEvidenceBoundsToPageRect(bounds, width, height) {
1182
+ return {
1183
+ origin: {
1184
+ x: bounds.left / PDF_EVIDENCE_COORDINATE_BASIS * width,
1185
+ y: bounds.top / PDF_EVIDENCE_COORDINATE_BASIS * height
1186
+ },
1187
+ size: {
1188
+ height: (bounds.bottom - bounds.top) / PDF_EVIDENCE_COORDINATE_BASIS * height,
1189
+ width: (bounds.right - bounds.left) / PDF_EVIDENCE_COORDINATE_BASIS * width
1190
+ }
1191
+ };
1192
+ }
1193
+ function pdfEvidencePointToUnrotatedPage(point, pageWidth, pageHeight, rotation) {
1194
+ switch(rotation){
1195
+ case 1:
1196
+ return {
1197
+ x: point.y,
1198
+ y: pageHeight - point.x
1199
+ };
1200
+ case 2:
1201
+ return {
1202
+ x: pageWidth - point.x,
1203
+ y: pageHeight - point.y
1204
+ };
1205
+ case 3:
1206
+ return {
1207
+ x: pageWidth - point.y,
1208
+ y: point.x
1209
+ };
1210
+ default:
1211
+ return point;
1212
+ }
1213
+ }
1214
+ function pdfEvidenceHorizontalCenterOffset(viewportClientWidth, viewportGap, contentWidth, zoom) {
1215
+ const availableWidth = viewportClientWidth - 2 * viewportGap;
1216
+ const renderedContentWidth = contentWidth * zoom;
1217
+ return Math.max(0, (availableWidth - renderedContentWidth) / 2);
1218
+ }
1219
+ function boundedId(value) {
1220
+ return value.length > 0 && value.length <= MAX_ID_LENGTH;
1221
+ }
1222
+ function positiveFinite(value) {
1223
+ return Number.isFinite(value) && value > 0;
1224
+ }
1225
+ function isSha256(value) {
1226
+ return /^[a-f0-9]{64}$/.test(value);
1227
+ }
1228
+ function evidenceErrorMessage(error) {
1229
+ return error instanceof Error && error.message ? error.message : 'Unable to load PDF evidence.';
1230
+ }
409
1231
  const PDF_THUMBNAIL_ITEM_HEIGHT = 190;
410
1232
  const PDF_THUMBNAIL_WINDOW_THRESHOLD = 48;
411
1233
  const PDF_THUMBNAIL_OVERSCAN = 5;
@@ -701,7 +1523,8 @@ const pdfKeyboardShortcuts = {
701
1523
  zoomIn: 'Control+= Meta+= Control+Shift++ Meta+Shift++',
702
1524
  zoomOut: 'Control+- Meta+-'
703
1525
  };
704
- function PdfToolbar({ annotationState, can, commands, editable, pageNavigation, saveLabel, saveState, searchInputRef, state }) {
1526
+ function PdfToolbar({ annotationState, can, commands, editable, pageNavigation, saveAvailable, saveLabel, saveState, searchInputRef, state }) {
1527
+ const showSave = saveAvailable ?? editable;
705
1528
  const [pageValue, setPageValue] = useState('');
706
1529
  const [searchValue, setSearchValue] = useState('');
707
1530
  const cancelPageBlurCommitRef = useRef(false);
@@ -736,7 +1559,7 @@ function PdfToolbar({ annotationState, can, commands, editable, pageNavigation,
736
1559
  role: "toolbar",
737
1560
  "aria-label": "PDF 工具栏",
738
1561
  children: [
739
- editable && /*#__PURE__*/ jsxs("div", {
1562
+ showSave && /*#__PURE__*/ jsxs("div", {
740
1563
  className: "work-pdf-toolbar-group work-pdf-save",
741
1564
  children: [
742
1565
  /*#__PURE__*/ jsxs("output", {
@@ -1106,6 +1929,9 @@ function PdfToolbar({ annotationState, can, commands, editable, pageNavigation,
1106
1929
  children: "页宽"
1107
1930
  })
1108
1931
  ]
1932
+ }),
1933
+ /*#__PURE__*/ jsx(WorkOfficeCollaborationParticipants, {
1934
+ variant: "toolbar"
1109
1935
  })
1110
1936
  ]
1111
1937
  });
@@ -1534,6 +2360,7 @@ function searchStatus(state) {
1534
2360
  if (0 === search.total) return '0 / 0';
1535
2361
  return `${search.activeResultIndex + 1} / ${search.total}`;
1536
2362
  }
2363
+ const PDF_PAGE_NAVIGATION_RETRY_MS = 500;
1537
2364
  const EMPTY_FEATURES = {
1538
2365
  export: false,
1539
2366
  history: false,
@@ -1562,15 +2389,20 @@ const initialPdfViewerControllerState = {
1562
2389
  zoomMode: null,
1563
2390
  zoomPercent: 100
1564
2391
  };
1565
- function usePdfViewerController(registry) {
2392
+ function usePdfViewerController(registry, historyOverride) {
1566
2393
  const capabilitiesRef = useRef(null);
1567
- const pendingPageRef = useRef(null);
2394
+ const pendingPageRef = useRef({
2395
+ page: null,
2396
+ retryTimer: null
2397
+ });
1568
2398
  const [state, setState] = useState(initialPdfViewerControllerState);
2399
+ const historyOverrideRef = useRef(historyOverride);
2400
+ historyOverrideRef.current = historyOverride;
1569
2401
  useEffect(()=>{
1570
2402
  let disposed = false;
1571
2403
  const unsubscribe = [];
1572
2404
  capabilitiesRef.current = null;
1573
- pendingPageRef.current = null;
2405
+ clearPendingPdfPageNavigation(pendingPageRef);
1574
2406
  setState(initialPdfViewerControllerState);
1575
2407
  if (!registry) return;
1576
2408
  registry.pluginsReady().then(()=>{
@@ -1581,7 +2413,7 @@ function usePdfViewerController(registry) {
1581
2413
  if (!disposed) setState(readControllerState(capabilities));
1582
2414
  };
1583
2415
  const syncDocument = ()=>{
1584
- pendingPageRef.current = null;
2416
+ clearPendingPdfPageNavigation(pendingPageRef);
1585
2417
  sync();
1586
2418
  };
1587
2419
  pdf_viewer_controller_subscribe(unsubscribe, capabilities.documentManager?.onActiveDocumentChanged, syncDocument);
@@ -1591,7 +2423,7 @@ function usePdfViewerController(registry) {
1591
2423
  pdf_viewer_controller_subscribe(unsubscribe, capabilities.scroll?.onLayoutReady, sync);
1592
2424
  const stopPageChangeState = pdf_viewer_controller_safely(()=>capabilities.scroll?.onPageChangeState((event)=>{
1593
2425
  if (disposed || event.state.isChanging) return;
1594
- const pendingPage = pendingPageRef.current;
2426
+ const pendingPage = pendingPageRef.current.page;
1595
2427
  if (null === pendingPage) return;
1596
2428
  withActiveDocument(capabilities, ({ scroll }, documentId)=>{
1597
2429
  if (event.documentId !== documentId) return;
@@ -1614,7 +2446,7 @@ function usePdfViewerController(registry) {
1614
2446
  return ()=>{
1615
2447
  disposed = true;
1616
2448
  capabilitiesRef.current = null;
1617
- pendingPageRef.current = null;
2449
+ clearPendingPdfPageNavigation(pendingPageRef);
1618
2450
  for (const stop of unsubscribe)stop();
1619
2451
  };
1620
2452
  }, [
@@ -1708,11 +2540,15 @@ function usePdfViewerController(registry) {
1708
2540
  });
1709
2541
  }, []);
1710
2542
  const undo = useCallback(()=>{
2543
+ const collaborationHistory = historyOverrideRef.current;
2544
+ if (collaborationHistory) return void collaborationHistory.undo();
1711
2545
  withActiveDocument(capabilitiesRef.current, ({ history }, documentId)=>{
1712
2546
  history?.forDocument(documentId).undo();
1713
2547
  });
1714
2548
  }, []);
1715
2549
  const redo = useCallback(()=>{
2550
+ const collaborationHistory = historyOverrideRef.current;
2551
+ if (collaborationHistory) return void collaborationHistory.redo();
1716
2552
  withActiveDocument(capabilitiesRef.current, ({ history }, documentId)=>{
1717
2553
  history?.forDocument(documentId).redo();
1718
2554
  });
@@ -1728,8 +2564,20 @@ function usePdfViewerController(registry) {
1728
2564
  type: 'application/pdf'
1729
2565
  });
1730
2566
  }, []);
2567
+ const projectedState = useMemo(()=>historyOverride ? {
2568
+ ...state,
2569
+ canRedo: historyOverride.canRedo,
2570
+ canUndo: historyOverride.canUndo,
2571
+ features: {
2572
+ ...state.features,
2573
+ history: true
2574
+ }
2575
+ } : state, [
2576
+ historyOverride,
2577
+ state
2578
+ ]);
1731
2579
  return useMemo(()=>({
1732
- state,
2580
+ state: projectedState,
1733
2581
  clearSearch,
1734
2582
  fitPage,
1735
2583
  fitWidth,
@@ -1745,7 +2593,7 @@ function usePdfViewerController(registry) {
1745
2593
  zoomIn,
1746
2594
  zoomOut
1747
2595
  }), [
1748
- state,
2596
+ projectedState,
1749
2597
  clearSearch,
1750
2598
  fitPage,
1751
2599
  fitWidth,
@@ -1821,21 +2669,31 @@ function withActiveDocument(capabilities, action) {
1821
2669
  if (!capabilities || !documentId) return;
1822
2670
  pdf_viewer_controller_safely(()=>action(capabilities, documentId));
1823
2671
  }
1824
- function requestPdfPageNavigation(scope, page, pendingPage) {
2672
+ function requestPdfPageNavigation(scope, page, pendingPage, force = false) {
1825
2673
  const totalPages = scope.getTotalPages();
1826
2674
  if (totalPages < 1 || !Number.isFinite(page)) return;
1827
2675
  const nextPage = Math.min(totalPages, Math.max(1, Math.round(page)));
1828
2676
  const pageChangeInProgress = pdf_viewer_controller_safely(()=>scope.getPageChangeState().isChanging) ?? false;
1829
- if (pageChangeInProgress) {
1830
- pendingPage.current = nextPage;
2677
+ if (pageChangeInProgress && !force) {
2678
+ pendingPage.current.page = nextPage;
2679
+ pendingPage.current.retryTimer ??= setTimeout(()=>{
2680
+ const queuedPage = pendingPage.current.page;
2681
+ if (null === queuedPage) return;
2682
+ requestPdfPageNavigation(scope, queuedPage, pendingPage, true);
2683
+ }, PDF_PAGE_NAVIGATION_RETRY_MS);
1831
2684
  return;
1832
2685
  }
1833
- pendingPage.current = null;
2686
+ clearPendingPdfPageNavigation(pendingPage);
1834
2687
  scope.scrollToPage({
1835
2688
  pageNumber: nextPage,
1836
- behavior: 'auto'
2689
+ behavior: force ? 'instant' : 'auto'
1837
2690
  });
1838
2691
  }
2692
+ function clearPendingPdfPageNavigation(pendingPage) {
2693
+ if (null !== pendingPage.current.retryTimer) clearTimeout(pendingPage.current.retryTimer);
2694
+ pendingPage.current.page = null;
2695
+ pendingPage.current.retryTimer = null;
2696
+ }
1839
2697
  function pdf_viewer_controller_subscribe(unsubscribe, event, listener) {
1840
2698
  if (!event) return;
1841
2699
  const stop = pdf_viewer_controller_safely(()=>event(()=>listener()));
@@ -1864,23 +2722,34 @@ const a3sPdfUiSchema = {
1864
2722
  overlays: {},
1865
2723
  selectionMenus: {}
1866
2724
  };
1867
- function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel = '保存', sourceKey, wasmUrl }) {
2725
+ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf', loadSource, onCollaborationChange, onEvidenceRegionSelect, onPageChange, onSave, saveLabel = '保存', selectedEvidenceRegionId, sourceKey, wasmUrl, worker = true }) {
1868
2726
  const [sourceUrl, setSourceUrl] = useState(null);
1869
2727
  const [loadError, setLoadError] = useState(null);
1870
2728
  const [saveState, setSaveState] = useState('idle');
1871
2729
  const [retryCount, setRetryCount] = useState(0);
1872
2730
  const [registry, setRegistry] = useState(null);
2731
+ const [, refreshCollaborationHistory] = useState(0);
1873
2732
  const [mobilePageNavigationOpen, setMobilePageNavigationOpen] = useState(false);
1874
2733
  const pdfRootRef = useRef(null);
1875
2734
  const searchInputRef = useRef(null);
1876
2735
  const mobilePageNavigationId = useId();
1877
2736
  const mobilePageNavigationToggleRef = useRef(null);
1878
2737
  const mobilePageNavigationCloseRef = useRef(null);
1879
- const controller = usePdfViewerController(registry);
2738
+ const collaborationProjectionRef = useRef(void 0);
2739
+ const collaborationHistory = collaboration ? {
2740
+ canRedo: collaborationProjectionRef.current?.binding.canRedo() ?? false,
2741
+ canUndo: collaborationProjectionRef.current?.binding.canUndo() ?? false,
2742
+ redo: ()=>collaborationProjectionRef.current?.binding.redo(),
2743
+ undo: ()=>collaborationProjectionRef.current?.binding.undo()
2744
+ } : void 0;
2745
+ const controller = usePdfViewerController(registry, collaborationHistory);
1880
2746
  const annotation = usePdfAnnotationController(registry);
1881
2747
  const viewerReady = controller.state.ready && controller.state.documentOpen;
1882
2748
  const mobilePageNavigationModal = usePdfMobilePageNavigationModal();
1883
2749
  const mobilePageNavigationModalOpen = mobilePageNavigationOpen && mobilePageNavigationModal;
2750
+ const editable = collaboration ? 'edit' === collaboration.mode : Boolean(onSave);
2751
+ const collaborationRef = useRef(collaboration);
2752
+ if (collaborationRef.current !== collaboration) throw new Error('PdfViewer collaboration sessions cannot be replaced while mounted. Remount the viewer for another shared PDF.');
1884
2753
  const closeMobilePageNavigation = useCallback(()=>{
1885
2754
  setMobilePageNavigationOpen(false);
1886
2755
  }, []);
@@ -1902,7 +2771,9 @@ function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel =
1902
2771
  setSourceUrl(null);
1903
2772
  setLoadError(null);
1904
2773
  setMobilePageNavigationOpen(false);
1905
- loadSource().then((source)=>{
2774
+ loadSource().then(async (source)=>{
2775
+ if (disposed) return;
2776
+ if (collaboration) await assertPdfCollaborationBlob(collaboration, source);
1906
2777
  if (disposed) return;
1907
2778
  objectUrl = URL.createObjectURL('application/pdf' === source.type ? source : new Blob([
1908
2779
  source
@@ -1918,6 +2789,7 @@ function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel =
1918
2789
  if (objectUrl) URL.revokeObjectURL(objectUrl);
1919
2790
  };
1920
2791
  }, [
2792
+ collaboration,
1921
2793
  loadSource,
1922
2794
  retryCount,
1923
2795
  sourceKey
@@ -1927,6 +2799,69 @@ function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel =
1927
2799
  }, [
1928
2800
  controller.state.error
1929
2801
  ]);
2802
+ useEffect(()=>{
2803
+ if (!collaboration || !viewerReady) return;
2804
+ if (controller.state.totalPages < 1) return;
2805
+ const expectedPages = readWorkPdfSourcePageCount(collaboration);
2806
+ if (controller.state.totalPages !== expectedPages) {
2807
+ setRegistry(null);
2808
+ setLoadError(`The loaded PDF has ${controller.state.totalPages} pages, but collaboration artifact '${collaboration.artifactId}' requires ${expectedPages}.`);
2809
+ }
2810
+ }, [
2811
+ collaboration,
2812
+ controller.state.totalPages,
2813
+ viewerReady
2814
+ ]);
2815
+ useEffect(()=>{
2816
+ if (!registry || !collaboration || !viewerReady) return;
2817
+ if (controller.state.totalPages < 1) return;
2818
+ const expectedPages = readWorkPdfSourcePageCount(collaboration);
2819
+ if (controller.state.totalPages !== expectedPages) return;
2820
+ const projection = createWorkPdfCollaborationProjection(registry, collaboration);
2821
+ collaborationProjectionRef.current = projection;
2822
+ const handleProjectionError = (error)=>{
2823
+ if (collaborationProjectionRef.current !== projection) return;
2824
+ collaborationProjectionRef.current = void 0;
2825
+ setRegistry(null);
2826
+ setLoadError(pdfErrorMessage(error));
2827
+ };
2828
+ const unsubscribeError = projection.subscribeError(handleProjectionError);
2829
+ projection.ready.catch(handleProjectionError);
2830
+ const unsubscribeHistory = projection.binding.subscribeHistory(()=>refreshCollaborationHistory((value)=>value + 1));
2831
+ refreshCollaborationHistory((value)=>value + 1);
2832
+ return ()=>{
2833
+ unsubscribeError();
2834
+ unsubscribeHistory();
2835
+ if (collaborationProjectionRef.current === projection) collaborationProjectionRef.current = void 0;
2836
+ projection.destroy();
2837
+ };
2838
+ }, [
2839
+ collaboration,
2840
+ controller.state.totalPages,
2841
+ registry,
2842
+ viewerReady
2843
+ ]);
2844
+ useEffect(()=>{
2845
+ if (!collaboration || !onCollaborationChange) return;
2846
+ const handleUpdate = ()=>{
2847
+ const projection = collaborationProjectionRef.current;
2848
+ if (!projection) return;
2849
+ queueMicrotask(()=>{
2850
+ if (collaborationProjectionRef.current === projection) onCollaborationChange(projection.binding.content());
2851
+ });
2852
+ };
2853
+ collaboration.document.on('update', handleUpdate);
2854
+ return ()=>collaboration.document.off('update', handleUpdate);
2855
+ }, [
2856
+ collaboration,
2857
+ onCollaborationChange
2858
+ ]);
2859
+ useEffect(()=>{
2860
+ if (controller.state.currentPage > 0) onPageChange?.(controller.state.currentPage);
2861
+ }, [
2862
+ controller.state.currentPage,
2863
+ onPageChange
2864
+ ]);
1930
2865
  useEffect(()=>{
1931
2866
  if (!sourceUrl || viewerReady || loadError) return;
1932
2867
  const timeout = window.setTimeout(()=>{
@@ -1956,7 +2891,7 @@ function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel =
1956
2891
  const pdfExtensions = useMemo(createPdfEditorExtensions, []);
1957
2892
  const pdfEditor = useOfficeEditorRuntime({
1958
2893
  annotation,
1959
- editable: Boolean(onSave),
2894
+ editable,
1960
2895
  focusSearch: ()=>{
1961
2896
  searchInputRef.current?.focus();
1962
2897
  searchInputRef.current?.select();
@@ -1968,6 +2903,33 @@ function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel =
1968
2903
  viewer: controller
1969
2904
  }, pdfExtensions);
1970
2905
  const pdfCommands = pdfEditor.commands;
2906
+ const selectedAnnotationLocation = annotation.getSelectionLocation();
2907
+ const pdfPresenceLocation = viewerReady && controller.state.currentPage > 0 ? {
2908
+ kind: 'pdf',
2909
+ pageIndex: selectedAnnotationLocation?.pageIndex ?? controller.state.currentPage - 1,
2910
+ ...selectedAnnotationLocation ? {
2911
+ annotationId: selectedAnnotationLocation.annotationId
2912
+ } : {}
2913
+ } : null;
2914
+ useOfficePublishPresenceLocation(pdfPresenceLocation);
2915
+ const navigateToPdfParticipant = useCallback((participant)=>{
2916
+ const location = participant.location;
2917
+ if (location?.kind !== 'pdf' || !viewerReady || location.pageIndex >= controller.state.totalPages) return false;
2918
+ controller.goToPage(location.pageIndex + 1);
2919
+ if (location.annotationId) annotation.locateAnnotation(location.pageIndex, location.annotationId);
2920
+ requestAnimationFrame(()=>{
2921
+ const target = pdfRootRef.current?.querySelector('.work-pdf-embed');
2922
+ target?.focus({
2923
+ preventScroll: true
2924
+ });
2925
+ });
2926
+ return true;
2927
+ }, [
2928
+ annotation,
2929
+ controller,
2930
+ viewerReady
2931
+ ]);
2932
+ useOfficeCollaborationLocationNavigator(navigateToPdfParticipant);
1971
2933
  useOfficeEditorKeyboardShortcuts(pdfEditor, {
1972
2934
  capture: true,
1973
2935
  enabled: Boolean(sourceUrl),
@@ -2012,7 +2974,8 @@ function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel =
2012
2974
  annotationState: annotation.state,
2013
2975
  can: pdfEditor.can(),
2014
2976
  commands: pdfCommands,
2015
- editable: Boolean(onSave),
2977
+ editable: editable,
2978
+ saveAvailable: editable && Boolean(onSave),
2016
2979
  pageNavigation: viewerReady && registry && controller.state.totalPages > 0 ? {
2017
2980
  controlsId: mobilePageNavigationId,
2018
2981
  expanded: mobilePageNavigationOpen,
@@ -2050,8 +3013,11 @@ function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel =
2050
3013
  }),
2051
3014
  /*#__PURE__*/ jsxs("div", {
2052
3015
  className: "work-pdf-embed",
3016
+ role: "application",
3017
+ "aria-label": "PDF 页面画布",
2053
3018
  "aria-busy": !viewerReady,
2054
3019
  "data-ready": viewerReady || void 0,
3020
+ tabIndex: 0,
2055
3021
  children: [
2056
3022
  /*#__PURE__*/ jsx(PDFViewer, {
2057
3023
  className: "work-pdf-native-viewer",
@@ -2061,6 +3027,7 @@ function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel =
2061
3027
  },
2062
3028
  config: {
2063
3029
  src: sourceUrl,
3030
+ worker,
2064
3031
  wasmUrl: wasmUrl ?? new URL(PDFIUM_WASM_PATH, window.location.href).href,
2065
3032
  tabBar: 'never',
2066
3033
  theme: {
@@ -2102,7 +3069,7 @@ function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel =
2102
3069
  ui: null,
2103
3070
  signature: null
2104
3071
  },
2105
- disabledCategories: onSave ? void 0 : [
3072
+ disabledCategories: editable ? void 0 : [
2106
3073
  'annotation',
2107
3074
  'redaction',
2108
3075
  'form',
@@ -2111,6 +3078,17 @@ function PdfViewer({ fileName = 'document.pdf', loadSource, onSave, saveLabel =
2111
3078
  },
2112
3079
  onReady: setRegistry
2113
3080
  }, sourceUrl),
3081
+ viewerReady && controller.state.currentPage > 0 && /*#__PURE__*/ jsx(PdfCollaborationPresenceLayer, {
3082
+ pageIndex: controller.state.currentPage - 1
3083
+ }),
3084
+ evidenceOverlay && registry && viewerReady && /*#__PURE__*/ jsx(PdfEvidenceOverlayLayer, {
3085
+ currentPage: controller.state.currentPage,
3086
+ evidenceOverlay: evidenceOverlay,
3087
+ onEvidenceRegionSelect: onEvidenceRegionSelect,
3088
+ registry: registry,
3089
+ selectedEvidenceRegionId: selectedEvidenceRegionId,
3090
+ sourceKey: sourceKey
3091
+ }),
2114
3092
  !viewerReady && /*#__PURE__*/ jsxs("div", {
2115
3093
  className: "work-pdf-loading",
2116
3094
  role: "status",
@@ -2133,6 +3111,20 @@ function pdfErrorMessage(error) {
2133
3111
  if (error instanceof Error && error.message) return error.message;
2134
3112
  return 'Unable to read this PDF file.';
2135
3113
  }
3114
+ async function assertPdfCollaborationBlob(session, source) {
3115
+ const expected = readWorkOfficePdfCollaborationSource(session);
3116
+ if (source.size !== expected.byteLength) throw new Error(`The loaded PDF source does not match collaboration artifact '${session.artifactId}'.`);
3117
+ const subtle = globalThis.crypto?.subtle;
3118
+ if (!subtle) throw new Error('SHA-256 PDF source verification is unavailable.');
3119
+ const digest = new Uint8Array(await subtle.digest('SHA-256', await source.arrayBuffer()));
3120
+ assertWorkOfficePdfCollaborationSource(session, {
3121
+ ...expected,
3122
+ sha256: Array.from(digest, (value)=>value.toString(16).padStart(2, '0')).join('')
3123
+ });
3124
+ }
3125
+ function readWorkPdfSourcePageCount(session) {
3126
+ return readWorkOfficePdfCollaborationSource(session).pageCount;
3127
+ }
2136
3128
  function usePdfMobilePageNavigationModal() {
2137
3129
  const [matches, setMatches] = useState(()=>pdfMediaQueryMatches(PDF_MOBILE_PAGE_NAVIGATION_QUERY));
2138
3130
  useEffect(()=>{