@arbidocs/coedit 0.5.21 → 0.5.22
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/encoding.cjs +54 -56
- package/dist/encoding.cjs.map +1 -1
- package/dist/encoding.d.cts +50 -14
- package/dist/encoding.d.ts +50 -14
- package/dist/encoding.js +54 -57
- package/dist/encoding.js.map +1 -1
- package/dist/hocuspocus.cjs +211 -109
- package/dist/hocuspocus.cjs.map +1 -1
- package/dist/hocuspocus.js +211 -109
- package/dist/hocuspocus.js.map +1 -1
- package/dist/index.cjs +210 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +210 -108
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +214 -112
- 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 +214 -112
- package/dist/server.js.map +1 -1
- package/dist/template.cjs +3 -2
- package/dist/template.cjs.map +1 -1
- package/dist/template.d.cts +5 -0
- package/dist/template.d.ts +5 -0
- package/dist/template.js +3 -2
- package/dist/template.js.map +1 -1
- package/dist/{versions-DXiFQMA6.d.cts → versions-XSYTVmOU.d.cts} +1 -1
- package/dist/{versions-DXiFQMA6.d.ts → versions-XSYTVmOU.d.ts} +1 -1
- package/package.json +1 -1
package/dist/server.d.cts
CHANGED
|
@@ -6,7 +6,7 @@ import { TreePackageStore, PackageTransactResult, HeadlessDocumentView, ImageDec
|
|
|
6
6
|
import { R as ReferencesPort } from './port-BbN5OZHx.cjs';
|
|
7
7
|
import { R as Revision } from './track-eYC94jTp.cjs';
|
|
8
8
|
import { D as DocxCollaborationSession } from './session-WGvWs6yO.cjs';
|
|
9
|
-
export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-
|
|
9
|
+
export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-XSYTVmOU.cjs';
|
|
10
10
|
import 'y-protocols/awareness';
|
|
11
11
|
import '@docx-editor.dev/core/collaboration/replication';
|
|
12
12
|
|
package/dist/server.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { TreePackageStore, PackageTransactResult, HeadlessDocumentView, ImageDec
|
|
|
6
6
|
import { R as ReferencesPort } from './port-BbN5OZHx.js';
|
|
7
7
|
import { R as Revision } from './track-eYC94jTp.js';
|
|
8
8
|
import { D as DocxCollaborationSession } from './session-WGvWs6yO.js';
|
|
9
|
-
export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-
|
|
9
|
+
export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-XSYTVmOU.js';
|
|
10
10
|
import 'y-protocols/awareness';
|
|
11
11
|
import '@docx-editor.dev/core/collaboration/replication';
|
|
12
12
|
|
package/dist/server.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Awareness } from 'y-protocols/awareness';
|
|
2
2
|
import { createServerAutomationHost } from '@docx-editor.dev/core/automation';
|
|
3
3
|
import { createCollaborationDocumentPort, historyGroupOfJournal, mapOffsetAcrossText } from '@docx-editor.dev/core/collaboration/replication';
|
|
4
|
-
import { TreePackageStore, validateRasterHeader, headlessViewOfStore, ORIGIN_IDS, readOoxmlPackage, writeOoxmlPackage, readOoxmlPart, validateExternalTarget, relsPartNameFor, contentTypesPartBytes, withContentTypeOverride, readXml } from '@docx-editor.dev/core/store';
|
|
4
|
+
import { TreePackageStore, validateRasterHeader, headlessViewOfStore, ORIGIN_IDS, readOoxmlPackage, writeOoxmlPackage, readOoxmlPart, validateExternalTarget, relsPartNameFor, contentTypesPartBytes, withContentTypeOverride, readXml, XML_NAMESPACE_URI } from '@docx-editor.dev/core/store';
|
|
5
5
|
import { resolveNumberingLevel, resolveStoryListItems, buildStyleCascadeTable, withNumberingStyleLinks, buildNumberingIndex } from '@docx-editor.dev/core/layout';
|
|
6
6
|
import { zipSync, strToU8, strFromU8, unzipSync } from 'fflate';
|
|
7
|
-
import * as
|
|
7
|
+
import * as Y from 'yjs';
|
|
8
8
|
import { createCollaborationStatusTracker, safeParticipantColor } from '@docx-editor.dev/core/collaboration';
|
|
9
9
|
|
|
10
10
|
// src/server.ts
|
|
@@ -874,6 +874,92 @@ function writeUnits(text, units) {
|
|
|
874
874
|
function isRichParagraph(map) {
|
|
875
875
|
return map.has("inline");
|
|
876
876
|
}
|
|
877
|
+
var PACKED = "xml";
|
|
878
|
+
function isPackedElement(map) {
|
|
879
|
+
return map.has(PACKED);
|
|
880
|
+
}
|
|
881
|
+
function isCompact(map) {
|
|
882
|
+
return isRichParagraph(map) || isPackedElement(map);
|
|
883
|
+
}
|
|
884
|
+
var TEXT_BEARING = /* @__PURE__ */ new Set(["p", "t", "delText", "instrText", "delInstrText"]);
|
|
885
|
+
function packable(xml) {
|
|
886
|
+
if (xml.ns === WML2 && TEXT_BEARING.has(xml.local)) return false;
|
|
887
|
+
return xml.children.every((c) => typeof c === "string" || packable(c));
|
|
888
|
+
}
|
|
889
|
+
function createPacked(xml, scope) {
|
|
890
|
+
const map = new Y.Map();
|
|
891
|
+
map.set(PACKED, serializeXml(xml, scope));
|
|
892
|
+
return map;
|
|
893
|
+
}
|
|
894
|
+
function repack(map, xml) {
|
|
895
|
+
const next = serializeXml(xml, scopeOf(map));
|
|
896
|
+
if (map.get(PACKED) !== next) map.set(PACKED, next);
|
|
897
|
+
}
|
|
898
|
+
function scopeOf(node) {
|
|
899
|
+
const chain = [];
|
|
900
|
+
let parent = node.parent;
|
|
901
|
+
while (parent instanceof Y.Array) {
|
|
902
|
+
const owner = parent.parent;
|
|
903
|
+
if (!(owner instanceof Y.Map)) break;
|
|
904
|
+
chain.push(owner);
|
|
905
|
+
parent = owner.parent;
|
|
906
|
+
}
|
|
907
|
+
const scope = /* @__PURE__ */ new Map();
|
|
908
|
+
for (const el2 of chain.reverse()) {
|
|
909
|
+
for (const b of el2.get("bindings").toArray()) scope.set(b.prefix, b.ns);
|
|
910
|
+
}
|
|
911
|
+
return scope;
|
|
912
|
+
}
|
|
913
|
+
function scopeWithin(el2) {
|
|
914
|
+
const scope = scopeOf(el2);
|
|
915
|
+
if (!isPackedElement(el2)) {
|
|
916
|
+
for (const b of el2.get("bindings").toArray()) scope.set(b.prefix, b.ns);
|
|
917
|
+
}
|
|
918
|
+
return scope;
|
|
919
|
+
}
|
|
920
|
+
var unpackLimits = { maxBytes: 64 * 1024 * 1024, maxElements: 2e6 };
|
|
921
|
+
function unpack(text, scope) {
|
|
922
|
+
const read = readXml(text, unpackLimits);
|
|
923
|
+
if (!read.ok) throw new Error(`collab: a packed element is not readable XML (${read.reason})`);
|
|
924
|
+
const convert = (node, outer) => {
|
|
925
|
+
const bindings = [];
|
|
926
|
+
const named = [];
|
|
927
|
+
for (const [name, value] of Object.entries(node.attributes)) {
|
|
928
|
+
if (name === "xmlns") bindings.push({ prefix: "", ns: value });
|
|
929
|
+
else if (name.startsWith("xmlns:")) bindings.push({ prefix: name.slice(6), ns: value });
|
|
930
|
+
else named.push([name, value]);
|
|
931
|
+
}
|
|
932
|
+
const inner = new Map(outer);
|
|
933
|
+
for (const b of bindings) inner.set(b.prefix, b.ns);
|
|
934
|
+
const resolve = (prefix) => {
|
|
935
|
+
if (prefix === "xml") return XML_NAMESPACE_URI;
|
|
936
|
+
const ns = inner.get(prefix);
|
|
937
|
+
if (ns === void 0)
|
|
938
|
+
throw new Error(`collab: prefix ${prefix} is not bound in a packed element`);
|
|
939
|
+
return ns;
|
|
940
|
+
};
|
|
941
|
+
const colon = node.name.indexOf(":");
|
|
942
|
+
const xml = {
|
|
943
|
+
ns: colon < 0 ? inner.get("") ?? "" : resolve(node.name.slice(0, colon)),
|
|
944
|
+
local: colon < 0 ? node.name : node.name.slice(colon + 1),
|
|
945
|
+
attrs: named.map(([name, value]) => {
|
|
946
|
+
const at = name.indexOf(":");
|
|
947
|
+
if (at < 0) return { ns: "", local: name, value };
|
|
948
|
+
const prefix = name.slice(0, at);
|
|
949
|
+
return { ns: resolve(prefix), local: name.slice(at + 1), prefix, value };
|
|
950
|
+
}),
|
|
951
|
+
bindings,
|
|
952
|
+
children: node.children.map(
|
|
953
|
+
(child) => child.type === "text" ? child.value : convert(child, inner)
|
|
954
|
+
)
|
|
955
|
+
};
|
|
956
|
+
if (colon >= 0) xml.prefix = node.name.slice(0, colon);
|
|
957
|
+
return xml;
|
|
958
|
+
};
|
|
959
|
+
const root = read.nodes.find((n) => n.type === "element");
|
|
960
|
+
if (!root || root.type !== "element") throw new Error("collab: a packed element is empty");
|
|
961
|
+
return convert(root, scope);
|
|
962
|
+
}
|
|
877
963
|
function isParagraphEl(el2) {
|
|
878
964
|
return el2.ns === WML2 && el2.local === "p";
|
|
879
965
|
}
|
|
@@ -881,47 +967,53 @@ function fieldsOf(map, xml) {
|
|
|
881
967
|
map.set("ns", xml.ns);
|
|
882
968
|
map.set("local", xml.local);
|
|
883
969
|
if (xml.prefix !== void 0) map.set("prefix", xml.prefix);
|
|
884
|
-
const attrs = new
|
|
970
|
+
const attrs = new Y.Array();
|
|
885
971
|
attrs.push(xml.attrs.map((a) => ({ ...a })));
|
|
886
972
|
map.set("attrs", attrs);
|
|
887
|
-
const bindings = new
|
|
973
|
+
const bindings = new Y.Array();
|
|
888
974
|
bindings.push(xml.bindings.map((b) => ({ ...b })));
|
|
889
975
|
map.set("bindings", bindings);
|
|
890
976
|
}
|
|
891
977
|
function newText(value) {
|
|
892
|
-
const text = new
|
|
978
|
+
const text = new Y.Text();
|
|
893
979
|
text.insert(0, value);
|
|
894
980
|
return text;
|
|
895
981
|
}
|
|
896
|
-
function createRichParagraph(xml, encoded) {
|
|
897
|
-
const map = new
|
|
982
|
+
function createRichParagraph(xml, encoded, scope = /* @__PURE__ */ new Map()) {
|
|
983
|
+
const map = new Y.Map();
|
|
898
984
|
fieldsOf(map, xml);
|
|
899
|
-
const
|
|
900
|
-
children
|
|
985
|
+
const inner = withBindings(scope, xml.bindings);
|
|
986
|
+
const children = new Y.Array();
|
|
987
|
+
children.push(encoded.head.map((el2) => xmlToLive(el2, inner)));
|
|
901
988
|
map.set("children", children);
|
|
902
|
-
const inline = new
|
|
989
|
+
const inline = new Y.Text();
|
|
903
990
|
writeUnits(inline, encoded.units);
|
|
904
991
|
map.set("inline", inline);
|
|
905
992
|
return map;
|
|
906
993
|
}
|
|
907
|
-
function
|
|
994
|
+
function withBindings(scope, bindings) {
|
|
995
|
+
const inner = new Map(scope);
|
|
996
|
+
for (const b of bindings) inner.set(b.prefix, b.ns);
|
|
997
|
+
return inner;
|
|
998
|
+
}
|
|
999
|
+
function xmlToLive(xml, scope = /* @__PURE__ */ new Map(), root = false) {
|
|
908
1000
|
if (isParagraphEl(xml)) {
|
|
909
1001
|
const encoded = encodeParagraph(xml.children);
|
|
910
|
-
if (encoded) return createRichParagraph(xml, encoded);
|
|
1002
|
+
if (encoded) return createRichParagraph(xml, encoded, scope);
|
|
911
1003
|
}
|
|
912
|
-
return
|
|
913
|
-
|
|
914
|
-
function xmlToGenericLive(xml) {
|
|
915
|
-
const map = new Y3.Map();
|
|
1004
|
+
if (!root && packable(xml)) return createPacked(xml, scope);
|
|
1005
|
+
const map = new Y.Map();
|
|
916
1006
|
fieldsOf(map, xml);
|
|
917
|
-
const
|
|
918
|
-
children
|
|
1007
|
+
const inner = withBindings(scope, xml.bindings);
|
|
1008
|
+
const children = new Y.Array();
|
|
1009
|
+
children.push(xml.children.map((c) => typeof c === "string" ? newText(c) : xmlToLive(c, inner)));
|
|
919
1010
|
map.set("children", children);
|
|
920
1011
|
return map;
|
|
921
1012
|
}
|
|
922
1013
|
function liveToXml(map) {
|
|
1014
|
+
if (isPackedElement(map)) return unpack(map.get(PACKED), scopeOf(map));
|
|
923
1015
|
const prefix = map.get("prefix");
|
|
924
|
-
const stored = map.get("children").toArray().map((child) => child instanceof
|
|
1016
|
+
const stored = map.get("children").toArray().map((child) => child instanceof Y.Text ? child.toString() : liveToXml(child));
|
|
925
1017
|
const children = isRichParagraph(map) ? [...stored, ...decodeInline(readUnits(map.get("inline")))] : stored;
|
|
926
1018
|
const xml = {
|
|
927
1019
|
ns: map.get("ns"),
|
|
@@ -936,14 +1028,13 @@ function liveToXml(map) {
|
|
|
936
1028
|
var escapeText = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
937
1029
|
var escapeAttr = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/\t/g, "	").replace(/\n/g, " ").replace(/\r/g, " ");
|
|
938
1030
|
var qualified = (prefix, local) => prefix ? `${prefix}:${local}` : local;
|
|
939
|
-
function
|
|
940
|
-
const inScope =
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
for (const b of el2.bindings) {
|
|
1031
|
+
function startTag(prefix, local, bindings, attrs, scope) {
|
|
1032
|
+
const inScope = withBindings(scope, bindings);
|
|
1033
|
+
let out = `<${qualified(prefix, local)}`;
|
|
1034
|
+
for (const b of bindings) {
|
|
944
1035
|
out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
|
|
945
1036
|
}
|
|
946
|
-
for (const a of
|
|
1037
|
+
for (const a of attrs) {
|
|
947
1038
|
let p = a.prefix;
|
|
948
1039
|
if (a.ns !== "" && !p) {
|
|
949
1040
|
for (const [candidate, uri] of inScope) {
|
|
@@ -955,48 +1046,41 @@ function serializeXml(el2, scope) {
|
|
|
955
1046
|
}
|
|
956
1047
|
out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
|
|
957
1048
|
}
|
|
1049
|
+
return [out, inScope];
|
|
1050
|
+
}
|
|
1051
|
+
function serializeXml(el2, scope) {
|
|
1052
|
+
const [out, inScope] = startTag(el2.prefix, el2.local, el2.bindings, el2.attrs, scope);
|
|
958
1053
|
if (el2.children.length === 0) return `${out}/>`;
|
|
959
|
-
|
|
1054
|
+
let body = "";
|
|
960
1055
|
for (const child of el2.children) {
|
|
961
|
-
|
|
1056
|
+
body += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
|
|
962
1057
|
}
|
|
963
|
-
return `${out}</${qualified(el2.prefix, el2.local)}>`;
|
|
1058
|
+
return `${out}>${body}</${qualified(el2.prefix, el2.local)}>`;
|
|
964
1059
|
}
|
|
965
1060
|
function serializeLive(el2, scope) {
|
|
966
|
-
if (
|
|
1061
|
+
if (isPackedElement(el2)) return el2.get(PACKED);
|
|
967
1062
|
const prefix = el2.get("prefix");
|
|
968
1063
|
const local = el2.get("local");
|
|
969
1064
|
const bindings = el2.get("bindings").toArray();
|
|
970
1065
|
const attrs = el2.get("attrs").toArray();
|
|
971
1066
|
const children = el2.get("children").toArray();
|
|
972
|
-
const inScope =
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
|
|
977
|
-
}
|
|
978
|
-
for (const a of attrs) {
|
|
979
|
-
let p = a.prefix;
|
|
980
|
-
if (a.ns !== "" && !p) {
|
|
981
|
-
for (const [candidate, uri] of inScope) {
|
|
982
|
-
if (candidate !== "" && uri === a.ns) {
|
|
983
|
-
p = candidate;
|
|
984
|
-
break;
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
|
|
989
|
-
}
|
|
990
|
-
if (children.length === 0) return `${out}/>`;
|
|
991
|
-
out += ">";
|
|
1067
|
+
const [out, inScope] = startTag(prefix, local, bindings, attrs, scope);
|
|
1068
|
+
const inline = isRichParagraph(el2) ? decodeInline(readUnits(el2.get("inline"))) : [];
|
|
1069
|
+
if (children.length === 0 && inline.length === 0) return `${out}/>`;
|
|
1070
|
+
let body = "";
|
|
992
1071
|
for (const child of children) {
|
|
993
|
-
|
|
1072
|
+
body += child instanceof Y.Text ? escapeText(child.toString()) : serializeLive(child, inScope);
|
|
994
1073
|
}
|
|
995
|
-
|
|
1074
|
+
for (const child of inline) body += serializeXml(child, inScope);
|
|
1075
|
+
return `${out}>${body}</${qualified(prefix, local)}>`;
|
|
996
1076
|
}
|
|
997
1077
|
|
|
998
1078
|
// src/encoding.ts
|
|
999
|
-
var COLLAB_FORMAT = "arbi-docx/
|
|
1079
|
+
var COLLAB_FORMAT = "arbi-docx/2";
|
|
1080
|
+
var READABLE_FORMATS = /* @__PURE__ */ new Set(["arbi-docx/1", COLLAB_FORMAT]);
|
|
1081
|
+
function isReadableFormat(format) {
|
|
1082
|
+
return format !== void 0 && READABLE_FORMATS.has(format);
|
|
1083
|
+
}
|
|
1000
1084
|
function isText(node) {
|
|
1001
1085
|
return node.kind === "textValue";
|
|
1002
1086
|
}
|
|
@@ -1017,7 +1101,7 @@ function canonicalToXml(node) {
|
|
|
1017
1101
|
return xml;
|
|
1018
1102
|
}
|
|
1019
1103
|
function encodeElement(el2) {
|
|
1020
|
-
return xmlToLive(canonicalToXml(el2));
|
|
1104
|
+
return xmlToLive(canonicalToXml(el2), /* @__PURE__ */ new Map(), true);
|
|
1021
1105
|
}
|
|
1022
1106
|
function bootstrap(doc, pkg) {
|
|
1023
1107
|
doc.transact(() => {
|
|
@@ -1035,7 +1119,7 @@ function bootstrap(doc, pkg) {
|
|
|
1035
1119
|
}
|
|
1036
1120
|
const rels = doc.getMap("rels");
|
|
1037
1121
|
for (const [owner, records] of pkg.relationships) {
|
|
1038
|
-
const byId = new
|
|
1122
|
+
const byId = new Y.Map();
|
|
1039
1123
|
for (const r of records) {
|
|
1040
1124
|
byId.set(r.id, {
|
|
1041
1125
|
ownerPart: r.ownerPart,
|
|
@@ -1051,7 +1135,7 @@ function bootstrap(doc, pkg) {
|
|
|
1051
1135
|
for (const e of pkg.externalTargets) {
|
|
1052
1136
|
let byId = rels.get(e.ownerPart);
|
|
1053
1137
|
if (!byId) {
|
|
1054
|
-
byId = new
|
|
1138
|
+
byId = new Y.Map();
|
|
1055
1139
|
rels.set(e.ownerPart, byId);
|
|
1056
1140
|
}
|
|
1057
1141
|
byId.set(e.id, {
|
|
@@ -1371,7 +1455,7 @@ var NodeRegistry = class {
|
|
|
1371
1455
|
require(id) {
|
|
1372
1456
|
let entry = this.entries.get(id) ?? this.resolveInsideParagraph(id);
|
|
1373
1457
|
if (!entry) throw new RegistryError(`journal names unknown node ${id}`);
|
|
1374
|
-
if ("live" in entry && entry.live instanceof
|
|
1458
|
+
if ("live" in entry && entry.live instanceof Y.Map && isCompact(entry.live)) {
|
|
1375
1459
|
this.openParagraph(entry.live);
|
|
1376
1460
|
entry = this.entries.get(id);
|
|
1377
1461
|
}
|
|
@@ -1389,7 +1473,7 @@ var NodeRegistry = class {
|
|
|
1389
1473
|
for (let length = path.length - 1; length >= 1; length--) {
|
|
1390
1474
|
const ancestor = this.entries.get(id.slice(0, hash + 1) + path.slice(0, length).join("."));
|
|
1391
1475
|
if (!ancestor) continue;
|
|
1392
|
-
if ("live" in ancestor && ancestor.live instanceof
|
|
1476
|
+
if ("live" in ancestor && ancestor.live instanceof Y.Map && isCompact(ancestor.live)) {
|
|
1393
1477
|
this.openParagraph(ancestor.live);
|
|
1394
1478
|
return this.entries.get(id);
|
|
1395
1479
|
}
|
|
@@ -1421,13 +1505,13 @@ var NodeRegistry = class {
|
|
|
1421
1505
|
/**
|
|
1422
1506
|
* Walk a mirror root and register every node under its positional id, `${part}#0` for the root and
|
|
1423
1507
|
* `${parent}.${index}` below it. This is the id scheme core assigns when it reads a part. A rich
|
|
1424
|
-
* paragraph is registered as one node; its inner nodes appear when it is opened.
|
|
1508
|
+
* paragraph or a packed element is registered as one node; its inner nodes appear when it is opened.
|
|
1425
1509
|
*/
|
|
1426
1510
|
registerTree(partName, root) {
|
|
1427
1511
|
const walk = (node, id) => {
|
|
1428
1512
|
this.registerLive(id, node);
|
|
1429
|
-
if (node instanceof
|
|
1430
|
-
if (
|
|
1513
|
+
if (node instanceof Y.Text) return;
|
|
1514
|
+
if (isCompact(node)) return;
|
|
1431
1515
|
childrenOf(node).forEach((child, index) => walk(child, `${id}.${index}`));
|
|
1432
1516
|
};
|
|
1433
1517
|
walk(root, `${partName}#0`);
|
|
@@ -1484,7 +1568,7 @@ var NodeRegistry = class {
|
|
|
1484
1568
|
for (const child of children) this.own(child, owner);
|
|
1485
1569
|
if (owner) this.dirty.add(owner);
|
|
1486
1570
|
}
|
|
1487
|
-
/** Write every changed open paragraph back into the mirror. Call at the end of a journal. */
|
|
1571
|
+
/** Write every changed open paragraph or packed element back into the mirror. Call at the end of a journal. */
|
|
1488
1572
|
flushRich() {
|
|
1489
1573
|
for (const live of [...this.dirty]) {
|
|
1490
1574
|
this.dirty.delete(live);
|
|
@@ -1495,6 +1579,11 @@ var NodeRegistry = class {
|
|
|
1495
1579
|
writeBack(record) {
|
|
1496
1580
|
const { live, root } = record;
|
|
1497
1581
|
const xml = plainToXml(root);
|
|
1582
|
+
if (isPackedElement(live)) {
|
|
1583
|
+
if (packable(xml)) repack(live, xml);
|
|
1584
|
+
else this.degrade(record);
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1498
1587
|
const encoded = encodeParagraph(xml.children);
|
|
1499
1588
|
if (!encoded) {
|
|
1500
1589
|
this.degrade(record);
|
|
@@ -1511,21 +1600,22 @@ var NodeRegistry = class {
|
|
|
1511
1600
|
bindings.push(xml.bindings.map((b) => ({ ...b })));
|
|
1512
1601
|
}
|
|
1513
1602
|
const head = childrenOf(live);
|
|
1514
|
-
const
|
|
1515
|
-
|
|
1603
|
+
const scope = scopeWithin(live);
|
|
1604
|
+
const stored = head.toArray().map((child) => serializeLive(child, scope)).join("");
|
|
1605
|
+
if (stored !== encoded.head.map((el2) => serializeXml(el2, scope)).join("")) {
|
|
1516
1606
|
head.delete(0, head.length);
|
|
1517
|
-
head.push(encoded.head.map((el2) => xmlToLive(el2)));
|
|
1607
|
+
head.push(encoded.head.map((el2) => xmlToLive(el2, scope)));
|
|
1518
1608
|
}
|
|
1519
1609
|
const inline = live.get("inline");
|
|
1520
1610
|
applyUnits(inline, readUnits(inline), encoded.units);
|
|
1521
1611
|
}
|
|
1522
|
-
/** The paragraph can no longer be held as rich text: store it as
|
|
1612
|
+
/** The paragraph can no longer be held as rich text, or the element packed: store it as a tree. */
|
|
1523
1613
|
degrade(record) {
|
|
1524
1614
|
const parent = record.live.parent;
|
|
1525
|
-
if (!(parent instanceof
|
|
1615
|
+
if (!(parent instanceof Y.Array))
|
|
1526
1616
|
throw new RegistryError("a paragraph outside a parent cannot change form");
|
|
1527
1617
|
const index = parent.toArray().indexOf(record.live);
|
|
1528
|
-
const generic = this.toGenericLive(record.root);
|
|
1618
|
+
const generic = this.toGenericLive(record.root, scopeOf(record.live));
|
|
1529
1619
|
this.open.delete(record.live);
|
|
1530
1620
|
parent.delete(index, 1);
|
|
1531
1621
|
parent.insert(index, [generic]);
|
|
@@ -1540,8 +1630,8 @@ var NodeRegistry = class {
|
|
|
1540
1630
|
snapshotLive(node) {
|
|
1541
1631
|
const id = this.liveIds.get(node);
|
|
1542
1632
|
if (id === void 0) throw new RegistryError("live node has no registered id");
|
|
1543
|
-
if (node instanceof
|
|
1544
|
-
if (
|
|
1633
|
+
if (node instanceof Y.Text) return { type: "text", id, value: node.toString() };
|
|
1634
|
+
if (isCompact(node)) {
|
|
1545
1635
|
return this.open.get(node)?.root ?? xmlToPlain(liveToXml(node), id);
|
|
1546
1636
|
}
|
|
1547
1637
|
const prefix = node.get("prefix");
|
|
@@ -1569,8 +1659,8 @@ var NodeRegistry = class {
|
|
|
1569
1659
|
if (id !== void 0 && this.isCurrentLive(id, node)) {
|
|
1570
1660
|
this.entries.set(id, { plain });
|
|
1571
1661
|
}
|
|
1572
|
-
if (node instanceof
|
|
1573
|
-
if (
|
|
1662
|
+
if (node instanceof Y.Text || plain.type === "text") return;
|
|
1663
|
+
if (isCompact(node)) {
|
|
1574
1664
|
this.open.delete(node);
|
|
1575
1665
|
this.dirty.delete(node);
|
|
1576
1666
|
return;
|
|
@@ -1578,53 +1668,61 @@ var NodeRegistry = class {
|
|
|
1578
1668
|
const liveChildren = childrenOf(node).toArray();
|
|
1579
1669
|
liveChildren.forEach((child, index) => this.detachInto(child, plain.children[index]));
|
|
1580
1670
|
}
|
|
1581
|
-
/**
|
|
1582
|
-
|
|
1671
|
+
/**
|
|
1672
|
+
* Build Yjs types for a plain subtree placed where `scope` is in force, and register every node as
|
|
1673
|
+
* live. A rich paragraph or packed element stays open on the plain tree, which is already current.
|
|
1674
|
+
* A part's `root` is never packed.
|
|
1675
|
+
*/
|
|
1676
|
+
toLive(plain, scope = /* @__PURE__ */ new Map(), root = false) {
|
|
1583
1677
|
if (plain.type === "text") {
|
|
1584
|
-
const text = new
|
|
1678
|
+
const text = new Y.Text();
|
|
1585
1679
|
text.insert(0, plain.value);
|
|
1586
1680
|
this.registerLive(plain.id, text);
|
|
1587
1681
|
return text;
|
|
1588
1682
|
}
|
|
1683
|
+
const xml = plainToXml(plain);
|
|
1684
|
+
let map;
|
|
1589
1685
|
if (isParagraphEl(plain)) {
|
|
1590
|
-
const xml = plainToXml(plain);
|
|
1591
1686
|
const encoded = encodeParagraph(xml.children);
|
|
1592
|
-
if (encoded)
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
}
|
|
1687
|
+
if (encoded) map = createRichParagraph(xml, encoded, scope);
|
|
1688
|
+
}
|
|
1689
|
+
if (!map && !root && packable(xml)) map = xmlToLive(xml, scope);
|
|
1690
|
+
if (map) {
|
|
1691
|
+
this.liveIds.set(map, plain.id);
|
|
1692
|
+
this.registerPlain(plain);
|
|
1693
|
+
this.own(plain, map);
|
|
1694
|
+
this.open.set(map, { live: map, root: plain, id: plain.id });
|
|
1695
|
+
return map;
|
|
1600
1696
|
}
|
|
1601
|
-
return this.toGenericLive(plain);
|
|
1697
|
+
return this.toGenericLive(plain, scope);
|
|
1602
1698
|
}
|
|
1603
|
-
toGenericLive(plain) {
|
|
1699
|
+
toGenericLive(plain, scope) {
|
|
1604
1700
|
const map = xmlToGenericShell(plain);
|
|
1605
|
-
const
|
|
1606
|
-
children
|
|
1701
|
+
const inner = withBindings(scope, plain.bindings);
|
|
1702
|
+
const children = new Y.Array();
|
|
1703
|
+
children.push(plain.children.map((child) => this.toLive(child, inner)));
|
|
1607
1704
|
map.set("children", children);
|
|
1608
1705
|
this.registerLive(plain.id, map);
|
|
1609
1706
|
return map;
|
|
1610
1707
|
}
|
|
1611
1708
|
/**
|
|
1612
|
-
* A live node that can be inserted into a parent
|
|
1613
|
-
* attached node is copied, because a Yjs type cannot
|
|
1709
|
+
* A live node that can be inserted into a parent (`scope` is in force there), or a part's `root`.
|
|
1710
|
+
* Anything not yet attached is built fresh; an attached node is copied, because a Yjs type cannot
|
|
1711
|
+
* have two parents.
|
|
1614
1712
|
*/
|
|
1615
|
-
attachable(id) {
|
|
1616
|
-
return this.toLive(this.snapshot(id));
|
|
1713
|
+
attachable(id, scope = /* @__PURE__ */ new Map(), root = false) {
|
|
1714
|
+
return this.toLive(this.snapshot(id), scope, root);
|
|
1617
1715
|
}
|
|
1618
1716
|
};
|
|
1619
1717
|
function xmlToGenericShell(plain) {
|
|
1620
|
-
const map = new
|
|
1718
|
+
const map = new Y.Map();
|
|
1621
1719
|
map.set("ns", plain.ns);
|
|
1622
1720
|
map.set("local", plain.local);
|
|
1623
1721
|
if (plain.prefix !== void 0) map.set("prefix", plain.prefix);
|
|
1624
|
-
const attrs = new
|
|
1722
|
+
const attrs = new Y.Array();
|
|
1625
1723
|
attrs.push(plain.attrs.map((a) => ({ ...a })));
|
|
1626
1724
|
map.set("attrs", attrs);
|
|
1627
|
-
const bindings = new
|
|
1725
|
+
const bindings = new Y.Array();
|
|
1628
1726
|
bindings.push(plain.bindings.map((b) => ({ ...b })));
|
|
1629
1727
|
map.set("bindings", bindings);
|
|
1630
1728
|
return map;
|
|
@@ -1689,7 +1787,7 @@ function setNamespaceBinding(registry, id, prefix, uri) {
|
|
|
1689
1787
|
}
|
|
1690
1788
|
}
|
|
1691
1789
|
function asLiveElement(node, id) {
|
|
1692
|
-
if (node instanceof
|
|
1790
|
+
if (node instanceof Y.Text) throw new JournalError(`${id} is a text node, not an element`);
|
|
1693
1791
|
return node;
|
|
1694
1792
|
}
|
|
1695
1793
|
function asPlainElement(node, id) {
|
|
@@ -1705,7 +1803,7 @@ function spliceText(registry, id, start, deleteCount, insert) {
|
|
|
1705
1803
|
return;
|
|
1706
1804
|
}
|
|
1707
1805
|
const text = entry.live;
|
|
1708
|
-
if (!(text instanceof
|
|
1806
|
+
if (!(text instanceof Y.Text)) throw new JournalError(`${id} is not a text node`);
|
|
1709
1807
|
if (start < 0 || start + deleteCount > text.length) {
|
|
1710
1808
|
throw new JournalError(`text splice out of range on ${id}`);
|
|
1711
1809
|
}
|
|
@@ -1718,27 +1816,29 @@ function detachRemoved(registry, parent, start, count) {
|
|
|
1718
1816
|
function spliceChildren(registry, parentId, start, deleteCount, childIds) {
|
|
1719
1817
|
const parentEntry = registry.require(parentId);
|
|
1720
1818
|
if ("plain" in parentEntry) {
|
|
1721
|
-
const
|
|
1722
|
-
if (start < 0 || start + deleteCount >
|
|
1819
|
+
const parent2 = asPlainElement(parentEntry.plain, parentId);
|
|
1820
|
+
if (start < 0 || start + deleteCount > parent2.children.length) {
|
|
1723
1821
|
throw new JournalError(`child splice out of range on ${parentId}`);
|
|
1724
1822
|
}
|
|
1725
1823
|
const inserted2 = childIds.map((id) => registry.snapshot(id));
|
|
1726
|
-
|
|
1727
|
-
registry.adopt(
|
|
1824
|
+
parent2.children.splice(start, deleteCount, ...inserted2);
|
|
1825
|
+
registry.adopt(parent2, inserted2);
|
|
1728
1826
|
return;
|
|
1729
1827
|
}
|
|
1730
|
-
const
|
|
1828
|
+
const parent = asLiveElement(parentEntry.live, parentId);
|
|
1829
|
+
const children = childrenOf(parent);
|
|
1731
1830
|
if (start < 0 || start + deleteCount > children.length) {
|
|
1732
1831
|
throw new JournalError(`child splice out of range on ${parentId}`);
|
|
1733
1832
|
}
|
|
1734
1833
|
detachRemoved(registry, children, start, deleteCount);
|
|
1735
|
-
const
|
|
1834
|
+
const scope = scopeWithin(parent);
|
|
1835
|
+
const inserted = childIds.map((id) => registry.attachable(id, scope));
|
|
1736
1836
|
if (deleteCount > 0) children.delete(start, deleteCount);
|
|
1737
1837
|
if (inserted.length > 0) children.insert(start, inserted);
|
|
1738
1838
|
}
|
|
1739
1839
|
function locate(registry, id, node) {
|
|
1740
1840
|
const children = node.parent;
|
|
1741
|
-
if (!(children instanceof
|
|
1841
|
+
if (!(children instanceof Y.Array)) throw new JournalError(`${id} is not attached`);
|
|
1742
1842
|
const index = children.toArray().indexOf(node);
|
|
1743
1843
|
if (index < 0) throw new JournalError(`${id} is not in its parent`);
|
|
1744
1844
|
return { children, index };
|
|
@@ -1765,8 +1865,10 @@ function moveNode(registry, id, destinationId, destinationIndex) {
|
|
|
1765
1865
|
registry.adopt(destination2, [copy]);
|
|
1766
1866
|
return;
|
|
1767
1867
|
}
|
|
1768
|
-
const destination =
|
|
1769
|
-
destination.insert(destinationIndex, [
|
|
1868
|
+
const destination = asLiveElement(destinationEntry.live, destinationId);
|
|
1869
|
+
childrenOf(destination).insert(destinationIndex, [
|
|
1870
|
+
registry.toLive(copy, scopeWithin(destination))
|
|
1871
|
+
]);
|
|
1770
1872
|
}
|
|
1771
1873
|
function renameNode(registry, id, qname) {
|
|
1772
1874
|
const entry = registry.require(id);
|
|
@@ -1781,7 +1883,7 @@ function renameNode(registry, id, qname) {
|
|
|
1781
1883
|
return true;
|
|
1782
1884
|
}
|
|
1783
1885
|
const live = entry.live;
|
|
1784
|
-
if (live instanceof
|
|
1886
|
+
if (live instanceof Y.Text || isCompact(live)) return false;
|
|
1785
1887
|
if (live.get("ns") !== qname.namespaceUri) live.set("ns", qname.namespaceUri);
|
|
1786
1888
|
if (live.get("local") !== qname.localName) live.set("local", qname.localName);
|
|
1787
1889
|
if (qname.prefix === void 0) {
|
|
@@ -1811,7 +1913,7 @@ function putNode(registry, effect) {
|
|
|
1811
1913
|
}
|
|
1812
1914
|
function rootFor(registry, id) {
|
|
1813
1915
|
const entry = registry.require(id);
|
|
1814
|
-
const live = "live" in entry && entry.live.parent === null ? entry.live : registry.attachable(id);
|
|
1916
|
+
const live = "live" in entry && entry.live.parent === null ? entry.live : registry.attachable(id, /* @__PURE__ */ new Map(), true);
|
|
1815
1917
|
return asLiveElement(live, id);
|
|
1816
1918
|
}
|
|
1817
1919
|
function applyEffect(doc, registry, effect, context) {
|
|
@@ -1855,7 +1957,7 @@ function applyEffect(doc, registry, effect, context) {
|
|
|
1855
1957
|
const rels = doc.getMap("rels");
|
|
1856
1958
|
let byId = rels.get(effect.owner);
|
|
1857
1959
|
if (!byId) {
|
|
1858
|
-
byId = new
|
|
1960
|
+
byId = new Y.Map();
|
|
1859
1961
|
rels.set(effect.owner, byId);
|
|
1860
1962
|
}
|
|
1861
1963
|
byId.set(effect.record.id, { ...effect.record });
|
|
@@ -2060,7 +2162,7 @@ var DocxCollaborationSession = class {
|
|
|
2060
2162
|
this.doc = options.doc;
|
|
2061
2163
|
this.awareness = options.awareness;
|
|
2062
2164
|
this.offlineEditing = options.offlineEditing ?? false;
|
|
2063
|
-
this.undoManager = new
|
|
2165
|
+
this.undoManager = new Y.UndoManager(
|
|
2064
2166
|
[
|
|
2065
2167
|
this.doc.getMap("parts"),
|
|
2066
2168
|
this.doc.getMap("rels"),
|
|
@@ -2100,7 +2202,7 @@ var DocxCollaborationSession = class {
|
|
|
2100
2202
|
this.setStatus("error", "not-initialized");
|
|
2101
2203
|
return;
|
|
2102
2204
|
}
|
|
2103
|
-
if (readFormat(this.doc)
|
|
2205
|
+
if (!isReadableFormat(readFormat(this.doc))) {
|
|
2104
2206
|
this.setStatus("error", "collaboration-format-mismatch", readFormat(this.doc));
|
|
2105
2207
|
return;
|
|
2106
2208
|
}
|
|
@@ -2374,7 +2476,7 @@ function resolveAddress(port, address) {
|
|
|
2374
2476
|
};
|
|
2375
2477
|
}
|
|
2376
2478
|
function vectorKey(doc) {
|
|
2377
|
-
return Array.from(
|
|
2479
|
+
return Array.from(Y.encodeStateVector(doc)).join(",");
|
|
2378
2480
|
}
|
|
2379
2481
|
function randomToken() {
|
|
2380
2482
|
if (typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
@@ -2407,7 +2509,7 @@ function assertCompatibleVersions(versions) {
|
|
|
2407
2509
|
function assertRoomFormat(doc) {
|
|
2408
2510
|
if (!isBootstrapped(doc)) throw new CollaborationCompatibilityError("The room holds no document.");
|
|
2409
2511
|
const format = readFormat(doc);
|
|
2410
|
-
if (format
|
|
2512
|
+
if (!isReadableFormat(format)) {
|
|
2411
2513
|
throw new CollaborationCompatibilityError(`Unsupported room format ${String(format)}.`);
|
|
2412
2514
|
}
|
|
2413
2515
|
}
|