@arbidocs/coedit 0.4.120 → 0.4.122
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/commands.cjs +222 -12
- package/dist/commands.cjs.map +1 -1
- package/dist/commands.d.cts +82 -5
- package/dist/commands.d.ts +82 -5
- package/dist/commands.js +217 -13
- package/dist/commands.js.map +1 -1
- package/dist/encoding.d.cts +2 -0
- package/dist/encoding.d.ts +2 -0
- package/dist/hocuspocus.cjs +27 -0
- package/dist/hocuspocus.cjs.map +1 -1
- package/dist/hocuspocus.js +27 -0
- package/dist/hocuspocus.js.map +1 -1
- package/dist/index.cjs +27 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -1
- package/dist/{port-ip7WByvH.d.cts → port-BbN5OZHx.d.cts} +24 -1
- package/dist/{port-ip7WByvH.d.ts → port-BbN5OZHx.d.ts} +24 -1
- package/dist/server.cjs +65 -2
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +65 -2
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/commands.cjs
CHANGED
|
@@ -10,6 +10,14 @@ var fflate = require('fflate');
|
|
|
10
10
|
var DEFINED_TERM_STYLE = "DefinedTerm";
|
|
11
11
|
var CROSS_REFERENCE_STYLE = "CrossReference";
|
|
12
12
|
var BROKEN_CROSS_REFERENCE_STYLE = "CrossReferenceBroken";
|
|
13
|
+
function engineOffset(paragraph, offset, pastDeletions = false) {
|
|
14
|
+
let out = offset;
|
|
15
|
+
for (const r of paragraph.revisions ?? [])
|
|
16
|
+
if (r.kind === "delete" && (r.offset < offset || pastDeletions && r.offset === offset))
|
|
17
|
+
out += r.length;
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
var engineLength = (paragraph) => engineOffset(paragraph, paragraph.text.length, true);
|
|
13
21
|
var WATCHED_STYLES = /* @__PURE__ */ new Set([BROKEN_CROSS_REFERENCE_STYLE]);
|
|
14
22
|
var attr = (node, name) => node.attributes?.find((a) => a.localName === name)?.value;
|
|
15
23
|
var FIELD_ATOM = "\uFFFC";
|
|
@@ -44,6 +52,7 @@ function readParagraph(node, topLevel, bookmarks) {
|
|
|
44
52
|
let runStyle;
|
|
45
53
|
const fields = [];
|
|
46
54
|
const notes = [];
|
|
55
|
+
const revisions = [];
|
|
47
56
|
let current = null;
|
|
48
57
|
const field = (c) => {
|
|
49
58
|
const type = attr(c, "fldCharType");
|
|
@@ -85,7 +94,31 @@ function readParagraph(node, topLevel, bookmarks) {
|
|
|
85
94
|
}
|
|
86
95
|
continue;
|
|
87
96
|
}
|
|
88
|
-
if (c.localName === "
|
|
97
|
+
if (c.localName === "del") {
|
|
98
|
+
const deleted = deletedText(c);
|
|
99
|
+
if (deleted && fieldDepth === 0)
|
|
100
|
+
revisions.push({
|
|
101
|
+
kind: "delete",
|
|
102
|
+
offset: text.length,
|
|
103
|
+
length: deleted.length,
|
|
104
|
+
author: attr(c, "author"),
|
|
105
|
+
text: deleted
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (c.localName === "ins") {
|
|
110
|
+
const start = text.length;
|
|
111
|
+
walk(c, link);
|
|
112
|
+
if (text.length > start && fieldDepth === 0)
|
|
113
|
+
revisions.push({
|
|
114
|
+
kind: "insert",
|
|
115
|
+
offset: start,
|
|
116
|
+
length: text.length - start,
|
|
117
|
+
author: attr(c, "author")
|
|
118
|
+
});
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (c.localName === "delText" || c.localName === "rPr") continue;
|
|
89
122
|
if (c.localName === "r") {
|
|
90
123
|
const rStyle = c.children?.find((x) => x.localName === "rPr")?.children?.find((x) => x.localName === "rStyle");
|
|
91
124
|
const watched = rStyle && attr(rStyle, "val");
|
|
@@ -148,9 +181,20 @@ function readParagraph(node, topLevel, bookmarks) {
|
|
|
148
181
|
links,
|
|
149
182
|
...fields.length ? { fields } : {},
|
|
150
183
|
...marks.length ? { marks } : {},
|
|
151
|
-
...notes.length ? { notes } : {}
|
|
184
|
+
...notes.length ? { notes } : {},
|
|
185
|
+
...revisions.length ? { revisions } : {}
|
|
152
186
|
};
|
|
153
187
|
}
|
|
188
|
+
function deletedText(node) {
|
|
189
|
+
let out = "";
|
|
190
|
+
for (const c of node.children ?? []) {
|
|
191
|
+
if (c.localName === "instrText") continue;
|
|
192
|
+
if (c.localName === "delText") out += (c.children ?? []).map((t) => t.value ?? "").join("");
|
|
193
|
+
else if (c.localName === "tab") out += " ";
|
|
194
|
+
else if (c.localName !== "rPr") out += deletedText(c);
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
154
198
|
function styleNames(root) {
|
|
155
199
|
const names = /* @__PURE__ */ new Map();
|
|
156
200
|
for (const style of root?.children ?? []) {
|
|
@@ -1041,7 +1085,7 @@ function insertParagraphs(port, index, body, count, keep) {
|
|
|
1041
1085
|
port.setCaret({ paragraphId: before[index].id, offset: 0 });
|
|
1042
1086
|
} else {
|
|
1043
1087
|
const last = before[before.length - 1];
|
|
1044
|
-
port.setCaret({ paragraphId: last.id, offset: last
|
|
1088
|
+
port.setCaret({ paragraphId: last.id, offset: engineLength(last) });
|
|
1045
1089
|
port.splitParagraph();
|
|
1046
1090
|
const opened = port.paragraphs();
|
|
1047
1091
|
port.setCaret({ paragraphId: opened[opened.length - 1].id, offset: 0 });
|
|
@@ -1073,25 +1117,47 @@ function addDefinition(port, definition, keep) {
|
|
|
1073
1117
|
);
|
|
1074
1118
|
}
|
|
1075
1119
|
var hasBookmark = (paragraphs, name) => paragraphs.some((p) => p.bookmarks.includes(name));
|
|
1120
|
+
var paragraphOf = (port, id) => port.paragraphs().find((p) => p.id === id);
|
|
1121
|
+
function replaceSpan(port, span, insert, length) {
|
|
1122
|
+
const { paragraphId, from, to } = span;
|
|
1123
|
+
if (from > to) throw new Error("A replacement runs forwards.");
|
|
1124
|
+
const before = paragraphOf(port, paragraphId);
|
|
1125
|
+
if (!before) throw new Error("That paragraph is no longer in the document.");
|
|
1126
|
+
const length0 = engineLength(before);
|
|
1127
|
+
port.setCaret({ paragraphId, offset: to });
|
|
1128
|
+
if (!insert()) return false;
|
|
1129
|
+
if (from < to) {
|
|
1130
|
+
try {
|
|
1131
|
+
port.deleteRange({ paragraphId, offset: from }, { paragraphId, offset: to });
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
port.deleteRange({ paragraphId, offset: to }, { paragraphId, offset: to + length });
|
|
1134
|
+
throw error;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
const after = paragraphOf(port, paragraphId);
|
|
1138
|
+
const kept = after && engineLength(after) - length0 === length;
|
|
1139
|
+
port.setCaret({ paragraphId, offset: (kept ? to : from) + length });
|
|
1140
|
+
return true;
|
|
1141
|
+
}
|
|
1076
1142
|
function insertDefinedTerm(port, definition, options = {}) {
|
|
1077
1143
|
const start = port.caret();
|
|
1078
1144
|
if (!start) throw new Error("Place the cursor where the term should go.");
|
|
1079
|
-
let caret = start;
|
|
1080
|
-
if (options.replace) {
|
|
1081
|
-
caret = { paragraphId: start.paragraphId, offset: Math.max(0, start.offset - options.replace) };
|
|
1082
|
-
port.deleteRange(caret, start);
|
|
1083
|
-
}
|
|
1084
1145
|
const bookmark = definitionBookmark(definition.id);
|
|
1085
1146
|
const addedDefinition = !hasBookmark(port.paragraphs(), bookmark);
|
|
1086
|
-
|
|
1087
|
-
port.
|
|
1088
|
-
if (!port.paste(
|
|
1147
|
+
const caret = addedDefinition && addDefinition(port, definition, start) || start;
|
|
1148
|
+
const insert = () => port.paste(
|
|
1089
1149
|
termLinkXml(definition.term, bookmark, definitionTooltip(definition)),
|
|
1090
1150
|
definition.term,
|
|
1091
1151
|
false
|
|
1152
|
+
);
|
|
1153
|
+
const from = Math.max(0, caret.offset - (options.replace ?? 0));
|
|
1154
|
+
if (!replaceSpan(
|
|
1155
|
+
port,
|
|
1156
|
+
{ paragraphId: caret.paragraphId, from, to: caret.offset },
|
|
1157
|
+
insert,
|
|
1158
|
+
definition.term.length
|
|
1092
1159
|
))
|
|
1093
1160
|
throw new Error("The term could not be inserted.");
|
|
1094
|
-
port.setCaret({ paragraphId: caret.paragraphId, offset: caret.offset + definition.term.length });
|
|
1095
1161
|
return { addedDefinition };
|
|
1096
1162
|
}
|
|
1097
1163
|
function definitionsById(definitions) {
|
|
@@ -1102,6 +1168,144 @@ function definitionOfAnchor(anchor, definitions) {
|
|
|
1102
1168
|
return id ? definitions.get(id) : void 0;
|
|
1103
1169
|
}
|
|
1104
1170
|
|
|
1171
|
+
// src/commands/everywhere.ts
|
|
1172
|
+
var WORD = "[\\p{L}\\p{N}_\\-\u2010\u2011]";
|
|
1173
|
+
var escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1174
|
+
function needlesOf(spec) {
|
|
1175
|
+
const term = spec.term.replace(/\s+/g, " ").trim();
|
|
1176
|
+
const variants = [
|
|
1177
|
+
...new Set((spec.variants ?? []).map((v) => v.replace(/\s+/g, " ").trim()))
|
|
1178
|
+
].filter((v) => v && v !== term);
|
|
1179
|
+
const needles = [
|
|
1180
|
+
...variants.map((text) => ({ text, kind: "variant" })),
|
|
1181
|
+
...spec.link ? [{ text: term, kind: "term" }] : []
|
|
1182
|
+
];
|
|
1183
|
+
return needles.map((n) => ({
|
|
1184
|
+
...n,
|
|
1185
|
+
pattern: new RegExp(
|
|
1186
|
+
`(?<!${WORD})${n.text.split(" ").map(escape).join("\\s+")}(?!${WORD})`,
|
|
1187
|
+
"gu"
|
|
1188
|
+
)
|
|
1189
|
+
}));
|
|
1190
|
+
}
|
|
1191
|
+
function matchesIn(text, needles) {
|
|
1192
|
+
const found = [];
|
|
1193
|
+
for (const needle of needles)
|
|
1194
|
+
for (const m of text.matchAll(needle.pattern))
|
|
1195
|
+
found.push({ offset: m.index ?? 0, text: m[0], kind: needle.kind });
|
|
1196
|
+
found.sort((a, b) => a.offset - b.offset || b.text.length - a.text.length);
|
|
1197
|
+
const out = [];
|
|
1198
|
+
for (const m of found)
|
|
1199
|
+
if (!out.length || m.offset >= out[out.length - 1].offset + out[out.length - 1].text.length)
|
|
1200
|
+
out.push(m);
|
|
1201
|
+
return out;
|
|
1202
|
+
}
|
|
1203
|
+
var overlaps = (start, end, from, to) => start < to && from < end;
|
|
1204
|
+
function findTermUses(paragraphs, spec, bookmark) {
|
|
1205
|
+
const needles = needlesOf(spec);
|
|
1206
|
+
const defining = new Set([spec.term, ...spec.variants ?? []].map(termKey));
|
|
1207
|
+
const uses = [];
|
|
1208
|
+
const skipped = [];
|
|
1209
|
+
for (const p of paragraphs) {
|
|
1210
|
+
const at = { paragraphId: p.id, ...p.paraId ? { paraId: p.paraId } : {} };
|
|
1211
|
+
const defined = parseDefinition(p.text.split(FIELD_ATOM).join(""));
|
|
1212
|
+
const isDefinition = defined !== null && defining.has(termKey(defined.term));
|
|
1213
|
+
for (const m of matchesIn(p.text, needles)) {
|
|
1214
|
+
const use = { ...at, ...m };
|
|
1215
|
+
const end = m.offset + m.text.length;
|
|
1216
|
+
const link = p.links.find(
|
|
1217
|
+
(l) => l.offset !== void 0 && overlaps(m.offset, end, l.offset, l.offset + l.text.length)
|
|
1218
|
+
);
|
|
1219
|
+
const pending = (p.revisions ?? []).some(
|
|
1220
|
+
(r) => r.kind === "insert" ? overlaps(m.offset, end, r.offset, r.offset + r.length) : r.offset > m.offset && r.offset < end
|
|
1221
|
+
);
|
|
1222
|
+
const reason = isDefinition ? "definition" : link ? link.anchor === bookmark ? "linked" : "link" : pending ? "pending-change" : null;
|
|
1223
|
+
if (reason) skipped.push({ ...use, reason });
|
|
1224
|
+
else uses.push(use);
|
|
1225
|
+
}
|
|
1226
|
+
for (const field of p.fields ?? [])
|
|
1227
|
+
for (const m of matchesIn(field.result, needles))
|
|
1228
|
+
skipped.push({ ...at, offset: field.offset, text: m.text, kind: m.kind, reason: "field" });
|
|
1229
|
+
}
|
|
1230
|
+
return { uses, skipped };
|
|
1231
|
+
}
|
|
1232
|
+
function replacedText(text, uses, term) {
|
|
1233
|
+
let out = text;
|
|
1234
|
+
for (const use of [...uses].sort((a, b) => b.offset - a.offset))
|
|
1235
|
+
out = out.slice(0, use.offset) + term + out.slice(use.offset + use.text.length);
|
|
1236
|
+
return out;
|
|
1237
|
+
}
|
|
1238
|
+
var TermEverywhereError = class extends Error {
|
|
1239
|
+
constructor(message) {
|
|
1240
|
+
super(message);
|
|
1241
|
+
this.name = "TermEverywhereError";
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
function applyTermEverywhere(port, spec, { accept } = {}) {
|
|
1245
|
+
const term = spec.term.replace(/\s+/g, " ").trim();
|
|
1246
|
+
if (!term) throw new Error("Name the defined term.");
|
|
1247
|
+
let addedDefinition = false;
|
|
1248
|
+
let link;
|
|
1249
|
+
if (spec.link) {
|
|
1250
|
+
if (!spec.definition) throw new Error(`Linking "${term}" needs its definition.`);
|
|
1251
|
+
link = {
|
|
1252
|
+
bookmark: definitionBookmark(spec.definition.id),
|
|
1253
|
+
tooltip: definitionTooltip(spec.definition)
|
|
1254
|
+
};
|
|
1255
|
+
if (!port.paragraphs().some((p) => p.bookmarks.includes(link.bookmark))) {
|
|
1256
|
+
addDefinition(port, spec.definition);
|
|
1257
|
+
addedDefinition = true;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
const paragraphs = port.paragraphs();
|
|
1261
|
+
const { uses, skipped } = findTermUses(paragraphs, { ...spec, term }, link?.bookmark);
|
|
1262
|
+
const changed = [];
|
|
1263
|
+
const insert = link ? () => port.paste(termLinkXml(term, link.bookmark, link.tooltip), term, false) : () => {
|
|
1264
|
+
port.typeText(term);
|
|
1265
|
+
return true;
|
|
1266
|
+
};
|
|
1267
|
+
for (const p of paragraphs) {
|
|
1268
|
+
const mine = uses.filter((u) => u.paragraphId === p.id);
|
|
1269
|
+
if (!mine.length) continue;
|
|
1270
|
+
if (accept && !accept(p)) {
|
|
1271
|
+
skipped.push(...mine.map((u) => ({ ...u, reason: "unsupported-content" })));
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1274
|
+
const current = () => port.paragraphs().find((x) => p.paraId ? x.paraId === p.paraId : x.id === p.id);
|
|
1275
|
+
const done = [];
|
|
1276
|
+
for (const use of [...mine].sort((a, b) => b.offset - a.offset)) {
|
|
1277
|
+
const now = current();
|
|
1278
|
+
let ok = false;
|
|
1279
|
+
if (now) {
|
|
1280
|
+
const span = {
|
|
1281
|
+
paragraphId: now.id,
|
|
1282
|
+
from: engineOffset(p, use.offset, true),
|
|
1283
|
+
to: engineOffset(p, use.offset + use.text.length)
|
|
1284
|
+
};
|
|
1285
|
+
try {
|
|
1286
|
+
ok = replaceSpan(port, span, insert, term.length);
|
|
1287
|
+
} catch {
|
|
1288
|
+
ok = false;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
if (ok) {
|
|
1292
|
+
done.push(use);
|
|
1293
|
+
changed.push({ ...use, to: term, linked: Boolean(link) });
|
|
1294
|
+
} else skipped.push({ ...use, reason: "refused" });
|
|
1295
|
+
}
|
|
1296
|
+
const want = replacedText(p.text, done, term);
|
|
1297
|
+
const got = current()?.text;
|
|
1298
|
+
if (got !== want)
|
|
1299
|
+
throw new TermEverywhereError(
|
|
1300
|
+
`A paragraph did not read as intended after the changes: expected "${want}", got "${got ?? "(missing)"}".`
|
|
1301
|
+
);
|
|
1302
|
+
}
|
|
1303
|
+
changed.sort(
|
|
1304
|
+
(a, b) => paragraphs.findIndex((p) => p.id === a.paragraphId) - paragraphs.findIndex((p) => p.id === b.paragraphId) || a.offset - b.offset
|
|
1305
|
+
);
|
|
1306
|
+
return { changed, skipped, addedDefinition };
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1105
1309
|
// src/commands/index.ts
|
|
1106
1310
|
function documentReader(view) {
|
|
1107
1311
|
const root = () => view.part().root;
|
|
@@ -1150,9 +1354,11 @@ exports.DEFINITIONS_HEADING = DEFINITIONS_HEADING;
|
|
|
1150
1354
|
exports.DEFINITION_BOOKMARK_PREFIX = DEFINITION_BOOKMARK_PREFIX;
|
|
1151
1355
|
exports.FIELD_ATOM = FIELD_ATOM;
|
|
1152
1356
|
exports.REF_BOOKMARK_PREFIX = REF_BOOKMARK_PREFIX;
|
|
1357
|
+
exports.TermEverywhereError = TermEverywhereError;
|
|
1153
1358
|
exports.WATCHED_STYLES = WATCHED_STYLES;
|
|
1154
1359
|
exports.W_NAMESPACES = W_NAMESPACES;
|
|
1155
1360
|
exports.addDefinition = addDefinition;
|
|
1361
|
+
exports.applyTermEverywhere = applyTermEverywhere;
|
|
1156
1362
|
exports.brokenCrossReferences = brokenCrossReferences;
|
|
1157
1363
|
exports.compareTerms = compareTerms;
|
|
1158
1364
|
exports.crossReferenceXml = crossReferenceXml;
|
|
@@ -1170,11 +1376,14 @@ exports.documentReferenceTooltip = documentReferenceTooltip;
|
|
|
1170
1376
|
exports.documentStyleIds = documentStyleIds;
|
|
1171
1377
|
exports.documentXml = documentXml;
|
|
1172
1378
|
exports.docxPackage = docxPackage;
|
|
1379
|
+
exports.engineLength = engineLength;
|
|
1380
|
+
exports.engineOffset = engineOffset;
|
|
1173
1381
|
exports.ensureTargetBookmark = ensureTargetBookmark;
|
|
1174
1382
|
exports.esc = esc;
|
|
1175
1383
|
exports.fieldNodeIds = fieldNodeIds;
|
|
1176
1384
|
exports.filterTargets = filterTargets;
|
|
1177
1385
|
exports.findDefinitionsSection = findDefinitionsSection;
|
|
1386
|
+
exports.findTermUses = findTermUses;
|
|
1178
1387
|
exports.fragmentPackage = fragmentPackage;
|
|
1179
1388
|
exports.headingParagraphXml = headingParagraphXml;
|
|
1180
1389
|
exports.hyperlinkRelationship = hyperlinkRelationship;
|
|
@@ -1199,6 +1408,7 @@ exports.refInstruction = refInstruction;
|
|
|
1199
1408
|
exports.refResult = refResult;
|
|
1200
1409
|
exports.refTooltip = refTooltip;
|
|
1201
1410
|
exports.refreshCrossReferences = refreshCrossReferences;
|
|
1411
|
+
exports.replaceSpan = replaceSpan;
|
|
1202
1412
|
exports.sectionStates = sectionStates;
|
|
1203
1413
|
exports.staleCrossReferences = staleCrossReferences;
|
|
1204
1414
|
exports.styleNames = styleNames;
|