@ai-matrx/office 0.8.44 → 0.8.45

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/docs.js CHANGED
@@ -322,9 +322,12 @@ var DocumentModel = class {
322
322
  const cs = view.port.commandService;
323
323
  if (typeof cs.beforeCommandExecuted !== "function" || typeof cs.onCommandExecuted !== "function") return () => {
324
324
  };
325
- const before = cs.beforeCommandExecuted((info) => {
325
+ const before = cs.beforeCommandExecuted((info, options) => {
326
326
  if (info.type === COMMAND_TYPE) view.commands.push(info.id);
327
- else if (info.id === RICH_TEXT_EDITING_ID && this.otListeners.size && !this.relaying) {
327
+ else if (info.id === RICH_TEXT_EDITING_ID && !this.relaying && !options?.fromCollab && !options?.onlyLocal) {
328
+ keepInsideTheBody(info.params, () => view.port.snapshot());
329
+ }
330
+ if (info.id === RICH_TEXT_EDITING_ID && this.otListeners.size && !this.relaying) {
328
331
  try {
329
332
  const snap = view.port.snapshot();
330
333
  view.pre = snap ? structuredClone(snap) : null;
@@ -511,7 +514,7 @@ var DocumentModel = class {
511
514
  this.stored = snapshot;
512
515
  this.relaying = true;
513
516
  try {
514
- for (const view of this.views.values()) view.port.remount(snapshot);
517
+ for (const view of this.views.values()) remountKeepingCaret(view.port, snapshot);
515
518
  this.parked?.remount(snapshot);
516
519
  } finally {
517
520
  this.relaying = false;
@@ -615,7 +618,7 @@ var DocumentModel = class {
615
618
  this.savedFingerprint = contentFingerprint(s2);
616
619
  this.relaying = true;
617
620
  try {
618
- for (const view of this.views.values()) view.port.remount(s2);
621
+ for (const view of this.views.values()) remountKeepingCaret(view.port, s2);
619
622
  this.parked?.remount(s2);
620
623
  } finally {
621
624
  this.relaying = false;
@@ -729,6 +732,62 @@ var DocumentModel = class {
729
732
  this.pageClose = null;
730
733
  }
731
734
  };
735
+ var lastTextOffset = (dataStream) => Math.max(0, dataStream.length - 2);
736
+ function mapOffsetThroughReplace(before, after, offset) {
737
+ const max = Math.min(before.length, after.length);
738
+ let pre = 0;
739
+ while (pre < max && before[pre] === after[pre]) pre++;
740
+ let suf = 0;
741
+ while (suf < max - pre && before[before.length - 1 - suf] === after[after.length - 1 - suf]) suf++;
742
+ const at = offset <= pre ? offset : offset >= before.length - suf ? after.length - (before.length - offset) : after.length - suf;
743
+ return Math.max(0, Math.min(at, lastTextOffset(after)));
744
+ }
745
+ function remountKeepingCaret(port, snapshot) {
746
+ let sel = null;
747
+ let before = null;
748
+ try {
749
+ sel = port.selection?.get() ?? null;
750
+ before = port.snapshot()?.body?.dataStream ?? null;
751
+ } catch {
752
+ sel = null;
753
+ }
754
+ port.remount(snapshot);
755
+ const after = snapshot.body?.dataStream;
756
+ if (!sel?.length || before == null || typeof after !== "string" || sel.some((r) => r.segmentId)) return;
757
+ try {
758
+ port.selection?.set(
759
+ sel.map((r) => ({ startOffset: mapOffsetThroughReplace(before, after, r.startOffset), endOffset: mapOffsetThroughReplace(before, after, r.endOffset) }))
760
+ );
761
+ } catch (error) {
762
+ console.warn("[document-model] could not carry this view's caret into the replaced document", error);
763
+ }
764
+ }
765
+ function keepInsideTheBody(params, current) {
766
+ const p = params;
767
+ if (!p || p.segmentId || !Array.isArray(p.actions) || p.actions[0] !== "body") return;
768
+ const sub = p.actions[1];
769
+ if (!sub || sub.et !== "text-x" || !Array.isArray(sub.e)) return;
770
+ let doc = null;
771
+ try {
772
+ doc = current();
773
+ } catch {
774
+ return;
775
+ }
776
+ const ds = doc?.body?.dataStream;
777
+ if (typeof ds !== "string" || !ds.endsWith("\r\n")) return;
778
+ const last = lastTextOffset(ds);
779
+ let pos = 0;
780
+ let past = false;
781
+ for (const a of sub.e) {
782
+ if (a.t === "i" && pos > last) past = true;
783
+ if (a.t !== "i") pos += a.len;
784
+ }
785
+ if (!past) return;
786
+ const inserts = sub.e.filter((a) => a.t === "i");
787
+ const plain = sub.e.every((a) => a.t === "i" || a.t === "r" && a.body == null);
788
+ console.warn("[document-model] an edit aimed past the end of the document was kept inside it", { at: pos, length: ds.length });
789
+ p.actions = inserts.length === 1 && plain ? ["body", { ...sub, e: [...last ? [{ t: "r", len: last }] : [], inserts[0]] }] : [];
790
+ }
732
791
  function moveRangesThroughBodyEdit(ranges, actions) {
733
792
  const a = actions;
734
793
  if (!Array.isArray(a) || a[0] !== "body") return null;
@@ -1107,9 +1166,11 @@ export {
1107
1166
  getDocument,
1108
1167
  getLatestDocumentSnapshot,
1109
1168
  isDocsAnchorOnlyOp,
1169
+ keepInsideTheBody,
1110
1170
  listAccessibleDocuments,
1111
1171
  listDocumentSnapshots,
1112
1172
  listTrashedDocuments,
1173
+ mapOffsetThroughReplace,
1113
1174
  moveDocument,
1114
1175
  movePeerRangesThroughBodyEdit,
1115
1176
  moveRangesThroughBodyEdit,