@a3s-lab/office 0.7.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@ import { Fragment as external_react_Fragment, forwardRef, useCallback, useEffect
6
6
  import { Extension, createNodeFromContent } from "@tiptap/core";
7
7
  import { NodeSelection, Plugin, PluginKey, TextSelection } from "@tiptap/pm/state";
8
8
  import { Decoration, DecorationSet } from "@tiptap/pm/view";
9
- import { AArrowDown, AArrowUp, 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";
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, Unlink2, X, XCircle, ZoomIn, ZoomOut } from "lucide-react";
10
10
  import { Node as model_Node } from "@tiptap/pm/model";
11
11
  import extension_color from "@tiptap/extension-color";
12
12
  import { TableKit } from "@tiptap/extension-table";
@@ -1608,7 +1608,7 @@ function optionalValue(value) {
1608
1608
  function sameSourceDraft(left, right) {
1609
1609
  return Object.keys(left).every((key)=>left[key] === right[key]);
1610
1610
  }
1611
- const document_comment_composer_DocumentCommentComposer = /*#__PURE__*/ forwardRef(function({ draft, top, onCancel, onDirtyChange, onSubmit }, forwardedRef) {
1611
+ const document_comment_composer_DocumentCommentComposer = /*#__PURE__*/ forwardRef(function({ author = '我', draft, top, onCancel, onDirtyChange, onSubmit }, forwardedRef) {
1612
1612
  const titleId = useId();
1613
1613
  const inputRef = useRef(null);
1614
1614
  const [text, setText] = useState('');
@@ -1660,7 +1660,8 @@ const document_comment_composer_DocumentCommentComposer = /*#__PURE__*/ forwardR
1660
1660
  children: [
1661
1661
  /*#__PURE__*/ jsx("span", {
1662
1662
  className: "work-document-comment-avatar",
1663
- children: "我"
1663
+ title: author,
1664
+ children: commentAuthorInitials(author)
1664
1665
  }),
1665
1666
  /*#__PURE__*/ jsxs("span", {
1666
1667
  className: "work-document-comment-composer-heading",
@@ -1669,9 +1670,13 @@ const document_comment_composer_DocumentCommentComposer = /*#__PURE__*/ forwardR
1669
1670
  id: titleId,
1670
1671
  children: "添加批注"
1671
1672
  }),
1672
- /*#__PURE__*/ jsx("span", {
1673
+ /*#__PURE__*/ jsxs("span", {
1673
1674
  title: draft.anchorText,
1674
- children: draft.anchorText
1675
+ children: [
1676
+ author,
1677
+ " \xb7 ",
1678
+ draft.anchorText
1679
+ ]
1675
1680
  })
1676
1681
  ]
1677
1682
  })
@@ -1716,6 +1721,12 @@ const document_comment_composer_DocumentCommentComposer = /*#__PURE__*/ forwardR
1716
1721
  ]
1717
1722
  });
1718
1723
  });
1724
+ function commentAuthorInitials(author) {
1725
+ const words = author.trim().split(/\s+/).filter(Boolean);
1726
+ if (!words.length) return '审';
1727
+ if (1 === words.length) return Array.from(words[0]).slice(0, 1).join('');
1728
+ return words.slice(0, 2).map((word)=>Array.from(word)[0]).join('').toLocaleUpperCase();
1729
+ }
1719
1730
  const DOCUMENT_COMMENT_WINDOW_THRESHOLD = 48;
1720
1731
  const DOCUMENT_COMMENT_WINDOW_LIMIT = 32;
1721
1732
  const DOCUMENT_COMMENT_WINDOW_OVERSCAN = 4;
@@ -1907,7 +1918,7 @@ const emptyLayout = {
1907
1918
  trackHeight: 1,
1908
1919
  items: []
1909
1920
  };
1910
- function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, onToggleResolved, onDelete, onCancelDraft, onSubmitDraft, onClose, onDirtyChange }) {
1921
+ function DocumentCommentsPanel({ editor, comments, canDelete = canDeleteAnyComment, draft, draftAuthor = '我', surfaceRef, onReply, onToggleResolved, onDelete, onCancelDraft, onSubmitDraft, onClose, onDirtyChange }) {
1911
1922
  const [drafts, setDrafts] = useState({});
1912
1923
  const [activeCommentId, setActiveCommentId] = useState(()=>comments.find((comment)=>!comment.resolved)?.id ?? comments[0]?.id ?? null);
1913
1924
  const [draftDirty, setDraftDirty] = useState(false);
@@ -1974,7 +1985,7 @@ function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, o
1974
1985
  ])), [
1975
1986
  layout.items
1976
1987
  ]);
1977
- const visibleConnectorItems = useMemo(()=>layout.items.filter((item)=>'draft' === item.kind || mountedCommentIds.has(item.id)), [
1988
+ const visibleConnectorItems = useMemo(()=>layout.items.filter((item)=>'draft' === item.kind || mountedCommentIds.has(item.id) && !item.detached), [
1978
1989
  layout.items,
1979
1990
  mountedCommentIds
1980
1991
  ]);
@@ -2025,6 +2036,7 @@ function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, o
2025
2036
  onClose();
2026
2037
  };
2027
2038
  const deleteComment = async (commentId, index)=>{
2039
+ if (!canDelete(commentId)) return;
2028
2040
  const replyDirty = Boolean(drafts[commentId]?.trim());
2029
2041
  const confirmed = await officeDialog.confirm({
2030
2042
  title: '删除批注?',
@@ -2094,7 +2106,8 @@ function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, o
2094
2106
  id: comment.id,
2095
2107
  kind: 'comment',
2096
2108
  commentId: comment.id,
2097
- from: comment.from,
2109
+ from: comment.from ?? Number.MAX_SAFE_INTEGER,
2110
+ detached: comment.detached,
2098
2111
  anchorRect: anchorRectsById.get(comment.id)
2099
2112
  }));
2100
2113
  if (draft) {
@@ -2139,7 +2152,8 @@ function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, o
2139
2152
  startX,
2140
2153
  startY,
2141
2154
  endX,
2142
- endY
2155
+ endY,
2156
+ detached: entry.detached
2143
2157
  };
2144
2158
  });
