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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 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 98x 116x 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.
// Each inline element node also counts as exactly one character (the
// placeholder character covered by its `Atomic` slice in Peritext).
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 child = children[i];
if (typeof child?.text === 'string') textOffset += child.text.length;
else Eif (child && typeof child === 'object' && 'type' in child) textOffset += 1;
}
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 children at the computed path to find the right text node + offset,
// counting inline element nodes as 1 character each.
let slateNode: any = editor;
for (const idx of path) slateNode = slateNode.children[idx];
const children = slateNode?.children;
Iif (!children) return {path: [...path, 0], offset: 0};
let remaining = clampedOffset;
let lastTextIdx = -1;
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (typeof child?.text === 'string') {
lastTextIdx = i;
if (remaining <= child.text.length) {
return {path: [...path, i], offset: remaining};
}
remaining -= child.text.length;
} else Eif (child && typeof child === 'object' && 'type' in child) {
if (remaining === 0) {
return lastTextIdx >= 0
? {path: [...path, lastTextIdx], offset: (children[lastTextIdx].text as string).length}
: {path: [...path, i], offset: 0};
}
remaining -= 1;
}
}
// Past the end — clamp to last text node.
const lastIdx = children.length - 1;
const lastChild = children[lastIdx];
if (lastChild && typeof lastChild.text === 'string') {
return {path: [...path, lastIdx], offset: (lastChild.text as string).length};
}
return {path: [...path, lastIdx], offset: 0};
};
|