Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 4x 142x 142x 142x 142x 139x 139x 183x 183x 183x 182x 138x 138x 138x 182x 138x 138x 27x 27x 138x 138x 138x 4x 35x 35x 34x 4x 98x 98x 98x 140x 140x 140x 140x 171x 171x 171x 131x 131x 131x 131x 140x 9x 9x 9x 98x 98x 98x 98x 140x 98x 98x 98x 98x 116x 116x 116x 98x 18x | import type {Block, LeafBlock, Peritext} from 'json-joy/lib/json-crdt-extensions';
import type {Point} from 'json-joy/lib/json-crdt-extensions/peritext/rga/Point';
import type {Editor} from 'slate';
import type {SlatePoint} from './types';
/**
* Convert a Slate point (path + offset) to a Peritext gap position (the integer
* coordinate system used by `Peritext.insAt` / `Peritext.delAt`). Returns `-1`
* if the position cannot be resolved (e.g. structural mismatch).
*/
export const slatePointToGap = (txt: Peritext, editor: Editor, point: SlatePoint): number => {
try {
const {path, offset} = point;
const depth = path.length;
if (depth < 2) return -1;
// Navigate block tree to the leaf block.
let block: Block<string> | LeafBlock<string> = txt.blocks.root;
for (let d = 0; d < depth - 1; d++) {
const children = block.children;
const idx = path[d];
if (idx >= children.length) return -1;
block = children[idx];
}
// Sum text lengths of all text nodes before the target text node, then add offset.
const textNodeIndex = path[depth - 1];
let textOffset = 0;
let node: any = editor;
for (let d = 0; d < depth - 1; d++) node = node.children[path[d]];
const children = node.children;
for (let i = 0; i < textNodeIndex && i < children.length; i++) {
const text = children[i].text;
Eif (typeof text === 'string') textOffset += text.length;
}
textOffset += offset;
const hasMarker = !!(block as LeafBlock<string>).marker;
return hasMarker ? block.start.viewPos() + 1 + textOffset : textOffset;
} catch {
return -1;
}
};
/** Convert a Slate point to a Peritext {@link Point} in CRDT-space. */
export const slatePointToPoint = (txt: Peritext, editor: Editor, slatePoint: SlatePoint): Point<string> => {
const gap = slatePointToGap(txt, editor, slatePoint);
if (gap < 0) return txt.pointStart() ?? txt.pointAbsStart();
return txt.pointIn(gap);
};
/**
* Convert a Peritext {@link Point} to a Slate point (path + offset). Walks the
* Peritext block tree to find the leaf block containing the point, then maps
* back to the corresponding Slate path + offset.
*/
export const pointToSlatePoint = (
block: Block<string> | LeafBlock<string>,
point: Point<string>,
editor: Editor,
): SlatePoint => {
const viewPos = point.viewPos();
const path: number[] = [];
// Walk through non-leaf blocks to find the leaf containing viewPos.
while (!block.isLeaf()) {
const children = block.children;
const len = children.length;
let found = false;
for (let i = 0; i < len; i++) {
const child = children[i];
const childEndView = child.end.viewPos();
if (viewPos <= childEndView) {
path.push(i);
block = child;
found = true;
break;
}
}
if (!found) {
const lastIdx = len - 1;
path.push(lastIdx);
block = children[lastIdx];
}
}
// Compute text offset within the leaf block.
const hasMarker = !!(block as LeafBlock<string>).marker;
const textOffset = hasMarker ? viewPos - (block.start.viewPos() + 1) : viewPos;
const clampedOffset = Math.max(0, textOffset);
// Walk Slate text nodes at the computed path to find the right text node + offset.
let slateNode: any = editor;
for (const idx of path) slateNode = slateNode.children[idx];
const textChildren = slateNode?.children;
Iif (!textChildren) return {path: [...path, 0], offset: 0};
let remaining = clampedOffset;
for (let i = 0; i < textChildren.length; i++) {
const text = textChildren[i].text;
Iif (typeof text !== 'string') continue;
if (remaining <= text.length) {
return {path: [...path, i], offset: remaining};
}
remaining -= text.length;
}
// Past the end — clamp to last text node.
const lastIdx = textChildren.length - 1;
const lastText = textChildren[lastIdx]?.text ?? '';
return {path: [...path, lastIdx], offset: lastText.length};
};
|