2145
2159
  const nextLayout = {
@@ -2392,6 +2406,7 @@ function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, o
2392
2406
  draft && /*#__PURE__*/ jsx("li", {
2393
2407
  className: "work-document-comment-draft-item",
2394
2408
  children: /*#__PURE__*/ jsx(document_comment_composer_DocumentCommentComposer, {
2409
+ author: draftAuthor,
2395
2410
  ref: (element)=>{
2396
2411
  const id = `draft:${draft.id}`;
2397
2412
  if (element) cardRefs.current.set(id, element);
@@ -2416,8 +2431,9 @@ function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, o
2416
2431
  if (element) cardRefs.current.set(comment.id, element);
2417
2432
  else cardRefs.current.delete(comment.id);
2418
2433
  },
2419
- className: `work-document-comment-card${comment.resolved ? ' resolved' : ''}${active ? ' active' : ''}`,
2434
+ className: `work-document-comment-card${comment.resolved ? ' resolved' : ''}${comment.detached ? ' detached' : ''}${active ? ' active' : ''}`,
2420
2435
  "data-comment-id": comment.id,
2436
+ "data-document-comment-detached": comment.detached ? 'true' : 'false',
2421
2437
  "data-document-comment-card-top": Math.round(item?.cardTop ?? 8),
2422
2438
  "data-document-comment-item": index + 1,
2423
2439
  style: {
@@ -2433,18 +2449,19 @@ function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, o
2433
2449
  },
2434
2450
  type: "button",
2435
2451
  className: "work-document-comment-anchor",
2436
- "aria-label": `定位批注 ${index + 1}`,
2452
+ "aria-label": `${comment.detached ? '查看已脱离正文的' : '定位'}批注 ${index + 1}`,
2437
2453
  "aria-current": active ? 'location' : void 0,
2438
2454
  tabIndex: commentWindow.rovingIndex === index ? 0 : -1,
2439
2455
  onKeyDown: (event)=>handleCommentAnchorKeyDown(event, index),
2440
2456
  onClick: ()=>{
2441
2457
  setActiveCommentId(comment.id);
2442
- editor.chain().focus().setTextSelection(documentCommentSelection(editor, comment)).run();
2458
+ const selection = documentCommentSelection(editor, comment);
2459
+ if (selection) editor.chain().focus().setTextSelection(selection).run();
2443
2460
  },
2444
2461
  children: [
2445
2462
  /*#__PURE__*/ jsx("span", {
2446
2463
  className: "work-document-comment-avatar",
2447
- children: commentAuthorInitials(comment.author)
2464
+ children: document_comments_panel_commentAuthorInitials(comment.author)
2448
2465
  }),
2449
2466
  /*#__PURE__*/ jsxs("span", {
2450
2467
  className: "work-document-comment-meta",
@@ -2460,7 +2477,15 @@ function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, o
2460
2477
  }),
2461
2478
  /*#__PURE__*/ jsx("span", {
2462
2479
  className: "work-document-comment-quote",
2463
- children: comment.anchorText.trim() || '(空白字符)'
2480
+ children: comment.detached ? /*#__PURE__*/ jsxs(Fragment, {
2481
+ children: [
2482
+ /*#__PURE__*/ jsx(Unlink2, {
2483
+ size: 10,
2484
+ "aria-hidden": "true"
2485
+ }),
2486
+ "原文锚点已删除"
2487
+ ]
2488
+ }) : comment.anchorText.trim() || '(空白字符)'
2464
2489
  })
2465
2490
  ]
2466
2491
  }),
@@ -2543,6 +2568,8 @@ function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, o
2543
2568
  size: "compact",
2544
2569
  tone: "danger",
2545
2570
  "aria-label": `删除批注 ${index + 1}`,
2571
+ disabled: !canDelete(comment.id),
2572
+ title: canDelete(comment.id) ? '删除批注' : '批注模式只能删除自己创建的批注',
2546
2573
  onClick: ()=>void deleteComment(comment.id, index),
2547
2574
  children: [
2548
2575
  /*#__PURE__*/ jsx(Trash2, {
@@ -2572,6 +2599,9 @@ function DocumentCommentsPanel({ editor, comments, draft, surfaceRef, onReply, o
2572
2599
  ]
2573
2600
  });
2574
2601
  }
2602
+ function canDeleteAnyComment() {
2603
+ return true;
2604
+ }
2575
2605
  function draftCommentAnchorRect(editor, range) {
2576
2606
  try {
2577
2607
  const position = Math.max(0, Math.min(editor.state.doc.content.size, range.to));
@@ -2589,7 +2619,7 @@ function sameCommentTrackLayout(current, next) {
2589
2619
  if (Math.abs(current.width - next.width) > 0.5 || Math.abs(current.height - next.height) > 0.5 || Math.abs(current.trackHeight - next.trackHeight) > 0.5 || current.items.length !== next.items.length) return false;
2590
2620
  return current.items.every((item, index)=>{
2591
2621
  const candidate = next.items[index];
2592
- return candidate?.id === item.id && candidate.kind === item.kind && candidate.commentId === item.commentId && Math.abs(candidate.cardTop - item.cardTop) <= 0.5 && Math.abs(candidate.startX - item.startX) <= 0.5 && Math.abs(candidate.startY - item.startY) <= 0.5 && Math.abs(candidate.endX - item.endX) <= 0.5 && Math.abs(candidate.endY - item.endY) <= 0.5;
2622
+ return candidate?.id === item.id && candidate.kind === item.kind && candidate.commentId === item.commentId && candidate.detached === item.detached && Math.abs(candidate.cardTop - item.cardTop) <= 0.5 && Math.abs(candidate.startX - item.startX) <= 0.5 && Math.abs(candidate.startY - item.startY) <= 0.5 && Math.abs(candidate.endX - item.endX) <= 0.5 && Math.abs(candidate.endY - item.endY) <= 0.5;
2593
2623
  });
2594
2624
  }
2595
2625
  function commentCardMeasurementKey(comment, active) {
@@ -2603,9 +2633,11 @@ function estimateCommentCardHeight(comment, active) {
2603
2633
  }
2604
2634
  function selectDocumentComment(editor, comment) {
2605
2635
  if (editor.isDestroyed) return;
2606
- editor.commands.setTextSelection(documentCommentSelection(editor, comment));
2636
+ const selection = documentCommentSelection(editor, comment);
2637
+ if (selection) editor.commands.setTextSelection(selection);
2607
2638
  }
2608
2639
  function documentCommentSelection(editor, comment) {
2640
+ if (comment.detached || null === comment.from || null === comment.to) return null;
2609
2641
  const maximum = Math.max(1, editor.state.doc.content.size - 1);
2610
2642
  return {
2611
2643
  from: Math.min(comment.from, maximum),
@@ -2615,7 +2647,7 @@ function documentCommentSelection(editor, comment) {
2615
2647
  function estimatedWrappedLines(value, charactersPerLine) {
2616
2648
  return Math.max(1, value.split('\n').reduce((lines, line)=>lines + Math.max(1, Math.ceil(line.length / charactersPerLine)), 0));
2617
2649
  }
2618
- function commentAuthorInitials(author) {
2650
+ function document_comments_panel_commentAuthorInitials(author) {
2619
2651
  const words = author.trim().split(/\s+/).filter(Boolean);
2620
2652
  if (!words.length) return '审';
2621
2653
  if (1 === words.length) return Array.from(words[0]).slice(0, 1).join('');
@@ -6226,7 +6258,7 @@ const bubbleMenuOptions = {
6226
6258
  inline: true
6227
6259
  };
6228
6260
  const selectionToolbarPluginKey = 'documentSelectionToolbar';
6229
- function DocumentSelectionToolbar({ editor, canInsertComment, layoutFonts = [], onInsertComment }) {
6261
+ function DocumentSelectionToolbar({ editor, canInsertComment, layoutFonts = [], onInsertComment, reviewOnly = false }) {
6230
6262
  const toolbarRef = useRef(null);
6231
6263
  const fontFamilyValue = documentFontFamilyValue(editor, layoutFonts);
6232
6264
  const fontSizeValue = documentFontSizeValue(editor);
@@ -6265,90 +6297,94 @@ function DocumentSelectionToolbar({ editor, canInsertComment, layoutFonts = [],
6265
6297
  if ('touch' !== event.nativeEvent.pointerType) event.preventDefault();
6266
6298
  },
6267
6299
  children: [
6268
- /*#__PURE__*/ jsx(OfficeSelect, {
6269
- ariaLabel: "快捷字体",
6270
- className: "work-document-selection-font-family",
6271
- value: fontFamilyValue,
6272
- options: documentFontFamilyOptionsForValue(fontFamilyValue, layoutFonts),
6273
- onValueChange: (value)=>{
6274
- if ('default' === value) editor.chain().focus().unsetFontFamily().run();
6275
- else editor.chain().focus().setFontFamily(value).run();
6276
- }
6277
- }),
6278
- /*#__PURE__*/ jsx(OfficeSelect, {
6279
- ariaLabel: "快捷字号",
6280
- className: "work-document-selection-font-size",
6281
- value: fontSizeValue,
6282
- options: documentFontSizeOptionsForValue(fontSizeValue),
6283
- onValueChange: (value)=>{
6284
- if ('default' === value) editor.chain().focus().unsetFontSize().run();
6285
- else editor.chain().focus().setFontSize(value).run();
6286
- }
6287
- }),
6288
- /*#__PURE__*/ jsx("span", {
6289
- className: "work-document-selection-divider",
6290
- "aria-hidden": "true"
6291
- }),
6292
- /*#__PURE__*/ jsx(SelectionToolbarButton, {
6293
- label: "加粗",
6294
- active: editor.isActive('bold'),
6295
- onClick: ()=>editor.chain().focus().toggleBold().run(),
6296
- children: /*#__PURE__*/ jsx(Bold, {
6297
- size: 15
6298
- })
6299
- }),
6300
- /*#__PURE__*/ jsx(SelectionToolbarButton, {
6301
- label: "斜体",
6302
- active: editor.isActive('italic'),
6303
- onClick: ()=>editor.chain().focus().toggleItalic().run(),
6304
- children: /*#__PURE__*/ jsx(Italic, {
6305
- size: 15
6306
- })
6307
- }),
6308
- /*#__PURE__*/ jsx(SelectionToolbarButton, {
6309
- label: "下划线",
6310
- active: editor.isActive('underline'),
6311
- onClick: ()=>editor.chain().focus().toggleUnderline().run(),
6312
- children: /*#__PURE__*/ jsx(Underline, {
6313
- size: 15
6314
- })
6315
- }),
6316
- /*#__PURE__*/ jsx(SelectionToolbarButton, {
6317
- label: "删除线",
6318
- active: editor.isActive('strike'),
6319
- onClick: ()=>editor.chain().focus().toggleStrike().run(),
6320
- children: /*#__PURE__*/ jsx(Strikethrough, {
6321
- size: 15
6322
- })
6323
- }),
6324
- /*#__PURE__*/ jsx(OfficeColorPicker, {
6325
- compact: true,
6326
- className: "work-document-selection-color",
6327
- value: editor.getAttributes('textStyle').color ?? '#172033',
6328
- ariaLabel: "快捷文字颜色",
6329
- onValueChange: (color)=>editor.chain().focus().setColor(color).run()
6330
- }),
6331
- /*#__PURE__*/ jsx(SelectionToolbarButton, {
6332
- label: "突出显示",
6333
- active: editor.isActive('highlight'),
6334
- onClick: ()=>editor.chain().focus().toggleHighlight({
6335
- color: '#fff0a6'
6336
- }).run(),
6337
- children: /*#__PURE__*/ jsx(Highlighter, {
6338
- size: 15
6339
- })
6340
- }),
6341
- /*#__PURE__*/ jsx(SelectionToolbarButton, {
6342
- className: "secondary",
6343
- label: "清除格式",
6344
- onClick: ()=>editor.commands.clearDocumentFormatting(),
6345
- children: /*#__PURE__*/ jsx(Eraser, {
6346
- size: 15
6347
- })
6348
- }),
6349
- /*#__PURE__*/ jsx("span", {
6350
- className: "work-document-selection-divider",
6351
- "aria-hidden": "true"
6300
+ !reviewOnly && /*#__PURE__*/ jsxs(Fragment, {
6301
+ children: [
6302
+ /*#__PURE__*/ jsx(OfficeSelect, {
6303
+ ariaLabel: "快捷字体",
6304
+ className: "work-document-selection-font-family",
6305
+ value: fontFamilyValue,
6306
+ options: documentFontFamilyOptionsForValue(fontFamilyValue, layoutFonts),
6307
+ onValueChange: (value)=>{
6308
+ if ('default' === value) editor.chain().focus().unsetFontFamily().run();
6309
+ else editor.chain().focus().setFontFamily(value).run();
6310
+ }
6311
+ }),
6312
+ /*#__PURE__*/ jsx(OfficeSelect, {
6313
+ ariaLabel: "快捷字号",
6314
+ className: "work-document-selection-font-size",
6315
+ value: fontSizeValue,
6316
+ options: documentFontSizeOptionsForValue(fontSizeValue),
6317
+ onValueChange: (value)=>{
6318
+ if ('default' === value) editor.chain().focus().unsetFontSize().run();
6319
+ else editor.chain().focus().setFontSize(value).run();
6320
+ }
6321
+ }),
6322
+ /*#__PURE__*/ jsx("span", {
6323
+ className: "work-document-selection-divider",
6324
+ "aria-hidden": "true"
6325
+ }),
6326
+ /*#__PURE__*/ jsx(SelectionToolbarButton, {
6327
+ label: "加粗",
6328
+ active: editor.isActive('bold'),
6329
+ onClick: ()=>editor.chain().focus().toggleBold().run(),
6330
+ children: /*#__PURE__*/ jsx(Bold, {
6331
+ size: 15
6332
+ })
6333
+ }),
6334
+ /*#__PURE__*/ jsx(SelectionToolbarButton, {
6335
+ label: "斜体",
6336
+ active: editor.isActive('italic'),
6337
+ onClick: ()=>editor.chain().focus().toggleItalic().run(),
6338
+ children: /*#__PURE__*/ jsx(Italic, {
6339
+ size: 15
6340
+ })
6341
+ }),
6342
+ /*#__PURE__*/ jsx(SelectionToolbarButton, {
6343
+ label: "下划线",
6344
+ active: editor.isActive('underline'),
6345
+ onClick: ()=>editor.chain().focus().toggleUnderline().run(),
6346
+ children: /*#__PURE__*/ jsx(Underline, {
6347
+ size: 15
6348
+ })
6349
+ }),
6350
+ /*#__PURE__*/ jsx(SelectionToolbarButton, {
6351
+ label: "删除线",
6352
+ active: editor.isActive('strike'),
6353
+ onClick: ()=>editor.chain().focus().toggleStrike().run(),
6354
+ children: /*#__PURE__*/ jsx(Strikethrough, {
6355
+ size: 15
6356
+ })
6357
+ }),
6358
+ /*#__PURE__*/ jsx(OfficeColorPicker, {
6359
+ compact: true,
6360
+ className: "work-document-selection-color",
6361
+ value: editor.getAttributes('textStyle').color ?? '#172033',
6362
+ ariaLabel: "快捷文字颜色",
6363
+ onValueChange: (color)=>editor.chain().focus().setColor(color).run()
6364
+ }),
6365
+ /*#__PURE__*/ jsx(SelectionToolbarButton, {
6366
+ label: "突出显示",
6367
+ active: editor.isActive('highlight'),
6368
+ onClick: ()=>editor.chain().focus().toggleHighlight({
6369
+ color: '#fff0a6'
6370
+ }).run(),
6371
+ children: /*#__PURE__*/ jsx(Highlighter, {
6372
+ size: 15
6373
+ })
6374
+ }),
6375
+ /*#__PURE__*/ jsx(SelectionToolbarButton, {
6376
+ className: "secondary",
6377
+ label: "清除格式",
6378
+ onClick: ()=>editor.commands.clearDocumentFormatting(),
6379
+ children: /*#__PURE__*/ jsx(Eraser, {
6380
+ size: 15
6381
+ })
6382
+ }),
6383
+ /*#__PURE__*/ jsx("span", {
6384
+ className: "work-document-selection-divider",
6385
+ "aria-hidden": "true"
6386
+ })
6387
+ ]
6352
6388
  }),
6353
6389
  /*#__PURE__*/ jsx(SelectionToolbarButton, {
6354
6390
  label: "添加批注",
@@ -11660,8 +11696,8 @@ function headingLevelForShortcut(code, key) {
11660
11696
  if ('3' === value) return 3;
11661
11697
  return null;
11662
11698
  }
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 }) {
11664
- const [activeTab, setActiveTab] = useState('home');
11699
+ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, reviewOnly = 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 }) {
11700
+ const [activeTab, setActiveTab] = useState(reviewOnly ? 'review' : 'home');
11665
11701
  const officeDialog = useOfficeDialog();
11666
11702
  const prompt = officeDialog.prompt;
11667
11703
  const imageSelected = editor.isActive('image');
@@ -11678,7 +11714,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
11678
11714
  const spellingCommand = getDocumentCommandDefinition('spelling');
11679
11715
  const insertCommentCommand = getDocumentCommandDefinition('insertComment');
11680
11716
  const trackChangesCommand = getDocumentCommandDefinition('trackChanges');
11681
- const ribbonTabs = pageChromeEditor ? [
11717
+ const ribbonTabs = reviewOnly ? documentRibbonTabs.filter(({ id })=>'review' === id || 'view' === id) : pageChromeEditor ? [
11682
11718
  ...documentRibbonTabs,
11683
11719
  documentPageChromeRibbonTab
11684
11720
  ] : imageSelected ? [
@@ -11733,6 +11769,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
11733
11769
  ]);
11734
11770
  useEffect(()=>{
11735
11771
  setActiveTab((current)=>{
11772
+ if (reviewOnly) return 'view' === current ? 'view' : 'review';
11736
11773
  if (pageChromeEditor) return 'pageChrome';
11737
11774
  if (imageSelected) return 'picture';
11738
11775
  if (tableSelected) return 'tableDesign' === current || 'tableLayout' === current ? current : 'tableDesign';
@@ -11741,6 +11778,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
11741
11778
  }, [
11742
11779
  imageSelected,
11743
11780
  pageChromeEditor,
11781
+ reviewOnly,
11744
11782
  tableSelected
11745
11783
  ]);
11746
11784
  useEffect(()=>{
@@ -11750,6 +11788,13 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
11750
11788
  if (event.defaultPrevented || event.repeat || isOfficeShortcutBlocked(event.target)) return;
11751
11789
  const key = event.key.toLowerCase();
11752
11790
  const insideEditor = event.target instanceof Node && Boolean(editorDom?.contains(event.target));
11791
+ if (reviewOnly) {
11792
+ if (insideEditor && (event.metaKey || event.ctrlKey) && event.altKey && !event.shiftKey && 'm' === key) {
11793
+ event.preventDefault();
11794
+ if (canInsertComment) onInsertComment();
11795
+ }
11796
+ return;
11797
+ }
11753
11798
  if (insideEditor && runDocumentWpsShortcut(editor, event, {
11754
11799
  canInsertComment,
11755
11800
  canRefreshFields: hasRefreshableFields,
@@ -11830,6 +11875,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
11830
11875
  onOpenFindReplace,
11831
11876
  onOpenWordCount,
11832
11877
  onRefreshFields,
11878
+ reviewOnly,
11833
11879
  onToggleSpellcheck,
11834
11880
  onToggleTrackChanges,
11835
11881
  toggleLink
@@ -11867,7 +11913,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
11867
11913
  /*#__PURE__*/ jsx(WorkOfficeRibbon, {
11868
11914
  ariaLabel: "文字功能区",
11869
11915
  tabs: ribbonTabs,
11870
- defaultTab: "home",
11916
+ defaultTab: reviewOnly ? 'review' : 'home',
11871
11917
  activeTab: activeTab,
11872
11918
  onTabChange: (tab)=>{
11873
11919
  const accepted = onRibbonTabChange?.(tab);
@@ -11879,7 +11925,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
11879
11925
  adaptive: true,
11880
11926
  collapsible: true,
11881
11927
  fileActions: fileActions,
11882
- quickAccessActions: [
11928
+ quickAccessActions: reviewOnly ? [] : [
11883
11929
  {
11884
11930
  id: undoCommand.id,
11885
11931
  label: undoCommand.label,
@@ -11913,13 +11959,13 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
11913
11959
  className: "work-document-ribbon",
11914
11960
  toolbarClassName: "document-toolbar",
11915
11961
  panels: {
11916
- home: /*#__PURE__*/ jsx(DocumentHomeRibbon, {
11962
+ home: reviewOnly ? null : /*#__PURE__*/ jsx(DocumentHomeRibbon, {
11917
11963
  editor: editor,
11918
11964
  findReplaceMode: findReplaceMode,
11919
11965
  layoutFonts: layoutFonts,
11920
11966
  onFindText: (replace)=>onOpenFindReplace(replace ? 'replace' : 'find')
11921
11967
  }),
11922
- insert: /*#__PURE__*/ jsxs(Fragment, {
11968
+ insert: reviewOnly ? null : /*#__PURE__*/ jsxs(Fragment, {
11923
11969
  children: [
11924
11970
  /*#__PURE__*/ jsx(document_toolbar_RibbonGroup, {
11925
11971
  label: "页面",
@@ -12020,7 +12066,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
12020
12066
  })
12021
12067
  ]
12022
12068
  }),
12023
- page: /*#__PURE__*/ jsx(DocumentPageLayoutRibbon, {
12069
+ page: reviewOnly ? null : /*#__PURE__*/ jsx(DocumentPageLayoutRibbon, {
12024
12070
  editor: editor,
12025
12071
  layout: layout,
12026
12072
  layoutOpen: layoutOpen,
@@ -12031,7 +12077,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
12031
12077
  onPageColorChange: onPageColorChange,
12032
12078
  onInsertSection: onInsertSection
12033
12079
  }),
12034
- references: /*#__PURE__*/ jsxs(Fragment, {
12080
+ references: reviewOnly ? null : /*#__PURE__*/ jsxs(Fragment, {
12035
12081
  children: [
12036
12082
  /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
12037
12083
  label: "脚注",
@@ -12119,7 +12165,7 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
12119
12165
  }),
12120
12166
  review: /*#__PURE__*/ jsxs(Fragment, {
12121
12167
  children: [
12122
- /*#__PURE__*/ jsx(document_toolbar_RibbonGroup, {
12168
+ !reviewOnly && /*#__PURE__*/ jsx(document_toolbar_RibbonGroup, {
12123
12169
  label: "校对",
12124
12170
  priority: "high",
12125
12171
  children: /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
@@ -12161,73 +12207,77 @@ function DocumentToolbar({ editor, defaultRibbonCollapsed = false, history, layo
12161
12207
  })
12162
12208
  ]
12163
12209
  }),
12164
- /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
12165
- label: "修订",
12166
- priority: "high",
12167
- children: [
12168
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12169
- label: "修订模式",
12170
- displayLabel: true,
12171
- shortcut: trackChangesCommand.shortcut?.label,
12172
- ariaKeyShortcuts: trackChangesCommand.shortcut?.aria,
12173
- active: trackChanges,
12174
- onClick: onToggleTrackChanges,
12175
- children: /*#__PURE__*/ jsx(FileDiff, {
12176
- size: 19
12177
- })
12178
- }),
12179
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12180
- label: `查看修订${changeCount ? `(${changeCount})` : ''}`,
12181
- displayLabel: true,
12182
- active: changesOpen,
12183
- onClick: onToggleChanges,
12184
- children: /*#__PURE__*/ jsx(ListChecks, {
12185
- size: 19
12186
- })
12187
- })
12188
- ]
12189
- }),
12190
- /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
12191
- label: "更改",
12192
- priority: "high",
12210
+ !reviewOnly && /*#__PURE__*/ jsxs(Fragment, {
12193
12211
  children: [
12194
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12195
- label: "接受修订",
12196
- displayLabel: true,
12197
- disabled: null === actionableChangeIndex,
12198
- title: "接受当前修订并转到下一处",
12199
- onClick: ()=>decideDocumentChange('accept'),
12200
- children: /*#__PURE__*/ jsx(Check, {
12201
- size: 19
12202
- })
12203
- }),
12204
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12205
- label: "拒绝修订",
12206
- displayLabel: true,
12207
- disabled: null === actionableChangeIndex,
12208
- title: "拒绝当前修订并转到下一处",
12209
- onClick: ()=>decideDocumentChange('reject'),
12210
- children: /*#__PURE__*/ jsx(XCircle, {
12211
- size: 19
12212
- })
12213
- }),
12214
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12215
- label: "上一处修订",
12216
- displayLabel: true,
12217
- disabled: null === previousChangeIndex,
12218
- onClick: ()=>navigateDocumentChange(-1),
12219
- children: /*#__PURE__*/ jsx(ChevronUp, {
12220
- size: 19
12221
- })
12212
+ /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
12213
+ label: "修订",
12214
+ priority: "high",
12215
+ children: [
12216
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12217
+ label: "修订模式",
12218
+ displayLabel: true,
12219
+ shortcut: trackChangesCommand.shortcut?.label,
12220
+ ariaKeyShortcuts: trackChangesCommand.shortcut?.aria,
12221
+ active: trackChanges,
12222
+ onClick: onToggleTrackChanges,
12223
+ children: /*#__PURE__*/ jsx(FileDiff, {
12224
+ size: 19
12225
+ })
12226
+ }),
12227
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12228
+ label: `查看修订${changeCount ? `(${changeCount})` : ''}`,
12229
+ displayLabel: true,
12230
+ active: changesOpen,
12231
+ onClick: onToggleChanges,
12232
+ children: /*#__PURE__*/ jsx(ListChecks, {
12233
+ size: 19
12234
+ })
12235
+ })
12236
+ ]
12222
12237
  }),
12223
- /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12224
- label: "下一处修订",
12225
- displayLabel: true,
12226
- disabled: null === nextChangeIndex,
12227
- onClick: ()=>navigateDocumentChange(1),
12228
- children: /*#__PURE__*/ jsx(ChevronDown, {
12229
- size: 19
12230
- })
12238
+ /*#__PURE__*/ jsxs(document_toolbar_RibbonGroup, {
12239
+ label: "更改",
12240
+ priority: "high",
12241
+ children: [
12242
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12243
+ label: "接受修订",
12244
+ displayLabel: true,
12245
+ disabled: null === actionableChangeIndex,
12246
+ title: "接受当前修订并转到下一处",
12247
+ onClick: ()=>decideDocumentChange('accept'),
12248
+ children: /*#__PURE__*/ jsx(Check, {
12249
+ size: 19
12250
+ })
12251
+ }),
12252
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12253
+ label: "拒绝修订",
12254
+ displayLabel: true,
12255
+ disabled: null === actionableChangeIndex,
12256
+ title: "拒绝当前修订并转到下一处",
12257
+ onClick: ()=>decideDocumentChange('reject'),
12258
+ children: /*#__PURE__*/ jsx(XCircle, {
12259
+ size: 19
12260
+ })
12261
+ }),
12262
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12263
+ label: "上一处修订",
12264
+ displayLabel: true,
12265
+ disabled: null === previousChangeIndex,
12266
+ onClick: ()=>navigateDocumentChange(-1),
12267
+ children: /*#__PURE__*/ jsx(ChevronUp, {
12268
+ size: 19
12269
+ })
12270
+ }),
12271
+ /*#__PURE__*/ jsx(document_toolbar_ToolbarButton, {
12272
+ label: "下一处修订",
12273
+ displayLabel: true,
12274
+ disabled: null === nextChangeIndex,
12275
+ onClick: ()=>navigateDocumentChange(1),
12276
+ children: /*#__PURE__*/ jsx(ChevronDown, {
12277
+ size: 19
12278
+ })
12279
+ })
12280
+ ]
12231
12281
  })
12232
12282
  ]
12233
12283
  })
@@ -12594,7 +12644,7 @@ function document_vertical_ruler_formatRulerValue(value) {
12594
12644
  return Number.isInteger(value) ? String(value) : value.toFixed(1);
12595
12645
  }
12596
12646
  const CURRENT_COMMENT_AUTHOR = '我';
12597
- function useDocumentComments({ contentRef, editor, onBeforeDraft }) {
12647
+ function useDocumentComments({ actor, contentRef, deleteOwnOnly = false, editor, enabled = true, onBeforeDraft }) {
12598
12648
  const [open, setOpen] = useState(false);
12599
12649
  const [draft, setDraft] = useState(null);
12600
12650
  useEffect(()=>()=>{
@@ -12603,23 +12653,40 @@ function useDocumentComments({ contentRef, editor, onBeforeDraft }) {
12603
12653
  editor
12604
12654
  ]);
12605
12655
  const reply = useCallback((id, text)=>{
12656
+ if (!enabled) return;
12606
12657
  editor?.commands.addDocumentCommentReply(id, {
12607
12658
  id: createWorkId('comment-reply'),
12608
- author: CURRENT_COMMENT_AUTHOR,
12659
+ ...commentActor(actor),
12609
12660
  date: new Date().toISOString(),
12610
12661
  text
12611
12662
  });
12612
12663
  }, [
12613
- editor
12664
+ actor,
12665
+ editor,
12666
+ enabled
12614
12667
  ]);
12615
12668
  const toggleResolved = useCallback((id)=>{
12669
+ if (!enabled) return;
12616
12670
  editor?.commands.toggleDocumentCommentResolved(id);
12617
12671
  }, [
12618
- editor
12672
+ editor,
12673
+ enabled
12674
+ ]);
12675
+ const canDeleteComment = useCallback((id)=>{
12676
+ if (!enabled) return false;
12677
+ if (!deleteOwnOnly) return true;
12678
+ return Boolean(actor && contentRef.current.comments?.some((comment)=>comment.id === id && comment.actorId === actor.id));
12679
+ }, [
12680
+ actor,
12681
+ contentRef,
12682
+ deleteOwnOnly,
12683
+ enabled
12619
12684
  ]);
12620
12685
  const deleteComment = useCallback((id)=>{
12686
+ if (!canDeleteComment(id)) return;
12621
12687
  editor?.commands.deleteDocumentComment(id);
12622
12688
  }, [
12689
+ canDeleteComment,
12623
12690
  editor
12624
12691
  ]);
12625
12692
  const closeDraft = useCallback((restoreEditorFocus = true)=>{
@@ -12638,7 +12705,7 @@ function useDocumentComments({ contentRef, editor, onBeforeDraft }) {
12638
12705
  closeDraft
12639
12706
  ]);
12640
12707
  const startDraft = useCallback(()=>{
12641
- if (!editor) return;
12708
+ if (!editor || !enabled) return;
12642
12709
  const { from, to } = editor.state.selection;
12643
12710
  const range = {
12644
12711
  from,
@@ -12657,17 +12724,18 @@ function useDocumentComments({ contentRef, editor, onBeforeDraft }) {
12657
12724
  setOpen(true);
12658
12725
  }, [
12659
12726
  editor,
12727
+ enabled,
12660
12728
  onBeforeDraft
12661
12729
  ]);
12662
12730
  const submitDraft = useCallback((text)=>{
12663
- if (!editor || !draft) return '批注草稿已经关闭,请重新选择文字。';
12731
+ if (!enabled || !editor || !draft) return '批注草稿已经关闭,请重新选择文字。';
12664
12732
  const range = documentCommentDraftRange(editor);
12665
12733
  if (!range) return '所选文字已变化,请重新选择。';
12666
12734
  const anchorText = editor.state.doc.textBetween(range.from, range.to, '\n');
12667
12735
  if (anchorText !== draft.anchorText) return '所选文字已变化,请重新选择。';
12668
12736
  const comment = {
12669
12737
  id: draft.id,
12670
- author: CURRENT_COMMENT_AUTHOR,
12738
+ ...commentActor(actor),
12671
12739
  date: new Date().toISOString(),
12672
12740
  text,
12673
12741
  resolved: false
@@ -12677,11 +12745,13 @@ function useDocumentComments({ contentRef, editor, onBeforeDraft }) {
12677
12745
  setOpen(true);
12678
12746
  return null;
12679
12747
  }, [
12748
+ actor,
12680
12749
  draft,
12681
- editor
12750
+ editor,
12751
+ enabled
12682
12752
  ]);
12683
12753
  const comments = editor ? documentCommentViews(contentRef.current.comments ?? [], collectDocumentCommentAnchors(editor.state.doc)) : [];
12684
- const canInsert = Boolean(editor && canInsertDocumentComment(editor));
12754
+ const canInsert = Boolean(enabled && editor && canInsertDocumentComment(editor));
12685
12755
  const toggleOpen = useCallback(()=>{
12686
12756
  if (open) close();
12687
12757
  else setOpen(true);
@@ -12690,6 +12760,7 @@ function useDocumentComments({ contentRef, editor, onBeforeDraft }) {
12690
12760
  open
12691
12761
  ]);
12692
12762
  return {
12763
+ canDeleteComment,
12693
12764
  canInsert,
12694
12765
  close,
12695
12766
  closeDraft,
@@ -12705,6 +12776,14 @@ function useDocumentComments({ contentRef, editor, onBeforeDraft }) {
12705
12776
  toggleResolved
12706
12777
  };
12707
12778
  }
12779
+ function commentActor(actor) {
12780
+ return actor ? {
12781
+ actorId: actor.id,
12782
+ author: actor.name
12783
+ } : {
12784
+ author: CURRENT_COMMENT_AUTHOR
12785
+ };
12786
+ }
12708
12787
  function useDocumentInsertCommands({ contentRef, editor, resolveFieldContext }) {
12709
12788
  const officeDialog = useOfficeDialog();
12710
12789
  const [insertDialog, setInsertDialog] = useState(null);
@@ -14177,8 +14256,11 @@ function CollaborativeDocumentEditor(props) {
14177
14256
  });
14178
14257
  }
14179
14258
  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;
14259
+ const reviewOnly = collaboration?.mode === 'comment' && !requestedPreview;
14260
+ const collaborationInteractive = !collaboration || 'edit' === collaboration.mode || 'comment' === collaboration.mode;
14261
+ const preview = requestedPreview || !collaborationInteractive;
14262
+ const canEditDocument = !preview && (!collaboration || 'edit' === collaboration.mode);
14263
+ const canCommentDocument = !preview && (!collaboration || 'edit' === collaboration.mode || 'comment' === collaboration.mode && Boolean(collaboration.actor));
14182
14264
  const readOnly = preview;
14183
14265
  const initialContentRef = useRef(collaborationInitialContent ?? content);
14184
14266
  const effectiveContent = collaboration ? initialContentRef.current : content;
@@ -14282,10 +14364,13 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
14282
14364
  attributes: {
14283
14365
  'aria-label': '文档正文',
14284
14366
  'aria-multiline': 'true',
14367
+ 'aria-readonly': reviewOnly ? 'true' : 'false',
14285
14368
  role: 'textbox',
14286
14369
  spellcheck: 'true'
14287
14370
  }
14288
- }), []);
14371
+ }), [
14372
+ reviewOnly
14373
+ ]);
14289
14374
  const editor = useEditor({
14290
14375
  extensions: editorExtensions,
14291
14376
  content: collaboration ? void 0 : initialEditorSourceRef.current,
@@ -14363,8 +14448,11 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
14363
14448
  reconcileControlledUpdates: !collaboration
14364
14449
  });
14365
14450
  const documentComments = useDocumentComments({
14451
+ actor: collaboration?.actor,
14366
14452
  contentRef,
14453
+ deleteOwnOnly: reviewOnly,
14367
14454
  editor,
14455
+ enabled: canCommentDocument,
14368
14456
  onBeforeDraft: ()=>setTaskPane(null)
14369
14457
  });
14370
14458
  const [collaborationVersion, setCollaborationVersion] = useState(0);
@@ -14601,6 +14689,7 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
14601
14689
  editor.setEditable(!readOnly);
14602
14690
  const editorDom = editor.view.dom;
14603
14691
  editorDom.setAttribute('role', preview ? 'document' : 'textbox');
14692
+ editorDom.setAttribute('aria-readonly', String(reviewOnly || preview));
14604
14693
  if (preview) editorDom.removeAttribute('aria-multiline');
14605
14694
  else editorDom.setAttribute('aria-multiline', 'true');
14606
14695
  };
@@ -14612,7 +14701,8 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
14612
14701
  }, [
14613
14702
  editor,
14614
14703
  preview,
14615
- readOnly
14704
+ readOnly,
14705
+ reviewOnly
14616
14706
  ]);
14617
14707
  useEffect(()=>{
14618
14708
  if (!editor) return;
@@ -14700,7 +14790,7 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
14700
14790
  const lastPageMargins = resolveDocumentPageMargins(lastPageDescriptor.layout, lastPageDescriptor.physicalPage);
14701
14791
  const { chromeEditor: pageChromeEditor, close: closePageChrome, edit: editPageChrome, editing: pageChromeEditing, footerChrome, headerChrome, reset: resetPageChrome, setChromeEditor: setPageChromeEditor, togglePageNumber: toggleVisiblePageNumber, update: updateVisiblePageChrome, visibleChrome } = useDocumentPageChrome({
14702
14792
  editor,
14703
- enabled: !preview && 'page' === viewMode,
14793
+ enabled: canEditDocument && 'page' === viewMode,
14704
14794
  firstPage: firstPageDescriptor,
14705
14795
  footerRef: pageFooterRef,
14706
14796
  headerRef: pageHeaderRef,
@@ -14807,7 +14897,7 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
14807
14897
  restoreDocumentBodyFocus();
14808
14898
  };
14809
14899
  const openSelectionContextMenu = (event)=>{
14810
- if (preview || !getSelectionMenuItems && !onAgentRequest) return false;
14900
+ if (!canEditDocument || !getSelectionMenuItems && !onAgentRequest) return false;
14811
14901
  const snapshot = createWorkDocumentSelectionSnapshot(editor, contentRef.current);
14812
14902
  if (!snapshot) return false;
14813
14903
  event.preventDefault();
@@ -14834,10 +14924,11 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
14834
14924
  };
14835
14925
  return /*#__PURE__*/ jsxs("section", {
14836
14926
  className: `work-document-editor${preview ? ' preview' : ''}`,
14927
+ "data-collaboration-mode": collaboration?.mode,
14837
14928
  "data-work-pdf-artifact": artifactId,
14838
14929
  "data-work-pdf-surface": artifactId ? 'live' : void 0,
14839
14930
  children: [
14840
- !preview && /*#__PURE__*/ jsx(office_text_field_OfficeFileInput, {
14931
+ canEditDocument && /*#__PURE__*/ jsx(office_text_field_OfficeFileInput, {
14841
14932
  ref: imageInputRef,
14842
14933
  accept: "image/bmp,image/gif,image/jpeg,image/png,image/webp",
14843
14934
  "aria-label": "插入文档图片",
@@ -14852,7 +14943,8 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
14852
14943
  }) : /*#__PURE__*/ jsx(DocumentToolbar, {
14853
14944
  editor: editor,
14854
14945
  defaultRibbonCollapsed: defaultRibbonCollapsed,
14855
- history: collaborationBinding ? {
14946
+ reviewOnly: reviewOnly,
14947
+ history: collaborationBinding && !reviewOnly ? {
14856
14948
  canRedo: collaborationBinding.canRedo(),
14857
14949
  canUndo: collaborationBinding.canUndo(),
14858
14950
  redo: ()=>collaborationBinding.redo(),
@@ -14988,7 +15080,7 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
14988
15080
  }),
14989
15081
  /*#__PURE__*/ jsxs("article", {
14990
15082
  ref: pageSurfaceRef,
14991
- className: `work-document-page${preview ? ' work-document-preview-page' : ''} ${layout.pageSize} ${layout.orientation}${pagination.pageCount ? ' paginated' : ''}${!preview && pageChromeEditing ? ' page-chrome-editing' : ''}`,
15083
+ className: `work-document-page${preview ? ' work-document-preview-page' : ''} ${layout.pageSize} ${layout.orientation}${pagination.pageCount ? ' paginated' : ''}${canEditDocument && pageChromeEditing ? ' page-chrome-editing' : ''}`,
14992
15084
  "data-work-pdf-live-document": artifactId && pagination.pageCount ? '' : void 0,
14993
15085
  "data-pdf-orientation": artifactId && pagination.pageCount ? layout.orientation : void 0,
14994
15086
  "data-pdf-page-count": artifactId ? pagination.pageCount : void 0,
@@ -15030,7 +15122,7 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
15030
15122
  }),
15031
15123
  'page' === viewMode && (!preview || Boolean(headerChrome.headerHtml)) && /*#__PURE__*/ jsx("header", {
15032
15124
  ref: pageHeaderRef,
15033
- className: `work-document-page-header${!preview && pageChromeEditing?.part === 'header' ? ' editing' : ''}`,
15125
+ className: `work-document-page-header${canEditDocument && pageChromeEditing?.part === 'header' ? ' editing' : ''}`,
15034
15126
  "data-document-page-chrome": firstPageDescriptor.pageChrome.variant,
15035
15127
  style: firstPageFrame && paginationGeometry ? {
15036
15128
  height: firstPageDescriptor.page.headerHeight,
@@ -15038,17 +15130,12 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
15038
15130
  right: paginationGeometry.width - firstPageFrame.left - firstPageFrame.width + firstPageDescriptor.page.marginRight,
15039
15131
  top: work_document_layout_millimetersToPixels(firstPageMargins.headerDistance)
15040
15132
  } : void 0,
15041
- onDoubleClick: preview ? void 0 : (event)=>{
15133
+ onDoubleClick: canEditDocument ? (event)=>{
15042
15134
  if (pageChromeEditing?.part === 'header') return;
15043
15135
  event.preventDefault();
15044
15136
  editPageChrome('header');
15045
- },
15046
- children: preview || pageChromeEditing?.part !== 'header' ? headerChrome.headerHtml ? /*#__PURE__*/ jsx("div", {
15047
- className: "work-document-page-chrome-html",
15048
- dangerouslySetInnerHTML: {
15049
- __html: headerChrome.headerHtml
15050
- }
15051
- }) : null : /*#__PURE__*/ jsx(DocumentPageChromeRichTextEditor, {
15137
+ } : void 0,
15138
+ children: canEditDocument && pageChromeEditing?.part === 'header' ? /*#__PURE__*/ jsx(DocumentPageChromeRichTextEditor, {
15052
15139
  autoFocus: true,
15053
15140
  className: "work-document-page-chrome-inline-editor",
15054
15141
  label: "页内页眉",
@@ -15059,14 +15146,19 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
15059
15146
  }),
15060
15147
  onEditorChange: setPageChromeEditor,
15061
15148
  onExit: closePageChrome
15062
- }, `${pageChromeEditing.sectionId}-${pageChromeEditing.variant}-header`)
15149
+ }, `${pageChromeEditing.sectionId}-${pageChromeEditing.variant}-header`) : headerChrome.headerHtml ? /*#__PURE__*/ jsx("div", {
15150
+ className: "work-document-page-chrome-html",
15151
+ dangerouslySetInnerHTML: {
15152
+ __html: headerChrome.headerHtml
15153
+ }
15154
+ }) : null
15063
15155
  }),
15064
15156
  /*#__PURE__*/ jsxs("section", {
15065
15157
  ref: editableSurfaceRef,
15066
15158
  className: `work-document-editable ${viewMode}`,
15067
15159
  "aria-label": "文档内容编辑区域",
15068
15160
  onDoubleClick: ()=>{
15069
- if (!preview && pageChromeEditing) closePageChrome();
15161
+ if (canEditDocument && pageChromeEditing) closePageChrome();
15070
15162
  },
15071
15163
  onContextMenu: openSelectionContextMenu,
15072
15164
  onKeyDownCapture: (event)=>{
@@ -15086,13 +15178,14 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
15086
15178
  editor: editor,
15087
15179
  canInsertComment: documentComments.canInsert,
15088
15180
  layoutFonts: layoutFonts,
15089
- onInsertComment: ()=>void startCommentDraft()
15181
+ onInsertComment: ()=>void startCommentDraft(),
15182
+ reviewOnly: reviewOnly
15090
15183
  })
15091
15184
  ]
15092
15185
  }),
15093
15186
  'page' === viewMode && (!preview || Boolean(footerChrome.footerHtml) || footerChrome.showPageNumber) && /*#__PURE__*/ jsxs("footer", {
15094
15187
  ref: pageFooterRef,
15095
- className: `work-document-page-footer${!preview && pageChromeEditing?.part === 'footer' ? ' editing' : ''}`,
15188
+ className: `work-document-page-footer${canEditDocument && pageChromeEditing?.part === 'footer' ? ' editing' : ''}`,
15096
15189
  "data-document-page-chrome": lastPageDescriptor.pageChrome.variant,
15097
15190
  style: lastPageFrame && paginationGeometry ? {
15098
15191
  bottom: work_document_layout_millimetersToPixels(lastPageMargins.footerDistance),
@@ -15100,20 +15193,15 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
15100
15193
  left: lastPageFrame.left + lastPageDescriptor.page.marginLeft,
15101
15194
  right: paginationGeometry.width - lastPageFrame.left - lastPageFrame.width + lastPageDescriptor.page.marginRight
15102
15195
  } : void 0,
15103
- onDoubleClick: preview ? void 0 : (event)=>{
15196
+ onDoubleClick: canEditDocument ? (event)=>{
15104
15197
  if (pageChromeEditing?.part === 'footer') return;
15105
15198
  event.preventDefault();
15106
15199
  editPageChrome('footer');
15107
- },
15200
+ } : void 0,
15108
15201
  children: [
15109
15202
  /*#__PURE__*/ jsx("div", {
15110
15203
  className: "work-document-page-footer-content",
15111
- children: preview || pageChromeEditing?.part !== 'footer' ? footerChrome.footerHtml ? /*#__PURE__*/ jsx("div", {
15112
- className: "work-document-page-chrome-html",
15113
- dangerouslySetInnerHTML: {
15114
- __html: footerChrome.footerHtml
15115
- }
15116
- }) : null : /*#__PURE__*/ jsx(DocumentPageChromeRichTextEditor, {
15204
+ children: canEditDocument && pageChromeEditing?.part === 'footer' ? /*#__PURE__*/ jsx(DocumentPageChromeRichTextEditor, {
15117
15205
  autoFocus: true,
15118
15206
  className: "work-document-page-chrome-inline-editor",
15119
15207
  label: "页内页脚",
@@ -15124,7 +15212,12 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
15124
15212
  }),
15125
15213
  onEditorChange: setPageChromeEditor,
15126
15214
  onExit: closePageChrome
15127
- }, `${pageChromeEditing.sectionId}-${pageChromeEditing.variant}-footer`)
15215
+ }, `${pageChromeEditing.sectionId}-${pageChromeEditing.variant}-footer`) : footerChrome.footerHtml ? /*#__PURE__*/ jsx("div", {
15216
+ className: "work-document-page-chrome-html",
15217
+ dangerouslySetInnerHTML: {
15218
+ __html: footerChrome.footerHtml
15219
+ }
15220
+ }) : null
15128
15221
  }),
15129
15222
  footerChrome.showPageNumber && /*#__PURE__*/ jsxs("span", {
15130
15223
  className: "work-document-page-number",
@@ -15145,7 +15238,9 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
15145
15238
  !preview && documentComments.open && /*#__PURE__*/ jsx(DocumentCommentsPanel, {
15146
15239
  editor: editor,
15147
15240
  comments: documentComments.comments,
15241
+ canDelete: documentComments.canDeleteComment,
15148
15242
  draft: documentComments.draft,
15243
+ draftAuthor: collaboration?.actor?.name,
15149
15244
  surfaceRef: reviewSurfaceRef,
15150
15245
  onReply: documentComments.reply,
15151
15246
  onToggleResolved: documentComments.toggleResolved,
@@ -15158,20 +15253,20 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
15158
15253
  ]
15159
15254
  })
15160
15255
  }),
15161
- !preview && citationsOpen && /*#__PURE__*/ jsx(DocumentCitationsPanel, {
15256
+ canEditDocument && citationsOpen && /*#__PURE__*/ jsx(DocumentCitationsPanel, {
15162
15257
  editor: editor,
15163
15258
  content: currentContent,
15164
15259
  onClose: closeTaskPane,
15165
15260
  onDirtyChange: setCitationsDirty
15166
15261
  }),
15167
- !preview && changesOpen && /*#__PURE__*/ jsx(DocumentChangesPanel, {
15262
+ canEditDocument && changesOpen && /*#__PURE__*/ jsx(DocumentChangesPanel, {
15168
15263
  editor: editor,
15169
15264
  changes: changes,
15170
15265
  trackChanges: Boolean(currentContent.trackChanges),
15171
15266
  onTrackChangesChange: (enabled)=>editor.commands.setDocumentTrackChanges(enabled),
15172
15267
  onClose: closeTaskPane
15173
15268
  }),
15174
- !preview && layoutOpen && section && /*#__PURE__*/ jsx(DocumentLayoutPanel, {
15269
+ canEditDocument && layoutOpen && section && /*#__PURE__*/ jsx(DocumentLayoutPanel, {
15175
15270
  layout: layout,
15176
15271
  activeTab: layoutPanelTab,
15177
15272
  sectionIndex: section.index,
@@ -15182,7 +15277,7 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
15182
15277
  onMergeSection: ()=>editor.commands.mergeDocumentSectionWithPrevious(),
15183
15278
  onClose: closeTaskPane
15184
15279
  }),
15185
- !preview && findReplaceMode && /*#__PURE__*/ jsx(DocumentFindReplacePanel, {
15280
+ canEditDocument && findReplaceMode && /*#__PURE__*/ jsx(DocumentFindReplacePanel, {
15186
15281
  editor: editor,
15187
15282
  mode: findReplaceMode,
15188
15283
  focusRequest: findReplaceFocusRequest,
@@ -15227,14 +15322,14 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
15227
15322
  onViewModeChange: changeViewMode,
15228
15323
  onZoomChange: setZoom
15229
15324
  }),
15230
- !preview && selectionMenu && /*#__PURE__*/ jsx(DocumentSelectionContextMenu, {
15325
+ canEditDocument && selectionMenu && /*#__PURE__*/ jsx(DocumentSelectionContextMenu, {
15231
15326
  editor: editor,
15232
15327
  menu: selectionMenu,
15233
15328
  getTrackChanges: ()=>trackChangesRef.current,
15234
15329
  onAgentRequest: onAgentRequest,
15235
15330
  onClose: ()=>setSelectionMenu(null)
15236
15331
  }),
15237
- !preview && documentInsert.dialog,
15332
+ canEditDocument && documentInsert.dialog,
15238
15333
  !preview && taskPaneDialog.dialog,
15239
15334
  !preview && statisticsOpen && /*#__PURE__*/ jsx(DocumentStatisticsDialog, {
15240
15335
  pageCount: pageCount,