@arbidocs/coedit 0.5.21 → 0.5.23
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/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { COLLAB_FORMAT, MaterializeError } from './encoding.cjs';
|
|
2
2
|
export { C as CollaborationSessionError, D as DocxCollaborationSession, a as DocxCollaborationSessionOptions } from './session-WGvWs6yO.cjs';
|
|
3
|
-
export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-
|
|
3
|
+
export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-XSYTVmOU.cjs';
|
|
4
4
|
import { EditorModule } from '@docx-editor.dev/core';
|
|
5
5
|
import { EditorCollaborationSession } from '@docx-editor.dev/core/collaboration';
|
|
6
6
|
import '@docx-editor.dev/core/store';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { COLLAB_FORMAT, MaterializeError } from './encoding.js';
|
|
2
2
|
export { C as CollaborationSessionError, D as DocxCollaborationSession, a as DocxCollaborationSessionOptions } from './session-WGvWs6yO.js';
|
|
3
|
-
export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-
|
|
3
|
+
export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-XSYTVmOU.js';
|
|
4
4
|
import { EditorModule } from '@docx-editor.dev/core';
|
|
5
5
|
import { EditorCollaborationSession } from '@docx-editor.dev/core/collaboration';
|
|
6
6
|
import '@docx-editor.dev/core/store';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { ORIGIN_IDS, readOoxmlPart, validateExternalTarget, relsPartNameFor, contentTypesPartBytes, withContentTypeOverride } from '@docx-editor.dev/core/store';
|
|
1
|
+
import * as Y from 'yjs';
|
|
2
|
+
import { ORIGIN_IDS, readOoxmlPart, validateExternalTarget, relsPartNameFor, contentTypesPartBytes, withContentTypeOverride, readXml, XML_NAMESPACE_URI } from '@docx-editor.dev/core/store';
|
|
3
3
|
import { createCollaborationStatusTracker, safeParticipantColor } from '@docx-editor.dev/core/collaboration';
|
|
4
4
|
import { historyGroupOfJournal, mapOffsetAcrossText } from '@docx-editor.dev/core/collaboration/replication';
|
|
5
5
|
|
|
@@ -314,6 +314,92 @@ function writeUnits(text, units) {
|
|
|
314
314
|
function isRichParagraph(map) {
|
|
315
315
|
return map.has("inline");
|
|
316
316
|
}
|
|
317
|
+
var PACKED = "xml";
|
|
318
|
+
function isPackedElement(map) {
|
|
319
|
+
return map.has(PACKED);
|
|
320
|
+
}
|
|
321
|
+
function isCompact(map) {
|
|
322
|
+
return isRichParagraph(map) || isPackedElement(map);
|
|
323
|
+
}
|
|
324
|
+
var TEXT_BEARING = /* @__PURE__ */ new Set(["p", "t", "delText", "instrText", "delInstrText"]);
|
|
325
|
+
function packable(xml) {
|
|
326
|
+
if (xml.ns === WML && TEXT_BEARING.has(xml.local)) return false;
|
|
327
|
+
return xml.children.every((c) => typeof c === "string" || packable(c));
|
|
328
|
+
}
|
|
329
|
+
function createPacked(xml, scope) {
|
|
330
|
+
const map = new Y.Map();
|
|
331
|
+
map.set(PACKED, serializeXml(xml, scope));
|
|
332
|
+
return map;
|
|
333
|
+
}
|
|
334
|
+
function repack(map, xml) {
|
|
335
|
+
const next = serializeXml(xml, scopeOf(map));
|
|
336
|
+
if (map.get(PACKED) !== next) map.set(PACKED, next);
|
|
337
|
+
}
|
|
338
|
+
function scopeOf(node) {
|
|
339
|
+
const chain = [];
|
|
340
|
+
let parent = node.parent;
|
|
341
|
+
while (parent instanceof Y.Array) {
|
|
342
|
+
const owner = parent.parent;
|
|
343
|
+
if (!(owner instanceof Y.Map)) break;
|
|
344
|
+
chain.push(owner);
|
|
345
|
+
parent = owner.parent;
|
|
346
|
+
}
|
|
347
|
+
const scope = /* @__PURE__ */ new Map();
|
|
348
|
+
for (const el of chain.reverse()) {
|
|
349
|
+
for (const b of el.get("bindings").toArray()) scope.set(b.prefix, b.ns);
|
|
350
|
+
}
|
|
351
|
+
return scope;
|
|
352
|
+
}
|
|
353
|
+
function scopeWithin(el) {
|
|
354
|
+
const scope = scopeOf(el);
|
|
355
|
+
if (!isPackedElement(el)) {
|
|
356
|
+
for (const b of el.get("bindings").toArray()) scope.set(b.prefix, b.ns);
|
|
357
|
+
}
|
|
358
|
+
return scope;
|
|
359
|
+
}
|
|
360
|
+
var unpackLimits = { maxBytes: 64 * 1024 * 1024, maxElements: 2e6 };
|
|
361
|
+
function unpack(text, scope) {
|
|
362
|
+
const read = readXml(text, unpackLimits);
|
|
363
|
+
if (!read.ok) throw new Error(`collab: a packed element is not readable XML (${read.reason})`);
|
|
364
|
+
const convert = (node, outer) => {
|
|
365
|
+
const bindings = [];
|
|
366
|
+
const named = [];
|
|
367
|
+
for (const [name, value] of Object.entries(node.attributes)) {
|
|
368
|
+
if (name === "xmlns") bindings.push({ prefix: "", ns: value });
|
|
369
|
+
else if (name.startsWith("xmlns:")) bindings.push({ prefix: name.slice(6), ns: value });
|
|
370
|
+
else named.push([name, value]);
|
|
371
|
+
}
|
|
372
|
+
const inner = new Map(outer);
|
|
373
|
+
for (const b of bindings) inner.set(b.prefix, b.ns);
|
|
374
|
+
const resolve = (prefix) => {
|
|
375
|
+
if (prefix === "xml") return XML_NAMESPACE_URI;
|
|
376
|
+
const ns = inner.get(prefix);
|
|
377
|
+
if (ns === void 0)
|
|
378
|
+
throw new Error(`collab: prefix ${prefix} is not bound in a packed element`);
|
|
379
|
+
return ns;
|
|
380
|
+
};
|
|
381
|
+
const colon = node.name.indexOf(":");
|
|
382
|
+
const xml = {
|
|
383
|
+
ns: colon < 0 ? inner.get("") ?? "" : resolve(node.name.slice(0, colon)),
|
|
384
|
+
local: colon < 0 ? node.name : node.name.slice(colon + 1),
|
|
385
|
+
attrs: named.map(([name, value]) => {
|
|
386
|
+
const at = name.indexOf(":");
|
|
387
|
+
if (at < 0) return { ns: "", local: name, value };
|
|
388
|
+
const prefix = name.slice(0, at);
|
|
389
|
+
return { ns: resolve(prefix), local: name.slice(at + 1), prefix, value };
|
|
390
|
+
}),
|
|
391
|
+
bindings,
|
|
392
|
+
children: node.children.map(
|
|
393
|
+
(child) => child.type === "text" ? child.value : convert(child, inner)
|
|
394
|
+
)
|
|
395
|
+
};
|
|
396
|
+
if (colon >= 0) xml.prefix = node.name.slice(0, colon);
|
|
397
|
+
return xml;
|
|
398
|
+
};
|
|
399
|
+
const root = read.nodes.find((n) => n.type === "element");
|
|
400
|
+
if (!root || root.type !== "element") throw new Error("collab: a packed element is empty");
|
|
401
|
+
return convert(root, scope);
|
|
402
|
+
}
|
|
317
403
|
function isParagraphEl(el) {
|
|
318
404
|
return el.ns === WML && el.local === "p";
|
|
319
405
|
}
|
|
@@ -321,47 +407,53 @@ function fieldsOf(map, xml) {
|
|
|
321
407
|
map.set("ns", xml.ns);
|
|
322
408
|
map.set("local", xml.local);
|
|
323
409
|
if (xml.prefix !== void 0) map.set("prefix", xml.prefix);
|
|
324
|
-
const attrs = new
|
|
410
|
+
const attrs = new Y.Array();
|
|
325
411
|
attrs.push(xml.attrs.map((a) => ({ ...a })));
|
|
326
412
|
map.set("attrs", attrs);
|
|
327
|
-
const bindings = new
|
|
413
|
+
const bindings = new Y.Array();
|
|
328
414
|
bindings.push(xml.bindings.map((b) => ({ ...b })));
|
|
329
415
|
map.set("bindings", bindings);
|
|
330
416
|
}
|
|
331
417
|
function newText(value) {
|
|
332
|
-
const text = new
|
|
418
|
+
const text = new Y.Text();
|
|
333
419
|
text.insert(0, value);
|
|
334
420
|
return text;
|
|
335
421
|
}
|
|
336
|
-
function createRichParagraph(xml, encoded) {
|
|
337
|
-
const map = new
|
|
422
|
+
function createRichParagraph(xml, encoded, scope = /* @__PURE__ */ new Map()) {
|
|
423
|
+
const map = new Y.Map();
|
|
338
424
|
fieldsOf(map, xml);
|
|
339
|
-
const
|
|
340
|
-
children
|
|
425
|
+
const inner = withBindings(scope, xml.bindings);
|
|
426
|
+
const children = new Y.Array();
|
|
427
|
+
children.push(encoded.head.map((el) => xmlToLive(el, inner)));
|
|
341
428
|
map.set("children", children);
|
|
342
|
-
const inline = new
|
|
429
|
+
const inline = new Y.Text();
|
|
343
430
|
writeUnits(inline, encoded.units);
|
|
344
431
|
map.set("inline", inline);
|
|
345
432
|
return map;
|
|
346
433
|
}
|
|
347
|
-
function
|
|
434
|
+
function withBindings(scope, bindings) {
|
|
435
|
+
const inner = new Map(scope);
|
|
436
|
+
for (const b of bindings) inner.set(b.prefix, b.ns);
|
|
437
|
+
return inner;
|
|
438
|
+
}
|
|
439
|
+
function xmlToLive(xml, scope = /* @__PURE__ */ new Map(), root = false) {
|
|
348
440
|
if (isParagraphEl(xml)) {
|
|
349
441
|
const encoded = encodeParagraph(xml.children);
|
|
350
|
-
if (encoded) return createRichParagraph(xml, encoded);
|
|
442
|
+
if (encoded) return createRichParagraph(xml, encoded, scope);
|
|
351
443
|
}
|
|
352
|
-
return
|
|
353
|
-
|
|
354
|
-
function xmlToGenericLive(xml) {
|
|
355
|
-
const map = new Y3.Map();
|
|
444
|
+
if (!root && packable(xml)) return createPacked(xml, scope);
|
|
445
|
+
const map = new Y.Map();
|
|
356
446
|
fieldsOf(map, xml);
|
|
357
|
-
const
|
|
358
|
-
children
|
|
447
|
+
const inner = withBindings(scope, xml.bindings);
|
|
448
|
+
const children = new Y.Array();
|
|
449
|
+
children.push(xml.children.map((c) => typeof c === "string" ? newText(c) : xmlToLive(c, inner)));
|
|
359
450
|
map.set("children", children);
|
|
360
451
|
return map;
|
|
361
452
|
}
|
|
362
453
|
function liveToXml(map) {
|
|
454
|
+
if (isPackedElement(map)) return unpack(map.get(PACKED), scopeOf(map));
|
|
363
455
|
const prefix = map.get("prefix");
|
|
364
|
-
const stored = map.get("children").toArray().map((child) => child instanceof
|
|
456
|
+
const stored = map.get("children").toArray().map((child) => child instanceof Y.Text ? child.toString() : liveToXml(child));
|
|
365
457
|
const children = isRichParagraph(map) ? [...stored, ...decodeInline(readUnits(map.get("inline")))] : stored;
|
|
366
458
|
const xml = {
|
|
367
459
|
ns: map.get("ns"),
|
|
@@ -376,14 +468,13 @@ function liveToXml(map) {
|
|
|
376
468
|
var escapeText = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
377
469
|
var escapeAttr = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/\t/g, "	").replace(/\n/g, " ").replace(/\r/g, " ");
|
|
378
470
|
var qualified = (prefix, local) => prefix ? `${prefix}:${local}` : local;
|
|
379
|
-
function
|
|
380
|
-
const inScope =
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
for (const b of el.bindings) {
|
|
471
|
+
function startTag(prefix, local, bindings, attrs, scope) {
|
|
472
|
+
const inScope = withBindings(scope, bindings);
|
|
473
|
+
let out = `<${qualified(prefix, local)}`;
|
|
474
|
+
for (const b of bindings) {
|
|
384
475
|
out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
|
|
385
476
|
}
|
|
386
|
-
for (const a of
|
|
477
|
+
for (const a of attrs) {
|
|
387
478
|
let p = a.prefix;
|
|
388
479
|
if (a.ns !== "" && !p) {
|
|
389
480
|
for (const [candidate, uri] of inScope) {
|
|
@@ -395,48 +486,41 @@ function serializeXml(el, scope) {
|
|
|
395
486
|
}
|
|
396
487
|
out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
|
|
397
488
|
}
|
|
489
|
+
return [out, inScope];
|
|
490
|
+
}
|
|
491
|
+
function serializeXml(el, scope) {
|
|
492
|
+
const [out, inScope] = startTag(el.prefix, el.local, el.bindings, el.attrs, scope);
|
|
398
493
|
if (el.children.length === 0) return `${out}/>`;
|
|
399
|
-
|
|
494
|
+
let body = "";
|
|
400
495
|
for (const child of el.children) {
|
|
401
|
-
|
|
496
|
+
body += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
|
|
402
497
|
}
|
|
403
|
-
return `${out}</${qualified(el.prefix, el.local)}>`;
|
|
498
|
+
return `${out}>${body}</${qualified(el.prefix, el.local)}>`;
|
|
404
499
|
}
|
|
405
500
|
function serializeLive(el, scope) {
|
|
406
|
-
if (
|
|
501
|
+
if (isPackedElement(el)) return el.get(PACKED);
|
|
407
502
|
const prefix = el.get("prefix");
|
|
408
503
|
const local = el.get("local");
|
|
409
504
|
const bindings = el.get("bindings").toArray();
|
|
410
505
|
const attrs = el.get("attrs").toArray();
|
|
411
506
|
const children = el.get("children").toArray();
|
|
412
|
-
const inScope =
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
|
|
417
|
-
}
|
|
418
|
-
for (const a of attrs) {
|
|
419
|
-
let p = a.prefix;
|
|
420
|
-
if (a.ns !== "" && !p) {
|
|
421
|
-
for (const [candidate, uri] of inScope) {
|
|
422
|
-
if (candidate !== "" && uri === a.ns) {
|
|
423
|
-
p = candidate;
|
|
424
|
-
break;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
|
|
429
|
-
}
|
|
430
|
-
if (children.length === 0) return `${out}/>`;
|
|
431
|
-
out += ">";
|
|
507
|
+
const [out, inScope] = startTag(prefix, local, bindings, attrs, scope);
|
|
508
|
+
const inline = isRichParagraph(el) ? decodeInline(readUnits(el.get("inline"))) : [];
|
|
509
|
+
if (children.length === 0 && inline.length === 0) return `${out}/>`;
|
|
510
|
+
let body = "";
|
|
432
511
|
for (const child of children) {
|
|
433
|
-
|
|
512
|
+
body += child instanceof Y.Text ? escapeText(child.toString()) : serializeLive(child, inScope);
|
|
434
513
|
}
|
|
435
|
-
|
|
514
|
+
for (const child of inline) body += serializeXml(child, inScope);
|
|
515
|
+
return `${out}>${body}</${qualified(prefix, local)}>`;
|
|
436
516
|
}
|
|
437
517
|
|
|
438
518
|
// src/encoding.ts
|
|
439
|
-
var COLLAB_FORMAT = "arbi-docx/
|
|
519
|
+
var COLLAB_FORMAT = "arbi-docx/2";
|
|
520
|
+
var READABLE_FORMATS = /* @__PURE__ */ new Set(["arbi-docx/1", COLLAB_FORMAT]);
|
|
521
|
+
function isReadableFormat(format) {
|
|
522
|
+
return format !== void 0 && READABLE_FORMATS.has(format);
|
|
523
|
+
}
|
|
440
524
|
function isBootstrapped(doc) {
|
|
441
525
|
return doc.getMap("meta").get("format") !== void 0;
|
|
442
526
|
}
|
|
@@ -730,7 +814,7 @@ var NodeRegistry = class {
|
|
|
730
814
|
require(id) {
|
|
731
815
|
let entry = this.entries.get(id) ?? this.resolveInsideParagraph(id);
|
|
732
816
|
if (!entry) throw new RegistryError(`journal names unknown node ${id}`);
|
|
733
|
-
if ("live" in entry && entry.live instanceof
|
|
817
|
+
if ("live" in entry && entry.live instanceof Y.Map && isCompact(entry.live)) {
|
|
734
818
|
this.openParagraph(entry.live);
|
|
735
819
|
entry = this.entries.get(id);
|
|
736
820
|
}
|
|
@@ -748,7 +832,7 @@ var NodeRegistry = class {
|
|
|
748
832
|
for (let length = path.length - 1; length >= 1; length--) {
|
|
749
833
|
const ancestor = this.entries.get(id.slice(0, hash + 1) + path.slice(0, length).join("."));
|
|
750
834
|
if (!ancestor) continue;
|
|
751
|
-
if ("live" in ancestor && ancestor.live instanceof
|
|
835
|
+
if ("live" in ancestor && ancestor.live instanceof Y.Map && isCompact(ancestor.live)) {
|
|
752
836
|
this.openParagraph(ancestor.live);
|
|
753
837
|
return this.entries.get(id);
|
|
754
838
|
}
|
|
@@ -780,13 +864,13 @@ var NodeRegistry = class {
|
|
|
780
864
|
/**
|
|
781
865
|
* Walk a mirror root and register every node under its positional id, `${part}#0` for the root and
|
|
782
866
|
* `${parent}.${index}` below it. This is the id scheme core assigns when it reads a part. A rich
|
|
783
|
-
* paragraph is registered as one node; its inner nodes appear when it is opened.
|
|
867
|
+
* paragraph or a packed element is registered as one node; its inner nodes appear when it is opened.
|
|
784
868
|
*/
|
|
785
869
|
registerTree(partName, root) {
|
|
786
870
|
const walk = (node, id) => {
|
|
787
871
|
this.registerLive(id, node);
|
|
788
|
-
if (node instanceof
|
|
789
|
-
if (
|
|
872
|
+
if (node instanceof Y.Text) return;
|
|
873
|
+
if (isCompact(node)) return;
|
|
790
874
|
childrenOf(node).forEach((child, index) => walk(child, `${id}.${index}`));
|
|
791
875
|
};
|
|
792
876
|
walk(root, `${partName}#0`);
|
|
@@ -843,7 +927,7 @@ var NodeRegistry = class {
|
|
|
843
927
|
for (const child of children) this.own(child, owner);
|
|
844
928
|
if (owner) this.dirty.add(owner);
|
|
845
929
|
}
|
|
846
|
-
/** Write every changed open paragraph back into the mirror. Call at the end of a journal. */
|
|
930
|
+
/** Write every changed open paragraph or packed element back into the mirror. Call at the end of a journal. */
|
|
847
931
|
flushRich() {
|
|
848
932
|
for (const live of [...this.dirty]) {
|
|
849
933
|
this.dirty.delete(live);
|
|
@@ -854,6 +938,11 @@ var NodeRegistry = class {
|
|
|
854
938
|
writeBack(record) {
|
|
855
939
|
const { live, root } = record;
|
|
856
940
|
const xml = plainToXml(root);
|
|
941
|
+
if (isPackedElement(live)) {
|
|
942
|
+
if (packable(xml)) repack(live, xml);
|
|
943
|
+
else this.degrade(record);
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
857
946
|
const encoded = encodeParagraph(xml.children);
|
|
858
947
|
if (!encoded) {
|
|
859
948
|
this.degrade(record);
|
|
@@ -870,21 +959,22 @@ var NodeRegistry = class {
|
|
|
870
959
|
bindings.push(xml.bindings.map((b) => ({ ...b })));
|
|
871
960
|
}
|
|
872
961
|
const head = childrenOf(live);
|
|
873
|
-
const
|
|
874
|
-
|
|
962
|
+
const scope = scopeWithin(live);
|
|
963
|
+
const stored = head.toArray().map((child) => serializeLive(child, scope)).join("");
|
|
964
|
+
if (stored !== encoded.head.map((el) => serializeXml(el, scope)).join("")) {
|
|
875
965
|
head.delete(0, head.length);
|
|
876
|
-
head.push(encoded.head.map((el) => xmlToLive(el)));
|
|
966
|
+
head.push(encoded.head.map((el) => xmlToLive(el, scope)));
|
|
877
967
|
}
|
|
878
968
|
const inline = live.get("inline");
|
|
879
969
|
applyUnits(inline, readUnits(inline), encoded.units);
|
|
880
970
|
}
|
|
881
|
-
/** The paragraph can no longer be held as rich text: store it as
|
|
971
|
+
/** The paragraph can no longer be held as rich text, or the element packed: store it as a tree. */
|
|
882
972
|
degrade(record) {
|
|
883
973
|
const parent = record.live.parent;
|
|
884
|
-
if (!(parent instanceof
|
|
974
|
+
if (!(parent instanceof Y.Array))
|
|
885
975
|
throw new RegistryError("a paragraph outside a parent cannot change form");
|
|
886
976
|
const index = parent.toArray().indexOf(record.live);
|
|
887
|
-
const generic = this.toGenericLive(record.root);
|
|
977
|
+
const generic = this.toGenericLive(record.root, scopeOf(record.live));
|
|
888
978
|
this.open.delete(record.live);
|
|
889
979
|
parent.delete(index, 1);
|
|
890
980
|
parent.insert(index, [generic]);
|
|
@@ -899,8 +989,8 @@ var NodeRegistry = class {
|
|
|
899
989
|
snapshotLive(node) {
|
|
900
990
|
const id = this.liveIds.get(node);
|
|
901
991
|
if (id === void 0) throw new RegistryError("live node has no registered id");
|
|
902
|
-
if (node instanceof
|
|
903
|
-
if (
|
|
992
|
+
if (node instanceof Y.Text) return { type: "text", id, value: node.toString() };
|
|
993
|
+
if (isCompact(node)) {
|
|
904
994
|
return this.open.get(node)?.root ?? xmlToPlain(liveToXml(node), id);
|
|
905
995
|
}
|
|
906
996
|
const prefix = node.get("prefix");
|
|
@@ -928,8 +1018,8 @@ var NodeRegistry = class {
|
|
|
928
1018
|
if (id !== void 0 && this.isCurrentLive(id, node)) {
|
|
929
1019
|
this.entries.set(id, { plain });
|
|
930
1020
|
}
|
|
931
|
-
if (node instanceof
|
|
932
|
-
if (
|
|
1021
|
+
if (node instanceof Y.Text || plain.type === "text") return;
|
|
1022
|
+
if (isCompact(node)) {
|
|
933
1023
|
this.open.delete(node);
|
|
934
1024
|
this.dirty.delete(node);
|
|
935
1025
|
return;
|
|
@@ -937,53 +1027,61 @@ var NodeRegistry = class {
|
|
|
937
1027
|
const liveChildren = childrenOf(node).toArray();
|
|
938
1028
|
liveChildren.forEach((child, index) => this.detachInto(child, plain.children[index]));
|
|
939
1029
|
}
|
|
940
|
-
/**
|
|
941
|
-
|
|
1030
|
+
/**
|
|
1031
|
+
* Build Yjs types for a plain subtree placed where `scope` is in force, and register every node as
|
|
1032
|
+
* live. A rich paragraph or packed element stays open on the plain tree, which is already current.
|
|
1033
|
+
* A part's `root` is never packed.
|
|
1034
|
+
*/
|
|
1035
|
+
toLive(plain, scope = /* @__PURE__ */ new Map(), root = false) {
|
|
942
1036
|
if (plain.type === "text") {
|
|
943
|
-
const text = new
|
|
1037
|
+
const text = new Y.Text();
|
|
944
1038
|
text.insert(0, plain.value);
|
|
945
1039
|
this.registerLive(plain.id, text);
|
|
946
1040
|
return text;
|
|
947
1041
|
}
|
|
1042
|
+
const xml = plainToXml(plain);
|
|
1043
|
+
let map;
|
|
948
1044
|
if (isParagraphEl(plain)) {
|
|
949
|
-
const xml = plainToXml(plain);
|
|
950
1045
|
const encoded = encodeParagraph(xml.children);
|
|
951
|
-
if (encoded)
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
}
|
|
1046
|
+
if (encoded) map = createRichParagraph(xml, encoded, scope);
|
|
1047
|
+
}
|
|
1048
|
+
if (!map && !root && packable(xml)) map = xmlToLive(xml, scope);
|
|
1049
|
+
if (map) {
|
|
1050
|
+
this.liveIds.set(map, plain.id);
|
|
1051
|
+
this.registerPlain(plain);
|
|
1052
|
+
this.own(plain, map);
|
|
1053
|
+
this.open.set(map, { live: map, root: plain, id: plain.id });
|
|
1054
|
+
return map;
|
|
959
1055
|
}
|
|
960
|
-
return this.toGenericLive(plain);
|
|
1056
|
+
return this.toGenericLive(plain, scope);
|
|
961
1057
|
}
|
|
962
|
-
toGenericLive(plain) {
|
|
1058
|
+
toGenericLive(plain, scope) {
|
|
963
1059
|
const map = xmlToGenericShell(plain);
|
|
964
|
-
const
|
|
965
|
-
children
|
|
1060
|
+
const inner = withBindings(scope, plain.bindings);
|
|
1061
|
+
const children = new Y.Array();
|
|
1062
|
+
children.push(plain.children.map((child) => this.toLive(child, inner)));
|
|
966
1063
|
map.set("children", children);
|
|
967
1064
|
this.registerLive(plain.id, map);
|
|
968
1065
|
return map;
|
|
969
1066
|
}
|
|
970
1067
|
/**
|
|
971
|
-
* A live node that can be inserted into a parent
|
|
972
|
-
* attached node is copied, because a Yjs type cannot
|
|
1068
|
+
* A live node that can be inserted into a parent (`scope` is in force there), or a part's `root`.
|
|
1069
|
+
* Anything not yet attached is built fresh; an attached node is copied, because a Yjs type cannot
|
|
1070
|
+
* have two parents.
|
|
973
1071
|
*/
|
|
974
|
-
attachable(id) {
|
|
975
|
-
return this.toLive(this.snapshot(id));
|
|
1072
|
+
attachable(id, scope = /* @__PURE__ */ new Map(), root = false) {
|
|
1073
|
+
return this.toLive(this.snapshot(id), scope, root);
|
|
976
1074
|
}
|
|
977
1075
|
};
|
|
978
1076
|
function xmlToGenericShell(plain) {
|
|
979
|
-
const map = new
|
|
1077
|
+
const map = new Y.Map();
|
|
980
1078
|
map.set("ns", plain.ns);
|
|
981
1079
|
map.set("local", plain.local);
|
|
982
1080
|
if (plain.prefix !== void 0) map.set("prefix", plain.prefix);
|
|
983
|
-
const attrs = new
|
|
1081
|
+
const attrs = new Y.Array();
|
|
984
1082
|
attrs.push(plain.attrs.map((a) => ({ ...a })));
|
|
985
1083
|
map.set("attrs", attrs);
|
|
986
|
-
const bindings = new
|
|
1084
|
+
const bindings = new Y.Array();
|
|
987
1085
|
bindings.push(plain.bindings.map((b) => ({ ...b })));
|
|
988
1086
|
map.set("bindings", bindings);
|
|
989
1087
|
return map;
|
|
@@ -1048,7 +1146,7 @@ function setNamespaceBinding(registry, id, prefix, uri) {
|
|
|
1048
1146
|
}
|
|
1049
1147
|
}
|
|
1050
1148
|
function asLiveElement(node, id) {
|
|
1051
|
-
if (node instanceof
|
|
1149
|
+
if (node instanceof Y.Text) throw new JournalError(`${id} is a text node, not an element`);
|
|
1052
1150
|
return node;
|
|
1053
1151
|
}
|
|
1054
1152
|
function asPlainElement(node, id) {
|
|
@@ -1064,7 +1162,7 @@ function spliceText(registry, id, start, deleteCount, insert) {
|
|
|
1064
1162
|
return;
|
|
1065
1163
|
}
|
|
1066
1164
|
const text = entry.live;
|
|
1067
|
-
if (!(text instanceof
|
|
1165
|
+
if (!(text instanceof Y.Text)) throw new JournalError(`${id} is not a text node`);
|
|
1068
1166
|
if (start < 0 || start + deleteCount > text.length) {
|
|
1069
1167
|
throw new JournalError(`text splice out of range on ${id}`);
|
|
1070
1168
|
}
|
|
@@ -1077,27 +1175,29 @@ function detachRemoved(registry, parent, start, count) {
|
|
|
1077
1175
|
function spliceChildren(registry, parentId, start, deleteCount, childIds) {
|
|
1078
1176
|
const parentEntry = registry.require(parentId);
|
|
1079
1177
|
if ("plain" in parentEntry) {
|
|
1080
|
-
const
|
|
1081
|
-
if (start < 0 || start + deleteCount >
|
|
1178
|
+
const parent2 = asPlainElement(parentEntry.plain, parentId);
|
|
1179
|
+
if (start < 0 || start + deleteCount > parent2.children.length) {
|
|
1082
1180
|
throw new JournalError(`child splice out of range on ${parentId}`);
|
|
1083
1181
|
}
|
|
1084
1182
|
const inserted2 = childIds.map((id) => registry.snapshot(id));
|
|
1085
|
-
|
|
1086
|
-
registry.adopt(
|
|
1183
|
+
parent2.children.splice(start, deleteCount, ...inserted2);
|
|
1184
|
+
registry.adopt(parent2, inserted2);
|
|
1087
1185
|
return;
|
|
1088
1186
|
}
|
|
1089
|
-
const
|
|
1187
|
+
const parent = asLiveElement(parentEntry.live, parentId);
|
|
1188
|
+
const children = childrenOf(parent);
|
|
1090
1189
|
if (start < 0 || start + deleteCount > children.length) {
|
|
1091
1190
|
throw new JournalError(`child splice out of range on ${parentId}`);
|
|
1092
1191
|
}
|
|
1093
1192
|
detachRemoved(registry, children, start, deleteCount);
|
|
1094
|
-
const
|
|
1193
|
+
const scope = scopeWithin(parent);
|
|
1194
|
+
const inserted = childIds.map((id) => registry.attachable(id, scope));
|
|
1095
1195
|
if (deleteCount > 0) children.delete(start, deleteCount);
|
|
1096
1196
|
if (inserted.length > 0) children.insert(start, inserted);
|
|
1097
1197
|
}
|
|
1098
1198
|
function locate(registry, id, node) {
|
|
1099
1199
|
const children = node.parent;
|
|
1100
|
-
if (!(children instanceof
|
|
1200
|
+
if (!(children instanceof Y.Array)) throw new JournalError(`${id} is not attached`);
|
|
1101
1201
|
const index = children.toArray().indexOf(node);
|
|
1102
1202
|
if (index < 0) throw new JournalError(`${id} is not in its parent`);
|
|
1103
1203
|
return { children, index };
|
|
@@ -1124,8 +1224,10 @@ function moveNode(registry, id, destinationId, destinationIndex) {
|
|
|
1124
1224
|
registry.adopt(destination2, [copy]);
|
|
1125
1225
|
return;
|
|
1126
1226
|
}
|
|
1127
|
-
const destination =
|
|
1128
|
-
destination.insert(destinationIndex, [
|
|
1227
|
+
const destination = asLiveElement(destinationEntry.live, destinationId);
|
|
1228
|
+
childrenOf(destination).insert(destinationIndex, [
|
|
1229
|
+
registry.toLive(copy, scopeWithin(destination))
|
|
1230
|
+
]);
|
|
1129
1231
|
}
|
|
1130
1232
|
function renameNode(registry, id, qname) {
|
|
1131
1233
|
const entry = registry.require(id);
|
|
@@ -1140,7 +1242,7 @@ function renameNode(registry, id, qname) {
|
|
|
1140
1242
|
return true;
|
|
1141
1243
|
}
|
|
1142
1244
|
const live = entry.live;
|
|
1143
|
-
if (live instanceof
|
|
1245
|
+
if (live instanceof Y.Text || isCompact(live)) return false;
|
|
1144
1246
|
if (live.get("ns") !== qname.namespaceUri) live.set("ns", qname.namespaceUri);
|
|
1145
1247
|
if (live.get("local") !== qname.localName) live.set("local", qname.localName);
|
|
1146
1248
|
if (qname.prefix === void 0) {
|
|
@@ -1170,7 +1272,7 @@ function putNode(registry, effect) {
|
|
|
1170
1272
|
}
|
|
1171
1273
|
function rootFor(registry, id) {
|
|
1172
1274
|
const entry = registry.require(id);
|
|
1173
|
-
const live = "live" in entry && entry.live.parent === null ? entry.live : registry.attachable(id);
|
|
1275
|
+
const live = "live" in entry && entry.live.parent === null ? entry.live : registry.attachable(id, /* @__PURE__ */ new Map(), true);
|
|
1174
1276
|
return asLiveElement(live, id);
|
|
1175
1277
|
}
|
|
1176
1278
|
function applyEffect(doc, registry, effect, context) {
|
|
@@ -1214,7 +1316,7 @@ function applyEffect(doc, registry, effect, context) {
|
|
|
1214
1316
|
const rels = doc.getMap("rels");
|
|
1215
1317
|
let byId = rels.get(effect.owner);
|
|
1216
1318
|
if (!byId) {
|
|
1217
|
-
byId = new
|
|
1319
|
+
byId = new Y.Map();
|
|
1218
1320
|
rels.set(effect.owner, byId);
|
|
1219
1321
|
}
|
|
1220
1322
|
byId.set(effect.record.id, { ...effect.record });
|
|
@@ -1419,7 +1521,7 @@ var DocxCollaborationSession = class {
|
|
|
1419
1521
|
this.doc = options.doc;
|
|
1420
1522
|
this.awareness = options.awareness;
|
|
1421
1523
|
this.offlineEditing = options.offlineEditing ?? false;
|
|
1422
|
-
this.undoManager = new
|
|
1524
|
+
this.undoManager = new Y.UndoManager(
|
|
1423
1525
|
[
|
|
1424
1526
|
this.doc.getMap("parts"),
|
|
1425
1527
|
this.doc.getMap("rels"),
|
|
@@ -1459,7 +1561,7 @@ var DocxCollaborationSession = class {
|
|
|
1459
1561
|
this.setStatus("error", "not-initialized");
|
|
1460
1562
|
return;
|
|
1461
1563
|
}
|
|
1462
|
-
if (readFormat(this.doc)
|
|
1564
|
+
if (!isReadableFormat(readFormat(this.doc))) {
|
|
1463
1565
|
this.setStatus("error", "collaboration-format-mismatch", readFormat(this.doc));
|
|
1464
1566
|
return;
|
|
1465
1567
|
}
|
|
@@ -1733,7 +1835,7 @@ function resolveAddress(port, address) {
|
|
|
1733
1835
|
};
|
|
1734
1836
|
}
|
|
1735
1837
|
function vectorKey(doc) {
|
|
1736
|
-
return Array.from(
|
|
1838
|
+
return Array.from(Y.encodeStateVector(doc)).join(",");
|
|
1737
1839
|
}
|
|
1738
1840
|
function randomToken() {
|
|
1739
1841
|
if (typeof crypto.randomUUID === "function") return crypto.randomUUID();
|