@a3s-lab/office 0.7.2 → 0.8.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.
- package/COLLABORATION_ROADMAP.md +81 -26
- package/README.md +48 -4
- package/dist/0~document-editor.js +414 -247
- package/dist/8928.js +689 -49
- package/dist/9787.js +14 -4
- package/dist/core.d.ts +2 -2
- package/dist/internal/collaboration/office-collaboration.d.ts +4 -2
- package/dist/internal/collaboration/office-document-collaboration-change-decisions.d.ts +7 -0
- package/dist/internal/collaboration/office-document-collaboration-claims.d.ts +2 -1
- package/dist/internal/collaboration/office-document-collaboration-comment-permissions.d.ts +3 -0
- package/dist/internal/collaboration/office-document-collaboration-sidecars.d.ts +3 -1
- package/dist/internal/collaboration/office-document-collaboration-suggestions.d.ts +6 -0
- package/dist/internal/collaboration/office-document-collaboration.d.ts +6 -2
- package/dist/internal/features/work/editors/document-changes-panel.d.ts +7 -1
- package/dist/internal/features/work/editors/document-comment-composer.d.ts +1 -0
- package/dist/internal/features/work/editors/document-comments-panel.d.ts +3 -1
- package/dist/internal/features/work/editors/document-selection-toolbar.d.ts +2 -1
- package/dist/internal/features/work/editors/document-toolbar.d.ts +5 -1
- package/dist/internal/features/work/editors/use-document-comments.d.ts +6 -1
- package/dist/internal/features/work/work-document-changes.d.ts +5 -2
- package/dist/internal/features/work/work-document-comments.d.ts +5 -1
- package/dist/internal/features/work/work-document-extensions.d.ts +1 -0
- package/dist/internal/features/work/work-document-paragraph-identity.d.ts +1 -0
- package/dist/internal/features/work/work-document-table-row-identity.d.ts +5 -1
- package/dist/internal/features/work/work-types.d.ts +22 -0
- package/dist/office-kernel.wasm +0 -0
- package/dist/styles.css +76 -0
- package/package.json +12 -3
package/dist/8928.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Extension, Mark, Node as core_Node, ResizableNodeView, generateHTML, getSchema, mergeAttributes } from "@tiptap/core";
|
|
2
2
|
import { Collaboration, isChangeOrigin } from "@tiptap/extension-collaboration";
|
|
3
3
|
import { NodeSelection, Plugin, PluginKey, TextSelection } from "@tiptap/pm/state";
|
|
4
|
+
import { AddMarkStep, Mapping, RemoveMarkStep, ReplaceStep } from "@tiptap/pm/transform";
|
|
4
5
|
import { defaultDeleteFilter, defaultProtectedNodes, prosemirrorJSONToYXmlFragment, ySyncPluginKey, yXmlFragmentToProsemirrorJSON } from "@tiptap/y-tiptap";
|
|
5
6
|
import extension_color from "@tiptap/extension-color";
|
|
6
7
|
import { Table, TableCell, TableHeader, TableKit, TableRow, TableView, createTable } from "@tiptap/extension-table";
|
|
@@ -8,7 +9,6 @@ import extension_text_align from "@tiptap/extension-text-align";
|
|
|
8
9
|
import font_size from "@tiptap/extension-text-style/font-size";
|
|
9
10
|
import extension_underline from "@tiptap/extension-underline";
|
|
10
11
|
import starter_kit from "@tiptap/starter-kit";
|
|
11
|
-
import { Mapping } from "@tiptap/pm/transform";
|
|
12
12
|
import { DOMParser as model_DOMParser, Fragment, Slice } from "@tiptap/pm/model";
|
|
13
13
|
import jszip from "jszip";
|
|
14
14
|
import { closeHistory, isHistoryTransaction } from "@tiptap/pm/history";
|
|
@@ -23,7 +23,7 @@ import { TextStyle } from "@tiptap/extension-text-style";
|
|
|
23
23
|
import extension_highlight from "@tiptap/extension-highlight";
|
|
24
24
|
import { createOfficeId as createWorkId, parseXml, xmlNamespacePrefix } from "./5184.js";
|
|
25
25
|
import { workOfficeCollaborationJsonEqual, canonicalWorkOfficeCollaborationJson, isWorkOfficeCollaborationRecord, cloneWorkOfficeCollaborationJson } from "./4650.js";
|
|
26
|
-
import { initializeWorkOfficeCollaborationMetadata,
|
|
26
|
+
import { assertWorkOfficeCollaborationEditable, markWorkOfficeCollaborationInitialized, assertWorkOfficeCollaborationOrigin, initializeWorkOfficeCollaborationMetadata, assertWorkOfficeCollaborationWritable, OfficeCollaborationError as WorkOfficeCollaborationError, readOfficeCollaborationMetadata as readWorkOfficeCollaborationMetadata, registerWorkOfficeCollaborationInitializer } from "./9787.js";
|
|
27
27
|
import * as __rspack_external_yjs from "yjs";
|
|
28
28
|
const BOOKMARK_REFERENCE_SELECTOR = 'span[data-document-cross-reference][data-reference-target-type="bookmark"]';
|
|
29
29
|
const BLOCK_TEXT_CONTAINER_NAMES = new Set([
|
|
@@ -6697,6 +6697,7 @@ const DocumentParagraphIdentity = Extension.create({
|
|
|
6697
6697
|
name: 'documentParagraphIdentity',
|
|
6698
6698
|
addOptions () {
|
|
6699
6699
|
return {
|
|
6700
|
+
rotateTextId: ()=>true,
|
|
6700
6701
|
types: [
|
|
6701
6702
|
'paragraph',
|
|
6702
6703
|
'heading',
|
|
@@ -6735,7 +6736,7 @@ const DocumentParagraphIdentity = Extension.create({
|
|
|
6735
6736
|
},
|
|
6736
6737
|
addProseMirrorPlugins () {
|
|
6737
6738
|
return [
|
|
6738
|
-
createDocumentParagraphIdentityPlugin(this.options.types)
|
|
6739
|
+
createDocumentParagraphIdentityPlugin(this.options.types, this.options.rotateTextId)
|
|
6739
6740
|
];
|
|
6740
6741
|
}
|
|
6741
6742
|
});
|
|
@@ -6776,11 +6777,11 @@ function applyDocumentParagraphIdentityToElement(element, source) {
|
|
|
6776
6777
|
element.setAttribute(DOCUMENT_PARAGRAPH_TEXT_ID_ATTRIBUTE, identity.textId);
|
|
6777
6778
|
return identity;
|
|
6778
6779
|
}
|
|
6779
|
-
function createDocumentParagraphIdentityPlugin(types) {
|
|
6780
|
+
function createDocumentParagraphIdentityPlugin(types, rotateTextId) {
|
|
6780
6781
|
const trackedTypes = new Set(types);
|
|
6781
6782
|
return new Plugin({
|
|
6782
6783
|
view (view) {
|
|
6783
|
-
const transaction = normalizeDocumentParagraphIdentities(view.state, trackedTypes);
|
|
6784
|
+
const transaction = normalizeDocumentParagraphIdentities(view.state, trackedTypes, rotateTextId);
|
|
6784
6785
|
if (transaction) {
|
|
6785
6786
|
transaction.setMeta('addToHistory', false);
|
|
6786
6787
|
view.dispatch(transaction);
|
|
@@ -6789,16 +6790,16 @@ function createDocumentParagraphIdentityPlugin(types) {
|
|
|
6789
6790
|
},
|
|
6790
6791
|
appendTransaction (transactions, oldState, newState) {
|
|
6791
6792
|
if (!transactions.some((transaction)=>transaction.docChanged)) return null;
|
|
6792
|
-
return normalizeDocumentParagraphIdentities(newState, trackedTypes, oldState, transactions);
|
|
6793
|
+
return normalizeDocumentParagraphIdentities(newState, trackedTypes, rotateTextId, oldState, transactions);
|
|
6793
6794
|
}
|
|
6794
6795
|
});
|
|
6795
6796
|
}
|
|
6796
|
-
function normalizeDocumentParagraphIdentities(state, trackedTypes, oldState, transactions = []) {
|
|
6797
|
+
function normalizeDocumentParagraphIdentities(state, trackedTypes, rotateTextId, oldState, transactions = []) {
|
|
6797
6798
|
const paragraphs = documentParagraphs(state.doc, trackedTypes);
|
|
6798
6799
|
const identifiedParagraphs = paragraphs.filter(({ node })=>hasDocumentParagraphIdentityComponent(node));
|
|
6799
6800
|
if (!identifiedParagraphs.length) return null;
|
|
6800
6801
|
const retainedPositions = oldState ? retainedDocumentParagraphPositions(oldState, state, transactions, trackedTypes) : new Set();
|
|
6801
|
-
const editedPositions = oldState && !transactions.some(isHistoryTransaction) ? editedDocumentParagraphPositions(oldState, state, transactions, trackedTypes) : new Set();
|
|
6802
|
+
const editedPositions = oldState && rotateTextId() && !transactions.some(isHistoryTransaction) ? editedDocumentParagraphPositions(oldState, state, transactions, trackedTypes) : new Set();
|
|
6802
6803
|
const ordered = [
|
|
6803
6804
|
...identifiedParagraphs.filter((item)=>retainedPositions.has(item.position)),
|
|
6804
6805
|
...identifiedParagraphs.filter((item)=>!retainedPositions.has(item.position))
|
|
@@ -6971,6 +6972,11 @@ const DOCUMENT_TABLE_ROW_ID_ATTRIBUTE = 'data-office-row-id';
|
|
|
6971
6972
|
const DOCUMENT_TABLE_ROW_TEXT_ID_ATTRIBUTE = 'data-office-row-text-id';
|
|
6972
6973
|
const DocumentTableRowIdentity = Extension.create({
|
|
6973
6974
|
name: 'documentTableRowIdentity',
|
|
6975
|
+
addOptions () {
|
|
6976
|
+
return {
|
|
6977
|
+
rotateTextId: ()=>true
|
|
6978
|
+
};
|
|
6979
|
+
},
|
|
6974
6980
|
addGlobalAttributes () {
|
|
6975
6981
|
return [
|
|
6976
6982
|
{
|
|
@@ -6986,7 +6992,7 @@ const DocumentTableRowIdentity = Extension.create({
|
|
|
6986
6992
|
},
|
|
6987
6993
|
addProseMirrorPlugins () {
|
|
6988
6994
|
return [
|
|
6989
|
-
createDocumentTableRowIdentityPlugin()
|
|
6995
|
+
createDocumentTableRowIdentityPlugin(this.options.rotateTextId)
|
|
6990
6996
|
];
|
|
6991
6997
|
}
|
|
6992
6998
|
});
|
|
@@ -7028,10 +7034,10 @@ function identityAttribute(htmlName) {
|
|
|
7028
7034
|
}
|
|
7029
7035
|
};
|
|
7030
7036
|
}
|
|
7031
|
-
function createDocumentTableRowIdentityPlugin() {
|
|
7037
|
+
function createDocumentTableRowIdentityPlugin(rotateTextId) {
|
|
7032
7038
|
return new Plugin({
|
|
7033
7039
|
view (view) {
|
|
7034
|
-
const transaction = normalizeDocumentTableRowIdentities(view.state);
|
|
7040
|
+
const transaction = normalizeDocumentTableRowIdentities(view.state, rotateTextId);
|
|
7035
7041
|
if (transaction) {
|
|
7036
7042
|
transaction.setMeta('addToHistory', false);
|
|
7037
7043
|
view.dispatch(transaction);
|
|
@@ -7040,16 +7046,16 @@ function createDocumentTableRowIdentityPlugin() {
|
|
|
7040
7046
|
},
|
|
7041
7047
|
appendTransaction (transactions, oldState, newState) {
|
|
7042
7048
|
if (!transactions.some((transaction)=>transaction.docChanged)) return null;
|
|
7043
|
-
return normalizeDocumentTableRowIdentities(newState, oldState, transactions);
|
|
7049
|
+
return normalizeDocumentTableRowIdentities(newState, rotateTextId, oldState, transactions);
|
|
7044
7050
|
}
|
|
7045
7051
|
});
|
|
7046
7052
|
}
|
|
7047
|
-
function normalizeDocumentTableRowIdentities(state, oldState, transactions = []) {
|
|
7053
|
+
function normalizeDocumentTableRowIdentities(state, rotateTextId, oldState, transactions = []) {
|
|
7048
7054
|
const rows = documentTableRows(state.doc);
|
|
7049
7055
|
const identifiedRows = rows.filter(({ node })=>hasDocumentTableRowIdentityComponent(node));
|
|
7050
7056
|
if (!identifiedRows.length) return null;
|
|
7051
7057
|
const retainedPositions = oldState ? retainedDocumentTableRowPositions(oldState, state, transactions) : new Set();
|
|
7052
|
-
const editedPositions = oldState && !transactions.some(isHistoryTransaction) ? editedDocumentTableRowPositions(oldState, state, transactions) : new Set();
|
|
7058
|
+
const editedPositions = oldState && rotateTextId() && !transactions.some(isHistoryTransaction) ? editedDocumentTableRowPositions(oldState, state, transactions) : new Set();
|
|
7053
7059
|
const ordered = [
|
|
7054
7060
|
...identifiedRows.filter((item)=>retainedPositions.has(item.position)),
|
|
7055
7061
|
...identifiedRows.filter((item)=>!retainedPositions.has(item.position))
|
|
@@ -8410,6 +8416,13 @@ const DocumentChange = Mark.create({
|
|
|
8410
8416
|
'data-change-id': attributes.id
|
|
8411
8417
|
})
|
|
8412
8418
|
},
|
|
8419
|
+
actorId: {
|
|
8420
|
+
default: '',
|
|
8421
|
+
parseHTML: (element)=>element.getAttribute('data-change-actor-id') ?? '',
|
|
8422
|
+
renderHTML: (attributes)=>attributes.actorId ? {
|
|
8423
|
+
'data-change-actor-id': attributes.actorId
|
|
8424
|
+
} : {}
|
|
8425
|
+
},
|
|
8413
8426
|
author: {
|
|
8414
8427
|
default: '',
|
|
8415
8428
|
parseHTML: (element)=>element.getAttribute('data-change-author') ?? '',
|
|
@@ -8451,10 +8464,12 @@ const DocumentChange = Mark.create({
|
|
|
8451
8464
|
acceptDocumentChange: (id)=>(props)=>resolveDocumentChangesCommand(props, this.type, 'accept', new Set([
|
|
8452
8465
|
id
|
|
8453
8466
|
])) > 0,
|
|
8467
|
+
acceptDocumentChanges: (ids)=>(props)=>resolveDocumentChangesCommand(props, this.type, 'accept', new Set(ids)) > 0,
|
|
8454
8468
|
rejectAllDocumentChanges: ()=>(props)=>resolveDocumentChangesCommand(props, this.type, 'reject') > 0,
|
|
8455
8469
|
rejectDocumentChange: (id)=>(props)=>resolveDocumentChangesCommand(props, this.type, 'reject', new Set([
|
|
8456
8470
|
id
|
|
8457
8471
|
])) > 0,
|
|
8472
|
+
rejectDocumentChanges: (ids)=>(props)=>resolveDocumentChangesCommand(props, this.type, 'reject', new Set(ids)) > 0,
|
|
8458
8473
|
replaceDocumentTextWithTrackedChange: (from, to, text)=>({ state, tr })=>{
|
|
8459
8474
|
if (from < 0 || to < from || to > state.doc.content.size) return false;
|
|
8460
8475
|
trackedReplacement(tr, state.doc, this.type, from, to, text, this.options.createChange);
|
|
@@ -8537,6 +8552,9 @@ function collectDocumentChanges(document1) {
|
|
|
8537
8552
|
changes.set(key, {
|
|
8538
8553
|
id,
|
|
8539
8554
|
kind,
|
|
8555
|
+
...work_document_changes_stringAttribute(mark.attrs.actorId) ? {
|
|
8556
|
+
actorId: work_document_changes_stringAttribute(mark.attrs.actorId)
|
|
8557
|
+
} : {},
|
|
8540
8558
|
author: work_document_changes_stringAttribute(mark.attrs.author) || '未知审阅者',
|
|
8541
8559
|
date: work_document_changes_stringAttribute(mark.attrs.date),
|
|
8542
8560
|
from: position,
|
|
@@ -8640,6 +8658,7 @@ function changeMark(type, kind, createChange) {
|
|
|
8640
8658
|
return type.create({
|
|
8641
8659
|
kind,
|
|
8642
8660
|
id: identity.id || createDocumentChangeId(),
|
|
8661
|
+
actorId: identity.actorId ?? '',
|
|
8643
8662
|
author: identity.author || 'A3S Work',
|
|
8644
8663
|
date: identity.date || new Date().toISOString()
|
|
8645
8664
|
});
|
|
@@ -9169,20 +9188,35 @@ function collectDocumentCommentAnchors(document1) {
|
|
|
9169
9188
|
return Array.from(anchors.values()).sort((left, right)=>left.from - right.from);
|
|
9170
9189
|
}
|
|
9171
9190
|
function documentCommentViews(comments, anchors) {
|
|
9172
|
-
const
|
|
9173
|
-
|
|
9174
|
-
|
|
9191
|
+
const anchorsById = new Map(anchors.map((anchor)=>[
|
|
9192
|
+
anchor.id,
|
|
9193
|
+
anchor
|
|
9175
9194
|
]));
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9195
|
+
const commentIds = new Set(comments.map((comment)=>comment.id));
|
|
9196
|
+
const views = comments.map((comment)=>{
|
|
9197
|
+
const normalized = normalizeDocumentComment(comment);
|
|
9198
|
+
const anchor = anchorsById.get(comment.id);
|
|
9199
|
+
return anchor ? {
|
|
9200
|
+
...normalized,
|
|
9201
|
+
...anchor,
|
|
9202
|
+
detached: false
|
|
9203
|
+
} : {
|
|
9204
|
+
...normalized,
|
|
9205
|
+
from: null,
|
|
9206
|
+
to: null,
|
|
9207
|
+
anchorText: '',
|
|
9208
|
+
detached: true
|
|
9209
|
+
};
|
|
9210
|
+
});
|
|
9211
|
+
for (const anchor of anchors)if (!commentIds.has(anchor.id)) views.push({
|
|
9212
|
+
author: '未知审阅者',
|
|
9213
|
+
date: '',
|
|
9214
|
+
text: '此批注的内容不可用。',
|
|
9215
|
+
resolved: false,
|
|
9216
|
+
...anchor,
|
|
9217
|
+
detached: false
|
|
9218
|
+
});
|
|
9219
|
+
return views;
|
|
9186
9220
|
}
|
|
9187
9221
|
function retainAnchoredDocumentComments(comments, anchors, retainedIds = new Set()) {
|
|
9188
9222
|
const ids = new Set(anchors.map((anchor)=>anchor.id));
|
|
@@ -9259,19 +9293,25 @@ function documentCommentMark(marks) {
|
|
|
9259
9293
|
return marks.find((mark)=>'documentComment' === mark.type.name);
|
|
9260
9294
|
}
|
|
9261
9295
|
function normalizeDocumentComment(comment) {
|
|
9262
|
-
|
|
9296
|
+
const normalized = {
|
|
9263
9297
|
id: comment.id,
|
|
9264
9298
|
author: comment.author || '未知审阅者',
|
|
9265
9299
|
date: comment.date || '',
|
|
9266
9300
|
text: comment.text || '(空批注)',
|
|
9267
|
-
resolved: Boolean(comment.resolved)
|
|
9268
|
-
replies: comment.replies?.map((reply)=>({
|
|
9269
|
-
id: reply.id,
|
|
9270
|
-
author: reply.author || '未知审阅者',
|
|
9271
|
-
date: reply.date || '',
|
|
9272
|
-
text: reply.text || '(空回复)'
|
|
9273
|
-
}))
|
|
9301
|
+
resolved: Boolean(comment.resolved)
|
|
9274
9302
|
};
|
|
9303
|
+
if (void 0 !== comment.actorId) normalized.actorId = comment.actorId;
|
|
9304
|
+
if (void 0 !== comment.replies) normalized.replies = comment.replies.map((reply)=>{
|
|
9305
|
+
const normalizedReply = {
|
|
9306
|
+
id: reply.id,
|
|
9307
|
+
author: reply.author || '未知审阅者',
|
|
9308
|
+
date: reply.date || '',
|
|
9309
|
+
text: reply.text || '(空回复)'
|
|
9310
|
+
};
|
|
9311
|
+
if (void 0 !== reply.actorId) normalizedReply.actorId = reply.actorId;
|
|
9312
|
+
return normalizedReply;
|
|
9313
|
+
});
|
|
9314
|
+
return normalized;
|
|
9275
9315
|
}
|
|
9276
9316
|
function work_document_comments_stringAttribute(value) {
|
|
9277
9317
|
return 'string' == typeof value ? value : '';
|
|
@@ -15576,7 +15616,9 @@ function createWorkDocumentExtensions(options = {}) {
|
|
|
15576
15616
|
DocumentTableCell,
|
|
15577
15617
|
DocumentTableHeader,
|
|
15578
15618
|
DocumentTableRow,
|
|
15579
|
-
DocumentTableRowIdentity
|
|
15619
|
+
DocumentTableRowIdentity.configure({
|
|
15620
|
+
rotateTextId: options.rotateTrackedTextIdentities ?? (()=>true)
|
|
15621
|
+
}),
|
|
15580
15622
|
DocumentTableCommands,
|
|
15581
15623
|
DocumentTableFormatting,
|
|
15582
15624
|
DocumentTableSizing,
|
|
@@ -15593,7 +15635,9 @@ function createWorkDocumentExtensions(options = {}) {
|
|
|
15593
15635
|
'paragraph'
|
|
15594
15636
|
]
|
|
15595
15637
|
}),
|
|
15596
|
-
DocumentParagraphIdentity
|
|
15638
|
+
DocumentParagraphIdentity.configure({
|
|
15639
|
+
rotateTextId: options.rotateTrackedTextIdentities ?? (()=>true)
|
|
15640
|
+
}),
|
|
15597
15641
|
DocumentParagraphFormatting,
|
|
15598
15642
|
DocumentParagraphTabStops,
|
|
15599
15643
|
DocumentTab,
|
|
@@ -15767,6 +15811,218 @@ function invalidSharedSidecars(label) {
|
|
|
15767
15811
|
function office_document_collaboration_sidecar_utils_isRecord(value) {
|
|
15768
15812
|
return isWorkOfficeCollaborationRecord(value);
|
|
15769
15813
|
}
|
|
15814
|
+
function assertWorkOfficeDocumentCommentMutationAllowed(session, previous, next) {
|
|
15815
|
+
if ('edit' === session.mode) return;
|
|
15816
|
+
if ('comment' !== session.mode) deniedReviewMutation(session);
|
|
15817
|
+
const actor = requiredReviewActor(session);
|
|
15818
|
+
assertRetainedOrder(previous, next, 'comments');
|
|
15819
|
+
const nextById = new Map(next.map((comment)=>[
|
|
15820
|
+
comment.id,
|
|
15821
|
+
comment
|
|
15822
|
+
]));
|
|
15823
|
+
const previousIds = new Set(previous.map((comment)=>comment.id));
|
|
15824
|
+
for (const comment of previous){
|
|
15825
|
+
const updated = nextById.get(comment.id);
|
|
15826
|
+
if (!updated) {
|
|
15827
|
+
assertOwnedRecord(comment.actorId, actor, 'own comment');
|
|
15828
|
+
continue;
|
|
15829
|
+
}
|
|
15830
|
+
assertExistingCommentMutation(actor, comment, updated);
|
|
15831
|
+
}
|
|
15832
|
+
for (const comment of next)if (!previousIds.has(comment.id)) assertNewComment(actor, comment);
|
|
15833
|
+
}
|
|
15834
|
+
function assertExistingCommentMutation(actor, previous, next) {
|
|
15835
|
+
if (previous.id !== next.id || previous.actorId !== next.actorId || previous.author !== next.author || previous.date !== next.date || previous.text !== next.text) deniedImmutableRecord('comment');
|
|
15836
|
+
assertReplyMutations(actor, previous.id, previous.replies ?? [], next.replies ?? []);
|
|
15837
|
+
}
|
|
15838
|
+
function assertReplyMutations(actor, commentId, previous, next) {
|
|
15839
|
+
assertRetainedOrder(previous, next, `replies in comment '${commentId}'`);
|
|
15840
|
+
const nextById = new Map(next.map((reply)=>[
|
|
15841
|
+
reply.id,
|
|
15842
|
+
reply
|
|
15843
|
+
]));
|
|
15844
|
+
const previousIds = new Set(previous.map((reply)=>reply.id));
|
|
15845
|
+
for (const reply of previous){
|
|
15846
|
+
const updated = nextById.get(reply.id);
|
|
15847
|
+
if (!updated) {
|
|
15848
|
+
assertOwnedRecord(reply.actorId, actor, 'own comment reply');
|
|
15849
|
+
continue;
|
|
15850
|
+
}
|
|
15851
|
+
if (!jsonEqual(reply, updated)) deniedImmutableRecord('comment reply');
|
|
15852
|
+
}
|
|
15853
|
+
for (const reply of next)if (!previousIds.has(reply.id)) assertActorRecord(actor, reply, 'reply');
|
|
15854
|
+
}
|
|
15855
|
+
function assertNewComment(actor, comment) {
|
|
15856
|
+
assertActorRecord(actor, comment, 'comment');
|
|
15857
|
+
for (const reply of comment.replies ?? [])assertActorRecord(actor, reply, 'reply');
|
|
15858
|
+
}
|
|
15859
|
+
function assertActorRecord(actor, record, label) {
|
|
15860
|
+
if (record.actorId === actor.id && record.author === actor.name) return;
|
|
15861
|
+
throw new WorkOfficeCollaborationError('office.collaboration.permission_denied', `A Document ${label} created in comment mode must use collaboration actor '${actor.id}' and its current display name.`);
|
|
15862
|
+
}
|
|
15863
|
+
function assertOwnedRecord(actorId, actor, label) {
|
|
15864
|
+
if (actorId === actor.id) return;
|
|
15865
|
+
throw new WorkOfficeCollaborationError('office.collaboration.permission_denied', `Comment mode can delete only the collaboration actor's ${label}.`);
|
|
15866
|
+
}
|
|
15867
|
+
function assertRetainedOrder(previous, next, label) {
|
|
15868
|
+
const previousIds = new Set(previous.map((record)=>record.id));
|
|
15869
|
+
const nextIds = new Set(next.map((record)=>record.id));
|
|
15870
|
+
const retainedBefore = previous.filter((record)=>nextIds.has(record.id)).map((record)=>record.id);
|
|
15871
|
+
const retainedAfter = next.filter((record)=>previousIds.has(record.id)).map((record)=>record.id);
|
|
15872
|
+
let encounteredNewRecord = false;
|
|
15873
|
+
let existingAfterNewRecord = false;
|
|
15874
|
+
for (const record of next)if (previousIds.has(record.id)) {
|
|
15875
|
+
if (encounteredNewRecord) existingAfterNewRecord = true;
|
|
15876
|
+
} else encounteredNewRecord = true;
|
|
15877
|
+
if (jsonEqual(retainedBefore, retainedAfter) && !existingAfterNewRecord) return;
|
|
15878
|
+
throw new WorkOfficeCollaborationError('office.collaboration.permission_denied', `Comment mode must preserve existing ${label} order and append new review records.`);
|
|
15879
|
+
}
|
|
15880
|
+
function requiredReviewActor(session) {
|
|
15881
|
+
if (session.actor) return session.actor;
|
|
15882
|
+
throw new WorkOfficeCollaborationError('office.collaboration.permission_denied', 'Comment mode requires a collaboration actor before it can write review records.');
|
|
15883
|
+
}
|
|
15884
|
+
function deniedImmutableRecord(label) {
|
|
15885
|
+
throw new WorkOfficeCollaborationError('office.collaboration.permission_denied', `Comment mode cannot rewrite an existing ${label}; it may append replies or change resolution state.`);
|
|
15886
|
+
}
|
|
15887
|
+
function deniedReviewMutation(session) {
|
|
15888
|
+
throw new WorkOfficeCollaborationError('office.collaboration.permission_denied', `The '${session.mode}' collaboration mode cannot modify Document review records.`);
|
|
15889
|
+
}
|
|
15890
|
+
function validatedWorkOfficeDocumentChangeDecisions(value) {
|
|
15891
|
+
if (!Array.isArray(value)) invalidInputSidecars('an array of tracked-change decisions');
|
|
15892
|
+
const ids = new Set();
|
|
15893
|
+
const changes = new Set();
|
|
15894
|
+
return value.map((candidate)=>{
|
|
15895
|
+
const decision = validatedDecision(candidate);
|
|
15896
|
+
if (ids.has(decision.id)) invalidInputSidecars(`a unique tracked-change decision ID; '${decision.id}' is repeated`);
|
|
15897
|
+
const change = changeIdentity(decision);
|
|
15898
|
+
if (changes.has(change)) invalidInputSidecars(`one final decision for tracked change '${decision.changeId}'`);
|
|
15899
|
+
ids.add(decision.id);
|
|
15900
|
+
changes.add(change);
|
|
15901
|
+
return decision;
|
|
15902
|
+
});
|
|
15903
|
+
}
|
|
15904
|
+
function initializeWorkOfficeDocumentChangeDecisions(records, order, decisions) {
|
|
15905
|
+
for (const decision of decisions){
|
|
15906
|
+
createDecisionRecord(records, decision);
|
|
15907
|
+
order.push([
|
|
15908
|
+
decision.id
|
|
15909
|
+
]);
|
|
15910
|
+
}
|
|
15911
|
+
}
|
|
15912
|
+
function readWorkOfficeDocumentChangeDecisions(records, order) {
|
|
15913
|
+
const decisions = validatedOrder(order, records, 'tracked-change decision').map((id)=>readDecisionRecord(requiredSharedMap(records, id, 'tracked-change decision'), id));
|
|
15914
|
+
const changes = new Set();
|
|
15915
|
+
for (const decision of decisions){
|
|
15916
|
+
const identity = changeIdentity(decision);
|
|
15917
|
+
if (changes.has(identity)) invalidSharedSidecars(`multiple final decisions for tracked change '${decision.changeId}'`);
|
|
15918
|
+
changes.add(identity);
|
|
15919
|
+
}
|
|
15920
|
+
return decisions;
|
|
15921
|
+
}
|
|
15922
|
+
function patchWorkOfficeDocumentChangeDecisions(records, order, previous, next) {
|
|
15923
|
+
const beforeById = new Map(previous.map((decision)=>[
|
|
15924
|
+
decision.id,
|
|
15925
|
+
decision
|
|
15926
|
+
]));
|
|
15927
|
+
for (const decision of previous){
|
|
15928
|
+
const candidate = next.find(({ id })=>id === decision.id);
|
|
15929
|
+
if (!candidate || !jsonEqual(candidate, decision)) immutableDecision(decision.id);
|
|
15930
|
+
}
|
|
15931
|
+
for (const decision of next)if (!beforeById.has(decision.id)) {
|
|
15932
|
+
if (!records.has(decision.id)) createDecisionRecord(records, decision);
|
|
15933
|
+
insertIntoOrder(order, next.map(({ id })=>id), decision.id);
|
|
15934
|
+
}
|
|
15935
|
+
}
|
|
15936
|
+
function assertWorkOfficeDocumentChangeDecisionConflicts(previous, next, shared) {
|
|
15937
|
+
assertNoAddedCollision(previous, next, shared, 'tracked-change decision');
|
|
15938
|
+
const nextById = new Map(next.map((decision)=>[
|
|
15939
|
+
decision.id,
|
|
15940
|
+
decision
|
|
15941
|
+
]));
|
|
15942
|
+
for (const decision of previous)if (!jsonEqual(nextById.get(decision.id), decision)) immutableDecision(decision.id);
|
|
15943
|
+
const sharedByChange = new Map(shared.map((decision)=>[
|
|
15944
|
+
changeIdentity(decision),
|
|
15945
|
+
decision
|
|
15946
|
+
]));
|
|
15947
|
+
const previousIds = new Set(previous.map(({ id })=>id));
|
|
15948
|
+
for (const decision of next){
|
|
15949
|
+
if (previousIds.has(decision.id)) continue;
|
|
15950
|
+
const current = sharedByChange.get(changeIdentity(decision));
|
|
15951
|
+
if (current && !jsonEqual(current, decision)) throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `Tracked change '${decision.changeId}' already has a different final decision.`);
|
|
15952
|
+
}
|
|
15953
|
+
}
|
|
15954
|
+
function validatedDecision(value) {
|
|
15955
|
+
if (!office_document_collaboration_sidecar_utils_isRecord(value)) invalidInputSidecars('valid tracked-change decision records');
|
|
15956
|
+
const decision = {
|
|
15957
|
+
id: requiredIdentifier(value.id, 'tracked-change decision'),
|
|
15958
|
+
changeId: requiredIdentifier(value.changeId, 'tracked change'),
|
|
15959
|
+
changeKind: office_document_collaboration_change_decisions_changeKind(value.changeKind, false),
|
|
15960
|
+
suggestedBy: requiredString(value.suggestedBy, 'suggestion author'),
|
|
15961
|
+
suggestedAt: requiredString(value.suggestedAt, 'suggestion date'),
|
|
15962
|
+
text: requiredString(value.text, 'suggestion text'),
|
|
15963
|
+
decision: decisionAction(value.decision, false),
|
|
15964
|
+
decidedBy: requiredString(value.decidedBy, 'decision author'),
|
|
15965
|
+
decidedAt: requiredString(value.decidedAt, 'decision date')
|
|
15966
|
+
};
|
|
15967
|
+
if (void 0 !== value.suggestedByActorId) decision.suggestedByActorId = requiredIdentifier(value.suggestedByActorId, 'suggestion actor');
|
|
15968
|
+
if (void 0 !== value.decidedByActorId) decision.decidedByActorId = requiredIdentifier(value.decidedByActorId, 'decision actor');
|
|
15969
|
+
return decision;
|
|
15970
|
+
}
|
|
15971
|
+
function createDecisionRecord(records, decision) {
|
|
15972
|
+
const record = new __rspack_external_yjs.Map();
|
|
15973
|
+
records.set(decision.id, record);
|
|
15974
|
+
for (const [key, value] of Object.entries(decision))record.set(key, value);
|
|
15975
|
+
}
|
|
15976
|
+
function readDecisionRecord(record, expectedId) {
|
|
15977
|
+
const id = requiredSharedIdentifier(record.get('id'), 'tracked-change decision');
|
|
15978
|
+
if (id !== expectedId) invalidSharedSidecars('tracked-change decision identity');
|
|
15979
|
+
const allowed = new Set([
|
|
15980
|
+
'id',
|
|
15981
|
+
'changeId',
|
|
15982
|
+
'changeKind',
|
|
15983
|
+
'suggestedByActorId',
|
|
15984
|
+
'suggestedBy',
|
|
15985
|
+
'suggestedAt',
|
|
15986
|
+
'text',
|
|
15987
|
+
'decision',
|
|
15988
|
+
'decidedByActorId',
|
|
15989
|
+
'decidedBy',
|
|
15990
|
+
'decidedAt'
|
|
15991
|
+
]);
|
|
15992
|
+
if (Array.from(record.keys()).some((key)=>!allowed.has(key))) invalidSharedSidecars('tracked-change decision fields');
|
|
15993
|
+
const decision = {
|
|
15994
|
+
id,
|
|
15995
|
+
changeId: requiredSharedIdentifier(record.get('changeId'), 'tracked change'),
|
|
15996
|
+
changeKind: office_document_collaboration_change_decisions_changeKind(record.get('changeKind'), true),
|
|
15997
|
+
suggestedBy: requiredSharedString(record.get('suggestedBy'), 'suggestion author'),
|
|
15998
|
+
suggestedAt: requiredSharedString(record.get('suggestedAt'), 'suggestion date'),
|
|
15999
|
+
text: requiredSharedString(record.get('text'), 'suggestion text'),
|
|
16000
|
+
decision: decisionAction(record.get('decision'), true),
|
|
16001
|
+
decidedBy: requiredSharedString(record.get('decidedBy'), 'decision author'),
|
|
16002
|
+
decidedAt: requiredSharedString(record.get('decidedAt'), 'decision date')
|
|
16003
|
+
};
|
|
16004
|
+
const suggestedByActorId = record.get('suggestedByActorId');
|
|
16005
|
+
if (void 0 !== suggestedByActorId) decision.suggestedByActorId = requiredSharedIdentifier(suggestedByActorId, 'suggestion actor');
|
|
16006
|
+
const decidedByActorId = record.get('decidedByActorId');
|
|
16007
|
+
if (void 0 !== decidedByActorId) decision.decidedByActorId = requiredSharedIdentifier(decidedByActorId, 'decision actor');
|
|
16008
|
+
return decision;
|
|
16009
|
+
}
|
|
16010
|
+
function changeIdentity(decision) {
|
|
16011
|
+
return `${decision.changeKind}:${decision.changeId}`;
|
|
16012
|
+
}
|
|
16013
|
+
function office_document_collaboration_change_decisions_changeKind(value, shared) {
|
|
16014
|
+
if ('insertion' === value || 'deletion' === value) return value;
|
|
16015
|
+
if (shared) invalidSharedSidecars('tracked-change decision kind');
|
|
16016
|
+
invalidInputSidecars('an insertion or deletion tracked-change kind');
|
|
16017
|
+
}
|
|
16018
|
+
function decisionAction(value, shared) {
|
|
16019
|
+
if ('accept' === value || 'reject' === value) return value;
|
|
16020
|
+
if (shared) invalidSharedSidecars('tracked-change decision action');
|
|
16021
|
+
invalidInputSidecars('an accept or reject tracked-change decision');
|
|
16022
|
+
}
|
|
16023
|
+
function immutableDecision(id) {
|
|
16024
|
+
throw new WorkOfficeCollaborationError('office.collaboration.permission_denied', `Tracked-change decision '${id}' is immutable and cannot be rewritten or removed.`);
|
|
16025
|
+
}
|
|
15770
16026
|
function validatedWorkOfficeDocumentBibliography(value) {
|
|
15771
16027
|
if (!office_document_collaboration_sidecar_utils_isRecord(value) || !isCitationStyle(value.style)) invalidInputSidecars('a supported bibliography style');
|
|
15772
16028
|
if (!Array.isArray(value.sources)) invalidInputSidecars('an array of bibliography sources');
|
|
@@ -15995,6 +16251,7 @@ function validatedComment(value) {
|
|
|
15995
16251
|
text: requiredString(value.text, 'comment text'),
|
|
15996
16252
|
resolved: requiredBoolean(value.resolved, 'comment resolution')
|
|
15997
16253
|
};
|
|
16254
|
+
if (void 0 !== value.actorId) comment.actorId = requiredIdentifier(value.actorId, 'comment actor');
|
|
15998
16255
|
if (void 0 !== value.replies) {
|
|
15999
16256
|
if (!Array.isArray(value.replies)) invalidInputSidecars('an array of comment replies');
|
|
16000
16257
|
const ids = new Set();
|
|
@@ -16009,17 +16266,20 @@ function validatedComment(value) {
|
|
|
16009
16266
|
}
|
|
16010
16267
|
function validatedReply(value) {
|
|
16011
16268
|
if (!office_document_collaboration_sidecar_utils_isRecord(value)) invalidInputSidecars('valid comment reply records');
|
|
16012
|
-
|
|
16269
|
+
const reply = {
|
|
16013
16270
|
id: requiredIdentifier(value.id, 'comment reply'),
|
|
16014
16271
|
author: requiredString(value.author, 'comment reply author'),
|
|
16015
16272
|
date: requiredString(value.date, 'comment reply date'),
|
|
16016
16273
|
text: requiredString(value.text, 'comment reply text')
|
|
16017
16274
|
};
|
|
16275
|
+
if (void 0 !== value.actorId) reply.actorId = requiredIdentifier(value.actorId, 'comment reply actor');
|
|
16276
|
+
return reply;
|
|
16018
16277
|
}
|
|
16019
16278
|
function createCommentRecord(records, comment) {
|
|
16020
16279
|
const record = new __rspack_external_yjs.Map();
|
|
16021
16280
|
records.set(comment.id, record);
|
|
16022
16281
|
record.set('id', comment.id);
|
|
16282
|
+
if (void 0 !== comment.actorId) record.set('actorId', comment.actorId);
|
|
16023
16283
|
record.set('author', comment.author);
|
|
16024
16284
|
record.set('date', comment.date);
|
|
16025
16285
|
record.set('text', comment.text);
|
|
@@ -16039,6 +16299,7 @@ function createReplyRecord(records, reply) {
|
|
|
16039
16299
|
const record = new __rspack_external_yjs.Map();
|
|
16040
16300
|
records.set(reply.id, record);
|
|
16041
16301
|
record.set('id', reply.id);
|
|
16302
|
+
if (void 0 !== reply.actorId) record.set('actorId', reply.actorId);
|
|
16042
16303
|
record.set('author', reply.author);
|
|
16043
16304
|
record.set('date', reply.date);
|
|
16044
16305
|
record.set('text', reply.text);
|
|
@@ -16056,20 +16317,26 @@ function readCommentRecord(record, expectedId) {
|
|
|
16056
16317
|
text: requiredSharedString(record.get('text'), 'comment text'),
|
|
16057
16318
|
resolved: requiredSharedBoolean(record.get('resolved'), 'comment resolution')
|
|
16058
16319
|
};
|
|
16320
|
+
const actorId = optionalSharedIdentifier(record.get('actorId'), 'comment actor');
|
|
16321
|
+
if (void 0 !== actorId) comment.actorId = actorId;
|
|
16059
16322
|
if (orderedReplies.length > 0) comment.replies = orderedReplies;
|
|
16060
16323
|
return comment;
|
|
16061
16324
|
}
|
|
16062
16325
|
function readReplyRecord(record, expectedId) {
|
|
16063
16326
|
const id = requiredSharedIdentifier(record.get('id'), 'comment reply');
|
|
16064
16327
|
if (id !== expectedId) invalidSharedSidecars('comment reply identity');
|
|
16065
|
-
|
|
16328
|
+
const reply = {
|
|
16066
16329
|
id,
|
|
16067
16330
|
author: requiredSharedString(record.get('author'), 'comment reply author'),
|
|
16068
16331
|
date: requiredSharedString(record.get('date'), 'comment reply date'),
|
|
16069
16332
|
text: requiredSharedString(record.get('text'), 'comment reply text')
|
|
16070
16333
|
};
|
|
16334
|
+
const actorId = optionalSharedIdentifier(record.get('actorId'), 'comment reply actor');
|
|
16335
|
+
if (void 0 !== actorId) reply.actorId = actorId;
|
|
16336
|
+
return reply;
|
|
16071
16337
|
}
|
|
16072
16338
|
function patchCommentRecord(record, previous, next) {
|
|
16339
|
+
patchOptionalScalar(record, 'actorId', previous.actorId, next.actorId);
|
|
16073
16340
|
patchRequiredScalar(record, 'author', previous.author, next.author);
|
|
16074
16341
|
patchRequiredScalar(record, 'date', previous.date, next.date);
|
|
16075
16342
|
patchRequiredScalar(record, 'text', previous.text, next.text);
|
|
@@ -16098,12 +16365,18 @@ function patchReplies(records, order, previous, next) {
|
|
|
16098
16365
|
}
|
|
16099
16366
|
if (jsonEqual(before, reply)) continue;
|
|
16100
16367
|
const record = requiredSharedMap(records, reply.id, 'comment reply');
|
|
16368
|
+
patchOptionalScalar(record, 'actorId', before.actorId, reply.actorId);
|
|
16101
16369
|
patchRequiredScalar(record, 'author', before.author, reply.author);
|
|
16102
16370
|
patchRequiredScalar(record, 'date', before.date, reply.date);
|
|
16103
16371
|
patchRequiredScalar(record, 'text', before.text, reply.text);
|
|
16104
16372
|
}
|
|
16105
16373
|
}
|
|
16374
|
+
function optionalSharedIdentifier(value, label) {
|
|
16375
|
+
return void 0 === value ? void 0 : requiredSharedIdentifier(value, label);
|
|
16376
|
+
}
|
|
16106
16377
|
function appendWorkOfficeDocumentRecordClaims(claims, previous, next) {
|
|
16378
|
+
const previousDecisions = new Set((previous.changeDecisions ?? []).map((decision)=>decision.id));
|
|
16379
|
+
for (const decision of next.changeDecisions ?? [])if (!previousDecisions.has(decision.id)) appendClaim(claims, 'change-decision', decision.id, decision);
|
|
16107
16380
|
const previousComments = new Map((previous.comments ?? []).map((comment)=>[
|
|
16108
16381
|
comment.id,
|
|
16109
16382
|
comment
|
|
@@ -16126,6 +16399,7 @@ function assertWorkOfficeDocumentRecordClaims(claims, sidecars) {
|
|
|
16126
16399
|
if (void 0 !== existing && existing !== claim.fingerprint) throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `The ${claimLabel(claim)} ID '${claim.id}' was concurrently assigned to different records.`);
|
|
16127
16400
|
fingerprints.set(identity, claim.fingerprint);
|
|
16128
16401
|
}
|
|
16402
|
+
for (const decision of sidecars.changeDecisions ?? [])assertClaimExists(fingerprints, 'change-decision', decision.id);
|
|
16129
16403
|
for (const comment of sidecars.comments ?? []){
|
|
16130
16404
|
assertClaimExists(fingerprints, 'comment', comment.id);
|
|
16131
16405
|
for (const reply of comment.replies ?? [])assertClaimExists(fingerprints, 'comment-reply', reply.id, comment.id);
|
|
@@ -16157,7 +16431,7 @@ function parsedClaim(rawClaim) {
|
|
|
16157
16431
|
const keys = Object.keys(value);
|
|
16158
16432
|
if (keys.some((key)=>'fingerprint' !== key && 'id' !== key && 'kind' !== key && 'parentId' !== key)) invalidSharedSidecars('record claim');
|
|
16159
16433
|
const kind = value.kind;
|
|
16160
|
-
if ('bibliography-source' !== kind && 'comment' !== kind && 'comment-reply' !== kind) invalidSharedSidecars('record claim kind');
|
|
16434
|
+
if ('bibliography-source' !== kind && 'change-decision' !== kind && 'comment' !== kind && 'comment-reply' !== kind) invalidSharedSidecars('record claim kind');
|
|
16161
16435
|
const id = requiredSharedIdentifier(value.id, 'record claim');
|
|
16162
16436
|
const fingerprint = requiredSharedString(value.fingerprint, 'record claim fingerprint');
|
|
16163
16437
|
const parentId = void 0 === value.parentId ? void 0 : requiredSharedIdentifier(value.parentId, 'record claim parent');
|
|
@@ -16190,6 +16464,7 @@ function claimIdentity(kind, id, parentId) {
|
|
|
16190
16464
|
]);
|
|
16191
16465
|
}
|
|
16192
16466
|
function claimLabel(claim) {
|
|
16467
|
+
if ('change-decision' === claim.kind) return 'tracked-change decision';
|
|
16193
16468
|
if ('comment' === claim.kind) return 'comment';
|
|
16194
16469
|
if ('bibliography-source' === claim.kind) return 'bibliography source';
|
|
16195
16470
|
return `reply in comment '${claim.parentId}'`;
|
|
@@ -16197,13 +16472,15 @@ function claimLabel(claim) {
|
|
|
16197
16472
|
const DOCUMENT_OPTIONS_ROOT = 'document.options';
|
|
16198
16473
|
const DOCUMENT_COMMENTS_ROOT = 'document.comments';
|
|
16199
16474
|
const DOCUMENT_COMMENT_ORDER_ROOT = 'document.comment-order';
|
|
16475
|
+
const DOCUMENT_CHANGE_DECISIONS_ROOT = 'document.change-decisions';
|
|
16476
|
+
const DOCUMENT_CHANGE_DECISION_ORDER_ROOT = 'document.change-decision-order';
|
|
16200
16477
|
const DOCUMENT_BIBLIOGRAPHY_ROOT = 'document.bibliography';
|
|
16201
16478
|
const DOCUMENT_BIBLIOGRAPHY_SOURCES_ROOT = 'document.bibliography.sources';
|
|
16202
16479
|
const DOCUMENT_BIBLIOGRAPHY_SOURCE_ORDER_ROOT = 'document.bibliography.source-order';
|
|
16203
16480
|
const DOCUMENT_RECORD_CLAIMS_ROOT = 'document.record-claims';
|
|
16204
16481
|
function assertWorkOfficeDocumentSidecarsEmpty(session) {
|
|
16205
16482
|
const roots = documentSidecarRoots(session);
|
|
16206
|
-
if (roots.options.size > 0 || roots.comments.size > 0 || roots.commentOrder.length > 0 || roots.bibliography.size > 0 || roots.bibliographySources.size > 0 || roots.bibliographySourceOrder.length > 0 || roots.recordClaims.length > 0) throw new WorkOfficeCollaborationError('office.collaboration.bootstrap_ambiguous', 'The Document collaboration sidecars contain data without initialized metadata.');
|
|
16483
|
+
if (roots.options.size > 0 || roots.comments.size > 0 || roots.commentOrder.length > 0 || roots.changeDecisions.size > 0 || roots.changeDecisionOrder.length > 0 || roots.bibliography.size > 0 || roots.bibliographySources.size > 0 || roots.bibliographySourceOrder.length > 0 || roots.recordClaims.length > 0) throw new WorkOfficeCollaborationError('office.collaboration.bootstrap_ambiguous', 'The Document collaboration sidecars contain data without initialized metadata.');
|
|
16207
16484
|
}
|
|
16208
16485
|
function initializeWorkOfficeDocumentSidecars(session, content) {
|
|
16209
16486
|
const roots = documentSidecarRoots(session);
|
|
@@ -16211,6 +16488,7 @@ function initializeWorkOfficeDocumentSidecars(session, content) {
|
|
|
16211
16488
|
appendWorkOfficeDocumentRecordClaims(roots.recordClaims, {}, sidecars);
|
|
16212
16489
|
if (void 0 !== sidecars.pageColor) roots.options.set('pageColor', sidecars.pageColor);
|
|
16213
16490
|
if (void 0 !== sidecars.trackChanges) roots.options.set('trackChanges', sidecars.trackChanges);
|
|
16491
|
+
if (void 0 !== sidecars.changeDecisions) initializeWorkOfficeDocumentChangeDecisions(roots.changeDecisions, roots.changeDecisionOrder, sidecars.changeDecisions);
|
|
16214
16492
|
if (void 0 !== sidecars.comments) {
|
|
16215
16493
|
roots.options.set('commentsPresent', true);
|
|
16216
16494
|
initializeWorkOfficeDocumentComments(roots.comments, roots.commentOrder, sidecars.comments);
|
|
@@ -16233,6 +16511,8 @@ function readWorkOfficeDocumentSidecars(session) {
|
|
|
16233
16511
|
if ('boolean' != typeof trackChanges) invalidSharedSidecars('track-changes setting');
|
|
16234
16512
|
result.trackChanges = trackChanges;
|
|
16235
16513
|
}
|
|
16514
|
+
if (roots.changeDecisionOrder.length > 0) result.changeDecisions = readWorkOfficeDocumentChangeDecisions(roots.changeDecisions, roots.changeDecisionOrder);
|
|
16515
|
+
else if (roots.changeDecisions.size > 0) invalidSharedSidecars('tracked-change decision order and record set');
|
|
16236
16516
|
const commentsPresent = optionalPresence(roots.options.get('commentsPresent'), 'comment');
|
|
16237
16517
|
if (true === commentsPresent || roots.commentOrder.length > 0) result.comments = readWorkOfficeDocumentComments(roots.comments, roots.commentOrder);
|
|
16238
16518
|
else if (roots.comments.size > 0) invalidSharedSidecars('comment order and record set');
|
|
@@ -16246,14 +16526,17 @@ function updateWorkOfficeDocumentSidecars(session, previous, next, origin) {
|
|
|
16246
16526
|
const before = validatedWorkOfficeDocumentSidecars(previous);
|
|
16247
16527
|
const after = validatedWorkOfficeDocumentSidecars(next);
|
|
16248
16528
|
if (jsonEqual(before, after)) return false;
|
|
16529
|
+
assertDocumentSidecarMutationAllowed(session, before, after);
|
|
16249
16530
|
const roots = documentSidecarRoots(session);
|
|
16250
16531
|
const shared = readWorkOfficeDocumentSidecars(session);
|
|
16251
16532
|
assertWorkOfficeDocumentCommentConflicts(before.comments ?? [], after.comments ?? [], shared.comments ?? []);
|
|
16533
|
+
assertWorkOfficeDocumentChangeDecisionConflicts(before.changeDecisions ?? [], after.changeDecisions ?? [], shared.changeDecisions ?? []);
|
|
16252
16534
|
assertWorkOfficeDocumentBibliographyConflicts(before.bibliography, after.bibliography, shared.bibliography);
|
|
16253
16535
|
session.transact(()=>{
|
|
16254
16536
|
appendWorkOfficeDocumentRecordClaims(roots.recordClaims, before, after);
|
|
16255
16537
|
patchOptionalScalar(roots.options, 'pageColor', before.pageColor, after.pageColor);
|
|
16256
16538
|
patchOptionalScalar(roots.options, 'trackChanges', before.trackChanges, after.trackChanges);
|
|
16539
|
+
if (!jsonEqual(before.changeDecisions, after.changeDecisions)) patchWorkOfficeDocumentChangeDecisions(roots.changeDecisions, roots.changeDecisionOrder, before.changeDecisions ?? [], after.changeDecisions ?? []);
|
|
16257
16540
|
if (!jsonEqual(before.comments, after.comments)) {
|
|
16258
16541
|
patchPresence(roots.options, 'commentsPresent', before.comments, after.comments);
|
|
16259
16542
|
patchWorkOfficeDocumentComments(roots.comments, roots.commentOrder, before.comments ?? [], after.comments ?? []);
|
|
@@ -16262,7 +16545,7 @@ function updateWorkOfficeDocumentSidecars(session, previous, next, origin) {
|
|
|
16262
16545
|
patchPresence(roots.options, 'bibliographyPresent', before.bibliography, after.bibliography, false);
|
|
16263
16546
|
patchWorkOfficeDocumentBibliography(roots.bibliography, roots.bibliographySources, roots.bibliographySourceOrder, before.bibliography, after.bibliography);
|
|
16264
16547
|
}
|
|
16265
|
-
}, origin);
|
|
16548
|
+
}, origin, 'comment' === session.mode ? 'document-comment' : 'content');
|
|
16266
16549
|
return true;
|
|
16267
16550
|
}
|
|
16268
16551
|
function workOfficeDocumentSidecarUndoScope(session) {
|
|
@@ -16277,9 +16560,19 @@ function workOfficeDocumentSidecarUndoScope(session) {
|
|
|
16277
16560
|
roots.recordClaims
|
|
16278
16561
|
];
|
|
16279
16562
|
}
|
|
16563
|
+
function workOfficeDocumentDecisionRootsChanged(session, transaction) {
|
|
16564
|
+
const roots = documentSidecarRoots(session);
|
|
16565
|
+
const changed = new Set(transaction.changedParentTypes.keys());
|
|
16566
|
+
return changed.has(roots.changeDecisions) || changed.has(roots.changeDecisionOrder);
|
|
16567
|
+
}
|
|
16280
16568
|
function workOfficeDocumentSidecarsChanged(session, transaction) {
|
|
16281
16569
|
const changed = new Set(transaction.changedParentTypes.keys());
|
|
16282
|
-
|
|
16570
|
+
const roots = documentSidecarRoots(session);
|
|
16571
|
+
return [
|
|
16572
|
+
...workOfficeDocumentSidecarUndoScope(session),
|
|
16573
|
+
roots.changeDecisions,
|
|
16574
|
+
roots.changeDecisionOrder
|
|
16575
|
+
].some((root)=>changed.has(root));
|
|
16283
16576
|
}
|
|
16284
16577
|
function validatedWorkOfficeDocumentSidecars(content) {
|
|
16285
16578
|
if (!content || 'document' !== content.type) invalidInputSidecars('a Document content value');
|
|
@@ -16292,15 +16585,24 @@ function validatedWorkOfficeDocumentSidecars(content) {
|
|
|
16292
16585
|
if ('boolean' != typeof content.trackChanges) invalidInputSidecars('a boolean track-changes setting');
|
|
16293
16586
|
result.trackChanges = content.trackChanges;
|
|
16294
16587
|
}
|
|
16588
|
+
if (void 0 !== content.changeDecisions) result.changeDecisions = validatedWorkOfficeDocumentChangeDecisions(content.changeDecisions);
|
|
16295
16589
|
if (void 0 !== content.comments) result.comments = validatedWorkOfficeDocumentComments(content.comments);
|
|
16296
16590
|
if (void 0 !== content.bibliography) result.bibliography = validatedWorkOfficeDocumentBibliography(content.bibliography);
|
|
16297
16591
|
return result;
|
|
16298
16592
|
}
|
|
16593
|
+
function assertDocumentSidecarMutationAllowed(session, previous, next) {
|
|
16594
|
+
if ('edit' === session.mode) return;
|
|
16595
|
+
if ('comment' !== session.mode) return void assertWorkOfficeCollaborationEditable(session);
|
|
16596
|
+
if (!jsonEqual(previous.pageColor, next.pageColor) || !jsonEqual(previous.trackChanges, next.trackChanges) || !jsonEqual(previous.changeDecisions, next.changeDecisions) || !jsonEqual(previous.bibliography, next.bibliography)) throw new WorkOfficeCollaborationError('office.collaboration.permission_denied', 'The comment collaboration mode can modify only Document review records.');
|
|
16597
|
+
assertWorkOfficeDocumentCommentMutationAllowed(session, previous.comments ?? [], next.comments ?? []);
|
|
16598
|
+
}
|
|
16299
16599
|
function documentSidecarRoots(session) {
|
|
16300
16600
|
return {
|
|
16301
16601
|
options: session.document.getMap(session.rootName(DOCUMENT_OPTIONS_ROOT)),
|
|
16302
16602
|
comments: session.document.getMap(session.rootName(DOCUMENT_COMMENTS_ROOT)),
|
|
16303
16603
|
commentOrder: session.document.getArray(session.rootName(DOCUMENT_COMMENT_ORDER_ROOT)),
|
|
16604
|
+
changeDecisions: session.document.getMap(session.rootName(DOCUMENT_CHANGE_DECISIONS_ROOT)),
|
|
16605
|
+
changeDecisionOrder: session.document.getArray(session.rootName(DOCUMENT_CHANGE_DECISION_ORDER_ROOT)),
|
|
16304
16606
|
bibliography: session.document.getMap(session.rootName(DOCUMENT_BIBLIOGRAPHY_ROOT)),
|
|
16305
16607
|
bibliographySources: session.document.getMap(session.rootName(DOCUMENT_BIBLIOGRAPHY_SOURCES_ROOT)),
|
|
16306
16608
|
bibliographySourceOrder: session.document.getArray(session.rootName(DOCUMENT_BIBLIOGRAPHY_SOURCE_ORDER_ROOT)),
|
|
@@ -16317,7 +16619,154 @@ function patchPresence(target, key, previous, next, absentValue) {
|
|
|
16317
16619
|
else if (void 0 !== previous) if (false === absentValue) target.set(key, false);
|
|
16318
16620
|
else target.delete(key);
|
|
16319
16621
|
}
|
|
16622
|
+
function workOfficeDocumentSuggestionTransactionAllowed(session, transaction) {
|
|
16623
|
+
const actor = session.actor;
|
|
16624
|
+
if (!actor || !transaction.docChanged || 0 === transaction.steps.length) return false;
|
|
16625
|
+
if (!transaction.steps.every((step)=>step instanceof ReplaceStep || (step instanceof AddMarkStep || step instanceof RemoveMarkStep) && 'documentChange' === step.mark.type.name)) return false;
|
|
16626
|
+
const before = strictDocumentChanges(transaction.before);
|
|
16627
|
+
const after = strictDocumentChanges(transaction.doc);
|
|
16628
|
+
if (!before || !after) return false;
|
|
16629
|
+
if (JSON.stringify(documentSuggestionBaseline(transaction.before)) !== JSON.stringify(documentSuggestionBaseline(transaction.doc))) return false;
|
|
16630
|
+
const beforeById = new Map(before.map((change)=>[
|
|
16631
|
+
change.id,
|
|
16632
|
+
change
|
|
16633
|
+
]));
|
|
16634
|
+
const afterById = new Map(after.map((change)=>[
|
|
16635
|
+
change.id,
|
|
16636
|
+
change
|
|
16637
|
+
]));
|
|
16638
|
+
for (const previous of before){
|
|
16639
|
+
const current = afterById.get(previous.id);
|
|
16640
|
+
if (!current) {
|
|
16641
|
+
if (previous.actorId !== actor.id) return false;
|
|
16642
|
+
continue;
|
|
16643
|
+
}
|
|
16644
|
+
if (!sameChangeIdentity(previous, current)) return false;
|
|
16645
|
+
if (previous.actorId !== actor.id && previous.text !== current.text) return false;
|
|
16646
|
+
if (previous.actorId === actor.id && 'deletion' === previous.kind && previous.text !== current.text) return false;
|
|
16647
|
+
}
|
|
16648
|
+
for (const current of after)if (!beforeById.has(current.id)) {
|
|
16649
|
+
if (current.actorId !== actor.id || current.author !== actor.name || !current.date || !current.text) return false;
|
|
16650
|
+
}
|
|
16651
|
+
return true;
|
|
16652
|
+
}
|
|
16653
|
+
function createWorkOfficeDocumentChangeDecisions(session, changes, decision, decidedAt = new Date().toISOString()) {
|
|
16654
|
+
const decidedBy = session.actor?.name ?? 'A3S Work 用户';
|
|
16655
|
+
return changes.map((change)=>({
|
|
16656
|
+
id: `${change.kind}:${change.id}`,
|
|
16657
|
+
changeId: change.id,
|
|
16658
|
+
changeKind: change.kind,
|
|
16659
|
+
...change.actorId ? {
|
|
16660
|
+
suggestedByActorId: change.actorId
|
|
16661
|
+
} : {},
|
|
16662
|
+
suggestedBy: change.author,
|
|
16663
|
+
suggestedAt: change.date,
|
|
16664
|
+
text: change.text,
|
|
16665
|
+
decision,
|
|
16666
|
+
...session.actor ? {
|
|
16667
|
+
decidedByActorId: session.actor.id
|
|
16668
|
+
} : {},
|
|
16669
|
+
decidedBy,
|
|
16670
|
+
decidedAt
|
|
16671
|
+
}));
|
|
16672
|
+
}
|
|
16673
|
+
function strictDocumentChanges(document1) {
|
|
16674
|
+
const changes = new Map();
|
|
16675
|
+
let valid = true;
|
|
16676
|
+
document1.descendants((node)=>{
|
|
16677
|
+
if (!valid || !node.isText || !node.text) return;
|
|
16678
|
+
const marks = node.marks.filter((mark)=>'documentChange' === mark.type.name);
|
|
16679
|
+
if (0 === marks.length) return;
|
|
16680
|
+
if (1 !== marks.length) {
|
|
16681
|
+
valid = false;
|
|
16682
|
+
return false;
|
|
16683
|
+
}
|
|
16684
|
+
const mark = marks[0];
|
|
16685
|
+
const id = strictString(mark.attrs.id);
|
|
16686
|
+
const kind = strictChangeKind(mark.attrs.kind);
|
|
16687
|
+
const author = strictString(mark.attrs.author);
|
|
16688
|
+
const date = strictString(mark.attrs.date);
|
|
16689
|
+
const actorId = optionalStrictString(mark.attrs.actorId);
|
|
16690
|
+
if (!id || !kind || null === author || null === date || null === actorId) {
|
|
16691
|
+
valid = false;
|
|
16692
|
+
return false;
|
|
16693
|
+
}
|
|
16694
|
+
const current = changes.get(id);
|
|
16695
|
+
const candidate = {
|
|
16696
|
+
id,
|
|
16697
|
+
kind,
|
|
16698
|
+
...actorId ? {
|
|
16699
|
+
actorId
|
|
16700
|
+
} : {},
|
|
16701
|
+
author,
|
|
16702
|
+
date,
|
|
16703
|
+
text: node.text
|
|
16704
|
+
};
|
|
16705
|
+
if (!current) return void changes.set(id, candidate);
|
|
16706
|
+
if (!sameChangeIdentity(current, candidate)) {
|
|
16707
|
+
valid = false;
|
|
16708
|
+
return false;
|
|
16709
|
+
}
|
|
16710
|
+
current.text += node.text;
|
|
16711
|
+
});
|
|
16712
|
+
return valid ? Array.from(changes.values()) : null;
|
|
16713
|
+
}
|
|
16714
|
+
function documentSuggestionBaseline(node) {
|
|
16715
|
+
const json = node.toJSON();
|
|
16716
|
+
if (node.isText) {
|
|
16717
|
+
const change = node.marks.find((mark)=>'documentChange' === mark.type.name);
|
|
16718
|
+
if (change?.attrs.kind === 'insertion') return null;
|
|
16719
|
+
const marks = node.marks.filter((mark)=>'documentChange' !== mark.type.name).map((mark)=>mark.toJSON());
|
|
16720
|
+
if (marks.length > 0) return {
|
|
16721
|
+
...json,
|
|
16722
|
+
marks
|
|
16723
|
+
};
|
|
16724
|
+
const { marks: _marks, ...withoutMarks } = json;
|
|
16725
|
+
return withoutMarks;
|
|
16726
|
+
}
|
|
16727
|
+
const content = [];
|
|
16728
|
+
node.forEach((child)=>{
|
|
16729
|
+
const projected = documentSuggestionBaseline(child);
|
|
16730
|
+
if (null !== projected) appendSuggestionBaselineNode(content, projected);
|
|
16731
|
+
});
|
|
16732
|
+
if (content.length > 0) return {
|
|
16733
|
+
...json,
|
|
16734
|
+
content
|
|
16735
|
+
};
|
|
16736
|
+
const { content: _content, ...withoutContent } = json;
|
|
16737
|
+
return withoutContent;
|
|
16738
|
+
}
|
|
16739
|
+
function appendSuggestionBaselineNode(content, projected) {
|
|
16740
|
+
const previous = content.at(-1);
|
|
16741
|
+
if (isSuggestionBaselineText(previous) && isSuggestionBaselineText(projected) && JSON.stringify(previous.marks ?? []) === JSON.stringify(projected.marks ?? [])) {
|
|
16742
|
+
content[content.length - 1] = {
|
|
16743
|
+
...previous,
|
|
16744
|
+
text: previous.text + projected.text
|
|
16745
|
+
};
|
|
16746
|
+
return;
|
|
16747
|
+
}
|
|
16748
|
+
content.push(projected);
|
|
16749
|
+
}
|
|
16750
|
+
function isSuggestionBaselineText(value) {
|
|
16751
|
+
if (!value || 'object' != typeof value) return false;
|
|
16752
|
+
const candidate = value;
|
|
16753
|
+
return 'text' === candidate.type && 'string' == typeof candidate.text;
|
|
16754
|
+
}
|
|
16755
|
+
function sameChangeIdentity(left, right) {
|
|
16756
|
+
return left.id === right.id && left.kind === right.kind && left.actorId === right.actorId && left.author === right.author && left.date === right.date;
|
|
16757
|
+
}
|
|
16758
|
+
function strictString(value) {
|
|
16759
|
+
return 'string' == typeof value && value.trim() === value && value ? value : null;
|
|
16760
|
+
}
|
|
16761
|
+
function optionalStrictString(value) {
|
|
16762
|
+
if (void 0 === value || '' === value) return;
|
|
16763
|
+
return strictString(value);
|
|
16764
|
+
}
|
|
16765
|
+
function strictChangeKind(value) {
|
|
16766
|
+
return 'insertion' === value || 'deletion' === value ? value : null;
|
|
16767
|
+
}
|
|
16320
16768
|
const DOCUMENT_CONTENT_ROOT = 'document.content';
|
|
16769
|
+
const MAX_DOCUMENT_COMMENT_HISTORY = 100;
|
|
16321
16770
|
const mountedDocumentBindings = new WeakMap();
|
|
16322
16771
|
function initializeWorkOfficeDocumentCollaboration(session, content) {
|
|
16323
16772
|
assertDocumentSession(session);
|
|
@@ -16381,6 +16830,9 @@ class WorkOfficeDocumentCollaborationBindingImpl {
|
|
|
16381
16830
|
#listeners = new Set();
|
|
16382
16831
|
#errorListeners = new Set();
|
|
16383
16832
|
#historyListeners = new Set();
|
|
16833
|
+
#commentUndoStack = [];
|
|
16834
|
+
#commentRedoStack = [];
|
|
16835
|
+
#applyingCommentHistory = false;
|
|
16384
16836
|
#destroyed = false;
|
|
16385
16837
|
#destroyRequested = false;
|
|
16386
16838
|
#observersAttached = false;
|
|
@@ -16395,7 +16847,8 @@ class WorkOfficeDocumentCollaborationBindingImpl {
|
|
|
16395
16847
|
assertWorkOfficeCollaborationOrigin(this.origin);
|
|
16396
16848
|
const builtIns = createWorkDocumentExtensions({
|
|
16397
16849
|
...options.workExtensions,
|
|
16398
|
-
collaborative: true
|
|
16850
|
+
collaborative: true,
|
|
16851
|
+
rotateTrackedTextIdentities: ()=>'suggest' !== session.mode
|
|
16399
16852
|
});
|
|
16400
16853
|
const additional = options.additionalExtensions ?? [];
|
|
16401
16854
|
assertBehaviorOnlyExtensions(additional);
|
|
@@ -16418,10 +16871,13 @@ class WorkOfficeDocumentCollaborationBindingImpl {
|
|
|
16418
16871
|
]));
|
|
16419
16872
|
const existing = mountedDocumentBindings.get(session.document);
|
|
16420
16873
|
if (existing) throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', 'Only one Document collaboration binding may use a local Y.Doc at a time. Give each editor client its own synchronized Y.Doc.');
|
|
16421
|
-
|
|
16874
|
+
const undoScope = 'comment' === session.mode || 'suggest' === session.mode ? [
|
|
16875
|
+
this.fragment
|
|
16876
|
+
] : [
|
|
16422
16877
|
this.fragment,
|
|
16423
16878
|
...workOfficeDocumentSidecarUndoScope(session)
|
|
16424
|
-
]
|
|
16879
|
+
];
|
|
16880
|
+
this.#undoManager = new __rspack_external_yjs.UndoManager(undoScope, {
|
|
16425
16881
|
captureTimeout: options.captureTimeoutMs ?? 500,
|
|
16426
16882
|
captureTransaction: (transaction)=>false !== transaction.meta.get('addToHistory'),
|
|
16427
16883
|
deleteFilter: (item)=>defaultDeleteFilter(item, defaultProtectedNodes),
|
|
@@ -16433,7 +16889,7 @@ class WorkOfficeDocumentCollaborationBindingImpl {
|
|
|
16433
16889
|
const restore = restorable.restore?.bind(this.#undoManager);
|
|
16434
16890
|
if (restore) restorable.restore = ()=>{
|
|
16435
16891
|
restore();
|
|
16436
|
-
this.#undoManager.addToScope([
|
|
16892
|
+
this.#undoManager.addToScope('comment' === this.#session.mode || 'suggest' === this.#session.mode ? this.fragment : [
|
|
16437
16893
|
this.fragment,
|
|
16438
16894
|
...workOfficeDocumentSidecarUndoScope(this.#session)
|
|
16439
16895
|
]);
|
|
@@ -16466,23 +16922,79 @@ class WorkOfficeDocumentCollaborationBindingImpl {
|
|
|
16466
16922
|
}
|
|
16467
16923
|
updateSidecars(previous, next) {
|
|
16468
16924
|
this.ensureActive();
|
|
16469
|
-
|
|
16925
|
+
const historyEntry = 'comment' !== this.#session.mode || this.#applyingCommentHistory ? null : createDocumentCommentHistoryEntry(previous, next);
|
|
16926
|
+
if (historyEntry?.commentMembershipChanged) this.#undoManager.stopCapturing();
|
|
16927
|
+
const changed = updateWorkOfficeDocumentSidecars(this.#session, previous, next, this.origin);
|
|
16928
|
+
if (changed && historyEntry) {
|
|
16929
|
+
appendBoundedCommentHistory(this.#commentUndoStack, historyEntry);
|
|
16930
|
+
this.#commentRedoStack.length = 0;
|
|
16931
|
+
this.#onHistoryChange();
|
|
16932
|
+
}
|
|
16933
|
+
return changed;
|
|
16934
|
+
}
|
|
16935
|
+
decideChanges(editor, changeIds, decision, options = {}) {
|
|
16936
|
+
this.ensureActive();
|
|
16937
|
+
assertWorkOfficeCollaborationEditable(this.#session);
|
|
16938
|
+
if (editor.isDestroyed || 0 === changeIds.length) return false;
|
|
16939
|
+
const requested = new Set(changeIds);
|
|
16940
|
+
const changes = collectDocumentChanges(editor.state.doc).filter((change)=>requested.has(change.id));
|
|
16941
|
+
if (changes.length !== requested.size) return false;
|
|
16942
|
+
const before = this.content();
|
|
16943
|
+
const decisions = createWorkOfficeDocumentChangeDecisions(this.#session, changes, decision, options.decidedAt);
|
|
16944
|
+
const existing = new Set((before.changeDecisions ?? []).map(({ id })=>id));
|
|
16945
|
+
if (decisions.some(({ id })=>existing.has(id))) throw new WorkOfficeCollaborationError('office.collaboration.permission_denied', 'A tracked change with a final decision cannot be decided again.');
|
|
16946
|
+
const origin = this.#session.createOrigin(this.#session.localOrigin.kind);
|
|
16947
|
+
let handled = false;
|
|
16948
|
+
this.#session.transact(()=>{
|
|
16949
|
+
handled = 'accept' === decision ? editor.commands.acceptDocumentChanges(changeIds) : editor.commands.rejectDocumentChanges(changeIds);
|
|
16950
|
+
if (!handled) return;
|
|
16951
|
+
updateWorkOfficeDocumentSidecars(this.#session, before, {
|
|
16952
|
+
...before,
|
|
16953
|
+
changeDecisions: [
|
|
16954
|
+
...before.changeDecisions ?? [],
|
|
16955
|
+
...decisions
|
|
16956
|
+
]
|
|
16957
|
+
}, origin);
|
|
16958
|
+
}, origin);
|
|
16959
|
+
if (handled) {
|
|
16960
|
+
this.#undoManager.clear();
|
|
16961
|
+
this.#onHistoryChange();
|
|
16962
|
+
}
|
|
16963
|
+
return handled;
|
|
16470
16964
|
}
|
|
16471
16965
|
canUndo() {
|
|
16472
16966
|
this.ensureActive();
|
|
16967
|
+
if ('comment' === this.#session.mode) return this.#commentUndoStack.length > 0 || this.#undoManager.canUndo();
|
|
16473
16968
|
return this.#undoManager.canUndo();
|
|
16474
16969
|
}
|
|
16475
16970
|
canRedo() {
|
|
16476
16971
|
this.ensureActive();
|
|
16972
|
+
if ('comment' === this.#session.mode) return this.#commentRedoStack.length > 0 || this.#undoManager.canRedo();
|
|
16477
16973
|
return this.#undoManager.canRedo();
|
|
16478
16974
|
}
|
|
16479
16975
|
undo() {
|
|
16480
16976
|
this.ensureActive();
|
|
16977
|
+
if ('comment' === this.#session.mode) {
|
|
16978
|
+
assertWorkOfficeCollaborationWritable(this.#session, 'document-comment');
|
|
16979
|
+
return this.applyCommentHistory('undo');
|
|
16980
|
+
}
|
|
16981
|
+
if ('suggest' === this.#session.mode) {
|
|
16982
|
+
assertWorkOfficeCollaborationWritable(this.#session, 'document-suggestion');
|
|
16983
|
+
return null !== this.#undoManager.undo();
|
|
16984
|
+
}
|
|
16481
16985
|
assertWorkOfficeCollaborationEditable(this.#session);
|
|
16482
16986
|
return null !== this.#undoManager.undo();
|
|
16483
16987
|
}
|
|
16484
16988
|
redo() {
|
|
16485
16989
|
this.ensureActive();
|
|
16990
|
+
if ('comment' === this.#session.mode) {
|
|
16991
|
+
assertWorkOfficeCollaborationWritable(this.#session, 'document-comment');
|
|
16992
|
+
return this.applyCommentHistory('redo');
|
|
16993
|
+
}
|
|
16994
|
+
if ('suggest' === this.#session.mode) {
|
|
16995
|
+
assertWorkOfficeCollaborationWritable(this.#session, 'document-suggestion');
|
|
16996
|
+
return null !== this.#undoManager.redo();
|
|
16997
|
+
}
|
|
16486
16998
|
assertWorkOfficeCollaborationEditable(this.#session);
|
|
16487
16999
|
return null !== this.#undoManager.redo();
|
|
16488
17000
|
}
|
|
@@ -16525,6 +17037,10 @@ class WorkOfficeDocumentCollaborationBindingImpl {
|
|
|
16525
17037
|
if (transaction.origin === ySyncPluginKey) transaction.origin = this.origin;
|
|
16526
17038
|
};
|
|
16527
17039
|
#onTransaction = (transaction)=>{
|
|
17040
|
+
if ('suggest' === this.#session.mode && workOfficeDocumentDecisionRootsChanged(this.#session, transaction)) {
|
|
17041
|
+
this.#undoManager.clear();
|
|
17042
|
+
this.#onHistoryChange();
|
|
17043
|
+
}
|
|
16528
17044
|
if (this.#destroyed || this.#destroyRequested || !transactionTouchesRoot(transaction, this.fragment) && !workOfficeDocumentSidecarsChanged(this.#session, transaction)) return;
|
|
16529
17045
|
const local = transaction.origin === ySyncPluginKey || transaction.origin === this.origin || transaction.origin === this.#undoManager;
|
|
16530
17046
|
this.#pendingLocal = this.#pendingChange ? this.#pendingLocal && local : local;
|
|
@@ -16559,6 +17075,30 @@ class WorkOfficeDocumentCollaborationBindingImpl {
|
|
|
16559
17075
|
#onHistoryChange = ()=>{
|
|
16560
17076
|
for (const listener of this.#historyListeners)listener();
|
|
16561
17077
|
};
|
|
17078
|
+
applyCommentHistory(direction) {
|
|
17079
|
+
const sourceStack = 'undo' === direction ? this.#commentUndoStack : this.#commentRedoStack;
|
|
17080
|
+
const targetStack = 'undo' === direction ? this.#commentRedoStack : this.#commentUndoStack;
|
|
17081
|
+
const entry = sourceStack.pop();
|
|
17082
|
+
if (!entry) return 'undo' === direction ? null !== this.#undoManager.undo() : null !== this.#undoManager.redo();
|
|
17083
|
+
const from = 'undo' === direction ? entry.after : entry.before;
|
|
17084
|
+
const to = 'undo' === direction ? entry.before : entry.after;
|
|
17085
|
+
const current = this.content();
|
|
17086
|
+
const comments = reconcileDocumentCommentHistory(current.comments, from, to);
|
|
17087
|
+
let sidecarsChanged = false;
|
|
17088
|
+
this.#applyingCommentHistory = true;
|
|
17089
|
+
try {
|
|
17090
|
+
if (!jsonEqual(current.comments, comments)) sidecarsChanged = this.updateSidecars(current, {
|
|
17091
|
+
...current,
|
|
17092
|
+
comments
|
|
17093
|
+
});
|
|
17094
|
+
} finally{
|
|
17095
|
+
this.#applyingCommentHistory = false;
|
|
17096
|
+
}
|
|
17097
|
+
const anchorChanged = entry.commentMembershipChanged ? 'undo' === direction ? null !== this.#undoManager.undo() : null !== this.#undoManager.redo() : false;
|
|
17098
|
+
appendBoundedCommentHistory(targetStack, entry);
|
|
17099
|
+
this.#onHistoryChange();
|
|
17100
|
+
return sidecarsChanged || anchorChanged;
|
|
17101
|
+
}
|
|
16562
17102
|
finishDestroy(destroyUndoManager) {
|
|
16563
17103
|
if (this.#destroyed) return;
|
|
16564
17104
|
this.#destroyed = true;
|
|
@@ -16572,6 +17112,8 @@ class WorkOfficeDocumentCollaborationBindingImpl {
|
|
|
16572
17112
|
this.#listeners.clear();
|
|
16573
17113
|
this.#errorListeners.clear();
|
|
16574
17114
|
this.#historyListeners.clear();
|
|
17115
|
+
this.#commentUndoStack.length = 0;
|
|
17116
|
+
this.#commentRedoStack.length = 0;
|
|
16575
17117
|
}
|
|
16576
17118
|
detachObservers() {
|
|
16577
17119
|
if (!this.#observersAttached) return;
|
|
@@ -16587,6 +17129,104 @@ class WorkOfficeDocumentCollaborationBindingImpl {
|
|
|
16587
17129
|
throw new WorkOfficeCollaborationError('office.collaboration.binding_destroyed', 'The Document collaboration binding has been destroyed.');
|
|
16588
17130
|
}
|
|
16589
17131
|
}
|
|
17132
|
+
function createDocumentCommentHistoryEntry(previous, next) {
|
|
17133
|
+
if (jsonEqual(previous.comments, next.comments)) return null;
|
|
17134
|
+
const before = cloneDocumentComments(previous.comments);
|
|
17135
|
+
const after = cloneDocumentComments(next.comments);
|
|
17136
|
+
return {
|
|
17137
|
+
before,
|
|
17138
|
+
after,
|
|
17139
|
+
commentMembershipChanged: !sameDocumentCommentMembership(before, after)
|
|
17140
|
+
};
|
|
17141
|
+
}
|
|
17142
|
+
function appendBoundedCommentHistory(stack, entry) {
|
|
17143
|
+
stack.push(entry);
|
|
17144
|
+
if (stack.length > MAX_DOCUMENT_COMMENT_HISTORY) stack.shift();
|
|
17145
|
+
}
|
|
17146
|
+
function sameDocumentCommentMembership(left, right) {
|
|
17147
|
+
return jsonEqual((left ?? []).map(({ id })=>id), (right ?? []).map(({ id })=>id));
|
|
17148
|
+
}
|
|
17149
|
+
function reconcileDocumentCommentHistory(current, from, to) {
|
|
17150
|
+
const fromById = new Map((from ?? []).map((comment)=>[
|
|
17151
|
+
comment.id,
|
|
17152
|
+
comment
|
|
17153
|
+
]));
|
|
17154
|
+
const toById = new Map((to ?? []).map((comment)=>[
|
|
17155
|
+
comment.id,
|
|
17156
|
+
comment
|
|
17157
|
+
]));
|
|
17158
|
+
const result = cloneDocumentComments(current) ?? [];
|
|
17159
|
+
const resultById = ()=>new Map(result.map((comment)=>[
|
|
17160
|
+
comment.id,
|
|
17161
|
+
comment
|
|
17162
|
+
]));
|
|
17163
|
+
const orderedIds = [
|
|
17164
|
+
...(from ?? []).map(({ id })=>id),
|
|
17165
|
+
...(to ?? []).map(({ id })=>id)
|
|
17166
|
+
].filter((id, index, ids)=>ids.indexOf(id) === index);
|
|
17167
|
+
for (const id of orderedIds){
|
|
17168
|
+
const source = fromById.get(id);
|
|
17169
|
+
const target = toById.get(id);
|
|
17170
|
+
const currentById = resultById();
|
|
17171
|
+
const existing = currentById.get(id);
|
|
17172
|
+
if (!source && target) {
|
|
17173
|
+
if (!existing) result.push(cloneDocumentComment(target));
|
|
17174
|
+
continue;
|
|
17175
|
+
}
|
|
17176
|
+
if (source && !target) {
|
|
17177
|
+
if (existing && jsonEqual(existing, source)) result.splice(result.findIndex((comment)=>comment.id === id), 1);
|
|
17178
|
+
continue;
|
|
17179
|
+
}
|
|
17180
|
+
if (!source || !target || !existing) continue;
|
|
17181
|
+
const next = reconcileExistingDocumentComment(existing, source, target);
|
|
17182
|
+
const index = result.findIndex((comment)=>comment.id === id);
|
|
17183
|
+
if (index >= 0) result[index] = next;
|
|
17184
|
+
}
|
|
17185
|
+
if (result.length > 0) return result;
|
|
17186
|
+
return void 0 === to ? void 0 : [];
|
|
17187
|
+
}
|
|
17188
|
+
function reconcileExistingDocumentComment(current, from, to) {
|
|
17189
|
+
const next = cloneDocumentComment(current);
|
|
17190
|
+
if (from.resolved !== to.resolved && current.resolved === from.resolved) next.resolved = to.resolved;
|
|
17191
|
+
const fromReplies = new Map((from.replies ?? []).map((reply)=>[
|
|
17192
|
+
reply.id,
|
|
17193
|
+
reply
|
|
17194
|
+
]));
|
|
17195
|
+
const toReplies = new Map((to.replies ?? []).map((reply)=>[
|
|
17196
|
+
reply.id,
|
|
17197
|
+
reply
|
|
17198
|
+
]));
|
|
17199
|
+
const replies = (next.replies ?? []).map((reply)=>({
|
|
17200
|
+
...reply
|
|
17201
|
+
}));
|
|
17202
|
+
const replyIds = [
|
|
17203
|
+
...(from.replies ?? []).map(({ id })=>id),
|
|
17204
|
+
...(to.replies ?? []).map(({ id })=>id)
|
|
17205
|
+
].filter((id, index, ids)=>ids.indexOf(id) === index);
|
|
17206
|
+
for (const id of replyIds){
|
|
17207
|
+
const source = fromReplies.get(id);
|
|
17208
|
+
const target = toReplies.get(id);
|
|
17209
|
+
const index = replies.findIndex((reply)=>reply.id === id);
|
|
17210
|
+
if (!source && target && index < 0) replies.push({
|
|
17211
|
+
...target
|
|
17212
|
+
});
|
|
17213
|
+
else if (source && !target && index >= 0 && jsonEqual(replies[index], source)) replies.splice(index, 1);
|
|
17214
|
+
}
|
|
17215
|
+
if (replies.length > 0 || void 0 !== to.replies) next.replies = replies;
|
|
17216
|
+
else delete next.replies;
|
|
17217
|
+
return next;
|
|
17218
|
+
}
|
|
17219
|
+
function cloneDocumentComments(comments) {
|
|
17220
|
+
return comments?.map(cloneDocumentComment);
|
|
17221
|
+
}
|
|
17222
|
+
function cloneDocumentComment(comment) {
|
|
17223
|
+
return {
|
|
17224
|
+
...comment,
|
|
17225
|
+
replies: comment.replies?.map((reply)=>({
|
|
17226
|
+
...reply
|
|
17227
|
+
}))
|
|
17228
|
+
};
|
|
17229
|
+
}
|
|
16590
17230
|
function workOfficeDocumentCollaborationFragment(session) {
|
|
16591
17231
|
assertDocumentSession(session);
|
|
16592
17232
|
return session.document.getXmlFragment(session.rootName(DOCUMENT_CONTENT_ROOT));
|
|
@@ -16676,7 +17316,7 @@ function documentCollaborationPermissionExtension(session) {
|
|
|
16676
17316
|
return [
|
|
16677
17317
|
new Plugin({
|
|
16678
17318
|
filterTransaction (transaction) {
|
|
16679
|
-
return 'edit' === session.mode || !transaction.docChanged || isChangeOrigin(transaction);
|
|
17319
|
+
return 'edit' === session.mode || !transaction.docChanged || isChangeOrigin(transaction) || 'comment' === session.mode && transaction.steps.length > 0 && transaction.steps.every((step)=>(step instanceof AddMarkStep || step instanceof RemoveMarkStep) && 'documentComment' === step.mark.type.name) || 'suggest' === session.mode && workOfficeDocumentSuggestionTransactionAllowed(session, transaction);
|
|
16680
17320
|
}
|
|
16681
17321
|
})
|
|
16682
17322
|
];
|