Skip to content

Commit a0c565e

Browse files
committed
feat(json-crdt-peritext-ui): 🎸 adjust cursor on text insert/delte undo/redo
1 parent f873360 commit a0c565e

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed

‎src/json-crdt-peritext-ui/dom/annals/AnnalsController.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {WebUndo} from './WebUndo';
22
import {printTree, type Printable} from 'tree-dump';
3+
import type {Patch} from '../../../json-crdt-patch';
34
import type {Peritext} from '../../../json-crdt-extensions';
45
import type {UiLifeCycles} from '../types';
5-
import type {Patch} from '../../../json-crdt-patch';
66
import type {RedoCallback, RedoItem, UndoCallback, UndoCollector, UndoItem} from '../../types';
77
import type {Log} from '../../../json-crdt/log/Log';
88
import type {PeritextEventTarget} from '../../events/PeritextEventTarget';

‎src/json-crdt-peritext-ui/events/defaults/PeritextEventDefaults.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {CursorAnchor} from '../../../json-crdt-extensions/peritext/slice/constants';
2+
import {placeCursor} from './annals';
23
import type {Range} from '../../../json-crdt-extensions/peritext/rga/Range';
34
import type {PeritextDataTransfer} from '../../../json-crdt-extensions/peritext/transfer/PeritextDataTransfer';
45
import type {PeritextEventHandlerMap, PeritextEventTarget} from '../PeritextEventTarget';
@@ -377,5 +378,8 @@ export class PeritextEventDefaults implements PeritextEventHandlerMap {
377378
public readonly annals = (event: CustomEvent<events.AnnalsDetail>) => {
378379
const {batch} = event.detail;
379380
this.txt.model.applyBatch(batch);
381+
const txt = this.txt;
382+
const cursor = placeCursor(txt, batch);
383+
if (cursor) txt.editor.cursor.setRange(cursor);
380384
};
381385
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {Peritext} from '../../../json-crdt-extensions';
2+
import {Anchor} from '../../../json-crdt-extensions/peritext/rga/constants';
3+
import {DelOp, equal, InsArrOp, InsBinOp, InsStrOp, Patch, Timestamp} from '../../../json-crdt-patch';
4+
import type {Range} from '../../../json-crdt-extensions/peritext/rga/Range';
5+
6+
/**
7+
* Given an undo/redo patch/batch, calculates a good cursor position to place
8+
* the cursor after the patch is applied, so that the user can continue typing
9+
* from the same logical position.
10+
*
11+
* @param patch Undo/Redo patch
12+
* @returns Range
13+
*/
14+
export const placeCursor = (txt: Peritext, batch: Patch[]): Range | undefined => {
15+
const batchLength = batch.length;
16+
for (let j = batchLength - 1; j >= 0; j--) {
17+
const patch = batch[j];
18+
const ops = patch.ops;
19+
const length = ops.length;
20+
for (let i = length - 1; i >= 0; i--) {
21+
const op = ops[i];
22+
if (op instanceof InsStrOp || op instanceof InsBinOp || op instanceof InsArrOp) {
23+
const opId = op.id;
24+
const lastCharId = new Timestamp(opId.sid, opId.time + op.span() - 1);
25+
const point = txt.point(lastCharId, Anchor.After);
26+
const cursor = txt.range(point);
27+
return cursor;
28+
} else if (op instanceof DelOp && equal(op.obj, txt.str.id)) {
29+
const lastSpan = op.what[op.what.length - 1];
30+
if (lastSpan) {
31+
const point = txt.point(lastSpan, Anchor.Before);
32+
point.halfstep(-1);
33+
const cursor = txt.range(point);
34+
return cursor;
35+
}
36+
}
37+
}
38+
}
39+
return;
40+
};

0 commit comments

Comments
 (0)