@blankdotpage/cake 0.1.68 → 0.1.69
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/cake/core/mapping/cursor-source-map.d.ts +11 -0
- package/dist/cake/core/mapping/cursor-source-map.d.ts.map +1 -1
- package/dist/cake/core/mapping/cursor-source-map.js +159 -21
- package/dist/cake/core/runtime.d.ts +4 -0
- package/dist/cake/core/runtime.d.ts.map +1 -1
- package/dist/cake/core/runtime.js +332 -215
- package/dist/cake/dom/render.d.ts +32 -2
- package/dist/cake/dom/render.d.ts.map +1 -1
- package/dist/cake/dom/render.js +401 -118
- package/dist/cake/editor/cake-editor.d.ts +8 -1
- package/dist/cake/editor/cake-editor.d.ts.map +1 -1
- package/dist/cake/editor/cake-editor.js +172 -100
- package/dist/cake/editor/internal/editor-text-model.d.ts +49 -0
- package/dist/cake/editor/internal/editor-text-model.d.ts.map +1 -0
- package/dist/cake/editor/internal/editor-text-model.js +284 -0
- package/dist/cake/editor/selection/selection-geometry-dom.d.ts +5 -1
- package/dist/cake/editor/selection/selection-geometry-dom.d.ts.map +1 -1
- package/dist/cake/editor/selection/selection-geometry-dom.js +4 -5
- package/dist/cake/editor/selection/selection-layout-dom.d.ts.map +1 -1
- package/dist/cake/editor/selection/selection-layout-dom.js +2 -5
- package/dist/cake/editor/selection/selection-layout.d.ts +2 -15
- package/dist/cake/editor/selection/selection-layout.d.ts.map +1 -1
- package/dist/cake/editor/selection/selection-layout.js +1 -99
- package/dist/cake/editor/selection/selection-navigation.d.ts +4 -0
- package/dist/cake/editor/selection/selection-navigation.d.ts.map +1 -1
- package/dist/cake/editor/selection/selection-navigation.js +1 -2
- package/dist/cake/extensions/link/link.d.ts.map +1 -1
- package/dist/cake/extensions/link/link.js +1 -7
- package/dist/cake/extensions/shared/structural-reparse-policy.js +2 -2
- package/package.json +5 -2
- package/dist/cake/editor/selection/visible-text.d.ts +0 -5
- package/dist/cake/editor/selection/visible-text.d.ts.map +0 -1
- package/dist/cake/editor/selection/visible-text.js +0 -66
- package/dist/cake/engine/cake-engine.d.ts +0 -230
- package/dist/cake/engine/cake-engine.d.ts.map +0 -1
- package/dist/cake/engine/cake-engine.js +0 -3589
- package/dist/cake/engine/selection/selection-geometry-dom.d.ts +0 -24
- package/dist/cake/engine/selection/selection-geometry-dom.d.ts.map +0 -1
- package/dist/cake/engine/selection/selection-geometry-dom.js +0 -302
- package/dist/cake/engine/selection/selection-geometry.d.ts +0 -22
- package/dist/cake/engine/selection/selection-geometry.d.ts.map +0 -1
- package/dist/cake/engine/selection/selection-geometry.js +0 -158
- package/dist/cake/engine/selection/selection-layout-dom.d.ts +0 -50
- package/dist/cake/engine/selection/selection-layout-dom.d.ts.map +0 -1
- package/dist/cake/engine/selection/selection-layout-dom.js +0 -781
- package/dist/cake/engine/selection/selection-layout.d.ts +0 -55
- package/dist/cake/engine/selection/selection-layout.d.ts.map +0 -1
- package/dist/cake/engine/selection/selection-layout.js +0 -128
- package/dist/cake/engine/selection/selection-navigation.d.ts +0 -22
- package/dist/cake/engine/selection/selection-navigation.d.ts.map +0 -1
- package/dist/cake/engine/selection/selection-navigation.js +0 -229
- package/dist/cake/engine/selection/visible-text.d.ts +0 -5
- package/dist/cake/engine/selection/visible-text.d.ts.map +0 -1
- package/dist/cake/engine/selection/visible-text.js +0 -66
- package/dist/cake/react/CakeEditor.d.ts +0 -58
- package/dist/cake/react/CakeEditor.d.ts.map +0 -1
- package/dist/cake/react/CakeEditor.js +0 -225
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { Doc } from "../../core/types";
|
|
2
|
-
export type LayoutRect = {
|
|
3
|
-
top: number;
|
|
4
|
-
left: number;
|
|
5
|
-
width: number;
|
|
6
|
-
height: number;
|
|
7
|
-
};
|
|
8
|
-
export type LayoutRow = {
|
|
9
|
-
startOffset: number;
|
|
10
|
-
endOffset: number;
|
|
11
|
-
rect: LayoutRect;
|
|
12
|
-
};
|
|
13
|
-
export type LineLayout = {
|
|
14
|
-
lineIndex: number;
|
|
15
|
-
lineStartOffset: number;
|
|
16
|
-
lineLength: number;
|
|
17
|
-
lineHasNewline: boolean;
|
|
18
|
-
lineBox: LayoutRect;
|
|
19
|
-
rows: LayoutRow[];
|
|
20
|
-
};
|
|
21
|
-
export type LayoutModel = {
|
|
22
|
-
container: LayoutRect;
|
|
23
|
-
lines: LineLayout[];
|
|
24
|
-
};
|
|
25
|
-
export type LineMeasurementInput = {
|
|
26
|
-
lineIndex: number;
|
|
27
|
-
lineText: string;
|
|
28
|
-
lineLength: number;
|
|
29
|
-
lineHasNewline: boolean;
|
|
30
|
-
top: number;
|
|
31
|
-
};
|
|
32
|
-
export type LineMeasurement = {
|
|
33
|
-
lineBox: LayoutRect;
|
|
34
|
-
rows: LayoutRow[];
|
|
35
|
-
};
|
|
36
|
-
export type LayoutMeasurer = {
|
|
37
|
-
container: LayoutRect;
|
|
38
|
-
measureLine: (input: LineMeasurementInput) => LineMeasurement;
|
|
39
|
-
};
|
|
40
|
-
export type LineInfo = {
|
|
41
|
-
lineIndex: number;
|
|
42
|
-
text: string;
|
|
43
|
-
cursorLength: number;
|
|
44
|
-
hasNewline: boolean;
|
|
45
|
-
cursorToCodeUnit: number[];
|
|
46
|
-
isAtomic: boolean;
|
|
47
|
-
};
|
|
48
|
-
export declare function buildLayoutModel(lines: LineInfo[], measurer: LayoutMeasurer): LayoutModel;
|
|
49
|
-
export declare function getDocLines(doc: Doc): LineInfo[];
|
|
50
|
-
export declare function getLineOffsets(lines: LineInfo[]): number[];
|
|
51
|
-
export declare function resolveOffsetToLine(lines: LineInfo[], offset: number): {
|
|
52
|
-
lineIndex: number;
|
|
53
|
-
offsetInLine: number;
|
|
54
|
-
};
|
|
55
|
-
//# sourceMappingURL=selection-layout.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"selection-layout.d.ts","sourceRoot":"","sources":["../../../../src/cake/engine/selection/selection-layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,GAAG,EAAU,MAAM,kBAAkB,CAAC;AAG3D,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,EAAE,UAAU,CAAC;IACtB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,UAAU,CAAC;IACtB,WAAW,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,eAAe,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,QAAQ,EAAE,EACjB,QAAQ,EAAE,cAAc,GACvB,WAAW,CAqCb;AAOD,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,EAAE,CAchD;AA6BD,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,CAW1D;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,EAAE,MAAM,GACb;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAsB7C"}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { graphemeSegments } from "../../shared/segmenter";
|
|
2
|
-
export function buildLayoutModel(lines, measurer) {
|
|
3
|
-
const layouts = [];
|
|
4
|
-
let top = measurer.container.top;
|
|
5
|
-
let lineStartOffset = 0;
|
|
6
|
-
lines.forEach((line) => {
|
|
7
|
-
const measurement = measurer.measureLine({
|
|
8
|
-
lineIndex: line.lineIndex,
|
|
9
|
-
lineText: line.text,
|
|
10
|
-
lineLength: line.cursorLength,
|
|
11
|
-
lineHasNewline: line.hasNewline,
|
|
12
|
-
top,
|
|
13
|
-
});
|
|
14
|
-
layouts.push({
|
|
15
|
-
lineIndex: line.lineIndex,
|
|
16
|
-
lineStartOffset,
|
|
17
|
-
lineLength: line.cursorLength,
|
|
18
|
-
lineHasNewline: line.hasNewline,
|
|
19
|
-
lineBox: measurement.lineBox,
|
|
20
|
-
rows: measurement.rows,
|
|
21
|
-
});
|
|
22
|
-
top = measurement.lineBox.top + measurement.lineBox.height;
|
|
23
|
-
lineStartOffset += line.cursorLength + (line.hasNewline ? 1 : 0);
|
|
24
|
-
});
|
|
25
|
-
const height = Math.max(measurer.container.height, top - measurer.container.top);
|
|
26
|
-
return {
|
|
27
|
-
container: {
|
|
28
|
-
...measurer.container,
|
|
29
|
-
height,
|
|
30
|
-
},
|
|
31
|
-
lines: layouts,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
export function getDocLines(doc) {
|
|
35
|
-
const flattenedLines = flattenBlocksWithAtomicInfo(doc.blocks);
|
|
36
|
-
return flattenedLines.map((line, index) => {
|
|
37
|
-
const cursorToCodeUnit = buildCursorToCodeUnit(line.text);
|
|
38
|
-
const cursorLength = Math.max(0, cursorToCodeUnit.length - 1);
|
|
39
|
-
return {
|
|
40
|
-
lineIndex: index,
|
|
41
|
-
text: line.text,
|
|
42
|
-
cursorLength,
|
|
43
|
-
hasNewline: index < flattenedLines.length - 1,
|
|
44
|
-
cursorToCodeUnit,
|
|
45
|
-
isAtomic: line.isAtomic,
|
|
46
|
-
};
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
function flattenBlocksWithAtomicInfo(blocks) {
|
|
50
|
-
const lines = [];
|
|
51
|
-
blocks.forEach((block) => {
|
|
52
|
-
lines.push(...flattenBlockWithAtomicInfo(block));
|
|
53
|
-
});
|
|
54
|
-
if (lines.length === 0) {
|
|
55
|
-
lines.push({ text: "", isAtomic: false });
|
|
56
|
-
}
|
|
57
|
-
return lines;
|
|
58
|
-
}
|
|
59
|
-
function flattenBlockWithAtomicInfo(block) {
|
|
60
|
-
if (block.type === "paragraph") {
|
|
61
|
-
return [
|
|
62
|
-
{ text: block.content.map(flattenInline).join(""), isAtomic: false },
|
|
63
|
-
];
|
|
64
|
-
}
|
|
65
|
-
if (block.type === "block-wrapper") {
|
|
66
|
-
return flattenBlocksWithAtomicInfo(block.blocks);
|
|
67
|
-
}
|
|
68
|
-
if (block.type === "block-atom") {
|
|
69
|
-
// Atomic blocks are represented as empty lines for layout purposes
|
|
70
|
-
return [{ text: "", isAtomic: true }];
|
|
71
|
-
}
|
|
72
|
-
return [];
|
|
73
|
-
}
|
|
74
|
-
export function getLineOffsets(lines) {
|
|
75
|
-
const offsets = [];
|
|
76
|
-
let current = 0;
|
|
77
|
-
lines.forEach((line) => {
|
|
78
|
-
offsets.push(current);
|
|
79
|
-
current += line.cursorLength;
|
|
80
|
-
if (line.hasNewline) {
|
|
81
|
-
current += 1;
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
return offsets;
|
|
85
|
-
}
|
|
86
|
-
export function resolveOffsetToLine(lines, offset) {
|
|
87
|
-
if (lines.length === 0) {
|
|
88
|
-
return { lineIndex: 0, offsetInLine: 0 };
|
|
89
|
-
}
|
|
90
|
-
const lineOffsets = getLineOffsets(lines);
|
|
91
|
-
const totalLength = lineOffsets[lineOffsets.length - 1] + lines[lines.length - 1].cursorLength;
|
|
92
|
-
const clamped = Math.max(0, Math.min(offset, totalLength));
|
|
93
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
94
|
-
const lineStart = lineOffsets[index];
|
|
95
|
-
const lineEnd = lineStart + lines[index].cursorLength;
|
|
96
|
-
if (clamped <= lineEnd || index === lines.length - 1) {
|
|
97
|
-
return { lineIndex: index, offsetInLine: clamped - lineStart };
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
const lastIndex = lines.length - 1;
|
|
101
|
-
return {
|
|
102
|
-
lineIndex: lastIndex,
|
|
103
|
-
offsetInLine: lines[lastIndex].cursorLength,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
function flattenInline(inline) {
|
|
107
|
-
if (inline.type === "text") {
|
|
108
|
-
return inline.text;
|
|
109
|
-
}
|
|
110
|
-
if (inline.type === "inline-wrapper") {
|
|
111
|
-
return inline.children.map(flattenInline).join("");
|
|
112
|
-
}
|
|
113
|
-
if (inline.type === "inline-atom") {
|
|
114
|
-
return " ";
|
|
115
|
-
}
|
|
116
|
-
return "";
|
|
117
|
-
}
|
|
118
|
-
function buildCursorToCodeUnit(text) {
|
|
119
|
-
const segments = graphemeSegments(text);
|
|
120
|
-
const mapping = [0];
|
|
121
|
-
for (const segment of segments) {
|
|
122
|
-
mapping.push(segment.index + segment.segment.length);
|
|
123
|
-
}
|
|
124
|
-
if (mapping.length === 0) {
|
|
125
|
-
mapping.push(0);
|
|
126
|
-
}
|
|
127
|
-
return mapping;
|
|
128
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { Selection } from "../../core/types";
|
|
2
|
-
import type { LayoutModel, LineInfo } from "./selection-layout";
|
|
3
|
-
type Affinity = "forward" | "backward";
|
|
4
|
-
export type VerticalNavigationResult = {
|
|
5
|
-
selection: Selection;
|
|
6
|
-
goalX: number;
|
|
7
|
-
};
|
|
8
|
-
export declare function moveSelectionVertically(params: {
|
|
9
|
-
lines: LineInfo[];
|
|
10
|
-
layout: LayoutModel;
|
|
11
|
-
selection: Selection;
|
|
12
|
-
direction: "up" | "down";
|
|
13
|
-
goalX: number | null;
|
|
14
|
-
focusRowIndex?: number;
|
|
15
|
-
hitTestCursorAt?: (x: number, y: number) => {
|
|
16
|
-
cursorOffset: number;
|
|
17
|
-
affinity: Affinity;
|
|
18
|
-
caretTop?: number;
|
|
19
|
-
} | null;
|
|
20
|
-
}): VerticalNavigationResult | null;
|
|
21
|
-
export {};
|
|
22
|
-
//# sourceMappingURL=selection-navigation.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"selection-navigation.d.ts","sourceRoot":"","sources":["../../../../src/cake/engine/selection/selection-navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAa,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG3E,KAAK,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvC,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAsIF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,CAChB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,KACN;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC7E,GAAG,wBAAwB,GAAG,IAAI,CA+JlC"}
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import { resolveOffsetToLine } from "./selection-layout";
|
|
2
|
-
function rectRight(rect) {
|
|
3
|
-
return rect.left + rect.width;
|
|
4
|
-
}
|
|
5
|
-
function clampNumber(value, min, max) {
|
|
6
|
-
return Math.max(min, Math.min(value, max));
|
|
7
|
-
}
|
|
8
|
-
function resolveSelectionAffinity(selection) {
|
|
9
|
-
if (selection.start === selection.end) {
|
|
10
|
-
return selection.affinity ?? "backward";
|
|
11
|
-
}
|
|
12
|
-
return selection.affinity ?? "forward";
|
|
13
|
-
}
|
|
14
|
-
function resolveSelectionAnchorAndFocus(selection) {
|
|
15
|
-
if (selection.start === selection.end) {
|
|
16
|
-
return { anchor: selection.start, focus: selection.start };
|
|
17
|
-
}
|
|
18
|
-
const affinity = resolveSelectionAffinity(selection);
|
|
19
|
-
if (affinity === "backward") {
|
|
20
|
-
return { anchor: selection.end, focus: selection.start };
|
|
21
|
-
}
|
|
22
|
-
return { anchor: selection.start, focus: selection.end };
|
|
23
|
-
}
|
|
24
|
-
function findRowIndexForOffset(rows, offsetInLine, affinity) {
|
|
25
|
-
if (rows.length === 0) {
|
|
26
|
-
return 0;
|
|
27
|
-
}
|
|
28
|
-
for (let index = 0; index < rows.length; index += 1) {
|
|
29
|
-
const row = rows[index];
|
|
30
|
-
if (offsetInLine === row.startOffset) {
|
|
31
|
-
if (affinity === "backward" && index > 0) {
|
|
32
|
-
return index - 1;
|
|
33
|
-
}
|
|
34
|
-
return index;
|
|
35
|
-
}
|
|
36
|
-
if (offsetInLine === row.endOffset) {
|
|
37
|
-
if (affinity === "forward" && index + 1 < rows.length) {
|
|
38
|
-
return index + 1;
|
|
39
|
-
}
|
|
40
|
-
return index;
|
|
41
|
-
}
|
|
42
|
-
if (offsetInLine > row.startOffset && offsetInLine < row.endOffset) {
|
|
43
|
-
return index;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return rows.length - 1;
|
|
47
|
-
}
|
|
48
|
-
function asSingleRow(line) {
|
|
49
|
-
return {
|
|
50
|
-
startOffset: 0,
|
|
51
|
-
endOffset: line.lineLength,
|
|
52
|
-
rect: line.lineBox,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
function rowOffsetToX(params) {
|
|
56
|
-
const { row, offsetInLine } = params;
|
|
57
|
-
const rowRight = rectRight(row.rect);
|
|
58
|
-
const clampedOffset = clampNumber(offsetInLine, row.startOffset, row.endOffset);
|
|
59
|
-
if (row.endOffset <= row.startOffset || row.rect.width <= 0) {
|
|
60
|
-
return row.rect.left;
|
|
61
|
-
}
|
|
62
|
-
if (clampedOffset === row.endOffset) {
|
|
63
|
-
return rowRight;
|
|
64
|
-
}
|
|
65
|
-
const rowLength = row.endOffset - row.startOffset;
|
|
66
|
-
const fraction = (clampedOffset - row.startOffset) / rowLength;
|
|
67
|
-
return row.rect.left + row.rect.width * fraction;
|
|
68
|
-
}
|
|
69
|
-
function rowXToOffset(params) {
|
|
70
|
-
const { row, x } = params;
|
|
71
|
-
const rowRight = rectRight(row.rect);
|
|
72
|
-
if (x <= row.rect.left) {
|
|
73
|
-
return { offsetInLine: row.startOffset, affinity: "forward" };
|
|
74
|
-
}
|
|
75
|
-
if (x >= rowRight) {
|
|
76
|
-
return { offsetInLine: row.endOffset, affinity: "backward" };
|
|
77
|
-
}
|
|
78
|
-
const rowLength = row.endOffset - row.startOffset;
|
|
79
|
-
if (rowLength <= 0 || row.rect.width <= 0) {
|
|
80
|
-
return { offsetInLine: row.startOffset, affinity: "forward" };
|
|
81
|
-
}
|
|
82
|
-
const fraction = (x - row.rect.left) / row.rect.width;
|
|
83
|
-
const raw = row.startOffset + fraction * rowLength;
|
|
84
|
-
const rounded = Math.round(raw);
|
|
85
|
-
const offsetInLine = clampNumber(rounded, row.startOffset, row.endOffset);
|
|
86
|
-
return { offsetInLine, affinity: "forward" };
|
|
87
|
-
}
|
|
88
|
-
function resolveTargetLineIndex(params) {
|
|
89
|
-
const delta = params.direction === "down" ? 1 : -1;
|
|
90
|
-
let index = params.fromLineIndex + delta;
|
|
91
|
-
while (index >= 0 && index < params.lines.length) {
|
|
92
|
-
if (!params.lines[index]?.isAtomic) {
|
|
93
|
-
return index;
|
|
94
|
-
}
|
|
95
|
-
index += delta;
|
|
96
|
-
}
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
export function moveSelectionVertically(params) {
|
|
100
|
-
const { lines, layout, direction } = params;
|
|
101
|
-
if (layout.lines.length === 0 || lines.length === 0) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
const affinity = resolveSelectionAffinity(params.selection);
|
|
105
|
-
const { anchor, focus } = resolveSelectionAnchorAndFocus(params.selection);
|
|
106
|
-
const focusResolved = resolveOffsetToLine(lines, focus);
|
|
107
|
-
const focusLineLayout = layout.lines[focusResolved.lineIndex];
|
|
108
|
-
if (!focusLineLayout) {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
const focusRows = focusLineLayout.rows.length > 0
|
|
112
|
-
? focusLineLayout.rows
|
|
113
|
-
: [asSingleRow(focusLineLayout)];
|
|
114
|
-
let focusRowIndex = params.focusRowIndex ??
|
|
115
|
-
findRowIndexForOffset(focusRows, focusResolved.offsetInLine, affinity);
|
|
116
|
-
focusRowIndex = clampNumber(focusRowIndex, 0, Math.max(0, focusRows.length - 1));
|
|
117
|
-
const focusRow = focusRows[focusRowIndex] ?? focusRows[focusRows.length - 1];
|
|
118
|
-
if (!focusRow) {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
const goalX = params.goalX ??
|
|
122
|
-
rowOffsetToX({ row: focusRow, offsetInLine: focusResolved.offsetInLine });
|
|
123
|
-
let targetLineIndex = focusResolved.lineIndex;
|
|
124
|
-
let targetRowIndex = focusRowIndex;
|
|
125
|
-
if (direction === "up") {
|
|
126
|
-
if (focusRowIndex > 0) {
|
|
127
|
-
targetRowIndex = focusRowIndex - 1;
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
const nextLineIndex = resolveTargetLineIndex({
|
|
131
|
-
lines,
|
|
132
|
-
fromLineIndex: focusResolved.lineIndex,
|
|
133
|
-
direction: "up",
|
|
134
|
-
});
|
|
135
|
-
if (nextLineIndex === null) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
targetLineIndex = nextLineIndex;
|
|
139
|
-
targetRowIndex = Number.POSITIVE_INFINITY;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
if (focusRowIndex + 1 < focusRows.length) {
|
|
144
|
-
targetRowIndex = focusRowIndex + 1;
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
const nextLineIndex = resolveTargetLineIndex({
|
|
148
|
-
lines,
|
|
149
|
-
fromLineIndex: focusResolved.lineIndex,
|
|
150
|
-
direction: "down",
|
|
151
|
-
});
|
|
152
|
-
if (nextLineIndex === null) {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
targetLineIndex = nextLineIndex;
|
|
156
|
-
targetRowIndex = 0;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const targetLineLayout = layout.lines[targetLineIndex];
|
|
160
|
-
if (!targetLineLayout) {
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
const targetRows = targetLineLayout.rows.length > 0
|
|
164
|
-
? targetLineLayout.rows
|
|
165
|
-
: [asSingleRow(targetLineLayout)];
|
|
166
|
-
const resolvedTargetRowIndex = targetRowIndex === Number.POSITIVE_INFINITY
|
|
167
|
-
? Math.max(0, targetRows.length - 1)
|
|
168
|
-
: clampNumber(targetRowIndex, 0, Math.max(0, targetRows.length - 1));
|
|
169
|
-
const targetRow = targetRows[resolvedTargetRowIndex] ?? targetRows[targetRows.length - 1];
|
|
170
|
-
if (!targetRow) {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
const targetLineStart = targetLineLayout.lineStartOffset;
|
|
174
|
-
const targetLineEnd = targetLineStart + targetLineLayout.lineLength;
|
|
175
|
-
if (params.hitTestCursorAt) {
|
|
176
|
-
const midY = targetRow.rect.top + targetRow.rect.height / 2;
|
|
177
|
-
const rowLeft = targetRow.rect.left;
|
|
178
|
-
const rowRight = rectRight(targetRow.rect);
|
|
179
|
-
const hitX = rowRight - rowLeft > 1
|
|
180
|
-
? clampNumber(goalX, rowLeft + 0.5, rowRight - 0.5)
|
|
181
|
-
: goalX;
|
|
182
|
-
let hit = params.hitTestCursorAt(hitX, midY);
|
|
183
|
-
// If we're in trailing whitespace, some caret APIs will return the end of
|
|
184
|
-
// the whole line (last visual row). Nudge left until we get a hit that
|
|
185
|
-
// actually lands on the requested visual row.
|
|
186
|
-
if (hit && hit.cursorOffset === focus) {
|
|
187
|
-
for (let attempt = 0; attempt < 80; attempt += 1) {
|
|
188
|
-
const nudgedX = hitX - (attempt + 1) * 1;
|
|
189
|
-
if (nudgedX <= rowLeft + 0.5) {
|
|
190
|
-
break;
|
|
191
|
-
}
|
|
192
|
-
const next = params.hitTestCursorAt(nudgedX, midY);
|
|
193
|
-
const matchesRow = next?.caretTop === undefined ||
|
|
194
|
-
Math.abs(next.caretTop - targetRow.rect.top) <= 2;
|
|
195
|
-
if (next && next.cursorOffset !== focus && matchesRow) {
|
|
196
|
-
hit = next;
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
const matchesRow = hit?.caretTop === undefined ||
|
|
202
|
-
Math.abs(hit.caretTop - targetRow.rect.top) <= 2;
|
|
203
|
-
if (hit &&
|
|
204
|
-
matchesRow &&
|
|
205
|
-
hit.cursorOffset >= targetLineStart &&
|
|
206
|
-
hit.cursorOffset <= targetLineEnd) {
|
|
207
|
-
const normalizedAffinity = targetLineLayout.lineLength === 0 ? "forward" : hit.affinity;
|
|
208
|
-
const nextSelection = anchor === focus
|
|
209
|
-
? {
|
|
210
|
-
start: hit.cursorOffset,
|
|
211
|
-
end: hit.cursorOffset,
|
|
212
|
-
affinity: normalizedAffinity,
|
|
213
|
-
}
|
|
214
|
-
: { start: anchor, end: hit.cursorOffset, affinity: normalizedAffinity };
|
|
215
|
-
return { selection: nextSelection, goalX };
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
const rowHit = rowXToOffset({ row: targetRow, x: goalX });
|
|
219
|
-
const nextPos = targetLineStart + rowHit.offsetInLine;
|
|
220
|
-
const normalizedAffinity = targetLineLayout.lineLength === 0 ? "forward" : rowHit.affinity;
|
|
221
|
-
const nextSelection = anchor === focus
|
|
222
|
-
? { start: nextPos, end: nextPos, affinity: normalizedAffinity }
|
|
223
|
-
: {
|
|
224
|
-
start: anchor,
|
|
225
|
-
end: nextPos,
|
|
226
|
-
affinity: normalizedAffinity,
|
|
227
|
-
};
|
|
228
|
-
return { selection: nextSelection, goalX };
|
|
229
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { type LineInfo } from "./selection-layout";
|
|
2
|
-
export declare function getVisibleText(lines: LineInfo[]): string;
|
|
3
|
-
export declare function visibleOffsetToCursorOffset(lines: LineInfo[], visibleOffset: number): number | null;
|
|
4
|
-
export declare function cursorOffsetToVisibleOffset(lines: LineInfo[], cursorOffset: number): number;
|
|
5
|
-
//# sourceMappingURL=visible-text.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"visible-text.d.ts","sourceRoot":"","sources":["../../../../src/cake/engine/selection/visible-text.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,QAAQ,EACd,MAAM,oBAAoB,CAAC;AAuB5B,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAKxD;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,QAAQ,EAAE,EACjB,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,IAAI,CA2Bf;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,QAAQ,EAAE,EACjB,YAAY,EAAE,MAAM,GACnB,MAAM,CAsBR"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { getLineOffsets, resolveOffsetToLine, } from "./selection-layout";
|
|
2
|
-
function cursorOffsetFromCodeUnit(line, codeUnitOffset) {
|
|
3
|
-
const maxCodeUnit = line.cursorToCodeUnit[line.cursorToCodeUnit.length - 1] ?? 0;
|
|
4
|
-
const clamped = Math.max(0, Math.min(codeUnitOffset, maxCodeUnit));
|
|
5
|
-
for (let i = 0; i < line.cursorToCodeUnit.length; i += 1) {
|
|
6
|
-
if (line.cursorToCodeUnit[i] === clamped) {
|
|
7
|
-
return i;
|
|
8
|
-
}
|
|
9
|
-
if (line.cursorToCodeUnit[i] > clamped) {
|
|
10
|
-
return Math.max(0, i - 1);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return line.cursorLength;
|
|
14
|
-
}
|
|
15
|
-
export function getVisibleText(lines) {
|
|
16
|
-
if (lines.length === 0) {
|
|
17
|
-
return "";
|
|
18
|
-
}
|
|
19
|
-
return lines.map((line) => line.text).join("\n");
|
|
20
|
-
}
|
|
21
|
-
export function visibleOffsetToCursorOffset(lines, visibleOffset) {
|
|
22
|
-
if (lines.length === 0) {
|
|
23
|
-
return 0;
|
|
24
|
-
}
|
|
25
|
-
const clampedOffset = Math.max(0, visibleOffset);
|
|
26
|
-
const lineOffsets = getLineOffsets(lines);
|
|
27
|
-
let codeUnitIndex = 0;
|
|
28
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
29
|
-
const line = lines[index];
|
|
30
|
-
const lineStart = codeUnitIndex;
|
|
31
|
-
const lineEnd = lineStart + line.text.length;
|
|
32
|
-
if (clampedOffset <= lineEnd || index === lines.length - 1) {
|
|
33
|
-
const offsetInLine = Math.max(0, Math.min(clampedOffset - lineStart, line.text.length));
|
|
34
|
-
const cursorOffsetInLine = cursorOffsetFromCodeUnit(line, offsetInLine);
|
|
35
|
-
const lineStartOffset = lineOffsets[index] ?? 0;
|
|
36
|
-
return lineStartOffset + cursorOffsetInLine;
|
|
37
|
-
}
|
|
38
|
-
codeUnitIndex = lineEnd + (line.hasNewline ? 1 : 0);
|
|
39
|
-
if (line.hasNewline && clampedOffset === codeUnitIndex) {
|
|
40
|
-
const lineStartOffset = lineOffsets[index] ?? 0;
|
|
41
|
-
return lineStartOffset + line.cursorLength + 1;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
export function cursorOffsetToVisibleOffset(lines, cursorOffset) {
|
|
47
|
-
if (lines.length === 0) {
|
|
48
|
-
return 0;
|
|
49
|
-
}
|
|
50
|
-
const lineOffsets = getLineOffsets(lines);
|
|
51
|
-
const { lineIndex, offsetInLine } = resolveOffsetToLine(lines, cursorOffset);
|
|
52
|
-
let codeUnitIndex = 0;
|
|
53
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
54
|
-
const line = lines[index];
|
|
55
|
-
if (index === lineIndex) {
|
|
56
|
-
const offset = Math.max(0, Math.min(offsetInLine, line.cursorLength));
|
|
57
|
-
return codeUnitIndex + (line.cursorToCodeUnit[offset] ?? 0);
|
|
58
|
-
}
|
|
59
|
-
codeUnitIndex += line.text.length + (line.hasNewline ? 1 : 0);
|
|
60
|
-
}
|
|
61
|
-
const lastLine = lines[lines.length - 1];
|
|
62
|
-
const lastStart = lineOffsets[lineOffsets.length - 1] ?? 0;
|
|
63
|
-
const lastOffset = Math.max(0, cursorOffset - lastStart);
|
|
64
|
-
return (codeUnitIndex +
|
|
65
|
-
(lastLine.cursorToCodeUnit[lastOffset] ?? lastLine.text.length));
|
|
66
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { CakeExtension, EditCommand } from "../core/runtime";
|
|
2
|
-
export type CakeEditorSelection = {
|
|
3
|
-
start: number;
|
|
4
|
-
end: number;
|
|
5
|
-
affinity?: "backward" | "forward";
|
|
6
|
-
};
|
|
7
|
-
export interface CakeEditorUpdate {
|
|
8
|
-
value?: string;
|
|
9
|
-
selection?: CakeEditorSelection;
|
|
10
|
-
focus?: boolean;
|
|
11
|
-
}
|
|
12
|
-
export interface CakeEditorProps {
|
|
13
|
-
value: string;
|
|
14
|
-
onChange: (value: string) => void;
|
|
15
|
-
selection?: CakeEditorSelection;
|
|
16
|
-
onSelectionChange?: (start: number, end: number, affinity?: "backward" | "forward") => void;
|
|
17
|
-
placeholder?: string;
|
|
18
|
-
disabled?: boolean;
|
|
19
|
-
spellCheck?: boolean;
|
|
20
|
-
className?: string;
|
|
21
|
-
style?: React.CSSProperties;
|
|
22
|
-
extensions: CakeExtension[];
|
|
23
|
-
onBlur?: (event?: FocusEvent) => void;
|
|
24
|
-
}
|
|
25
|
-
export interface CakeEditorRef {
|
|
26
|
-
element: HTMLElement | null;
|
|
27
|
-
focus: (selection?: CakeEditorSelection) => void;
|
|
28
|
-
blur: () => void;
|
|
29
|
-
hasFocus: () => boolean;
|
|
30
|
-
selectAll: () => void;
|
|
31
|
-
/**
|
|
32
|
-
* Execute a semantic edit command.
|
|
33
|
-
*
|
|
34
|
-
* Semantic commands are defined by extensions and allow callers to use
|
|
35
|
-
* high-level commands like `{ type: "toggle-bold" }` instead of
|
|
36
|
-
* syntax-specific commands like `{ type: "toggle-inline", marker: "**" }`.
|
|
37
|
-
*
|
|
38
|
-
* Available commands depend on which extensions are registered.
|
|
39
|
-
*
|
|
40
|
-
* @param command - The command to execute
|
|
41
|
-
* @param options.restoreFocus - If true, refocus the editor after executing.
|
|
42
|
-
* Use this when calling from a toolbar button that steals focus.
|
|
43
|
-
*/
|
|
44
|
-
executeCommand: (command: EditCommand, options?: {
|
|
45
|
-
restoreFocus?: boolean;
|
|
46
|
-
}) => boolean;
|
|
47
|
-
applyUpdate: (update: CakeEditorUpdate) => void;
|
|
48
|
-
getValue: () => string;
|
|
49
|
-
getSelection: () => {
|
|
50
|
-
start: number;
|
|
51
|
-
end: number;
|
|
52
|
-
} | null;
|
|
53
|
-
getCursorLength: () => number;
|
|
54
|
-
insertText: (text: string) => void;
|
|
55
|
-
replaceText: (oldText: string, newText: string) => void;
|
|
56
|
-
}
|
|
57
|
-
export declare const CakeEditor: import("react").ForwardRefExoticComponent<CakeEditorProps & import("react").RefAttributes<CakeEditorRef | null>>;
|
|
58
|
-
//# sourceMappingURL=CakeEditor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CakeEditor.d.ts","sourceRoot":"","sources":["../../../src/cake/react/CakeEditor.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EAEZ,MAAM,iBAAiB,CAAC;AAczB,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CACnC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,iBAAiB,CAAC,EAAE,CAClB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,UAAU,GAAG,SAAS,KAC9B,IAAI,CAAC;IACV,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACjD,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB;;;;;;;;;;;;OAYG;IACH,cAAc,EAAE,CACd,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,KACjC,OAAO,CAAC;IACb,WAAW,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAChD,QAAQ,EAAE,MAAM,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1D,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACzD;AAED,eAAO,MAAM,UAAU,kHAiQrB,CAAC"}
|