@ai-matrx/office 0.8.28 → 0.8.30

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/dist/docs.js CHANGED
@@ -50,11 +50,111 @@ function takeCovered(snapshot) {
50
50
  };
51
51
  }
52
52
 
53
+ // src/docs/comment-anchors.ts
54
+ import { BuildTextUtils, CustomRangeType, JSONX } from "@univerjs/core";
55
+ var COMMENT_ANCHOR_RANGE_TYPE = CustomRangeType.COMMENT;
56
+ var COMMENT_ANCHOR_PART_PREFIX = "anchor:";
57
+ var COMMENT_ANCHOR_LABEL_MAX = 200;
58
+ var ID_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
59
+ function newCommentAnchorId() {
60
+ const bytes = new Uint8Array(16);
61
+ globalThis.crypto.getRandomValues(bytes);
62
+ let out = "c";
63
+ for (const b of bytes) out += ID_ALPHABET[b % ID_ALPHABET.length];
64
+ return out;
65
+ }
66
+ function commentAnchorPartKey(anchorId) {
67
+ return `${COMMENT_ANCHOR_PART_PREFIX}${anchorId}`;
68
+ }
69
+ function anchorIdOfPartAnchor(anchor) {
70
+ if (!anchor || typeof anchor !== "object") return null;
71
+ const a = anchor;
72
+ if ((a.__kind === "part_anchor" || a.__kind === "text_anchor") && typeof a.anchor_id === "string" && a.anchor_id.length > 0) return a.anchor_id;
73
+ if (a.__kind !== "part_anchor" || typeof a.part_key !== "string") return null;
74
+ if (!a.part_key.startsWith(COMMENT_ANCHOR_PART_PREFIX)) return null;
75
+ const id = a.part_key.slice(COMMENT_ANCHOR_PART_PREFIX.length);
76
+ return id.length > 0 ? id : null;
77
+ }
78
+ function quoteOfCommentAnchor(anchor) {
79
+ if (!anchor || typeof anchor !== "object") return "";
80
+ const a = anchor;
81
+ return typeof a.label === "string" ? a.label : typeof a.exact === "string" ? a.exact : "";
82
+ }
83
+ function commentPartAnchor(anchorId, quote) {
84
+ const label = quote.replace(/\s+/g, " ").trim().slice(0, COMMENT_ANCHOR_LABEL_MAX);
85
+ return { __kind: "part_anchor", part_key: commentAnchorPartKey(anchorId), ...label ? { label } : {} };
86
+ }
87
+ var CONTROL = /[\u0000-\u001f\u007f]/g;
88
+ function commentAnchorsOf(body) {
89
+ const out = /* @__PURE__ */ new Map();
90
+ const b = body ?? {};
91
+ const stream = typeof b.dataStream === "string" ? b.dataStream : "";
92
+ for (const r of b.customRanges ?? []) {
93
+ if (!r || r.rangeType !== COMMENT_ANCHOR_RANGE_TYPE || typeof r.rangeId !== "string") continue;
94
+ const start = r.startIndex;
95
+ const end = r.endIndex + 1;
96
+ if (!(end > start)) continue;
97
+ const had = out.get(r.rangeId);
98
+ const s2 = had ? Math.min(had.start, start) : start;
99
+ const e2 = had ? Math.max(had.end, end) : end;
100
+ out.set(r.rangeId, { id: r.rangeId, start: s2, end: e2, text: "" });
101
+ }
102
+ for (const span of out.values()) span.text = stream.slice(span.start, span.end).replace(CONTROL, " ");
103
+ return out;
104
+ }
105
+ function addCommentAnchorActions(body, start, end, anchorId) {
106
+ if (!(end > start) || start < 0) return null;
107
+ const textX = BuildTextUtils.customRange.add({
108
+ body,
109
+ ranges: [{ startOffset: start, endOffset: end, collapsed: false }],
110
+ rangeId: anchorId,
111
+ rangeType: COMMENT_ANCHOR_RANGE_TYPE
112
+ });
113
+ if (!textX) return null;
114
+ return JSONX.getInstance().editOp(textX.serialize(), ["body"]);
115
+ }
116
+ function removeCommentAnchorActions(documentDataModel, anchorId) {
117
+ const textX = BuildTextUtils.customRange.delete({ documentDataModel, rangeId: anchorId });
118
+ if (!textX) return null;
119
+ return JSONX.getInstance().editOp(textX.serialize(), ["body"]);
120
+ }
121
+ var RETAIN_KEYS = /* @__PURE__ */ new Set(["t", "len", "body", "coverType"]);
122
+ var RANGE_KEYS = /* @__PURE__ */ new Set(["rangeId", "rangeType", "startIndex", "endIndex", "properties"]);
123
+ var isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
124
+ var keysOf = (o) => Object.keys(o).filter((k) => o[k] !== void 0);
125
+ function isDocsAnchorOnlyOp(op) {
126
+ if (!Array.isArray(op) || op.length !== 2 || op[0] !== "body") return false;
127
+ const x = op[1];
128
+ if (!isObj(x) || x.et !== "text-x" || !Array.isArray(x.e) || x.e.length === 0) return false;
129
+ if (keysOf(x).some((k) => k !== "et" && k !== "e")) return false;
130
+ for (const c of x.e) {
131
+ if (!isObj(c) || c.t !== "r" || typeof c.len !== "number") return false;
132
+ if (keysOf(c).some((k) => !RETAIN_KEYS.has(k))) return false;
133
+ if ("coverType" in c && typeof c.coverType !== "number") return false;
134
+ if ("body" in c) {
135
+ const b = c.body;
136
+ if (!isObj(b) || keysOf(b).some((k) => k !== "dataStream" && k !== "customRanges")) return false;
137
+ if (b.dataStream !== void 0 && b.dataStream !== "") return false;
138
+ if (!Array.isArray(b.customRanges) || b.customRanges.length === 0) return false;
139
+ for (const r of b.customRanges) {
140
+ if (!isObj(r) || r.rangeType !== COMMENT_ANCHOR_RANGE_TYPE) return false;
141
+ if (keysOf(r).some((k) => !RANGE_KEYS.has(k))) return false;
142
+ }
143
+ }
144
+ }
145
+ return true;
146
+ }
147
+
53
148
  // src/docs/document-model.ts
