@richscripts/richtexteditor 2.6.2 → 2.6.4
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/angular/component.ts +16 -3
- package/integrations/shared.js +27 -0
- package/package.json +1 -1
- package/react/index.d.ts +7 -0
- package/react/index.js +25 -3
- package/richtexteditor/CHANGELOG.md +87 -0
- package/richtexteditor/all_plugins.js +366 -48
- package/richtexteditor/all_plugins.min.js +57 -57
- package/richtexteditor/lang/rte-lang-de.js +96 -0
- package/richtexteditor/lang/rte-lang-zh-Hans.js +181 -0
- package/richtexteditor/lang/rte-lang-zh-Hant.js +181 -0
- package/richtexteditor/plugins/all_plugins.js +366 -48
- package/richtexteditor/plugins/all_plugins.min.js +57 -57
- package/richtexteditor/plugins/comments.js +11 -1
- package/richtexteditor/plugins/crdt-engine.js +244 -17
- package/richtexteditor/plugins/crdt-engine.min.js +5 -5
- package/richtexteditor/plugins/documentimport.js +6 -11
- package/richtexteditor/plugins/insertcode.js +2 -2
- package/richtexteditor/plugins/readabilitystats.js +6 -11
- package/richtexteditor/plugins/sanitizer.js +136 -10
- package/richtexteditor/plugins/shadowdom.js +11 -1
- package/richtexteditor/plugins/slashcommand.js +78 -7
- package/richtexteditor/plugins/yjscollab.js +116 -5
- package/richtexteditor/rte-webcomponent.js +15 -0
- package/richtexteditor/rte.js +2 -2
- package/richtexteditor/rte_theme_default.css +13 -4
- package/richtexteditor/rte_theme_default.min.css +1 -1
- package/vue/index.js +12 -2
|
@@ -230,7 +230,17 @@ function RTE_Plugin_Comments() {
|
|
|
230
230
|
|
|
231
231
|
var id = "cmt-" + Date.now() + "-" + Math.floor(Math.random() * 10000);
|
|
232
232
|
var user = getCurrentUser();
|
|
233
|
-
var anchor = options.anchor;
|
|
233
|
+
var anchor = options.anchor || options.range || null;
|
|
234
|
+
// Accept a plain DOM Range (or Range-like) as the anchor: it has the
|
|
235
|
+
// container/offset fields but no `.text`, so until 2026-09-02 the common
|
|
236
|
+
// call add({ anchor: range }) silently recorded the comment WITHOUT a
|
|
237
|
+
// highlight. Derive the text from the range instead.
|
|
238
|
+
if (anchor && anchor.startContainer && anchor.endContainer && !anchor.text) {
|
|
239
|
+
var derived = "";
|
|
240
|
+
try { derived = typeof anchor.toString === "function" ? String(anchor.toString()) : ""; } catch (e) { derived = ""; }
|
|
241
|
+
if (!derived) { try { derived = anchor.cloneContents ? (anchor.cloneContents().textContent || "") : ""; } catch (e2) { derived = ""; } }
|
|
242
|
+
anchor = { startContainer: anchor.startContainer, startOffset: anchor.startOffset, endContainer: anchor.endContainer, endOffset: anchor.endOffset, text: derived };
|
|
243
|
+
}
|
|
234
244
|
var wrappedText = "";
|
|
235
245
|
|
|
236
246
|
if (anchor && anchor.startContainer && anchor.endContainer && anchor.text) {
|
|
@@ -29,9 +29,6 @@
|
|
|
29
29
|
restoreSelection: () => restoreSelection
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
// src/types.ts
|
|
33
|
-
var LOCAL_ORIGIN = /* @__PURE__ */ Symbol.for("rte.crdt.local");
|
|
34
|
-
|
|
35
32
|
// node_modules/yjs/dist/yjs.mjs
|
|
36
33
|
var yjs_exports = {};
|
|
37
34
|
__export(yjs_exports, {
|
|
@@ -9716,6 +9713,9 @@ ${err.toString()}`);
|
|
|
9716
9713
|
}
|
|
9717
9714
|
glo[importIdentifier] = true;
|
|
9718
9715
|
|
|
9716
|
+
// src/types.ts
|
|
9717
|
+
var LOCAL_ORIGIN = /* @__PURE__ */ Symbol.for("rte.crdt.local");
|
|
9718
|
+
|
|
9719
9719
|
// src/schema.ts
|
|
9720
9720
|
var BLOCK_TAGS = /* @__PURE__ */ new Set([
|
|
9721
9721
|
"p",
|
|
@@ -9803,7 +9803,13 @@ ${err.toString()}`);
|
|
|
9803
9803
|
"fontSize",
|
|
9804
9804
|
"link",
|
|
9805
9805
|
// value: href
|
|
9806
|
-
"code"
|
|
9806
|
+
"code",
|
|
9807
|
+
// Passthrough for inline elements that are not a formatting mark: a JSON
|
|
9808
|
+
// string {tag, attrs} (span.rte-comment[data-comment-id], span.rte-mention,
|
|
9809
|
+
// span.rte-tc-insert, sup.rte-footnote-ref, ...). Without it these folded
|
|
9810
|
+
// into the text run with NO mark, so peers never received comment anchors,
|
|
9811
|
+
// mention chips or tracked-change spans (2026-09-02).
|
|
9812
|
+
"inline"
|
|
9807
9813
|
];
|
|
9808
9814
|
|
|
9809
9815
|
// src/render.ts
|
|
@@ -9871,31 +9877,72 @@ ${err.toString()}`);
|
|
|
9871
9877
|
var _a;
|
|
9872
9878
|
const frag = doc2.createDocumentFragment();
|
|
9873
9879
|
const delta = ytext.toDelta();
|
|
9880
|
+
let openInline = null;
|
|
9874
9881
|
for (const op of delta) {
|
|
9875
9882
|
if (typeof op.insert !== "string") continue;
|
|
9876
9883
|
if (op.insert.length === 0) continue;
|
|
9877
|
-
const
|
|
9884
|
+
const attrs = (_a = op.attributes) != null ? _a : {};
|
|
9885
|
+
const marks = sortedMarks(attrs);
|
|
9886
|
+
const inlineKey = typeof attrs["inline"] === "string" ? attrs["inline"] : "";
|
|
9878
9887
|
let target = doc2.createTextNode(op.insert);
|
|
9879
9888
|
for (let i = marks.length - 1; i >= 0; i--) {
|
|
9880
|
-
|
|
9889
|
+
if (marks[i] === "inline") continue;
|
|
9890
|
+
const wrapper = wrapForMark(marks[i], attrs, doc2);
|
|
9881
9891
|
wrapper.appendChild(target);
|
|
9882
9892
|
target = wrapper;
|
|
9883
9893
|
}
|
|
9884
|
-
|
|
9894
|
+
if (inlineKey) {
|
|
9895
|
+
if (!openInline || openInline.key !== inlineKey) {
|
|
9896
|
+
openInline = { key: inlineKey, el: wrapForMark("inline", attrs, doc2) };
|
|
9897
|
+
frag.appendChild(openInline.el);
|
|
9898
|
+
}
|
|
9899
|
+
openInline.el.appendChild(target);
|
|
9900
|
+
} else {
|
|
9901
|
+
openInline = null;
|
|
9902
|
+
frag.appendChild(target);
|
|
9903
|
+
}
|
|
9885
9904
|
}
|
|
9886
9905
|
return frag;
|
|
9887
9906
|
}
|
|
9888
|
-
var MARK_PRECEDENCE = ["link", "color", "backcolor", "fontFamily", "fontSize", "code", "underline", "strikethrough", "subscript", "superscript", "italic", "bold"];
|
|
9907
|
+
var MARK_PRECEDENCE = ["inline", "link", "color", "backcolor", "fontFamily", "fontSize", "code", "underline", "strikethrough", "subscript", "superscript", "italic", "bold"];
|
|
9889
9908
|
function sortedMarks(attrs) {
|
|
9909
|
+
var _a;
|
|
9890
9910
|
if (!attrs) return [];
|
|
9891
9911
|
const present = [];
|
|
9912
|
+
let inlineTag = "";
|
|
9913
|
+
if (typeof attrs["inline"] === "string") {
|
|
9914
|
+
try {
|
|
9915
|
+
inlineTag = String((_a = JSON.parse(attrs["inline"]).tag) != null ? _a : "").toLowerCase();
|
|
9916
|
+
} catch (e) {
|
|
9917
|
+
inlineTag = "";
|
|
9918
|
+
}
|
|
9919
|
+
}
|
|
9892
9920
|
for (const m of MARK_PRECEDENCE) {
|
|
9893
|
-
if (m in attrs
|
|
9921
|
+
if (!(m in attrs) || attrs[m] == null || attrs[m] === false) continue;
|
|
9922
|
+
if (m === "superscript" && inlineTag === "sup" || m === "subscript" && inlineTag === "sub") continue;
|
|
9923
|
+
present.push(m);
|
|
9894
9924
|
}
|
|
9895
9925
|
return present;
|
|
9896
9926
|
}
|
|
9897
9927
|
function wrapForMark(mark, attrs, doc2) {
|
|
9898
9928
|
switch (mark) {
|
|
9929
|
+
case "inline": {
|
|
9930
|
+
let tag = "span";
|
|
9931
|
+
let attrsMap = {};
|
|
9932
|
+
try {
|
|
9933
|
+
const parsed = JSON.parse(String(attrs["inline"]));
|
|
9934
|
+
if (parsed.tag) tag = parsed.tag;
|
|
9935
|
+
if (parsed.attrs) attrsMap = parsed.attrs;
|
|
9936
|
+
} catch (e) {
|
|
9937
|
+
}
|
|
9938
|
+
if (!/^[a-z][a-z0-9-]*$/.test(tag)) tag = "span";
|
|
9939
|
+
const el = doc2.createElementNS(HTML_NS, tag);
|
|
9940
|
+
for (const k of Object.keys(attrsMap)) {
|
|
9941
|
+
if (/^on/i.test(k) || k === "style") continue;
|
|
9942
|
+
el.setAttribute(k, attrsMap[k]);
|
|
9943
|
+
}
|
|
9944
|
+
return el;
|
|
9945
|
+
}
|
|
9899
9946
|
case "bold":
|
|
9900
9947
|
return doc2.createElementNS(HTML_NS, "strong");
|
|
9901
9948
|
case "italic":
|
|
@@ -10029,11 +10076,13 @@ ${err.toString()}`);
|
|
|
10029
10076
|
if (!win) {
|
|
10030
10077
|
return { disconnect: () => {
|
|
10031
10078
|
}, drainPending: () => {
|
|
10079
|
+
}, flushPending: () => {
|
|
10032
10080
|
} };
|
|
10033
10081
|
}
|
|
10034
10082
|
const MO = (_b = win.MutationObserver) != null ? _b : globalThis.MutationObserver;
|
|
10035
10083
|
if (!MO) return { disconnect: () => {
|
|
10036
10084
|
}, drainPending: () => {
|
|
10085
|
+
}, flushPending: () => {
|
|
10037
10086
|
} };
|
|
10038
10087
|
const observer = new MO((mutations) => {
|
|
10039
10088
|
var _a2;
|
|
@@ -10052,10 +10101,22 @@ ${err.toString()}`);
|
|
|
10052
10101
|
attributes: true,
|
|
10053
10102
|
attributeOldValue: true
|
|
10054
10103
|
});
|
|
10104
|
+
const process2 = (mutations) => {
|
|
10105
|
+
var _a2;
|
|
10106
|
+
if (!mutations.length || opts.isApplyingRemote()) return;
|
|
10107
|
+
(_a2 = opts.fragment.doc) == null ? void 0 : _a2.transact(() => {
|
|
10108
|
+
for (const m of mutations) {
|
|
10109
|
+
handleMutation(m, opts);
|
|
10110
|
+
}
|
|
10111
|
+
}, LOCAL_ORIGIN);
|
|
10112
|
+
};
|
|
10055
10113
|
return {
|
|
10056
10114
|
disconnect: () => observer.disconnect(),
|
|
10057
10115
|
drainPending: () => {
|
|
10058
10116
|
observer.takeRecords();
|
|
10117
|
+
},
|
|
10118
|
+
flushPending: () => {
|
|
10119
|
+
process2(observer.takeRecords());
|
|
10059
10120
|
}
|
|
10060
10121
|
};
|
|
10061
10122
|
}
|
|
@@ -10078,9 +10139,38 @@ ${err.toString()}`);
|
|
|
10078
10139
|
const yNode = locateYNodeForText(target, opts);
|
|
10079
10140
|
if (!yNode) return;
|
|
10080
10141
|
const newText = target.data;
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
|
|
10142
|
+
applyTextDiff(yNode, newText);
|
|
10143
|
+
}
|
|
10144
|
+
function applyTextDiff(yNode, newText) {
|
|
10145
|
+
const oldText = yNode.toString();
|
|
10146
|
+
if (oldText === newText) return;
|
|
10147
|
+
let prefix = 0;
|
|
10148
|
+
const maxPrefix = Math.min(oldText.length, newText.length);
|
|
10149
|
+
while (prefix < maxPrefix && oldText.charCodeAt(prefix) === newText.charCodeAt(prefix)) prefix++;
|
|
10150
|
+
let suffix = 0;
|
|
10151
|
+
const maxSuffix = Math.min(oldText.length, newText.length) - prefix;
|
|
10152
|
+
while (suffix < maxSuffix && oldText.charCodeAt(oldText.length - 1 - suffix) === newText.charCodeAt(newText.length - 1 - suffix)) suffix++;
|
|
10153
|
+
const removeLen = oldText.length - prefix - suffix;
|
|
10154
|
+
const insertStr = newText.slice(prefix, newText.length - suffix);
|
|
10155
|
+
if (removeLen > 0) yNode.delete(prefix, removeLen);
|
|
10156
|
+
if (insertStr.length > 0) {
|
|
10157
|
+
let attrs;
|
|
10158
|
+
try {
|
|
10159
|
+
const delta = yNode.toDelta();
|
|
10160
|
+
let pos = 0;
|
|
10161
|
+
for (const op of delta) {
|
|
10162
|
+
const len = typeof op.insert === "string" ? op.insert.length : 1;
|
|
10163
|
+
if (prefix > pos && prefix <= pos + len) {
|
|
10164
|
+
attrs = op.attributes;
|
|
10165
|
+
break;
|
|
10166
|
+
}
|
|
10167
|
+
pos += len;
|
|
10168
|
+
}
|
|
10169
|
+
} catch (e) {
|
|
10170
|
+
attrs = void 0;
|
|
10171
|
+
}
|
|
10172
|
+
yNode.insert(prefix, insertStr, attrs);
|
|
10173
|
+
}
|
|
10084
10174
|
}
|
|
10085
10175
|
function handleChildList(m, opts) {
|
|
10086
10176
|
if (m.target === opts.editable) {
|
|
@@ -10091,13 +10181,51 @@ ${err.toString()}`);
|
|
|
10091
10181
|
rebuildSubtreeAtElement(m.target, opts);
|
|
10092
10182
|
}
|
|
10093
10183
|
function rebuildSubtreeAtElement(domEl, opts) {
|
|
10184
|
+
var _a;
|
|
10094
10185
|
const yEl = locateYElement(domEl, opts);
|
|
10095
10186
|
if (!yEl) {
|
|
10096
10187
|
scheduleCoarseResync(opts);
|
|
10097
10188
|
return;
|
|
10098
10189
|
}
|
|
10099
|
-
yEl.delete(0, yEl.length);
|
|
10100
10190
|
const folded = foldInlineRunsToYChildren(Array.from(domEl.childNodes));
|
|
10191
|
+
if (yEl.length === 1 && folded.children.length === 1) {
|
|
10192
|
+
const existing = yEl.get(0);
|
|
10193
|
+
const fresh = folded.children[0];
|
|
10194
|
+
if (existing instanceof YXmlText && fresh instanceof YXmlText) {
|
|
10195
|
+
const plan = folded.marked.find((m) => m.text === fresh);
|
|
10196
|
+
const newText = (_a = domEl.textContent) != null ? _a : "";
|
|
10197
|
+
applyTextDiff(existing, newText);
|
|
10198
|
+
if (plan) {
|
|
10199
|
+
const len = existing.length;
|
|
10200
|
+
if (len > 0) {
|
|
10201
|
+
const reset = {};
|
|
10202
|
+
for (const m of SUPPORTED_MARKS) reset[m] = null;
|
|
10203
|
+
const wanted = plan.ranges.map((r) => JSON.stringify([r.start, r.end, r.marks])).join("|");
|
|
10204
|
+
const current = existing.toDelta();
|
|
10205
|
+
let pos = 0;
|
|
10206
|
+
const currentRanges = [];
|
|
10207
|
+
for (const op of current) {
|
|
10208
|
+
const l = typeof op.insert === "string" ? op.insert.length : 1;
|
|
10209
|
+
if (op.attributes && Object.keys(op.attributes).length) currentRanges.push(JSON.stringify([pos, pos + l, op.attributes]));
|
|
10210
|
+
pos += l;
|
|
10211
|
+
}
|
|
10212
|
+
if (currentRanges.join("|") !== wanted) {
|
|
10213
|
+
existing.format(0, len, reset);
|
|
10214
|
+
for (const r of plan.ranges) existing.format(r.start, r.end - r.start, r.marks);
|
|
10215
|
+
}
|
|
10216
|
+
}
|
|
10217
|
+
} else if (existing.length > 0) {
|
|
10218
|
+
const current = existing.toDelta();
|
|
10219
|
+
if (current.some((op) => op.attributes && Object.keys(op.attributes).length)) {
|
|
10220
|
+
const reset = {};
|
|
10221
|
+
for (const m of SUPPORTED_MARKS) reset[m] = null;
|
|
10222
|
+
existing.format(0, existing.length, reset);
|
|
10223
|
+
}
|
|
10224
|
+
}
|
|
10225
|
+
return;
|
|
10226
|
+
}
|
|
10227
|
+
}
|
|
10228
|
+
yEl.delete(0, yEl.length);
|
|
10101
10229
|
if (folded.children.length > 0) {
|
|
10102
10230
|
yEl.insert(0, folded.children);
|
|
10103
10231
|
applyDeferredMarks(folded.marked);
|
|
@@ -10300,6 +10428,20 @@ ${err.toString()}`);
|
|
|
10300
10428
|
applyDeferredMarks
|
|
10301
10429
|
};
|
|
10302
10430
|
function marksFromMarkElement(el) {
|
|
10431
|
+
const base = baseMarksFromMarkElement(el);
|
|
10432
|
+
const tag = el.tagName.toLowerCase();
|
|
10433
|
+
if (tag === "a") return base;
|
|
10434
|
+
const attrs = {};
|
|
10435
|
+
let hasIdentity = false;
|
|
10436
|
+
for (const attr of Array.from(el.attributes)) {
|
|
10437
|
+
if (attr.name === "style") continue;
|
|
10438
|
+
attrs[attr.name] = attr.value;
|
|
10439
|
+
hasIdentity = true;
|
|
10440
|
+
}
|
|
10441
|
+
if (hasIdentity) base["inline"] = JSON.stringify({ tag, attrs });
|
|
10442
|
+
return base;
|
|
10443
|
+
}
|
|
10444
|
+
function baseMarksFromMarkElement(el) {
|
|
10303
10445
|
const tag = el.tagName.toLowerCase();
|
|
10304
10446
|
switch (tag) {
|
|
10305
10447
|
case "strong":
|
|
@@ -10376,10 +10518,12 @@ ${err.toString()}`);
|
|
|
10376
10518
|
const fragment = options.ydoc.getXmlFragment(fragmentName);
|
|
10377
10519
|
const awareness = (_c = options.awareness) != null ? _c : (_b = options.provider) == null ? void 0 : _b.awareness;
|
|
10378
10520
|
let applyingRemote = false;
|
|
10521
|
+
let composing = false;
|
|
10379
10522
|
const observerHandle = startLocalObserver({
|
|
10380
10523
|
editable: options.editable,
|
|
10381
10524
|
fragment,
|
|
10382
|
-
|
|
10525
|
+
// `composing` also gates the observer: see the IME composition gate below.
|
|
10526
|
+
isApplyingRemote: () => applyingRemote || composing
|
|
10383
10527
|
});
|
|
10384
10528
|
applyingRemote = true;
|
|
10385
10529
|
try {
|
|
@@ -10388,8 +10532,8 @@ ${err.toString()}`);
|
|
|
10388
10532
|
observerHandle.drainPending();
|
|
10389
10533
|
applyingRemote = false;
|
|
10390
10534
|
}
|
|
10391
|
-
const
|
|
10392
|
-
|
|
10535
|
+
const renderRemote = () => {
|
|
10536
|
+
observerHandle.flushPending();
|
|
10393
10537
|
const captured = captureSelection(options.editable);
|
|
10394
10538
|
applyingRemote = true;
|
|
10395
10539
|
try {
|
|
@@ -10400,13 +10544,96 @@ ${err.toString()}`);
|
|
|
10400
10544
|
}
|
|
10401
10545
|
restoreSelection(options.editable, captured);
|
|
10402
10546
|
};
|
|
10547
|
+
let pendingRemoteRender = false;
|
|
10548
|
+
let composeTarget = null;
|
|
10549
|
+
const observerOpts = { editable: options.editable, fragment, isApplyingRemote: () => applyingRemote };
|
|
10550
|
+
const resolveTextAnchor = (container, offset) => {
|
|
10551
|
+
if (container.nodeType === 3) return { node: container, offset };
|
|
10552
|
+
if (container.nodeType !== 1) return null;
|
|
10553
|
+
const children = container.childNodes;
|
|
10554
|
+
const before = offset > 0 ? children[offset - 1] : null;
|
|
10555
|
+
if (before && before.nodeType === 3) {
|
|
10556
|
+
const t = before;
|
|
10557
|
+
return { node: t, offset: t.data.length };
|
|
10558
|
+
}
|
|
10559
|
+
const at = children[offset];
|
|
10560
|
+
if (at && at.nodeType === 3) return { node: at, offset: 0 };
|
|
10561
|
+
return null;
|
|
10562
|
+
};
|
|
10563
|
+
const onCompositionStart = () => {
|
|
10564
|
+
var _a2;
|
|
10565
|
+
composeTarget = null;
|
|
10566
|
+
try {
|
|
10567
|
+
const doc2 = options.editable.ownerDocument;
|
|
10568
|
+
const sel = (_a2 = doc2 == null ? void 0 : doc2.defaultView) == null ? void 0 : _a2.getSelection();
|
|
10569
|
+
const range = sel && sel.rangeCount ? sel.getRangeAt(0) : null;
|
|
10570
|
+
const startAnchor = range ? resolveTextAnchor(range.startContainer, range.startOffset) : null;
|
|
10571
|
+
const endAnchor = range ? resolveTextAnchor(range.endContainer, range.endOffset) : null;
|
|
10572
|
+
if (startAnchor && endAnchor && startAnchor.node === endAnchor.node) {
|
|
10573
|
+
const yText = locateYNodeForText(startAnchor.node, observerOpts);
|
|
10574
|
+
if (yText) {
|
|
10575
|
+
composeTarget = {
|
|
10576
|
+
yText,
|
|
10577
|
+
start: createRelativePositionFromTypeIndex(yText, startAnchor.offset),
|
|
10578
|
+
end: createRelativePositionFromTypeIndex(yText, endAnchor.offset)
|
|
10579
|
+
};
|
|
10580
|
+
}
|
|
10581
|
+
}
|
|
10582
|
+
} catch (err) {
|
|
10583
|
+
composeTarget = null;
|
|
10584
|
+
}
|
|
10585
|
+
composing = composeTarget !== null;
|
|
10586
|
+
};
|
|
10587
|
+
const endComposition = (event) => {
|
|
10588
|
+
var _a2;
|
|
10589
|
+
if (!composing) return;
|
|
10590
|
+
composing = false;
|
|
10591
|
+
const target = composeTarget;
|
|
10592
|
+
composeTarget = null;
|
|
10593
|
+
const data = (_a2 = event == null ? void 0 : event.data) != null ? _a2 : "";
|
|
10594
|
+
observerHandle.drainPending();
|
|
10595
|
+
try {
|
|
10596
|
+
if (target) {
|
|
10597
|
+
const start = createAbsolutePositionFromRelativePosition(target.start, options.ydoc);
|
|
10598
|
+
const end = createAbsolutePositionFromRelativePosition(target.end, options.ydoc);
|
|
10599
|
+
if (start && end && start.type === target.yText) {
|
|
10600
|
+
const from2 = Math.min(start.index, end.index);
|
|
10601
|
+
const to = Math.max(start.index, end.index);
|
|
10602
|
+
options.ydoc.transact(() => {
|
|
10603
|
+
if (to > from2) target.yText.delete(from2, to - from2);
|
|
10604
|
+
if (data) target.yText.insert(from2, data);
|
|
10605
|
+
}, LOCAL_ORIGIN);
|
|
10606
|
+
}
|
|
10607
|
+
}
|
|
10608
|
+
} catch (err) {
|
|
10609
|
+
console.error("[crdt-engine] composition commit failed:", err);
|
|
10610
|
+
}
|
|
10611
|
+
pendingRemoteRender = false;
|
|
10612
|
+
renderRemote();
|
|
10613
|
+
};
|
|
10614
|
+
options.editable.addEventListener("compositionstart", onCompositionStart);
|
|
10615
|
+
options.editable.addEventListener("compositionend", endComposition);
|
|
10616
|
+
options.editable.addEventListener("blur", endComposition);
|
|
10617
|
+
const unsubscribeComposition = () => {
|
|
10618
|
+
options.editable.removeEventListener("compositionstart", onCompositionStart);
|
|
10619
|
+
options.editable.removeEventListener("compositionend", endComposition);
|
|
10620
|
+
options.editable.removeEventListener("blur", endComposition);
|
|
10621
|
+
};
|
|
10622
|
+
const onUpdate = (_update, origin) => {
|
|
10623
|
+
if (origin === LOCAL_ORIGIN) return;
|
|
10624
|
+
if (composing) {
|
|
10625
|
+
pendingRemoteRender = true;
|
|
10626
|
+
return;
|
|
10627
|
+
}
|
|
10628
|
+
renderRemote();
|
|
10629
|
+
};
|
|
10403
10630
|
options.ydoc.on("update", onUpdate);
|
|
10404
10631
|
const unsubscribeRemote = () => {
|
|
10405
10632
|
options.ydoc.off("update", onUpdate);
|
|
10406
10633
|
};
|
|
10407
10634
|
const unsubscribeLocal = () => observerHandle.disconnect();
|
|
10408
10635
|
const unsubscribeAwareness = subscribeAwarenessStub(awareness, options.initialAwareness);
|
|
10409
|
-
let cleanups = [unsubscribeRemote, unsubscribeLocal, unsubscribeAwareness];
|
|
10636
|
+
let cleanups = [unsubscribeRemote, unsubscribeLocal, unsubscribeAwareness, unsubscribeComposition];
|
|
10410
10637
|
return {
|
|
10411
10638
|
ydoc: options.ydoc,
|
|
10412
10639
|
fragment,
|