@sofereditor/core 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +399 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +133 -1
- package/dist/index.d.ts +133 -1
- package/dist/index.js +386 -14
- package/dist/index.js.map +1 -1
- package/package.json +26 -14
package/dist/index.js
CHANGED
|
@@ -89,6 +89,19 @@ function withDefaults(partial) {
|
|
|
89
89
|
|
|
90
90
|
// src/document.ts
|
|
91
91
|
import * as Y from "yjs";
|
|
92
|
+
function newBlockId() {
|
|
93
|
+
try {
|
|
94
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
95
|
+
return crypto.randomUUID();
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
return `b-${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`;
|
|
100
|
+
}
|
|
101
|
+
function getBlockId(block) {
|
|
102
|
+
const id = block.get("id");
|
|
103
|
+
return typeof id === "string" ? id : void 0;
|
|
104
|
+
}
|
|
92
105
|
var EditorDocument = class _EditorDocument {
|
|
93
106
|
ydoc;
|
|
94
107
|
blocks;
|
|
@@ -121,7 +134,9 @@ var EditorDocument = class _EditorDocument {
|
|
|
121
134
|
marginTop: pick("marginTop") ?? DEFAULT_PAGE_SETTINGS.marginTop,
|
|
122
135
|
marginBottom: pick("marginBottom") ?? DEFAULT_PAGE_SETTINGS.marginBottom,
|
|
123
136
|
marginLeft: pick("marginLeft") ?? DEFAULT_PAGE_SETTINGS.marginLeft,
|
|
124
|
-
marginRight: pick("marginRight") ?? DEFAULT_PAGE_SETTINGS.marginRight
|
|
137
|
+
marginRight: pick("marginRight") ?? DEFAULT_PAGE_SETTINGS.marginRight,
|
|
138
|
+
systemHeaderTitle: pick("systemHeaderTitle"),
|
|
139
|
+
systemHeaderAuthor: pick("systemHeaderAuthor")
|
|
125
140
|
};
|
|
126
141
|
merged.preset = detectPreset(merged.width, merged.height);
|
|
127
142
|
return merged;
|
|
@@ -257,6 +272,17 @@ var EditorDocument = class _EditorDocument {
|
|
|
257
272
|
*/
|
|
258
273
|
loadFromJSON(serialized) {
|
|
259
274
|
const { blocks: sourceBlocks, pageSettings } = normalizeSerialized(serialized);
|
|
275
|
+
const LOCK_KEY = "__importLock";
|
|
276
|
+
const LOCK_TTL_MS = 5e3;
|
|
277
|
+
const existing = this.docSettings.get(LOCK_KEY);
|
|
278
|
+
if (typeof existing === "number" && Date.now() - existing < LOCK_TTL_MS) {
|
|
279
|
+
const ageMs = Date.now() - existing;
|
|
280
|
+
console.warn(
|
|
281
|
+
`[EditorDocument.loadFromJSON] import j\xE1 em andamento (${ageMs}ms atr\xE1s, TTL ${LOCK_TTL_MS}ms) \u2014 ignorando esta chamada para evitar duplica\xE7\xE3o de blocks.`
|
|
282
|
+
);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
this.docSettings.set(LOCK_KEY, Date.now());
|
|
260
286
|
this.ydoc.transact(() => {
|
|
261
287
|
if (this.blocks.length > 0) {
|
|
262
288
|
this.blocks.delete(0, this.blocks.length);
|
|
@@ -276,6 +302,7 @@ var EditorDocument = class _EditorDocument {
|
|
|
276
302
|
this.docSettings.set(k, v);
|
|
277
303
|
}
|
|
278
304
|
}
|
|
305
|
+
this.docSettings.delete(LOCK_KEY);
|
|
279
306
|
}, "import");
|
|
280
307
|
}
|
|
281
308
|
/** Build a fresh `EditorDocument` from a `SerializedDocument`. */
|
|
@@ -336,6 +363,7 @@ function createBlock(type, initialText = "", attrs) {
|
|
|
336
363
|
return createTableBlock(rows, cols);
|
|
337
364
|
}
|
|
338
365
|
const block = new Y.Map();
|
|
366
|
+
block.set("id", newBlockId());
|
|
339
367
|
block.set("type", type);
|
|
340
368
|
const yText = new Y.Text();
|
|
341
369
|
if (initialText.length > 0) yText.insert(0, initialText);
|
|
@@ -352,6 +380,7 @@ function createTableBlock(rows, cols) {
|
|
|
352
380
|
const r = Math.max(1, Math.trunc(rows));
|
|
353
381
|
const c = Math.max(1, Math.trunc(cols));
|
|
354
382
|
const block = new Y.Map();
|
|
383
|
+
block.set("id", newBlockId());
|
|
355
384
|
block.set("type", "table");
|
|
356
385
|
const attrsMap = new Y.Map();
|
|
357
386
|
attrsMap.set("rows", r);
|
|
@@ -386,6 +415,7 @@ function buildEmptyBlockFromSerialized(block) {
|
|
|
386
415
|
const rows = typeof block.attrs.rows === "number" ? Math.max(1, Math.trunc(block.attrs.rows)) : 1;
|
|
387
416
|
const cols = typeof block.attrs.cols === "number" ? Math.max(1, Math.trunc(block.attrs.cols)) : 1;
|
|
388
417
|
const yBlock = new Y.Map();
|
|
418
|
+
yBlock.set("id", typeof block.id === "string" ? block.id : newBlockId());
|
|
389
419
|
yBlock.set("type", "table");
|
|
390
420
|
const attrsMap = new Y.Map();
|
|
391
421
|
attrsMap.set("rows", rows);
|
|
@@ -408,7 +438,9 @@ function buildEmptyBlockFromSerialized(block) {
|
|
|
408
438
|
yBlock.set("cells", cellsArr);
|
|
409
439
|
return yBlock;
|
|
410
440
|
}
|
|
411
|
-
|
|
441
|
+
const yb = createBlock(block.type, "", block.attrs);
|
|
442
|
+
if (typeof block.id === "string") yb.set("id", block.id);
|
|
443
|
+
return yb;
|
|
412
444
|
}
|
|
413
445
|
function applyDeltaIntoBlock(yBlock, src) {
|
|
414
446
|
if (!yBlock) return;
|
|
@@ -436,6 +468,95 @@ function spanOf(value) {
|
|
|
436
468
|
return Math.max(1, Math.trunc(value));
|
|
437
469
|
}
|
|
438
470
|
|
|
471
|
+
// src/collab-cursor.ts
|
|
472
|
+
import * as Y2 from "yjs";
|
|
473
|
+
function toBase64(bytes) {
|
|
474
|
+
if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
|
|
475
|
+
let s = "";
|
|
476
|
+
for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
|
|
477
|
+
return btoa(s);
|
|
478
|
+
}
|
|
479
|
+
function fromBase64(b64) {
|
|
480
|
+
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(b64, "base64"));
|
|
481
|
+
const s = atob(b64);
|
|
482
|
+
const out = new Uint8Array(s.length);
|
|
483
|
+
for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i);
|
|
484
|
+
return out;
|
|
485
|
+
}
|
|
486
|
+
function yTextAt(doc, pos) {
|
|
487
|
+
const block = doc.blocks.get(pos.blockIndex);
|
|
488
|
+
if (!block) return null;
|
|
489
|
+
if (typeof pos.cellIndex === "number") {
|
|
490
|
+
const cells = block.get("cells");
|
|
491
|
+
const cell = cells?.get(pos.cellIndex);
|
|
492
|
+
const t2 = cell?.get("text");
|
|
493
|
+
return t2 instanceof Y2.Text ? t2 : null;
|
|
494
|
+
}
|
|
495
|
+
const t = block.get("text");
|
|
496
|
+
return t instanceof Y2.Text ? t : null;
|
|
497
|
+
}
|
|
498
|
+
function locateYText(doc, target) {
|
|
499
|
+
const blocks = doc.blocks;
|
|
500
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
501
|
+
const block = blocks.get(i);
|
|
502
|
+
if (block.get("text") === target) return { blockIndex: i };
|
|
503
|
+
const cells = block.get("cells");
|
|
504
|
+
if (cells) {
|
|
505
|
+
for (let c = 0; c < cells.length; c++) {
|
|
506
|
+
if (cells.get(c)?.get("text") === target) {
|
|
507
|
+
return { blockIndex: i, cellIndex: c };
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
function encodePoint(doc, pos) {
|
|
515
|
+
const yText = yTextAt(doc, pos);
|
|
516
|
+
if (!yText) return null;
|
|
517
|
+
const offset = Math.max(0, Math.min(pos.offset, yText.length));
|
|
518
|
+
const rel = Y2.createRelativePositionFromTypeIndex(yText, offset);
|
|
519
|
+
const block = doc.blocks.get(pos.blockIndex);
|
|
520
|
+
const point = { rel: toBase64(Y2.encodeRelativePosition(rel)) };
|
|
521
|
+
const blockId = block ? getBlockId(block) : void 0;
|
|
522
|
+
if (blockId) point.blockId = blockId;
|
|
523
|
+
if (typeof pos.cellIndex === "number") point.cellIndex = pos.cellIndex;
|
|
524
|
+
return point;
|
|
525
|
+
}
|
|
526
|
+
function decodePoint(doc, enc) {
|
|
527
|
+
try {
|
|
528
|
+
const rel = Y2.decodeRelativePosition(fromBase64(enc.rel));
|
|
529
|
+
const abs = Y2.createAbsolutePositionFromRelativePosition(rel, doc.ydoc);
|
|
530
|
+
if (abs && abs.type instanceof Y2.Text) {
|
|
531
|
+
const loc = locateYText(doc, abs.type);
|
|
532
|
+
if (loc) return { ...loc, offset: abs.index };
|
|
533
|
+
}
|
|
534
|
+
} catch {
|
|
535
|
+
}
|
|
536
|
+
if (enc.blockId) {
|
|
537
|
+
for (let i = 0; i < doc.blocks.length; i++) {
|
|
538
|
+
if (getBlockId(doc.blocks.get(i)) === enc.blockId) {
|
|
539
|
+
const p = { blockIndex: i, offset: 0 };
|
|
540
|
+
if (typeof enc.cellIndex === "number") p.cellIndex = enc.cellIndex;
|
|
541
|
+
return p;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
function encodeSelection(doc, sel) {
|
|
548
|
+
const anchor = encodePoint(doc, sel.anchor);
|
|
549
|
+
const focus = encodePoint(doc, sel.focus);
|
|
550
|
+
if (!anchor || !focus) return null;
|
|
551
|
+
return { anchor, focus };
|
|
552
|
+
}
|
|
553
|
+
function decodeSelection(doc, enc) {
|
|
554
|
+
const anchor = decodePoint(doc, enc.anchor);
|
|
555
|
+
const focus = decodePoint(doc, enc.focus);
|
|
556
|
+
if (!anchor || !focus) return null;
|
|
557
|
+
return { anchor, focus };
|
|
558
|
+
}
|
|
559
|
+
|
|
439
560
|
// src/selection.ts
|
|
440
561
|
function isCollapsed(sel) {
|
|
441
562
|
return positionsEqual(sel.anchor, sel.focus);
|
|
@@ -464,6 +585,20 @@ function sameTextRun(a, b) {
|
|
|
464
585
|
}
|
|
465
586
|
|
|
466
587
|
// src/marks.ts
|
|
588
|
+
var ALL_MARKS = {
|
|
589
|
+
bold: true,
|
|
590
|
+
italic: true,
|
|
591
|
+
underline: true,
|
|
592
|
+
strike: true,
|
|
593
|
+
color: true,
|
|
594
|
+
fontFamily: true,
|
|
595
|
+
fontSize: true,
|
|
596
|
+
link: true,
|
|
597
|
+
comment: true
|
|
598
|
+
};
|
|
599
|
+
var CLEAR_ALL_MARKS = Object.fromEntries(
|
|
600
|
+
Object.keys(ALL_MARKS).map((k) => [k, null])
|
|
601
|
+
);
|
|
467
602
|
function iterateRunsInRange(yText, start, end, visit) {
|
|
468
603
|
if (start >= end) return;
|
|
469
604
|
const delta = yText.toDelta();
|
|
@@ -588,7 +723,87 @@ function sameMarkValue(a, b) {
|
|
|
588
723
|
}
|
|
589
724
|
|
|
590
725
|
// src/commands.ts
|
|
591
|
-
import * as
|
|
726
|
+
import * as Y3 from "yjs";
|
|
727
|
+
|
|
728
|
+
// src/clipboard.ts
|
|
729
|
+
var SOFER_MIME = "application/x-sofer-slice";
|
|
730
|
+
function deltaToText(delta) {
|
|
731
|
+
let s = "";
|
|
732
|
+
for (const op of delta) if (typeof op.insert === "string") s += op.insert;
|
|
733
|
+
return s;
|
|
734
|
+
}
|
|
735
|
+
function blockFromDelta(doc, blockIndex, cellIndex, delta) {
|
|
736
|
+
if (cellIndex != null) {
|
|
737
|
+
return { type: "paragraph", text: deltaToText(delta), delta, attrs: {} };
|
|
738
|
+
}
|
|
739
|
+
return {
|
|
740
|
+
type: doc.getBlockType(blockIndex) ?? "paragraph",
|
|
741
|
+
text: deltaToText(delta),
|
|
742
|
+
delta,
|
|
743
|
+
attrs: doc.getBlockAttrs(blockIndex)
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
function serializeSelection(doc, sel) {
|
|
747
|
+
if (isCollapsed(sel)) return null;
|
|
748
|
+
const { start, end } = orderedRange(sel);
|
|
749
|
+
if ((start.cellIndex ?? -1) !== (end.cellIndex ?? -1)) return null;
|
|
750
|
+
if (start.blockIndex === end.blockIndex) {
|
|
751
|
+
const yText = doc.textAt(start.blockIndex, start.cellIndex);
|
|
752
|
+
if (!yText) return null;
|
|
753
|
+
const delta = sliceDelta(yText.toDelta(), start.offset, end.offset);
|
|
754
|
+
return {
|
|
755
|
+
blocks: [blockFromDelta(doc, start.blockIndex, start.cellIndex, delta)],
|
|
756
|
+
openStart: start.offset > 0,
|
|
757
|
+
openEnd: end.offset < yText.length
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
if (start.cellIndex != null || end.cellIndex != null) return null;
|
|
761
|
+
for (let i = start.blockIndex; i <= end.blockIndex; i++) {
|
|
762
|
+
if (doc.getBlockType(i) === "table") return null;
|
|
763
|
+
}
|
|
764
|
+
const startText = doc.getBlockText(start.blockIndex);
|
|
765
|
+
const endText = doc.getBlockText(end.blockIndex);
|
|
766
|
+
if (!startText || !endText) return null;
|
|
767
|
+
const blocks = [];
|
|
768
|
+
blocks.push(
|
|
769
|
+
blockFromDelta(
|
|
770
|
+
doc,
|
|
771
|
+
start.blockIndex,
|
|
772
|
+
void 0,
|
|
773
|
+
sliceDelta(startText.toDelta(), start.offset, startText.length)
|
|
774
|
+
)
|
|
775
|
+
);
|
|
776
|
+
for (let i = start.blockIndex + 1; i < end.blockIndex; i++) {
|
|
777
|
+
const t = doc.getBlockText(i);
|
|
778
|
+
blocks.push(blockFromDelta(doc, i, void 0, t?.toDelta() ?? []));
|
|
779
|
+
}
|
|
780
|
+
blocks.push(
|
|
781
|
+
blockFromDelta(
|
|
782
|
+
doc,
|
|
783
|
+
end.blockIndex,
|
|
784
|
+
void 0,
|
|
785
|
+
sliceDelta(endText.toDelta(), 0, end.offset)
|
|
786
|
+
)
|
|
787
|
+
);
|
|
788
|
+
return {
|
|
789
|
+
blocks,
|
|
790
|
+
openStart: start.offset > 0,
|
|
791
|
+
openEnd: end.offset < endText.length
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
function sliceToText(slice) {
|
|
795
|
+
return slice.blocks.map((b) => deltaToText(b.delta)).join("\n");
|
|
796
|
+
}
|
|
797
|
+
function sliceToInlineDelta(slice) {
|
|
798
|
+
const out = [];
|
|
799
|
+
slice.blocks.forEach((b, i) => {
|
|
800
|
+
if (i > 0) out.push({ insert: "\n" });
|
|
801
|
+
out.push(...b.delta);
|
|
802
|
+
});
|
|
803
|
+
return out;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// src/commands.ts
|
|
592
807
|
var COMMAND_ORIGIN = /* @__PURE__ */ Symbol("editor-command");
|
|
593
808
|
function transact(doc, fn) {
|
|
594
809
|
doc.ydoc.transact(fn, COMMAND_ORIGIN);
|
|
@@ -797,7 +1012,7 @@ function setBlockType(ctx, type, attrs) {
|
|
|
797
1012
|
const block = ctx.doc.getBlock(i);
|
|
798
1013
|
if (!block) continue;
|
|
799
1014
|
block.set("type", type);
|
|
800
|
-
const attrsMap = block.get("attrs") ?? new
|
|
1015
|
+
const attrsMap = block.get("attrs") ?? new Y3.Map();
|
|
801
1016
|
if (!block.get("attrs")) block.set("attrs", attrsMap);
|
|
802
1017
|
const next = { ...defaultAttrsFor(type), ...attrs ?? {} };
|
|
803
1018
|
const keep = new Set(Object.keys(next));
|
|
@@ -820,7 +1035,7 @@ function setBlockAttr(ctx, key, value) {
|
|
|
820
1035
|
for (const i of indices) {
|
|
821
1036
|
const block = ctx.doc.getBlock(i);
|
|
822
1037
|
if (!block) continue;
|
|
823
|
-
const attrsMap = block.get("attrs") ?? new
|
|
1038
|
+
const attrsMap = block.get("attrs") ?? new Y3.Map();
|
|
824
1039
|
if (!block.get("attrs")) block.set("attrs", attrsMap);
|
|
825
1040
|
if (value === null || value === void 0) {
|
|
826
1041
|
attrsMap.delete(key);
|
|
@@ -831,6 +1046,109 @@ function setBlockAttr(ctx, key, value) {
|
|
|
831
1046
|
ctx.setSelection(sel);
|
|
832
1047
|
});
|
|
833
1048
|
}
|
|
1049
|
+
function setCellAttr(ctx, key, value) {
|
|
1050
|
+
transact(ctx.doc, () => {
|
|
1051
|
+
const sel = ctx.getSelection();
|
|
1052
|
+
const { blockIndex, cellIndex } = sel.focus;
|
|
1053
|
+
if (cellIndex == null || !ctx.doc.isTable(blockIndex)) return;
|
|
1054
|
+
const { cols } = ctx.doc.getTableSize(blockIndex);
|
|
1055
|
+
if (cols <= 0) return;
|
|
1056
|
+
const rect = tableRectSelection(ctx.doc, sel);
|
|
1057
|
+
const targets = [];
|
|
1058
|
+
if (rect) {
|
|
1059
|
+
for (let r = rect.top; r <= rect.bottom; r++)
|
|
1060
|
+
for (let c = rect.left; c <= rect.right; c++) targets.push(r * cols + c);
|
|
1061
|
+
} else {
|
|
1062
|
+
const real = ctx.doc.realCellIndex(blockIndex, cellIndex);
|
|
1063
|
+
if (real != null) targets.push(real);
|
|
1064
|
+
}
|
|
1065
|
+
for (const flat of targets) {
|
|
1066
|
+
if (ctx.doc.getCellAttrs(blockIndex, flat).covered) continue;
|
|
1067
|
+
const m = ctx.doc.getCellAttrsMap(blockIndex, flat);
|
|
1068
|
+
if (!m) continue;
|
|
1069
|
+
if (value === null || value === void 0) m.delete(key);
|
|
1070
|
+
else m.set(key, value);
|
|
1071
|
+
}
|
|
1072
|
+
ctx.setSelection(sel);
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
function deleteSelection(ctx) {
|
|
1076
|
+
transact(ctx.doc, () => {
|
|
1077
|
+
const s = deleteRange(ctx.doc, ctx.getSelection());
|
|
1078
|
+
ctx.setSelection(s);
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
function insertSlice(ctx, slice) {
|
|
1082
|
+
if (!slice.blocks || slice.blocks.length === 0) return;
|
|
1083
|
+
transact(ctx.doc, () => {
|
|
1084
|
+
let sel = ctx.getSelection();
|
|
1085
|
+
if (!isCollapsed(sel)) sel = deleteRange(ctx.doc, sel);
|
|
1086
|
+
const { blockIndex, cellIndex, offset } = sel.focus;
|
|
1087
|
+
if (cellIndex != null) {
|
|
1088
|
+
const cellText = ctx.doc.textAt(blockIndex, cellIndex);
|
|
1089
|
+
if (!cellText) return;
|
|
1090
|
+
const after = writeDeltaInto(cellText, offset, sliceToInlineDelta(slice));
|
|
1091
|
+
ctx.setSelection(collapsedSelection({ blockIndex, cellIndex, offset: after }));
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
const targetText = ctx.doc.getBlockText(blockIndex);
|
|
1095
|
+
if (!targetText) return;
|
|
1096
|
+
const full = targetText.toDelta();
|
|
1097
|
+
const tailDelta = sliceDelta(full, offset, deltaLength(full));
|
|
1098
|
+
targetText.delete(offset, targetText.length - offset);
|
|
1099
|
+
const blocks = slice.blocks;
|
|
1100
|
+
if (blocks.length === 1) {
|
|
1101
|
+
const afterS0 = writeDeltaInto(targetText, offset, blocks[0].delta);
|
|
1102
|
+
writeDeltaInto(targetText, afterS0, tailDelta);
|
|
1103
|
+
ctx.setSelection(collapsedSelection({ blockIndex, offset: afterS0 }));
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
const originalType = ctx.doc.getBlockType(blockIndex);
|
|
1107
|
+
const originalAttrs = ctx.doc.getBlockAttrs(blockIndex);
|
|
1108
|
+
let insertAt = blockIndex;
|
|
1109
|
+
if (slice.openStart) {
|
|
1110
|
+
writeDeltaInto(targetText, offset, blocks[0].delta);
|
|
1111
|
+
} else if (offset === 0) {
|
|
1112
|
+
const blk = ctx.doc.blocks.get(blockIndex);
|
|
1113
|
+
blk.set("type", blocks[0].type);
|
|
1114
|
+
const am = blk.get("attrs");
|
|
1115
|
+
am.clear();
|
|
1116
|
+
for (const [k, v] of Object.entries(blocks[0].attrs)) {
|
|
1117
|
+
if (v !== void 0) am.set(k, v);
|
|
1118
|
+
}
|
|
1119
|
+
writeDeltaInto(targetText, 0, blocks[0].delta);
|
|
1120
|
+
} else {
|
|
1121
|
+
const b = createBlock(blocks[0].type, "", blocks[0].attrs);
|
|
1122
|
+
ctx.doc.blocks.insert(++insertAt, [b]);
|
|
1123
|
+
writeDeltaInto(b.get("text"), 0, blocks[0].delta);
|
|
1124
|
+
}
|
|
1125
|
+
for (let i = 1; i < blocks.length - 1; i++) {
|
|
1126
|
+
const b = createBlock(blocks[i].type, "", blocks[i].attrs);
|
|
1127
|
+
ctx.doc.blocks.insert(++insertAt, [b]);
|
|
1128
|
+
writeDeltaInto(b.get("text"), 0, blocks[i].delta);
|
|
1129
|
+
}
|
|
1130
|
+
const last = blocks[blocks.length - 1];
|
|
1131
|
+
if (slice.openEnd) {
|
|
1132
|
+
const b = createBlock(last.type, "", last.attrs);
|
|
1133
|
+
ctx.doc.blocks.insert(++insertAt, [b]);
|
|
1134
|
+
const t = b.get("text");
|
|
1135
|
+
const caretPos = writeDeltaInto(t, 0, last.delta);
|
|
1136
|
+
writeDeltaInto(t, caretPos, tailDelta);
|
|
1137
|
+
ctx.setSelection(collapsedSelection({ blockIndex: insertAt, offset: caretPos }));
|
|
1138
|
+
} else {
|
|
1139
|
+
const b = createBlock(last.type, "", last.attrs);
|
|
1140
|
+
ctx.doc.blocks.insert(++insertAt, [b]);
|
|
1141
|
+
const lastLen = writeDeltaInto(b.get("text"), 0, last.delta);
|
|
1142
|
+
const lastIdx = insertAt;
|
|
1143
|
+
if (tailDelta.length > 0) {
|
|
1144
|
+
const tb = createBlock(originalType ?? "paragraph", "", originalAttrs);
|
|
1145
|
+
ctx.doc.blocks.insert(++insertAt, [tb]);
|
|
1146
|
+
writeDeltaInto(tb.get("text"), 0, tailDelta);
|
|
1147
|
+
}
|
|
1148
|
+
ctx.setSelection(collapsedSelection({ blockIndex: lastIdx, offset: lastLen }));
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
834
1152
|
function selectedBlockIndices(sel, blockCount) {
|
|
835
1153
|
const { start, end } = orderedRange(sel);
|
|
836
1154
|
const out = [];
|
|
@@ -980,7 +1298,7 @@ function setBlockTypeAtIndex(doc, index, type, attrs) {
|
|
|
980
1298
|
const block = doc.getBlock(index);
|
|
981
1299
|
if (!block) return;
|
|
982
1300
|
block.set("type", type);
|
|
983
|
-
const attrsMap = block.get("attrs") ?? new
|
|
1301
|
+
const attrsMap = block.get("attrs") ?? new Y3.Map();
|
|
984
1302
|
if (!block.get("attrs")) block.set("attrs", attrsMap);
|
|
985
1303
|
const next = { ...defaultAttrsFor(type), ...attrs };
|
|
986
1304
|
const keep = new Set(Object.keys(next));
|
|
@@ -1004,6 +1322,9 @@ function insertTable(ctx, rows, cols) {
|
|
|
1004
1322
|
const focusBlock = Math.max(0, Math.min(sel.focus.blockIndex, ctx.doc.blockCount() - 1));
|
|
1005
1323
|
const insertAt = focusBlock + 1;
|
|
1006
1324
|
ctx.doc.blocks.insert(insertAt, [createTableBlock(r, c)]);
|
|
1325
|
+
if (insertAt === ctx.doc.blockCount() - 1) {
|
|
1326
|
+
ctx.doc.blocks.insert(insertAt + 1, [createBlock("paragraph")]);
|
|
1327
|
+
}
|
|
1007
1328
|
ctx.setSelection(
|
|
1008
1329
|
collapsedSelection({ blockIndex: insertAt, cellIndex: 0, offset: 0 })
|
|
1009
1330
|
);
|
|
@@ -1612,11 +1933,10 @@ function writeDeltaInto(yText, pos, delta) {
|
|
|
1612
1933
|
for (const op of delta) {
|
|
1613
1934
|
if (typeof op.insert === "string") {
|
|
1614
1935
|
if (op.insert.length === 0) continue;
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
}
|
|
1936
|
+
yText.insert(cursor, op.insert, {
|
|
1937
|
+
...CLEAR_ALL_MARKS,
|
|
1938
|
+
...op.attributes ?? {}
|
|
1939
|
+
});
|
|
1620
1940
|
cursor += op.insert.length;
|
|
1621
1941
|
} else if (op.insert != null) {
|
|
1622
1942
|
if (op.attributes && Object.keys(op.attributes).length > 0) {
|
|
@@ -1676,17 +1996,56 @@ function setImageAttrs(ctx, blockIndex, embedOffset, partial, cellIndex, newOffs
|
|
|
1676
1996
|
yText.insert(insertAt, merged);
|
|
1677
1997
|
});
|
|
1678
1998
|
}
|
|
1999
|
+
var REANCHORABLE_LAYOUTS = /* @__PURE__ */ new Set(["front", "behind"]);
|
|
2000
|
+
function moveEmbedAnchor(ctx, from, to, newOffsetX, newOffsetY) {
|
|
2001
|
+
transact(ctx.doc, () => {
|
|
2002
|
+
const srcText = ctx.doc.textAt(from.blockIndex, from.cellIndex);
|
|
2003
|
+
if (!srcText) return;
|
|
2004
|
+
const delta = srcText.toDelta();
|
|
2005
|
+
let cursor = 0;
|
|
2006
|
+
let prev = null;
|
|
2007
|
+
for (const op of delta) {
|
|
2008
|
+
if (typeof op.insert === "string") {
|
|
2009
|
+
cursor += op.insert.length;
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
if (cursor === from.offset && isImageEmbed(op.insert)) {
|
|
2013
|
+
prev = op.insert;
|
|
2014
|
+
break;
|
|
2015
|
+
}
|
|
2016
|
+
cursor += 1;
|
|
2017
|
+
}
|
|
2018
|
+
if (!prev) return;
|
|
2019
|
+
const layout = prev.layout ?? "inline";
|
|
2020
|
+
if (!REANCHORABLE_LAYOUTS.has(layout)) return;
|
|
2021
|
+
const dstText = ctx.doc.textAt(to.blockIndex, to.cellIndex);
|
|
2022
|
+
if (!dstText) return;
|
|
2023
|
+
const merged = { ...prev, offsetX: newOffsetX, offsetY: newOffsetY };
|
|
2024
|
+
const sameRun = srcText === dstText;
|
|
2025
|
+
srcText.delete(from.offset, 1);
|
|
2026
|
+
let landed = to.offset;
|
|
2027
|
+
if (sameRun && to.offset > from.offset) {
|
|
2028
|
+
landed = to.offset - 1;
|
|
2029
|
+
}
|
|
2030
|
+
landed = Math.max(0, Math.min(landed, dstText.length));
|
|
2031
|
+
dstText.insert(landed, merged);
|
|
2032
|
+
ctx.setSelection({
|
|
2033
|
+
anchor: { blockIndex: to.blockIndex, cellIndex: to.cellIndex, offset: landed },
|
|
2034
|
+
focus: { blockIndex: to.blockIndex, cellIndex: to.cellIndex, offset: landed + 1 }
|
|
2035
|
+
});
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
1679
2038
|
function isCommandOrigin(origin) {
|
|
1680
2039
|
return origin === COMMAND_ORIGIN;
|
|
1681
2040
|
}
|
|
1682
2041
|
var TRACKED_ORIGINS = /* @__PURE__ */ new Set([COMMAND_ORIGIN]);
|
|
1683
2042
|
|
|
1684
2043
|
// src/history.ts
|
|
1685
|
-
import * as
|
|
2044
|
+
import * as Y4 from "yjs";
|
|
1686
2045
|
var EditorHistory = class {
|
|
1687
2046
|
undoManager;
|
|
1688
2047
|
constructor(doc) {
|
|
1689
|
-
this.undoManager = new
|
|
2048
|
+
this.undoManager = new Y4.UndoManager(doc.blocks, {
|
|
1690
2049
|
trackedOrigins: TRACKED_ORIGINS,
|
|
1691
2050
|
captureTimeout: 400
|
|
1692
2051
|
});
|
|
@@ -1703,6 +2062,7 @@ var EditorHistory = class {
|
|
|
1703
2062
|
};
|
|
1704
2063
|
export {
|
|
1705
2064
|
BLOCK_SPECS,
|
|
2065
|
+
CLEAR_ALL_MARKS,
|
|
1706
2066
|
COMMAND_ORIGIN,
|
|
1707
2067
|
DEFAULT_PAGE_SETTINGS,
|
|
1708
2068
|
EditorDocument,
|
|
@@ -1710,8 +2070,9 @@ export {
|
|
|
1710
2070
|
MARGIN_PRESETS,
|
|
1711
2071
|
MAX_LIST_LEVEL,
|
|
1712
2072
|
PAGE_PRESETS,
|
|
2073
|
+
SOFER_MIME,
|
|
1713
2074
|
TRACKED_ORIGINS,
|
|
1714
|
-
|
|
2075
|
+
Y3 as Y,
|
|
1715
2076
|
cellsInRect,
|
|
1716
2077
|
clearRectCells,
|
|
1717
2078
|
collapsedSelection,
|
|
@@ -1719,20 +2080,25 @@ export {
|
|
|
1719
2080
|
createBlock,
|
|
1720
2081
|
createCell,
|
|
1721
2082
|
createTableBlock,
|
|
2083
|
+
decodeSelection,
|
|
1722
2084
|
dedentList,
|
|
1723
2085
|
defaultAttrsFor,
|
|
1724
2086
|
deleteBackward,
|
|
1725
2087
|
deleteForward,
|
|
2088
|
+
deleteSelection,
|
|
1726
2089
|
deleteTable,
|
|
1727
2090
|
deleteTableColumn,
|
|
1728
2091
|
deleteTableRow,
|
|
1729
2092
|
deltaLength,
|
|
1730
2093
|
detectPreset,
|
|
2094
|
+
encodeSelection,
|
|
1731
2095
|
expandTableRect,
|
|
2096
|
+
getBlockId,
|
|
1732
2097
|
getMarksInRange,
|
|
1733
2098
|
indentList,
|
|
1734
2099
|
insertImage,
|
|
1735
2100
|
insertParagraph,
|
|
2101
|
+
insertSlice,
|
|
1736
2102
|
insertTable,
|
|
1737
2103
|
insertTableColumn,
|
|
1738
2104
|
insertTableRow,
|
|
@@ -1747,19 +2113,25 @@ export {
|
|
|
1747
2113
|
mergeRight,
|
|
1748
2114
|
mergeSelection,
|
|
1749
2115
|
mmToPx,
|
|
2116
|
+
moveEmbedAnchor,
|
|
1750
2117
|
moveToNextCell,
|
|
1751
2118
|
moveToPrevCell,
|
|
2119
|
+
newBlockId,
|
|
1752
2120
|
orderedRange,
|
|
1753
2121
|
positionsEqual,
|
|
1754
2122
|
pxToMm,
|
|
1755
2123
|
removeMark,
|
|
1756
2124
|
sameTextRun,
|
|
2125
|
+
serializeSelection,
|
|
1757
2126
|
setBlockAttr,
|
|
1758
2127
|
setBlockType,
|
|
2128
|
+
setCellAttr,
|
|
1759
2129
|
setColumnWidth,
|
|
1760
2130
|
setImageAttrs,
|
|
1761
2131
|
setMark,
|
|
1762
2132
|
sliceDelta,
|
|
2133
|
+
sliceToInlineDelta,
|
|
2134
|
+
sliceToText,
|
|
1763
2135
|
spanOf,
|
|
1764
2136
|
splitCell,
|
|
1765
2137
|
splitListItem,
|