54
149
  var RICH_TEXT_EDITING_ID = "doc.mutation.rich-text-editing";
55
150
  var COMMAND_TYPE = 0;
56
151
  var DEPTH_GUARDED = /* @__PURE__ */ Symbol.for("ai-matrx.office.docs.command-depth-guard");
57
152
  var DOCUMENT_SAVE_DELAY_MS = 2500;
153
+ function isCommentAnchorMutation(info) {
154
+ if (info.id !== RICH_TEXT_EDITING_ID) return false;
155
+ const params = info.params;
156
+ return !!params && !params.segmentId && isDocsAnchorOnlyOp(params.actions);
157
+ }
58
158
  var viewIds = 0;
59
159
  var DocumentModel = class {
60
160
  documentId;
@@ -193,7 +293,9 @@ var DocumentModel = class {
193
293
  onViewMutation(viewId, info, options) {
194
294
  if (this.relaying || options?.fromCollab || options?.fromChangeset) return;
195
295
  const view = this.views.get(viewId);
196
- if (!view || !view.port.editable()) return;
296
+ if (!view) return;
297
+ const editable = view.port.editable();
298
+ if (!editable && !(view.port.commentOnly?.() === true && isCommentAnchorMutation(info))) return;
197
299
  this.lastEditedView = viewId;
198
300
  this.relay(info, viewId);
199
301
  for (const listener of this.collabListeners) listener(info, options);
@@ -208,7 +310,7 @@ var DocumentModel = class {
208
310
  view.pre = null;
209
311
  for (const listener of Array.from(this.otListeners)) listener(m);
210
312
  }
211
- if (isContentMutation(info)) this.markDirty();
313
+ if (editable && isContentMutation(info)) this.markDirty();
212
314
  }
213
315
  /**
214
316
  * Per view: the outermost command running (a mutation's group label) and, while a room listens, the document
@@ -959,101 +1061,6 @@ var RemoteCaretBook = class {
959
1061
  return out;
960
1062
  }
961
1063
  };
962
-
963
- // src/docs/comment-anchors.ts
964
- import { BuildTextUtils, CustomRangeType, JSONX } from "@univerjs/core";
965
- var COMMENT_ANCHOR_RANGE_TYPE = CustomRangeType.COMMENT;
966
- var COMMENT_ANCHOR_PART_PREFIX = "anchor:";
967
- var COMMENT_ANCHOR_LABEL_MAX = 200;
968
- var ID_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
969
- function newCommentAnchorId() {
970
- const bytes = new Uint8Array(16);
971
- globalThis.crypto.getRandomValues(bytes);
972
- let out = "c";
973
- for (const b of bytes) out += ID_ALPHABET[b % ID_ALPHABET.length];
974
- return out;
975
- }
976
- function commentAnchorPartKey(anchorId) {
977
- return `${COMMENT_ANCHOR_PART_PREFIX}${anchorId}`;
978
- }
979
- function anchorIdOfPartAnchor(anchor) {
980
- if (!anchor || typeof anchor !== "object") return null;
981
- const a = anchor;
982
- if ((a.__kind === "part_anchor" || a.__kind === "text_anchor") && typeof a.anchor_id === "string" && a.anchor_id.length > 0) return a.anchor_id;
983
- if (a.__kind !== "part_anchor" || typeof a.part_key !== "string") return null;
984
- if (!a.part_key.startsWith(COMMENT_ANCHOR_PART_PREFIX)) return null;
985
- const id = a.part_key.slice(COMMENT_ANCHOR_PART_PREFIX.length);
986
- return id.length > 0 ? id : null;
987
- }
988
- function quoteOfCommentAnchor(anchor) {
989
- if (!anchor || typeof anchor !== "object") return "";
990
- const a = anchor;
991
- return typeof a.label === "string" ? a.label : typeof a.exact === "string" ? a.exact : "";
992
- }
993
- function commentPartAnchor(anchorId, quote) {
994
- const label = quote.replace(/\s+/g, " ").trim().slice(0, COMMENT_ANCHOR_LABEL_MAX);
995
- return { __kind: "part_anchor", part_key: commentAnchorPartKey(anchorId), ...label ? { label } : {} };
996
- }
997
- var CONTROL = /[\u0000-\u001f\u007f]/g;
998
- function commentAnchorsOf(body) {
999
- const out = /* @__PURE__ */ new Map();
1000
- const b = body ?? {};
1001
- const stream = typeof b.dataStream === "string" ? b.dataStream : "";
1002
- for (const r of b.customRanges ?? []) {
1003
- if (!r || r.rangeType !== COMMENT_ANCHOR_RANGE_TYPE || typeof r.rangeId !== "string") continue;
1004
- const start = r.startIndex;
1005
- const end = r.endIndex + 1;
1006
- if (!(end > start)) continue;
1007
- const had = out.get(r.rangeId);
1008
- const s2 = had ? Math.min(had.start, start) : start;
1009
- const e2 = had ? Math.max(had.end, end) : end;
1010
- out.set(r.rangeId, { id: r.rangeId, start: s2, end: e2, text: "" });
1011
- }
1012
- for (const span of out.values()) span.text = stream.slice(span.start, span.end).replace(CONTROL, " ");
1013
- return out;
1014
- }
1015
- function addCommentAnchorActions(body, start, end, anchorId) {
1016
- if (!(end > start) || start < 0) return null;
1017
- const textX = BuildTextUtils.customRange.add({
1018
- body,
1019
- ranges: [{ startOffset: start, endOffset: end, collapsed: false }],
1020
- rangeId: anchorId,
1021
- rangeType: COMMENT_ANCHOR_RANGE_TYPE
1022
- });
1023
- if (!textX) return null;
1024
- return JSONX.getInstance().editOp(textX.serialize(), ["body"]);
1025
- }
1026
- function removeCommentAnchorActions(documentDataModel, anchorId) {
1027
- const textX = BuildTextUtils.customRange.delete({ documentDataModel, rangeId: anchorId });
1028
- if (!textX) return null;
1029
- return JSONX.getInstance().editOp(textX.serialize(), ["body"]);
1030
- }
1031
- var RETAIN_KEYS = /* @__PURE__ */ new Set(["t", "len", "body", "coverType"]);
1032
- var RANGE_KEYS = /* @__PURE__ */ new Set(["rangeId", "rangeType", "startIndex", "endIndex", "properties"]);
1033
- var isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
1034
- var keysOf = (o) => Object.keys(o).filter((k) => o[k] !== void 0);
1035
- function isDocsAnchorOnlyOp(op) {
1036
- if (!Array.isArray(op) || op.length !== 2 || op[0] !== "body") return false;
1037
- const x = op[1];
1038
- if (!isObj(x) || x.et !== "text-x" || !Array.isArray(x.e) || x.e.length === 0) return false;
1039
- if (keysOf(x).some((k) => k !== "et" && k !== "e")) return false;
1040
- for (const c of x.e) {
1041
- if (!isObj(c) || c.t !== "r" || typeof c.len !== "number") return false;
1042
- if (keysOf(c).some((k) => !RETAIN_KEYS.has(k))) return false;
1043
- if ("coverType" in c && typeof c.coverType !== "number") return false;
1044
- if ("body" in c) {
1045
- const b = c.body;
1046
- if (!isObj(b) || keysOf(b).some((k) => k !== "dataStream" && k !== "customRanges")) return false;
1047
- if (b.dataStream !== void 0 && b.dataStream !== "") return false;
1048
- if (!Array.isArray(b.customRanges) || b.customRanges.length === 0) return false;
1049
- for (const r of b.customRanges) {
1050
- if (!isObj(r) || r.rangeType !== COMMENT_ANCHOR_RANGE_TYPE) return false;
1051
- if (keysOf(r).some((k) => !RANGE_KEYS.has(k))) return false;
1052
- }
1053
- }
1054
- }
1055
- return true;
1056
- }
1057
1064
  export {
1058
1065
  CARET_HEARTBEAT_MS,
1059
1066
  CARET_MAX_RANGES,