@blankdotpage/cake 0.1.1 → 0.1.3
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/clipboard.d.ts +1 -1
- package/dist/cake/core/runtime.d.ts +51 -13
- package/dist/cake/dom/render.d.ts +1 -1
- package/dist/cake/engine/cake-engine.d.ts +14 -0
- package/dist/cake/engine/selection/selection-geometry.d.ts +1 -0
- package/dist/cake/engine/selection/selection-layout-dom.d.ts +7 -0
- package/dist/cake/extensions/blockquote/blockquote.d.ts +1 -2
- package/dist/cake/extensions/bold/bold.d.ts +1 -2
- package/dist/cake/extensions/combined-emphasis/combined-emphasis.d.ts +1 -2
- package/dist/cake/extensions/heading/heading.d.ts +1 -2
- package/dist/cake/extensions/image/image.d.ts +1 -2
- package/dist/cake/extensions/index.d.ts +1 -0
- package/dist/cake/extensions/italic/italic.d.ts +1 -2
- package/dist/cake/extensions/link/link-popover.d.ts +6 -0
- package/dist/cake/extensions/link/link.d.ts +15 -2
- package/dist/cake/extensions/list/list.d.ts +11 -2
- package/dist/cake/extensions/pipe-link/pipe-link.d.ts +1 -2
- package/dist/cake/extensions/scrollbar/index.d.ts +1 -2
- package/dist/cake/extensions/strikethrough/strikethrough.d.ts +1 -2
- package/dist/cake/react/CakeEditor.d.ts +2 -0
- package/dist/cake/test/harness.d.ts +12 -0
- package/dist/index.cjs +1455 -422
- package/dist/index.js +1455 -422
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,42 +1,51 @@
|
|
|
1
1
|
import require$$0, { useRef, useState, useCallback, useEffect, forwardRef, useImperativeHandle, Fragment } from "react";
|
|
2
|
-
import { createPortal } from "react-dom";
|
|
3
2
|
import { Pencil, ExternalLink, Unlink } from "lucide-react";
|
|
4
3
|
import TurndownService from "turndown";
|
|
5
4
|
import { EditorSelection, Transaction } from "@codemirror/state";
|
|
6
|
-
var
|
|
7
|
-
var
|
|
5
|
+
var jsxRuntime = { exports: {} };
|
|
6
|
+
var reactJsxRuntime_production_min = {};
|
|
8
7
|
/**
|
|
9
8
|
* @license React
|
|
10
|
-
* react-jsx-
|
|
9
|
+
* react-jsx-runtime.production.min.js
|
|
11
10
|
*
|
|
12
11
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
13
12
|
*
|
|
14
13
|
* This source code is licensed under the MIT license found in the
|
|
15
14
|
* LICENSE file in the root directory of this source tree.
|
|
16
15
|
*/
|
|
17
|
-
var
|
|
18
|
-
function
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
var hasRequiredReactJsxRuntime_production_min;
|
|
17
|
+
function requireReactJsxRuntime_production_min() {
|
|
18
|
+
if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min;
|
|
19
|
+
hasRequiredReactJsxRuntime_production_min = 1;
|
|
20
|
+
var f = require$$0, k = Symbol.for("react.element"), l = Symbol.for("react.fragment"), m = Object.prototype.hasOwnProperty, n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, p = { key: true, ref: true, __self: true, __source: true };
|
|
21
|
+
function q(c, a, g) {
|
|
22
|
+
var b, d = {}, e = null, h = null;
|
|
23
|
+
void 0 !== g && (e = "" + g);
|
|
24
|
+
void 0 !== a.key && (e = "" + a.key);
|
|
25
|
+
void 0 !== a.ref && (h = a.ref);
|
|
26
|
+
for (b in a) m.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);
|
|
27
|
+
if (c && c.defaultProps) for (b in a = c.defaultProps, a) void 0 === d[b] && (d[b] = a[b]);
|
|
28
|
+
return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current };
|
|
29
|
+
}
|
|
30
|
+
reactJsxRuntime_production_min.Fragment = l;
|
|
31
|
+
reactJsxRuntime_production_min.jsx = q;
|
|
32
|
+
reactJsxRuntime_production_min.jsxs = q;
|
|
33
|
+
return reactJsxRuntime_production_min;
|
|
34
|
+
}
|
|
35
|
+
var reactJsxRuntime_development = {};
|
|
27
36
|
/**
|
|
28
37
|
* @license React
|
|
29
|
-
* react-jsx-
|
|
38
|
+
* react-jsx-runtime.development.js
|
|
30
39
|
*
|
|
31
40
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
32
41
|
*
|
|
33
42
|
* This source code is licensed under the MIT license found in the
|
|
34
43
|
* LICENSE file in the root directory of this source tree.
|
|
35
44
|
*/
|
|
36
|
-
var
|
|
37
|
-
function
|
|
38
|
-
if (
|
|
39
|
-
|
|
45
|
+
var hasRequiredReactJsxRuntime_development;
|
|
46
|
+
function requireReactJsxRuntime_development() {
|
|
47
|
+
if (hasRequiredReactJsxRuntime_development) return reactJsxRuntime_development;
|
|
48
|
+
hasRequiredReactJsxRuntime_development = 1;
|
|
40
49
|
if (process.env.NODE_ENV !== "production") {
|
|
41
50
|
(function() {
|
|
42
51
|
var React = require$$0;
|
|
@@ -523,10 +532,6 @@ function requireReactJsxDevRuntime_development() {
|
|
|
523
532
|
};
|
|
524
533
|
var specialPropKeyWarningShown;
|
|
525
534
|
var specialPropRefWarningShown;
|
|
526
|
-
var didWarnAboutStringRefs;
|
|
527
|
-
{
|
|
528
|
-
didWarnAboutStringRefs = {};
|
|
529
|
-
}
|
|
530
535
|
function hasValidRef(config) {
|
|
531
536
|
{
|
|
532
537
|
if (hasOwnProperty.call(config, "ref")) {
|
|
@@ -551,13 +556,7 @@ function requireReactJsxDevRuntime_development() {
|
|
|
551
556
|
}
|
|
552
557
|
function warnIfStringRefCannotBeAutoConverted(config, self) {
|
|
553
558
|
{
|
|
554
|
-
if (typeof config.ref === "string" && ReactCurrentOwner.current && self
|
|
555
|
-
var componentName = getComponentNameFromType(ReactCurrentOwner.current.type);
|
|
556
|
-
if (!didWarnAboutStringRefs[componentName]) {
|
|
557
|
-
error('Component "%s" contains the string ref "%s". Support for string refs will be removed in a future major release. This case cannot be automatically converted to an arrow function. We ask you to manually fix this case by using useRef() or createRef() instead. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref', getComponentNameFromType(ReactCurrentOwner.current.type), config.ref);
|
|
558
|
-
didWarnAboutStringRefs[componentName] = true;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
559
|
+
if (typeof config.ref === "string" && ReactCurrentOwner.current && self) ;
|
|
561
560
|
}
|
|
562
561
|
}
|
|
563
562
|
function defineKeyPropWarningGetter(props, displayName) {
|
|
@@ -711,11 +710,6 @@ function requireReactJsxDevRuntime_development() {
|
|
|
711
710
|
}
|
|
712
711
|
function getSourceInfoErrorAddendum(source) {
|
|
713
712
|
{
|
|
714
|
-
if (source !== void 0) {
|
|
715
|
-
var fileName = source.fileName.replace(/^.*[\\\/]/, "");
|
|
716
|
-
var lineNumber = source.lineNumber;
|
|
717
|
-
return "\n\nCheck your code at " + fileName + ":" + lineNumber + ".";
|
|
718
|
-
}
|
|
719
713
|
return "";
|
|
720
714
|
}
|
|
721
715
|
}
|
|
@@ -841,7 +835,7 @@ function requireReactJsxDevRuntime_development() {
|
|
|
841
835
|
if (type === void 0 || typeof type === "object" && type !== null && Object.keys(type).length === 0) {
|
|
842
836
|
info += " You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.";
|
|
843
837
|
}
|
|
844
|
-
var sourceInfo = getSourceInfoErrorAddendum(
|
|
838
|
+
var sourceInfo = getSourceInfoErrorAddendum();
|
|
845
839
|
if (sourceInfo) {
|
|
846
840
|
info += sourceInfo;
|
|
847
841
|
} else {
|
|
@@ -905,19 +899,31 @@ function requireReactJsxDevRuntime_development() {
|
|
|
905
899
|
return element;
|
|
906
900
|
}
|
|
907
901
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
902
|
+
function jsxWithValidationStatic(type, props, key) {
|
|
903
|
+
{
|
|
904
|
+
return jsxWithValidation(type, props, key, true);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
function jsxWithValidationDynamic(type, props, key) {
|
|
908
|
+
{
|
|
909
|
+
return jsxWithValidation(type, props, key, false);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
var jsx = jsxWithValidationDynamic;
|
|
913
|
+
var jsxs = jsxWithValidationStatic;
|
|
914
|
+
reactJsxRuntime_development.Fragment = REACT_FRAGMENT_TYPE;
|
|
915
|
+
reactJsxRuntime_development.jsx = jsx;
|
|
916
|
+
reactJsxRuntime_development.jsxs = jsxs;
|
|
911
917
|
})();
|
|
912
918
|
}
|
|
913
|
-
return
|
|
919
|
+
return reactJsxRuntime_development;
|
|
914
920
|
}
|
|
915
921
|
if (process.env.NODE_ENV === "production") {
|
|
916
|
-
|
|
922
|
+
jsxRuntime.exports = requireReactJsxRuntime_production_min();
|
|
917
923
|
} else {
|
|
918
|
-
|
|
924
|
+
jsxRuntime.exports = requireReactJsxRuntime_development();
|
|
919
925
|
}
|
|
920
|
-
var
|
|
926
|
+
var jsxRuntimeExports = jsxRuntime.exports;
|
|
921
927
|
const graphemeSegmenter = new Intl.Segmenter(void 0, {
|
|
922
928
|
granularity: "grapheme"
|
|
923
929
|
});
|
|
@@ -991,15 +997,36 @@ class CursorSourceBuilder {
|
|
|
991
997
|
if (!text) {
|
|
992
998
|
return;
|
|
993
999
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1000
|
+
let isAllAscii = true;
|
|
1001
|
+
for (let i = 0; i < text.length; i++) {
|
|
1002
|
+
if (text.charCodeAt(i) >= 128) {
|
|
1003
|
+
isAllAscii = false;
|
|
1004
|
+
break;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (isAllAscii) {
|
|
1008
|
+
for (let i = 0; i < text.length; i++) {
|
|
1009
|
+
const char = text[i];
|
|
1010
|
+
this.sourceParts.push(char);
|
|
1011
|
+
this.sourceLengthValue += 1;
|
|
1012
|
+
const sourceLength = this.sourceLengthValue;
|
|
1013
|
+
this.cursorLength += 1;
|
|
1014
|
+
this.boundaries.push({
|
|
1015
|
+
sourceBackward: sourceLength,
|
|
1016
|
+
sourceForward: sourceLength
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
} else {
|
|
1020
|
+
for (const segment of graphemeSegments(text)) {
|
|
1021
|
+
this.sourceParts.push(segment.segment);
|
|
1022
|
+
this.sourceLengthValue += segment.segment.length;
|
|
1023
|
+
const sourceLength = this.sourceLengthValue;
|
|
1024
|
+
this.cursorLength += 1;
|
|
1025
|
+
this.boundaries.push({
|
|
1026
|
+
sourceBackward: sourceLength,
|
|
1027
|
+
sourceForward: sourceLength
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1003
1030
|
}
|
|
1004
1031
|
}
|
|
1005
1032
|
appendCursorAtom(sourceText, cursorUnits = 1) {
|
|
@@ -1049,6 +1076,15 @@ class CursorSourceBuilder {
|
|
|
1049
1076
|
};
|
|
1050
1077
|
}
|
|
1051
1078
|
}
|
|
1079
|
+
function isStructuralEdit(command) {
|
|
1080
|
+
return command.type === "insert" || command.type === "delete-backward" || command.type === "delete-forward" || command.type === "insert-line-break" || command.type === "exit-block-wrapper";
|
|
1081
|
+
}
|
|
1082
|
+
function isApplyEditCommand(command) {
|
|
1083
|
+
return command.type === "insert" || command.type === "insert-line-break" || command.type === "delete-backward" || command.type === "delete-forward";
|
|
1084
|
+
}
|
|
1085
|
+
function defineExtension(extension) {
|
|
1086
|
+
return extension;
|
|
1087
|
+
}
|
|
1052
1088
|
const defaultSelection$1 = { start: 0, end: 0, affinity: "forward" };
|
|
1053
1089
|
function createRuntime(extensions) {
|
|
1054
1090
|
const toggleMarkerToKind = /* @__PURE__ */ new Map();
|
|
@@ -1290,7 +1326,7 @@ function createRuntime(extensions) {
|
|
|
1290
1326
|
}
|
|
1291
1327
|
}
|
|
1292
1328
|
const selection = normalizeSelection$1(state.selection);
|
|
1293
|
-
if (command
|
|
1329
|
+
if (isStructuralEdit(command)) {
|
|
1294
1330
|
const structural = applyStructuralEdit(command, state.doc, selection);
|
|
1295
1331
|
if (!structural) {
|
|
1296
1332
|
if (command.type === "delete-backward" || command.type === "delete-forward") {
|
|
@@ -1332,6 +1368,39 @@ function createRuntime(extensions) {
|
|
|
1332
1368
|
}
|
|
1333
1369
|
};
|
|
1334
1370
|
}
|
|
1371
|
+
if (command.type === "insert" || command.type === "insert-line-break") {
|
|
1372
|
+
const cursorLength = state.map.cursorLength;
|
|
1373
|
+
const cursorStart = Math.max(
|
|
1374
|
+
0,
|
|
1375
|
+
Math.min(cursorLength, Math.min(selection.start, selection.end))
|
|
1376
|
+
);
|
|
1377
|
+
const cursorEnd = Math.max(
|
|
1378
|
+
0,
|
|
1379
|
+
Math.min(cursorLength, Math.max(selection.start, selection.end))
|
|
1380
|
+
);
|
|
1381
|
+
const range = { start: cursorStart, end: cursorEnd };
|
|
1382
|
+
const fullDocReplace = range.start === 0 && range.end === cursorLength;
|
|
1383
|
+
const from = fullDocReplace ? 0 : state.map.cursorToSource(range.start, "backward");
|
|
1384
|
+
const to = fullDocReplace ? state.source.length : state.map.cursorToSource(range.end, "forward");
|
|
1385
|
+
const fromClamped = Math.max(0, Math.min(from, state.source.length));
|
|
1386
|
+
const toClamped = Math.max(
|
|
1387
|
+
fromClamped,
|
|
1388
|
+
Math.min(to, state.source.length)
|
|
1389
|
+
);
|
|
1390
|
+
const insertText = command.type === "insert" ? command.text : "\n";
|
|
1391
|
+
const nextSource = state.source.slice(0, fromClamped) + insertText + state.source.slice(toClamped);
|
|
1392
|
+
const next2 = createState(nextSource);
|
|
1393
|
+
const caretSource2 = fromClamped + insertText.length;
|
|
1394
|
+
const caretCursor2 = next2.map.sourceToCursor(caretSource2, "forward");
|
|
1395
|
+
return {
|
|
1396
|
+
...next2,
|
|
1397
|
+
selection: {
|
|
1398
|
+
start: caretCursor2.cursorOffset,
|
|
1399
|
+
end: caretCursor2.cursorOffset,
|
|
1400
|
+
affinity: caretCursor2.affinity
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1335
1404
|
return state;
|
|
1336
1405
|
}
|
|
1337
1406
|
const interim = createStateFromDoc(structural.doc);
|
|
@@ -2409,13 +2478,150 @@ function createRuntime(extensions) {
|
|
|
2409
2478
|
const endInLine = lineIndex === endLoc.lineIndex ? endLoc.offsetInLine : line.cursorLength;
|
|
2410
2479
|
const selectedRuns = sliceRuns(runs, startInLine, endInLine).selected;
|
|
2411
2480
|
const content = runsToInlines(normalizeRuns(selectedRuns));
|
|
2412
|
-
|
|
2481
|
+
const paragraph = { type: "paragraph", content };
|
|
2482
|
+
if (line.path.length > 1) {
|
|
2483
|
+
const wrapperPath = line.path.slice(0, -1);
|
|
2484
|
+
const wrapper = getBlockAtPath(state.doc.blocks, wrapperPath);
|
|
2485
|
+
if (wrapper && wrapper.type === "block-wrapper") {
|
|
2486
|
+
blocks.push({
|
|
2487
|
+
type: "block-wrapper",
|
|
2488
|
+
kind: wrapper.kind,
|
|
2489
|
+
data: wrapper.data,
|
|
2490
|
+
blocks: [paragraph]
|
|
2491
|
+
});
|
|
2492
|
+
continue;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
blocks.push(paragraph);
|
|
2413
2496
|
}
|
|
2414
2497
|
const sliceDoc = {
|
|
2415
2498
|
blocks: blocks.length > 0 ? blocks : [{ type: "paragraph", content: [] }]
|
|
2416
2499
|
};
|
|
2417
2500
|
return serialize(normalize(sliceDoc)).source;
|
|
2418
2501
|
}
|
|
2502
|
+
function escapeHtml(text) {
|
|
2503
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2504
|
+
}
|
|
2505
|
+
function runsToHtml(runs) {
|
|
2506
|
+
var _a;
|
|
2507
|
+
let html = "";
|
|
2508
|
+
for (const run of runs) {
|
|
2509
|
+
let content = escapeHtml(run.text);
|
|
2510
|
+
const sortedMarks = [...run.marks].reverse();
|
|
2511
|
+
for (const mark of sortedMarks) {
|
|
2512
|
+
if (mark.kind === "bold") {
|
|
2513
|
+
content = `<strong>${content}</strong>`;
|
|
2514
|
+
} else if (mark.kind === "italic") {
|
|
2515
|
+
content = `<em>${content}</em>`;
|
|
2516
|
+
} else if (mark.kind === "strikethrough") {
|
|
2517
|
+
content = `<s>${content}</s>`;
|
|
2518
|
+
} else if (mark.kind === "link") {
|
|
2519
|
+
const url = ((_a = mark.data) == null ? void 0 : _a.url) ?? "";
|
|
2520
|
+
content = `<a href="${escapeHtml(url)}">${content}</a>`;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
html += content;
|
|
2524
|
+
}
|
|
2525
|
+
return html;
|
|
2526
|
+
}
|
|
2527
|
+
function serializeSelectionToHtml(state, selection) {
|
|
2528
|
+
const normalized = normalizeSelection$1(selection);
|
|
2529
|
+
const lines = flattenDocToLines(state.doc);
|
|
2530
|
+
const docCursorLength = cursorLengthForLines(lines);
|
|
2531
|
+
const cursorStart = Math.max(
|
|
2532
|
+
0,
|
|
2533
|
+
Math.min(docCursorLength, Math.min(normalized.start, normalized.end))
|
|
2534
|
+
);
|
|
2535
|
+
const cursorEnd = Math.max(
|
|
2536
|
+
0,
|
|
2537
|
+
Math.min(docCursorLength, Math.max(normalized.start, normalized.end))
|
|
2538
|
+
);
|
|
2539
|
+
if (cursorStart === cursorEnd) {
|
|
2540
|
+
return "";
|
|
2541
|
+
}
|
|
2542
|
+
const startLoc = resolveCursorToLine(lines, cursorStart);
|
|
2543
|
+
const endLoc = resolveCursorToLine(lines, cursorEnd);
|
|
2544
|
+
let html = "";
|
|
2545
|
+
let activeList = null;
|
|
2546
|
+
const closeList = () => {
|
|
2547
|
+
if (activeList) {
|
|
2548
|
+
html += `</${activeList.type}>`;
|
|
2549
|
+
activeList = null;
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
const openList = (type, indent) => {
|
|
2553
|
+
if (activeList && activeList.type === type && activeList.indent === indent) {
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
closeList();
|
|
2557
|
+
html += `<${type}>`;
|
|
2558
|
+
activeList = { type, indent };
|
|
2559
|
+
};
|
|
2560
|
+
for (let lineIndex = startLoc.lineIndex; lineIndex <= endLoc.lineIndex; lineIndex += 1) {
|
|
2561
|
+
const line = lines[lineIndex];
|
|
2562
|
+
if (!line) {
|
|
2563
|
+
continue;
|
|
2564
|
+
}
|
|
2565
|
+
const block = getBlockAtPath(state.doc.blocks, line.path);
|
|
2566
|
+
if (!block || block.type !== "paragraph") {
|
|
2567
|
+
continue;
|
|
2568
|
+
}
|
|
2569
|
+
const runs = paragraphToRuns(block);
|
|
2570
|
+
const startInLine = lineIndex === startLoc.lineIndex ? startLoc.offsetInLine : 0;
|
|
2571
|
+
const endInLine = lineIndex === endLoc.lineIndex ? endLoc.offsetInLine : line.cursorLength;
|
|
2572
|
+
const selectedRuns = sliceRuns(runs, startInLine, endInLine).selected;
|
|
2573
|
+
let wrapperKind = null;
|
|
2574
|
+
let wrapperData;
|
|
2575
|
+
if (line.path.length > 1) {
|
|
2576
|
+
const wrapperPath = line.path.slice(0, -1);
|
|
2577
|
+
const wrapper = getBlockAtPath(state.doc.blocks, wrapperPath);
|
|
2578
|
+
if (wrapper && wrapper.type === "block-wrapper") {
|
|
2579
|
+
wrapperKind = wrapper.kind;
|
|
2580
|
+
wrapperData = wrapper.data;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
const plainText = runs.map((r) => r.text).join("");
|
|
2584
|
+
const listMatch = plainText.match(/^(\s*)([-*+]|\d+\.)( )(.*)$/);
|
|
2585
|
+
let lineHtml;
|
|
2586
|
+
if (listMatch && !wrapperKind) {
|
|
2587
|
+
const prefixLength = listMatch[1].length + listMatch[2].length + listMatch[3].length;
|
|
2588
|
+
const contentRuns = sliceRuns(runs, prefixLength, runs.reduce((sum, r) => sum + r.text.length, 0)).selected;
|
|
2589
|
+
lineHtml = runsToHtml(normalizeRuns(contentRuns));
|
|
2590
|
+
} else {
|
|
2591
|
+
lineHtml = runsToHtml(normalizeRuns(selectedRuns));
|
|
2592
|
+
}
|
|
2593
|
+
if (wrapperKind === "heading") {
|
|
2594
|
+
closeList();
|
|
2595
|
+
const level = Math.min(
|
|
2596
|
+
(wrapperData == null ? void 0 : wrapperData.level) ?? 1,
|
|
2597
|
+
6
|
|
2598
|
+
);
|
|
2599
|
+
html += `<h${level} style="margin:0">${lineHtml}</h${level}>`;
|
|
2600
|
+
} else if (wrapperKind === "bullet-list") {
|
|
2601
|
+
openList("ul", 0);
|
|
2602
|
+
html += `<li>${lineHtml}</li>`;
|
|
2603
|
+
} else if (wrapperKind === "numbered-list") {
|
|
2604
|
+
openList("ol", 0);
|
|
2605
|
+
html += `<li>${lineHtml}</li>`;
|
|
2606
|
+
} else if (wrapperKind === "blockquote") {
|
|
2607
|
+
closeList();
|
|
2608
|
+
html += `<blockquote>${lineHtml}</blockquote>`;
|
|
2609
|
+
} else if (listMatch) {
|
|
2610
|
+
const isNumbered = /^\d+\.$/.test(listMatch[2]);
|
|
2611
|
+
const indent = Math.floor(listMatch[1].length / 2);
|
|
2612
|
+
openList(isNumbered ? "ol" : "ul", indent);
|
|
2613
|
+
html += `<li>${lineHtml}</li>`;
|
|
2614
|
+
} else {
|
|
2615
|
+
closeList();
|
|
2616
|
+
html += `<div>${lineHtml}</div>`;
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
closeList();
|
|
2620
|
+
if (!html) {
|
|
2621
|
+
return "";
|
|
2622
|
+
}
|
|
2623
|
+
return `<div>${html}</div>`;
|
|
2624
|
+
}
|
|
2419
2625
|
const runtime = {
|
|
2420
2626
|
extensions,
|
|
2421
2627
|
parse,
|
|
@@ -2423,6 +2629,7 @@ function createRuntime(extensions) {
|
|
|
2423
2629
|
createState,
|
|
2424
2630
|
updateSelection,
|
|
2425
2631
|
serializeSelection,
|
|
2632
|
+
serializeSelectionToHtml,
|
|
2426
2633
|
applyEdit
|
|
2427
2634
|
};
|
|
2428
2635
|
return runtime;
|
|
@@ -2436,7 +2643,20 @@ function parseLiteralBlock(source, start, context) {
|
|
|
2436
2643
|
return { block: { type: "paragraph", content }, nextPos: end };
|
|
2437
2644
|
}
|
|
2438
2645
|
function parseLiteralInline(source, start, end) {
|
|
2439
|
-
const
|
|
2646
|
+
const code = source.charCodeAt(start);
|
|
2647
|
+
if (code < 128) {
|
|
2648
|
+
const text2 = source[start] ?? "";
|
|
2649
|
+
return { inline: { type: "text", text: text2 }, nextPos: start + 1 };
|
|
2650
|
+
}
|
|
2651
|
+
if (code >= 55296 && code <= 56319) {
|
|
2652
|
+
const lowCode = source.charCodeAt(start + 1);
|
|
2653
|
+
if (lowCode >= 56320 && lowCode <= 57343) {
|
|
2654
|
+
const segment2 = graphemeSegments(source.slice(start, Math.min(start + 10, end)))[0];
|
|
2655
|
+
const text2 = segment2 ? segment2.segment : source.slice(start, start + 2);
|
|
2656
|
+
return { inline: { type: "text", text: text2 }, nextPos: start + text2.length };
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
const segment = graphemeSegments(source.slice(start, Math.min(start + 10, end)))[0];
|
|
2440
2660
|
const text = segment ? segment.segment : source[start] ?? "";
|
|
2441
2661
|
return { inline: { type: "text", text }, nextPos: start + text.length };
|
|
2442
2662
|
}
|
|
@@ -2479,29 +2699,66 @@ function normalizeSelection$1(selection) {
|
|
|
2479
2699
|
affinity: isRange ? "backward" : selection.affinity
|
|
2480
2700
|
};
|
|
2481
2701
|
}
|
|
2702
|
+
const graphemeCache = /* @__PURE__ */ new WeakMap();
|
|
2482
2703
|
function createTextRun(node, cursorStart) {
|
|
2483
|
-
const
|
|
2704
|
+
const data = node.data;
|
|
2705
|
+
const cached = graphemeCache.get(node);
|
|
2706
|
+
if (cached && cached.data === data) {
|
|
2707
|
+
const segmentCount = Math.max(0, cached.boundaryOffsets.length - 1);
|
|
2708
|
+
return {
|
|
2709
|
+
node,
|
|
2710
|
+
cursorStart,
|
|
2711
|
+
cursorEnd: cursorStart + segmentCount,
|
|
2712
|
+
boundaryOffsets: cached.boundaryOffsets
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
const segments = graphemeSegments(data);
|
|
2484
2716
|
const boundaryOffsets = [0];
|
|
2485
2717
|
for (const segment of segments) {
|
|
2486
2718
|
boundaryOffsets.push(segment.index + segment.segment.length);
|
|
2487
2719
|
}
|
|
2488
2720
|
const cursorEnd = cursorStart + segments.length;
|
|
2721
|
+
graphemeCache.set(node, { data, boundaryOffsets });
|
|
2489
2722
|
return { node, cursorStart, cursorEnd, boundaryOffsets };
|
|
2490
2723
|
}
|
|
2491
2724
|
function boundaryIndexForOffset(boundaryOffsets, offset) {
|
|
2492
2725
|
if (offset <= 0) {
|
|
2493
2726
|
return 0;
|
|
2494
2727
|
}
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2728
|
+
const lastIndex = boundaryOffsets.length - 1;
|
|
2729
|
+
if (lastIndex <= 0) {
|
|
2730
|
+
return 0;
|
|
2731
|
+
}
|
|
2732
|
+
const lastBoundary = boundaryOffsets[lastIndex];
|
|
2733
|
+
if (offset >= lastBoundary) {
|
|
2734
|
+
return lastIndex;
|
|
2735
|
+
}
|
|
2736
|
+
let low = 1;
|
|
2737
|
+
let high = lastIndex;
|
|
2738
|
+
while (low < high) {
|
|
2739
|
+
const mid = low + high >>> 1;
|
|
2740
|
+
const boundary2 = boundaryOffsets[mid];
|
|
2741
|
+
if (boundary2 < offset) {
|
|
2742
|
+
low = mid + 1;
|
|
2743
|
+
} else {
|
|
2744
|
+
high = mid;
|
|
2499
2745
|
}
|
|
2500
|
-
|
|
2501
|
-
|
|
2746
|
+
}
|
|
2747
|
+
const boundary = boundaryOffsets[low];
|
|
2748
|
+
return boundary === offset ? low : low - 1;
|
|
2749
|
+
}
|
|
2750
|
+
function firstRunStartingAfterCursor(runs, cursorOffset) {
|
|
2751
|
+
let low = 0;
|
|
2752
|
+
let high = runs.length;
|
|
2753
|
+
while (low < high) {
|
|
2754
|
+
const mid = low + high >>> 1;
|
|
2755
|
+
if (runs[mid].cursorStart <= cursorOffset) {
|
|
2756
|
+
low = mid + 1;
|
|
2757
|
+
} else {
|
|
2758
|
+
high = mid;
|
|
2502
2759
|
}
|
|
2503
2760
|
}
|
|
2504
|
-
return
|
|
2761
|
+
return low;
|
|
2505
2762
|
}
|
|
2506
2763
|
function createDomMap(runs) {
|
|
2507
2764
|
const runForNode = /* @__PURE__ */ new Map();
|
|
@@ -2516,33 +2773,34 @@ function createDomMap(runs) {
|
|
|
2516
2773
|
if (runs.length === 0) {
|
|
2517
2774
|
return null;
|
|
2518
2775
|
}
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
const
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
if (cursorOffset === run.cursorEnd && affinity === "forward" && next && next.cursorStart === cursorOffset) {
|
|
2538
|
-
return { node: next.node, offset: next.boundaryOffsets[0] };
|
|
2539
|
-
}
|
|
2540
|
-
const index = Math.max(0, cursorOffset - run.cursorStart);
|
|
2541
|
-
const boundedIndex = Math.min(index, run.boundaryOffsets.length - 1);
|
|
2542
|
-
return { node: run.node, offset: run.boundaryOffsets[boundedIndex] };
|
|
2776
|
+
const nextIndex = firstRunStartingAfterCursor(runs, cursorOffset);
|
|
2777
|
+
const runIndex = nextIndex - 1;
|
|
2778
|
+
if (runIndex < 0) {
|
|
2779
|
+
const first = runs[0];
|
|
2780
|
+
return { node: first.node, offset: first.boundaryOffsets[0] };
|
|
2781
|
+
}
|
|
2782
|
+
const run = runs[runIndex];
|
|
2783
|
+
const previous = runIndex > 0 ? runs[runIndex - 1] : null;
|
|
2784
|
+
const next = nextIndex < runs.length ? runs[nextIndex] : null;
|
|
2785
|
+
if (cursorOffset === run.cursorStart && previous && affinity === "backward") {
|
|
2786
|
+
return {
|
|
2787
|
+
node: previous.node,
|
|
2788
|
+
offset: previous.boundaryOffsets[previous.boundaryOffsets.length - 1]
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2791
|
+
if (cursorOffset <= run.cursorEnd) {
|
|
2792
|
+
if (cursorOffset === run.cursorEnd && affinity === "forward" && next && next.cursorStart === cursorOffset) {
|
|
2793
|
+
return { node: next.node, offset: next.boundaryOffsets[0] };
|
|
2543
2794
|
}
|
|
2795
|
+
const index = Math.max(0, cursorOffset - run.cursorStart);
|
|
2796
|
+
const boundedIndex = Math.min(index, run.boundaryOffsets.length - 1);
|
|
2797
|
+
return { node: run.node, offset: run.boundaryOffsets[boundedIndex] };
|
|
2544
2798
|
}
|
|
2545
|
-
|
|
2799
|
+
if (next) {
|
|
2800
|
+
const runEndOffset = run.boundaryOffsets[run.boundaryOffsets.length - 1];
|
|
2801
|
+
return affinity === "backward" ? { node: run.node, offset: runEndOffset } : { node: next.node, offset: next.boundaryOffsets[0] };
|
|
2802
|
+
}
|
|
2803
|
+
const last = run;
|
|
2546
2804
|
return {
|
|
2547
2805
|
node: last.node,
|
|
2548
2806
|
offset: last.boundaryOffsets[last.boundaryOffsets.length - 1]
|
|
@@ -2577,7 +2835,7 @@ function normalizeNodes(result) {
|
|
|
2577
2835
|
}
|
|
2578
2836
|
return Array.isArray(result) ? result : [result];
|
|
2579
2837
|
}
|
|
2580
|
-
function renderDocContent(doc, extensions,
|
|
2838
|
+
function renderDocContent(doc, extensions, root) {
|
|
2581
2839
|
const runs = [];
|
|
2582
2840
|
let cursorOffset = 0;
|
|
2583
2841
|
let lineIndex = 0;
|
|
@@ -2597,7 +2855,60 @@ function renderDocContent(doc, extensions, _root) {
|
|
|
2597
2855
|
lineIndex += 1;
|
|
2598
2856
|
}
|
|
2599
2857
|
};
|
|
2600
|
-
function
|
|
2858
|
+
function getBlockKey(block) {
|
|
2859
|
+
if (block.type === "paragraph") {
|
|
2860
|
+
return "paragraph";
|
|
2861
|
+
}
|
|
2862
|
+
if (block.type === "block-wrapper") {
|
|
2863
|
+
return `block-wrapper:${block.kind}`;
|
|
2864
|
+
}
|
|
2865
|
+
if (block.type === "block-atom") {
|
|
2866
|
+
return `block-atom:${block.kind}`;
|
|
2867
|
+
}
|
|
2868
|
+
return "unknown";
|
|
2869
|
+
}
|
|
2870
|
+
function getElementKey(element) {
|
|
2871
|
+
if (element.hasAttribute("data-block")) {
|
|
2872
|
+
const blockType = element.getAttribute("data-block") ?? "unknown";
|
|
2873
|
+
const lineKind = element instanceof HTMLElement ? element.dataset.lineKind : null;
|
|
2874
|
+
if (lineKind && lineKind !== blockType) {
|
|
2875
|
+
return lineKind;
|
|
2876
|
+
}
|
|
2877
|
+
return blockType;
|
|
2878
|
+
}
|
|
2879
|
+
if (element.hasAttribute("data-block-wrapper")) {
|
|
2880
|
+
return `block-wrapper:${element.getAttribute("data-block-wrapper")}`;
|
|
2881
|
+
}
|
|
2882
|
+
if (element.hasAttribute("data-block-atom")) {
|
|
2883
|
+
return `block-atom:${element.getAttribute("data-block-atom")}`;
|
|
2884
|
+
}
|
|
2885
|
+
return "unknown";
|
|
2886
|
+
}
|
|
2887
|
+
function getInlineKey(inline) {
|
|
2888
|
+
if (inline.type === "text") {
|
|
2889
|
+
return "text";
|
|
2890
|
+
}
|
|
2891
|
+
if (inline.type === "inline-wrapper") {
|
|
2892
|
+
return `inline-wrapper:${inline.kind}`;
|
|
2893
|
+
}
|
|
2894
|
+
if (inline.type === "inline-atom") {
|
|
2895
|
+
return `inline-atom:${inline.kind}`;
|
|
2896
|
+
}
|
|
2897
|
+
return "unknown";
|
|
2898
|
+
}
|
|
2899
|
+
function getInlineElementKey(element) {
|
|
2900
|
+
if (element.classList.contains("cake-text")) {
|
|
2901
|
+
return "text";
|
|
2902
|
+
}
|
|
2903
|
+
if (element.hasAttribute("data-inline")) {
|
|
2904
|
+
return `inline-wrapper:${element.getAttribute("data-inline")}`;
|
|
2905
|
+
}
|
|
2906
|
+
if (element.hasAttribute("data-inline-atom")) {
|
|
2907
|
+
return `inline-atom:${element.getAttribute("data-inline-atom")}`;
|
|
2908
|
+
}
|
|
2909
|
+
return "unknown";
|
|
2910
|
+
}
|
|
2911
|
+
function reconcileInline(inline, existing) {
|
|
2601
2912
|
for (const extension of extensions) {
|
|
2602
2913
|
const render = extension.renderInline;
|
|
2603
2914
|
if (!render) {
|
|
@@ -2609,6 +2920,17 @@ function renderDocContent(doc, extensions, _root) {
|
|
|
2609
2920
|
}
|
|
2610
2921
|
}
|
|
2611
2922
|
if (inline.type === "text") {
|
|
2923
|
+
const canReuse = existing && existing instanceof HTMLSpanElement && getInlineElementKey(existing) === "text";
|
|
2924
|
+
if (canReuse) {
|
|
2925
|
+
const textNode = existing.firstChild;
|
|
2926
|
+
if (textNode instanceof Text) {
|
|
2927
|
+
if (textNode.textContent !== inline.text) {
|
|
2928
|
+
textNode.textContent = inline.text;
|
|
2929
|
+
}
|
|
2930
|
+
createTextRun$1(textNode);
|
|
2931
|
+
return [existing];
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2612
2934
|
const element = document.createElement("span");
|
|
2613
2935
|
element.className = "cake-text";
|
|
2614
2936
|
const node = document.createTextNode(inline.text);
|
|
@@ -2617,16 +2939,29 @@ function renderDocContent(doc, extensions, _root) {
|
|
|
2617
2939
|
return [element];
|
|
2618
2940
|
}
|
|
2619
2941
|
if (inline.type === "inline-wrapper") {
|
|
2942
|
+
const canReuse = existing && existing instanceof HTMLSpanElement && getInlineElementKey(existing) === getInlineKey(inline);
|
|
2943
|
+
if (canReuse) {
|
|
2944
|
+
reconcileInlineChildren(existing, inline.children);
|
|
2945
|
+
return [existing];
|
|
2946
|
+
}
|
|
2620
2947
|
const element = document.createElement("span");
|
|
2621
2948
|
element.setAttribute("data-inline", inline.kind);
|
|
2622
2949
|
for (const child of inline.children) {
|
|
2623
|
-
for (const node of
|
|
2950
|
+
for (const node of reconcileInline(child, null)) {
|
|
2624
2951
|
element.append(node);
|
|
2625
2952
|
}
|
|
2626
2953
|
}
|
|
2627
2954
|
return [element];
|
|
2628
2955
|
}
|
|
2629
2956
|
if (inline.type === "inline-atom") {
|
|
2957
|
+
const canReuse = existing && existing instanceof HTMLSpanElement && getInlineElementKey(existing) === getInlineKey(inline);
|
|
2958
|
+
if (canReuse) {
|
|
2959
|
+
const textNode = existing.firstChild;
|
|
2960
|
+
if (textNode instanceof Text) {
|
|
2961
|
+
createTextRun$1(textNode);
|
|
2962
|
+
return [existing];
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2630
2965
|
const element = document.createElement("span");
|
|
2631
2966
|
element.setAttribute("data-inline-atom", inline.kind);
|
|
2632
2967
|
const node = document.createTextNode(" ");
|
|
@@ -2636,7 +2971,25 @@ function renderDocContent(doc, extensions, _root) {
|
|
|
2636
2971
|
}
|
|
2637
2972
|
return [];
|
|
2638
2973
|
}
|
|
2639
|
-
function
|
|
2974
|
+
function reconcileInlineChildren(parent, inlines) {
|
|
2975
|
+
const mergedInlines = mergeInlineForRender(inlines);
|
|
2976
|
+
const existingChildren2 = Array.from(parent.children);
|
|
2977
|
+
const newChildren = [];
|
|
2978
|
+
mergedInlines.forEach((inline, i) => {
|
|
2979
|
+
const existingChild = existingChildren2[i] ?? null;
|
|
2980
|
+
const canReuse = existingChild && getInlineElementKey(existingChild) === getInlineKey(inline);
|
|
2981
|
+
const nodes = reconcileInline(inline, canReuse ? existingChild : null);
|
|
2982
|
+
newChildren.push(...nodes);
|
|
2983
|
+
});
|
|
2984
|
+
if (newChildren.length === existingChildren2.length && newChildren.every((node, i) => node === existingChildren2[i])) {
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2987
|
+
parent.replaceChildren(...newChildren);
|
|
2988
|
+
}
|
|
2989
|
+
function renderInline(inline) {
|
|
2990
|
+
return reconcileInline(inline, null);
|
|
2991
|
+
}
|
|
2992
|
+
function reconcileBlock(block, existing) {
|
|
2640
2993
|
for (const extension of extensions) {
|
|
2641
2994
|
const render = extension.renderBlock;
|
|
2642
2995
|
if (!render) {
|
|
@@ -2648,12 +3001,35 @@ function renderDocContent(doc, extensions, _root) {
|
|
|
2648
3001
|
}
|
|
2649
3002
|
}
|
|
2650
3003
|
if (block.type === "paragraph") {
|
|
3004
|
+
const canReuse = existing && existing instanceof HTMLDivElement && getElementKey(existing) === "paragraph";
|
|
3005
|
+
const currentLineIndex = context.getLineIndex();
|
|
3006
|
+
context.incrementLineIndex();
|
|
3007
|
+
if (canReuse) {
|
|
3008
|
+
existing.setAttribute("data-line-index", String(currentLineIndex));
|
|
3009
|
+
if (block.content.length === 0) {
|
|
3010
|
+
const firstChild = existing.firstChild;
|
|
3011
|
+
if (firstChild instanceof Text && existing.querySelector("br")) {
|
|
3012
|
+
if (firstChild.textContent !== "") {
|
|
3013
|
+
firstChild.textContent = "";
|
|
3014
|
+
}
|
|
3015
|
+
createTextRun$1(firstChild);
|
|
3016
|
+
return [existing];
|
|
3017
|
+
}
|
|
3018
|
+
existing.replaceChildren();
|
|
3019
|
+
const textNode = document.createTextNode("");
|
|
3020
|
+
createTextRun$1(textNode);
|
|
3021
|
+
existing.append(textNode);
|
|
3022
|
+
existing.append(document.createElement("br"));
|
|
3023
|
+
return [existing];
|
|
3024
|
+
}
|
|
3025
|
+
reconcileInlineChildren(existing, block.content);
|
|
3026
|
+
return [existing];
|
|
3027
|
+
}
|
|
2651
3028
|
const element = document.createElement("div");
|
|
2652
3029
|
element.setAttribute("data-block", "paragraph");
|
|
2653
|
-
element.setAttribute("data-line-index", String(
|
|
3030
|
+
element.setAttribute("data-line-index", String(currentLineIndex));
|
|
2654
3031
|
element.classList.add("cake-line");
|
|
2655
3032
|
element.dataset.lineKind = "paragraph";
|
|
2656
|
-
context.incrementLineIndex();
|
|
2657
3033
|
if (block.content.length === 0) {
|
|
2658
3034
|
const textNode = document.createTextNode("");
|
|
2659
3035
|
createTextRun$1(textNode);
|
|
@@ -2662,7 +3038,7 @@ function renderDocContent(doc, extensions, _root) {
|
|
|
2662
3038
|
} else {
|
|
2663
3039
|
const mergedContent = mergeInlineForRender(block.content);
|
|
2664
3040
|
for (const inline of mergedContent) {
|
|
2665
|
-
for (const node of
|
|
3041
|
+
for (const node of reconcileInline(inline, null)) {
|
|
2666
3042
|
element.append(node);
|
|
2667
3043
|
}
|
|
2668
3044
|
}
|
|
@@ -2670,6 +3046,11 @@ function renderDocContent(doc, extensions, _root) {
|
|
|
2670
3046
|
return [element];
|
|
2671
3047
|
}
|
|
2672
3048
|
if (block.type === "block-wrapper") {
|
|
3049
|
+
const canReuse = existing && existing instanceof HTMLDivElement && getElementKey(existing) === getBlockKey(block);
|
|
3050
|
+
if (canReuse) {
|
|
3051
|
+
reconcileBlockChildren(existing, block.blocks);
|
|
3052
|
+
return [existing];
|
|
3053
|
+
}
|
|
2673
3054
|
const element = document.createElement("div");
|
|
2674
3055
|
element.setAttribute("data-block-wrapper", block.kind);
|
|
2675
3056
|
for (const node of renderBlocks(block.blocks)) {
|
|
@@ -2678,15 +3059,41 @@ function renderDocContent(doc, extensions, _root) {
|
|
|
2678
3059
|
return [element];
|
|
2679
3060
|
}
|
|
2680
3061
|
if (block.type === "block-atom") {
|
|
3062
|
+
const canReuse = existing && existing instanceof HTMLDivElement && getElementKey(existing) === getBlockKey(block);
|
|
3063
|
+
const currentLineIndex = context.getLineIndex();
|
|
3064
|
+
context.incrementLineIndex();
|
|
3065
|
+
if (canReuse) {
|
|
3066
|
+
existing.setAttribute("data-line-index", String(currentLineIndex));
|
|
3067
|
+
return [existing];
|
|
3068
|
+
}
|
|
2681
3069
|
const element = document.createElement("div");
|
|
2682
3070
|
element.setAttribute("data-block-atom", block.kind);
|
|
2683
|
-
element.setAttribute("data-line-index", String(
|
|
3071
|
+
element.setAttribute("data-line-index", String(currentLineIndex));
|
|
2684
3072
|
element.classList.add("cake-line");
|
|
2685
|
-
context.incrementLineIndex();
|
|
2686
3073
|
return [element];
|
|
2687
3074
|
}
|
|
2688
3075
|
return [];
|
|
2689
3076
|
}
|
|
3077
|
+
function reconcileBlockChildren(parent, blocks) {
|
|
3078
|
+
const existingChildren2 = Array.from(parent.children);
|
|
3079
|
+
const newChildren = [];
|
|
3080
|
+
blocks.forEach((block, index) => {
|
|
3081
|
+
const existingChild = existingChildren2[index] ?? null;
|
|
3082
|
+
const canReuse = existingChild && getElementKey(existingChild) === getBlockKey(block);
|
|
3083
|
+
const nodes = reconcileBlock(block, canReuse ? existingChild : null);
|
|
3084
|
+
newChildren.push(...nodes);
|
|
3085
|
+
if (index < blocks.length - 1) {
|
|
3086
|
+
cursorOffset += 1;
|
|
3087
|
+
}
|
|
3088
|
+
});
|
|
3089
|
+
if (newChildren.length === existingChildren2.length && newChildren.every((node, i) => node === existingChildren2[i])) {
|
|
3090
|
+
return;
|
|
3091
|
+
}
|
|
3092
|
+
parent.replaceChildren(...newChildren);
|
|
3093
|
+
}
|
|
3094
|
+
function renderBlock(block) {
|
|
3095
|
+
return reconcileBlock(block, null);
|
|
3096
|
+
}
|
|
2690
3097
|
function renderBlocks(blocks) {
|
|
2691
3098
|
const nodes = [];
|
|
2692
3099
|
blocks.forEach((block, index) => {
|
|
@@ -2697,10 +3104,17 @@ function renderDocContent(doc, extensions, _root) {
|
|
|
2697
3104
|
});
|
|
2698
3105
|
return nodes;
|
|
2699
3106
|
}
|
|
3107
|
+
const existingChildren = root ? Array.from(root.children) : [];
|
|
2700
3108
|
const contentNodes = [];
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
3109
|
+
doc.blocks.forEach((block, index) => {
|
|
3110
|
+
const existingChild = existingChildren[index] ?? null;
|
|
3111
|
+
const canReuse = existingChild && getElementKey(existingChild) === getBlockKey(block);
|
|
3112
|
+
const nodes = reconcileBlock(block, canReuse ? existingChild : null);
|
|
3113
|
+
contentNodes.push(...nodes);
|
|
3114
|
+
if (index < doc.blocks.length - 1) {
|
|
3115
|
+
cursorOffset += 1;
|
|
3116
|
+
}
|
|
3117
|
+
});
|
|
2704
3118
|
return { content: contentNodes, map: createDomMap(runs) };
|
|
2705
3119
|
}
|
|
2706
3120
|
function mergeInlineForRender(inlines) {
|
|
@@ -2774,7 +3188,9 @@ function applyDomSelection(selection, map) {
|
|
|
2774
3188
|
if (!anchorPoint || !focusPoint) {
|
|
2775
3189
|
return;
|
|
2776
3190
|
}
|
|
2777
|
-
domSelection.
|
|
3191
|
+
if (domSelection.rangeCount > 0 && domSelection.anchorNode === anchorPoint.node && domSelection.anchorOffset === anchorPoint.offset && domSelection.focusNode === focusPoint.node && domSelection.focusOffset === focusPoint.offset) {
|
|
3192
|
+
return;
|
|
3193
|
+
}
|
|
2778
3194
|
if (isCollapsed) {
|
|
2779
3195
|
domSelection.collapse(anchorPoint.node, anchorPoint.offset);
|
|
2780
3196
|
return;
|
|
@@ -2794,6 +3210,7 @@ function applyDomSelection(selection, map) {
|
|
|
2794
3210
|
domSelection.extend(focusPoint.node, focusPoint.offset);
|
|
2795
3211
|
return;
|
|
2796
3212
|
}
|
|
3213
|
+
domSelection.removeAllRanges();
|
|
2797
3214
|
const range = document.createRange();
|
|
2798
3215
|
const rangeStart = isForward ? anchorPoint : focusPoint;
|
|
2799
3216
|
const rangeEnd = isForward ? focusPoint : anchorPoint;
|
|
@@ -2937,7 +3354,7 @@ function findTextNodeAtOrBefore$1(nodes, start) {
|
|
|
2937
3354
|
return null;
|
|
2938
3355
|
}
|
|
2939
3356
|
const BOLD_KIND$1 = "bold";
|
|
2940
|
-
const boldExtension = {
|
|
3357
|
+
const boldExtension = defineExtension({
|
|
2941
3358
|
name: "bold",
|
|
2942
3359
|
toggleInline: { kind: BOLD_KIND$1, markers: ["**"] },
|
|
2943
3360
|
keybindings: [
|
|
@@ -3020,7 +3437,7 @@ const boldExtension = {
|
|
|
3020
3437
|
}
|
|
3021
3438
|
return element;
|
|
3022
3439
|
}
|
|
3023
|
-
};
|
|
3440
|
+
});
|
|
3024
3441
|
function countSingleAsterisks(source, start, end) {
|
|
3025
3442
|
let count = 0;
|
|
3026
3443
|
for (let i = start; i < end; i += 1) {
|
|
@@ -3039,7 +3456,7 @@ function countSingleAsterisks(source, start, end) {
|
|
|
3039
3456
|
const BOLD_KIND = "bold";
|
|
3040
3457
|
const ITALIC_KIND$1 = "italic";
|
|
3041
3458
|
const MARKERS = ["***", "___"];
|
|
3042
|
-
const combinedEmphasisExtension = {
|
|
3459
|
+
const combinedEmphasisExtension = defineExtension({
|
|
3043
3460
|
name: "combined-emphasis",
|
|
3044
3461
|
parseInline(source, start, end, context) {
|
|
3045
3462
|
const marker = MARKERS.find((m) => source.slice(start, start + 3) === m);
|
|
@@ -3073,7 +3490,7 @@ const combinedEmphasisExtension = {
|
|
|
3073
3490
|
nextPos: close + 3
|
|
3074
3491
|
};
|
|
3075
3492
|
}
|
|
3076
|
-
};
|
|
3493
|
+
});
|
|
3077
3494
|
function ensureHttpsProtocol(url) {
|
|
3078
3495
|
const trimmedUrl = url.trim();
|
|
3079
3496
|
if (!trimmedUrl) {
|
|
@@ -3121,7 +3538,7 @@ function getPopoverPosition(params) {
|
|
|
3121
3538
|
};
|
|
3122
3539
|
}
|
|
3123
3540
|
function CakeLinkPopover(params) {
|
|
3124
|
-
const { container, contentRoot, toOverlayRect } = params;
|
|
3541
|
+
const { container, contentRoot, toOverlayRect, getSelection, executeCommand } = params;
|
|
3125
3542
|
const anchorRef = useRef(null);
|
|
3126
3543
|
const popoverRef = useRef(null);
|
|
3127
3544
|
const inputRef = useRef(null);
|
|
@@ -3222,13 +3639,13 @@ function CakeLinkPopover(params) {
|
|
|
3222
3639
|
if (state.status !== "open") {
|
|
3223
3640
|
return;
|
|
3224
3641
|
}
|
|
3225
|
-
container.addEventListener("scroll",
|
|
3642
|
+
container.addEventListener("scroll", close, { passive: true });
|
|
3226
3643
|
window.addEventListener("resize", reposition);
|
|
3227
3644
|
return () => {
|
|
3228
|
-
container.removeEventListener("scroll",
|
|
3645
|
+
container.removeEventListener("scroll", close);
|
|
3229
3646
|
window.removeEventListener("resize", reposition);
|
|
3230
3647
|
};
|
|
3231
|
-
}, [container, reposition, state.status]);
|
|
3648
|
+
}, [close, container, reposition, state.status]);
|
|
3232
3649
|
const handleMouseDown = useCallback(
|
|
3233
3650
|
(event) => {
|
|
3234
3651
|
event.stopPropagation();
|
|
@@ -3285,6 +3702,12 @@ function CakeLinkPopover(params) {
|
|
|
3285
3702
|
window.open(displayUrl, "_blank", "noopener,noreferrer");
|
|
3286
3703
|
};
|
|
3287
3704
|
const handleUnlink = () => {
|
|
3705
|
+
const selection = getSelection();
|
|
3706
|
+
if (!selection) {
|
|
3707
|
+
close();
|
|
3708
|
+
return;
|
|
3709
|
+
}
|
|
3710
|
+
executeCommand({ type: "unlink", start: selection.start, end: selection.end });
|
|
3288
3711
|
close();
|
|
3289
3712
|
};
|
|
3290
3713
|
const handleInputKeyDown = (event) => {
|
|
@@ -3297,7 +3720,7 @@ function CakeLinkPopover(params) {
|
|
|
3297
3720
|
handleCancel();
|
|
3298
3721
|
}
|
|
3299
3722
|
};
|
|
3300
|
-
return /* @__PURE__ */
|
|
3723
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3301
3724
|
"div",
|
|
3302
3725
|
{
|
|
3303
3726
|
className: "cake-link-popover",
|
|
@@ -3310,7 +3733,7 @@ function CakeLinkPopover(params) {
|
|
|
3310
3733
|
},
|
|
3311
3734
|
onMouseDown: handleMouseDown,
|
|
3312
3735
|
onClick: (event) => event.stopPropagation(),
|
|
3313
|
-
children: state.isEditing ? /* @__PURE__ */
|
|
3736
|
+
children: state.isEditing ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3314
3737
|
"form",
|
|
3315
3738
|
{
|
|
3316
3739
|
className: "cake-link-editor",
|
|
@@ -3319,7 +3742,7 @@ function CakeLinkPopover(params) {
|
|
|
3319
3742
|
handleSave();
|
|
3320
3743
|
},
|
|
3321
3744
|
children: [
|
|
3322
|
-
/* @__PURE__ */
|
|
3745
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3323
3746
|
"input",
|
|
3324
3747
|
{
|
|
3325
3748
|
className: "cake-link-input",
|
|
@@ -3336,56 +3759,24 @@ function CakeLinkPopover(params) {
|
|
|
3336
3759
|
},
|
|
3337
3760
|
onKeyDown: handleInputKeyDown,
|
|
3338
3761
|
placeholder: "https://"
|
|
3339
|
-
}
|
|
3340
|
-
void 0,
|
|
3341
|
-
false,
|
|
3342
|
-
{
|
|
3343
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
|
|
3344
|
-
lineNumber: 281,
|
|
3345
|
-
columnNumber: 11
|
|
3346
|
-
},
|
|
3347
|
-
this
|
|
3762
|
+
}
|
|
3348
3763
|
),
|
|
3349
|
-
/* @__PURE__ */
|
|
3350
|
-
|
|
3351
|
-
lineNumber: 297,
|
|
3352
|
-
columnNumber: 11
|
|
3353
|
-
}, this),
|
|
3354
|
-
/* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
|
|
3764
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "submit", className: "cake-link-save", children: "Save" }),
|
|
3765
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3355
3766
|
"button",
|
|
3356
3767
|
{
|
|
3357
3768
|
type: "button",
|
|
3358
3769
|
className: "cake-link-cancel",
|
|
3359
3770
|
onClick: handleCancel,
|
|
3360
3771
|
children: "Cancel"
|
|
3361
|
-
}
|
|
3362
|
-
void 0,
|
|
3363
|
-
false,
|
|
3364
|
-
{
|
|
3365
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
|
|
3366
|
-
lineNumber: 300,
|
|
3367
|
-
columnNumber: 11
|
|
3368
|
-
},
|
|
3369
|
-
this
|
|
3772
|
+
}
|
|
3370
3773
|
)
|
|
3371
3774
|
]
|
|
3372
|
-
}
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
{
|
|
3376
|
-
|
|
3377
|
-
lineNumber: 274,
|
|
3378
|
-
columnNumber: 9
|
|
3379
|
-
},
|
|
3380
|
-
this
|
|
3381
|
-
) : /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(jsxDevRuntimeExports.Fragment, { children: [
|
|
3382
|
-
/* @__PURE__ */ jsxDevRuntimeExports.jsxDEV("div", { className: "cake-link-url", title: displayUrl, children: displayUrl }, void 0, false, {
|
|
3383
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
|
|
3384
|
-
lineNumber: 310,
|
|
3385
|
-
columnNumber: 11
|
|
3386
|
-
}, this),
|
|
3387
|
-
/* @__PURE__ */ jsxDevRuntimeExports.jsxDEV("div", { className: "cake-link-actions", children: [
|
|
3388
|
-
/* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
|
|
3775
|
+
}
|
|
3776
|
+
) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
3777
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "cake-link-url", title: displayUrl, children: displayUrl }),
|
|
3778
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "cake-link-actions", children: [
|
|
3779
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3389
3780
|
"button",
|
|
3390
3781
|
{
|
|
3391
3782
|
type: "button",
|
|
@@ -3393,22 +3784,10 @@ function CakeLinkPopover(params) {
|
|
|
3393
3784
|
onClick: handleEdit,
|
|
3394
3785
|
title: "Edit link",
|
|
3395
3786
|
"aria-label": "Edit link",
|
|
3396
|
-
children: /* @__PURE__ */
|
|
3397
|
-
|
|
3398
|
-
lineNumber: 321,
|
|
3399
|
-
columnNumber: 15
|
|
3400
|
-
}, this)
|
|
3401
|
-
},
|
|
3402
|
-
void 0,
|
|
3403
|
-
false,
|
|
3404
|
-
{
|
|
3405
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
|
|
3406
|
-
lineNumber: 314,
|
|
3407
|
-
columnNumber: 13
|
|
3408
|
-
},
|
|
3409
|
-
this
|
|
3787
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "cake-link-icon" })
|
|
3788
|
+
}
|
|
3410
3789
|
),
|
|
3411
|
-
/* @__PURE__ */
|
|
3790
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3412
3791
|
"button",
|
|
3413
3792
|
{
|
|
3414
3793
|
type: "button",
|
|
@@ -3416,22 +3795,10 @@ function CakeLinkPopover(params) {
|
|
|
3416
3795
|
onClick: handleOpen,
|
|
3417
3796
|
title: "Open link",
|
|
3418
3797
|
"aria-label": "Open link",
|
|
3419
|
-
children: /* @__PURE__ */
|
|
3420
|
-
|
|
3421
|
-
lineNumber: 330,
|
|
3422
|
-
columnNumber: 15
|
|
3423
|
-
}, this)
|
|
3424
|
-
},
|
|
3425
|
-
void 0,
|
|
3426
|
-
false,
|
|
3427
|
-
{
|
|
3428
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
|
|
3429
|
-
lineNumber: 323,
|
|
3430
|
-
columnNumber: 13
|
|
3431
|
-
},
|
|
3432
|
-
this
|
|
3798
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { className: "cake-link-icon" })
|
|
3799
|
+
}
|
|
3433
3800
|
),
|
|
3434
|
-
/* @__PURE__ */
|
|
3801
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3435
3802
|
"button",
|
|
3436
3803
|
{
|
|
3437
3804
|
type: "button",
|
|
@@ -3439,40 +3806,12 @@ function CakeLinkPopover(params) {
|
|
|
3439
3806
|
onClick: handleUnlink,
|
|
3440
3807
|
title: "Remove link",
|
|
3441
3808
|
"aria-label": "Remove link",
|
|
3442
|
-
children: /* @__PURE__ */
|
|
3443
|
-
|
|
3444
|
-
lineNumber: 339,
|
|
3445
|
-
columnNumber: 15
|
|
3446
|
-
}, this)
|
|
3447
|
-
},
|
|
3448
|
-
void 0,
|
|
3449
|
-
false,
|
|
3450
|
-
{
|
|
3451
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
|
|
3452
|
-
lineNumber: 332,
|
|
3453
|
-
columnNumber: 13
|
|
3454
|
-
},
|
|
3455
|
-
this
|
|
3809
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Unlink, { className: "cake-link-icon" })
|
|
3810
|
+
}
|
|
3456
3811
|
)
|
|
3457
|
-
] }
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
columnNumber: 11
|
|
3461
|
-
}, this)
|
|
3462
|
-
] }, void 0, true, {
|
|
3463
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
|
|
3464
|
-
lineNumber: 309,
|
|
3465
|
-
columnNumber: 9
|
|
3466
|
-
}, this)
|
|
3467
|
-
},
|
|
3468
|
-
void 0,
|
|
3469
|
-
false,
|
|
3470
|
-
{
|
|
3471
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
|
|
3472
|
-
lineNumber: 261,
|
|
3473
|
-
columnNumber: 5
|
|
3474
|
-
},
|
|
3475
|
-
this
|
|
3812
|
+
] })
|
|
3813
|
+
] })
|
|
3814
|
+
}
|
|
3476
3815
|
);
|
|
3477
3816
|
}
|
|
3478
3817
|
function buildLayoutModel(lines, measurer) {
|
|
@@ -3672,7 +4011,7 @@ function cursorOffsetToVisibleOffset(lines, cursorOffset) {
|
|
|
3672
4011
|
return codeUnitIndex + (lastLine.cursorToCodeUnit[lastOffset] ?? lastLine.text.length);
|
|
3673
4012
|
}
|
|
3674
4013
|
const LINK_KIND = "link";
|
|
3675
|
-
const linkExtension = {
|
|
4014
|
+
const linkExtension = defineExtension({
|
|
3676
4015
|
name: "link",
|
|
3677
4016
|
inlineWrapperAffinity: [{ kind: LINK_KIND, inclusive: false }],
|
|
3678
4017
|
keybindings: [
|
|
@@ -3700,6 +4039,39 @@ const linkExtension = {
|
|
|
3700
4039
|
}
|
|
3701
4040
|
],
|
|
3702
4041
|
onEdit(command, state) {
|
|
4042
|
+
if (command.type === "unlink") {
|
|
4043
|
+
const cursorPos = command.start;
|
|
4044
|
+
const sourcePos = state.map.cursorToSource(cursorPos, "forward");
|
|
4045
|
+
const source = state.source;
|
|
4046
|
+
let linkStart = sourcePos;
|
|
4047
|
+
while (linkStart > 0 && source[linkStart] !== "[") {
|
|
4048
|
+
linkStart--;
|
|
4049
|
+
}
|
|
4050
|
+
if (source[linkStart] !== "[") {
|
|
4051
|
+
return null;
|
|
4052
|
+
}
|
|
4053
|
+
const labelClose = source.indexOf("](", linkStart + 1);
|
|
4054
|
+
if (labelClose === -1) {
|
|
4055
|
+
return null;
|
|
4056
|
+
}
|
|
4057
|
+
const urlClose = source.indexOf(")", labelClose + 2);
|
|
4058
|
+
if (urlClose === -1) {
|
|
4059
|
+
return null;
|
|
4060
|
+
}
|
|
4061
|
+
const label2 = source.slice(linkStart + 1, labelClose);
|
|
4062
|
+
const nextSource2 = source.slice(0, linkStart) + label2 + source.slice(urlClose + 1);
|
|
4063
|
+
const newState = state.runtime.createState(nextSource2);
|
|
4064
|
+
const labelEndSource = linkStart + label2.length;
|
|
4065
|
+
const newCursor = newState.map.sourceToCursor(labelEndSource, "forward");
|
|
4066
|
+
return {
|
|
4067
|
+
source: nextSource2,
|
|
4068
|
+
selection: {
|
|
4069
|
+
start: newCursor.cursorOffset,
|
|
4070
|
+
end: newCursor.cursorOffset,
|
|
4071
|
+
affinity: "forward"
|
|
4072
|
+
}
|
|
4073
|
+
};
|
|
4074
|
+
}
|
|
3703
4075
|
if (command.type !== "wrap-link") {
|
|
3704
4076
|
return null;
|
|
3705
4077
|
}
|
|
@@ -3821,26 +4193,20 @@ const linkExtension = {
|
|
|
3821
4193
|
if (!context.contentRoot || !context.toOverlayRect) {
|
|
3822
4194
|
return null;
|
|
3823
4195
|
}
|
|
3824
|
-
return /* @__PURE__ */
|
|
4196
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3825
4197
|
CakeLinkPopover,
|
|
3826
4198
|
{
|
|
3827
4199
|
container: context.container,
|
|
3828
4200
|
contentRoot: context.contentRoot,
|
|
3829
|
-
toOverlayRect: context.toOverlayRect
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
{
|
|
3834
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link.tsx",
|
|
3835
|
-
lineNumber: 179,
|
|
3836
|
-
columnNumber: 7
|
|
3837
|
-
},
|
|
3838
|
-
this
|
|
4201
|
+
toOverlayRect: context.toOverlayRect,
|
|
4202
|
+
getSelection: context.getSelection,
|
|
4203
|
+
executeCommand: context.executeCommand
|
|
4204
|
+
}
|
|
3839
4205
|
);
|
|
3840
4206
|
}
|
|
3841
|
-
};
|
|
4207
|
+
});
|
|
3842
4208
|
const PIPE_LINK_KIND = "pipe-link";
|
|
3843
|
-
const pipeLinkExtension = {
|
|
4209
|
+
const pipeLinkExtension = defineExtension({
|
|
3844
4210
|
name: "pipe-link",
|
|
3845
4211
|
parseInline(source, start, end) {
|
|
3846
4212
|
if (source[start] !== "|") {
|
|
@@ -3911,10 +4277,10 @@ const pipeLinkExtension = {
|
|
|
3911
4277
|
}
|
|
3912
4278
|
return element;
|
|
3913
4279
|
}
|
|
3914
|
-
};
|
|
4280
|
+
});
|
|
3915
4281
|
const BLOCKQUOTE_KIND = "blockquote";
|
|
3916
4282
|
const PREFIX = "> ";
|
|
3917
|
-
const blockquoteExtension = {
|
|
4283
|
+
const blockquoteExtension = defineExtension({
|
|
3918
4284
|
name: "blockquote",
|
|
3919
4285
|
parseBlock(source, start, context) {
|
|
3920
4286
|
if (source.slice(start, start + PREFIX.length) !== PREFIX) {
|
|
@@ -3983,9 +4349,9 @@ const blockquoteExtension = {
|
|
|
3983
4349
|
}
|
|
3984
4350
|
return element;
|
|
3985
4351
|
}
|
|
3986
|
-
};
|
|
4352
|
+
});
|
|
3987
4353
|
const ITALIC_KIND = "italic";
|
|
3988
|
-
const italicExtension = {
|
|
4354
|
+
const italicExtension = defineExtension({
|
|
3989
4355
|
name: "italic",
|
|
3990
4356
|
toggleInline: { kind: ITALIC_KIND, markers: ["*", "_"] },
|
|
3991
4357
|
keybindings: [
|
|
@@ -4055,7 +4421,7 @@ const italicExtension = {
|
|
|
4055
4421
|
}
|
|
4056
4422
|
return element;
|
|
4057
4423
|
}
|
|
4058
|
-
};
|
|
4424
|
+
});
|
|
4059
4425
|
const HEADING_KIND = "heading";
|
|
4060
4426
|
const HEADING_PATTERN = /^(#{1,3}) /;
|
|
4061
4427
|
function findLineStartInSource(source, sourceOffset) {
|
|
@@ -4156,7 +4522,7 @@ function shouldExitHeadingOnLineBreak(state) {
|
|
|
4156
4522
|
const contentStart = lineStart + marker.length;
|
|
4157
4523
|
return sourcePos >= contentStart && sourcePos <= lineEnd;
|
|
4158
4524
|
}
|
|
4159
|
-
const headingExtension = {
|
|
4525
|
+
const headingExtension = defineExtension({
|
|
4160
4526
|
name: "heading",
|
|
4161
4527
|
onEdit(command, state) {
|
|
4162
4528
|
if (command.type === "delete-backward") {
|
|
@@ -4297,7 +4663,7 @@ const headingExtension = {
|
|
|
4297
4663
|
}
|
|
4298
4664
|
return lineElement;
|
|
4299
4665
|
}
|
|
4300
|
-
};
|
|
4666
|
+
});
|
|
4301
4667
|
const IMAGE_KIND = "image";
|
|
4302
4668
|
const IMAGE_PATTERN = /^!\[([^\]]*)\]\(([^)]*)\)$/;
|
|
4303
4669
|
const UPLOADING_PATTERN = /^!\[uploading:([^\]]+)\]\(\)$/;
|
|
@@ -4315,7 +4681,7 @@ function isImageData(data) {
|
|
|
4315
4681
|
}
|
|
4316
4682
|
return false;
|
|
4317
4683
|
}
|
|
4318
|
-
const imageExtension = {
|
|
4684
|
+
const imageExtension = defineExtension({
|
|
4319
4685
|
name: "image",
|
|
4320
4686
|
parseBlock(source, start) {
|
|
4321
4687
|
let lineEnd = source.indexOf("\n", start);
|
|
@@ -4410,7 +4776,7 @@ const imageExtension = {
|
|
|
4410
4776
|
}
|
|
4411
4777
|
return element;
|
|
4412
4778
|
}
|
|
4413
|
-
};
|
|
4779
|
+
});
|
|
4414
4780
|
const LIST_LINE_REGEX$2 = /^(\s*)([-*+]|\d+\.)( )(.*)$/;
|
|
4415
4781
|
const INDENT_SIZE = 2;
|
|
4416
4782
|
function parseMarkerType(marker) {
|
|
@@ -5229,7 +5595,7 @@ function getParagraphText(block) {
|
|
|
5229
5595
|
}
|
|
5230
5596
|
return text;
|
|
5231
5597
|
}
|
|
5232
|
-
const listExtension = {
|
|
5598
|
+
const listExtension = defineExtension({
|
|
5233
5599
|
name: "list",
|
|
5234
5600
|
keybindings: [
|
|
5235
5601
|
{
|
|
@@ -5322,7 +5688,7 @@ const listExtension = {
|
|
|
5322
5688
|
}
|
|
5323
5689
|
return element;
|
|
5324
5690
|
}
|
|
5325
|
-
};
|
|
5691
|
+
});
|
|
5326
5692
|
const THUMB_MIN_HEIGHT = 30;
|
|
5327
5693
|
const SCROLL_HIDE_DELAY = 500;
|
|
5328
5694
|
const TRACK_PADDING = 8;
|
|
@@ -5335,6 +5701,7 @@ function ScrollbarOverlay({ container }) {
|
|
|
5335
5701
|
const [isDragging, setIsDragging] = useState(false);
|
|
5336
5702
|
const [isHovered, setIsHovered] = useState(false);
|
|
5337
5703
|
const [isScrolling, setIsScrolling] = useState(false);
|
|
5704
|
+
const [isDarkMode, setIsDarkMode] = useState(false);
|
|
5338
5705
|
const dragStartRef = useRef(
|
|
5339
5706
|
null
|
|
5340
5707
|
);
|
|
@@ -5349,6 +5716,24 @@ function ScrollbarOverlay({ container }) {
|
|
|
5349
5716
|
);
|
|
5350
5717
|
const maxScrollTop = scrollHeight - clientHeight;
|
|
5351
5718
|
const thumbTop = maxScrollTop > 0 ? TRACK_PADDING + scrollTop / maxScrollTop * (trackHeight - thumbHeight) : TRACK_PADDING;
|
|
5719
|
+
useEffect(() => {
|
|
5720
|
+
function checkDarkMode() {
|
|
5721
|
+
const html = document.documentElement;
|
|
5722
|
+
setIsDarkMode(html.classList.contains("dark"));
|
|
5723
|
+
}
|
|
5724
|
+
checkDarkMode();
|
|
5725
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
5726
|
+
mediaQuery.addEventListener("change", checkDarkMode);
|
|
5727
|
+
const observer = new MutationObserver(checkDarkMode);
|
|
5728
|
+
observer.observe(document.documentElement, {
|
|
5729
|
+
attributes: true,
|
|
5730
|
+
attributeFilter: ["class"]
|
|
5731
|
+
});
|
|
5732
|
+
return () => {
|
|
5733
|
+
mediaQuery.removeEventListener("change", checkDarkMode);
|
|
5734
|
+
observer.disconnect();
|
|
5735
|
+
};
|
|
5736
|
+
}, []);
|
|
5352
5737
|
useEffect(() => {
|
|
5353
5738
|
function update() {
|
|
5354
5739
|
setState({
|
|
@@ -5486,6 +5871,13 @@ function ScrollbarOverlay({ container }) {
|
|
|
5486
5871
|
return null;
|
|
5487
5872
|
}
|
|
5488
5873
|
const isVisible = isDragging || isHovered || isScrolling;
|
|
5874
|
+
const wrapperStyle = {
|
|
5875
|
+
position: "absolute",
|
|
5876
|
+
inset: 0,
|
|
5877
|
+
pointerEvents: "none",
|
|
5878
|
+
overflow: "hidden",
|
|
5879
|
+
zIndex: 50
|
|
5880
|
+
};
|
|
5489
5881
|
const trackStyle = {
|
|
5490
5882
|
position: "absolute",
|
|
5491
5883
|
top: 0,
|
|
@@ -5495,6 +5887,12 @@ function ScrollbarOverlay({ container }) {
|
|
|
5495
5887
|
height: clientHeight,
|
|
5496
5888
|
pointerEvents: "auto"
|
|
5497
5889
|
};
|
|
5890
|
+
const getThumbColor = () => {
|
|
5891
|
+
if (isDarkMode) {
|
|
5892
|
+
return isDragging ? "rgba(255, 255, 255, 0.5)" : "rgba(255, 255, 255, 0.3)";
|
|
5893
|
+
}
|
|
5894
|
+
return isDragging ? "rgba(0, 0, 0, 0.5)" : "rgba(0, 0, 0, 0.3)";
|
|
5895
|
+
};
|
|
5498
5896
|
const thumbStyle = {
|
|
5499
5897
|
position: "absolute",
|
|
5500
5898
|
right: "2px",
|
|
@@ -5502,58 +5900,39 @@ function ScrollbarOverlay({ container }) {
|
|
|
5502
5900
|
borderRadius: "9999px",
|
|
5503
5901
|
height: thumbHeight,
|
|
5504
5902
|
top: thumbTop,
|
|
5505
|
-
opacity: isVisible ? 1 : 0
|
|
5903
|
+
opacity: isVisible ? 1 : 0,
|
|
5904
|
+
backgroundColor: getThumbColor(),
|
|
5905
|
+
transition: "opacity 150ms",
|
|
5906
|
+
cursor: "pointer"
|
|
5506
5907
|
};
|
|
5507
|
-
return /* @__PURE__ */
|
|
5908
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: wrapperStyle, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
5508
5909
|
"div",
|
|
5509
5910
|
{
|
|
5510
5911
|
"data-testid": "custom-scrollbar",
|
|
5511
5912
|
"aria-hidden": "true",
|
|
5512
|
-
className: "pointer-events-auto absolute top-0 right-0 bottom-0 z-50 w-3",
|
|
5513
5913
|
style: trackStyle,
|
|
5514
5914
|
onClick: handleTrackClick,
|
|
5515
5915
|
onMouseEnter: () => setIsHovered(true),
|
|
5516
5916
|
onMouseLeave: () => setIsHovered(false),
|
|
5517
|
-
children: /* @__PURE__ */
|
|
5917
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
5518
5918
|
"div",
|
|
5519
5919
|
{
|
|
5520
5920
|
"data-testid": "scrollbar-thumb",
|
|
5521
|
-
className: `absolute right-0.5 w-1.5 cursor-pointer rounded-full transition-opacity duration-150 ${isDragging ? "bg-black/50 dark:bg-white/50" : "bg-black/30 dark:bg-white/30"}`,
|
|
5522
5921
|
style: thumbStyle,
|
|
5523
5922
|
onMouseDown: handleThumbMouseDown
|
|
5524
|
-
}
|
|
5525
|
-
void 0,
|
|
5526
|
-
false,
|
|
5527
|
-
{
|
|
5528
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/scrollbar/index.tsx",
|
|
5529
|
-
lineNumber: 230,
|
|
5530
|
-
columnNumber: 7
|
|
5531
|
-
},
|
|
5532
|
-
this
|
|
5923
|
+
}
|
|
5533
5924
|
)
|
|
5534
|
-
}
|
|
5535
|
-
|
|
5536
|
-
false,
|
|
5537
|
-
{
|
|
5538
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/scrollbar/index.tsx",
|
|
5539
|
-
lineNumber: 221,
|
|
5540
|
-
columnNumber: 5
|
|
5541
|
-
},
|
|
5542
|
-
this
|
|
5543
|
-
);
|
|
5925
|
+
}
|
|
5926
|
+
) });
|
|
5544
5927
|
}
|
|
5545
|
-
const scrollbarExtension = {
|
|
5928
|
+
const scrollbarExtension = defineExtension({
|
|
5546
5929
|
name: "scrollbar",
|
|
5547
5930
|
renderOverlay(context) {
|
|
5548
|
-
return /* @__PURE__ */
|
|
5549
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/scrollbar/index.tsx",
|
|
5550
|
-
lineNumber: 247,
|
|
5551
|
-
columnNumber: 12
|
|
5552
|
-
}, this);
|
|
5931
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollbarOverlay, { container: context.container });
|
|
5553
5932
|
}
|
|
5554
|
-
};
|
|
5933
|
+
});
|
|
5555
5934
|
const STRIKE_KIND = "strikethrough";
|
|
5556
|
-
const strikethroughExtension = {
|
|
5935
|
+
const strikethroughExtension = defineExtension({
|
|
5557
5936
|
name: "strikethrough",
|
|
5558
5937
|
toggleInline: { kind: STRIKE_KIND, markers: ["~~"] },
|
|
5559
5938
|
keybindings: [
|
|
@@ -5626,8 +6005,8 @@ const strikethroughExtension = {
|
|
|
5626
6005
|
}
|
|
5627
6006
|
return element;
|
|
5628
6007
|
}
|
|
5629
|
-
};
|
|
5630
|
-
const
|
|
6008
|
+
});
|
|
6009
|
+
const bundledExtensionsWithoutImage = [
|
|
5631
6010
|
blockquoteExtension,
|
|
5632
6011
|
headingExtension,
|
|
5633
6012
|
listExtension,
|
|
@@ -5636,7 +6015,10 @@ const bundledExtensions = [
|
|
|
5636
6015
|
italicExtension,
|
|
5637
6016
|
strikethroughExtension,
|
|
5638
6017
|
pipeLinkExtension,
|
|
5639
|
-
linkExtension
|
|
6018
|
+
linkExtension
|
|
6019
|
+
];
|
|
6020
|
+
const bundledExtensions = [
|
|
6021
|
+
...bundledExtensionsWithoutImage,
|
|
5640
6022
|
imageExtension
|
|
5641
6023
|
];
|
|
5642
6024
|
function toLayoutRect(params) {
|
|
@@ -5696,6 +6078,46 @@ function cursorOffsetToCodeUnit(cursorToCodeUnit, offset) {
|
|
|
5696
6078
|
const clamped = Math.max(0, Math.min(offset, cursorToCodeUnit.length - 1));
|
|
5697
6079
|
return cursorToCodeUnit[clamped] ?? 0;
|
|
5698
6080
|
}
|
|
6081
|
+
function createDomPositionResolver(lineElement) {
|
|
6082
|
+
const textNodes = [];
|
|
6083
|
+
const cumulativeEnds = [];
|
|
6084
|
+
const walker = createTextWalker(lineElement);
|
|
6085
|
+
let current = walker.nextNode();
|
|
6086
|
+
let total = 0;
|
|
6087
|
+
while (current) {
|
|
6088
|
+
if (current instanceof Text) {
|
|
6089
|
+
const length = current.data.length;
|
|
6090
|
+
textNodes.push(current);
|
|
6091
|
+
total += length;
|
|
6092
|
+
cumulativeEnds.push(total);
|
|
6093
|
+
}
|
|
6094
|
+
current = walker.nextNode();
|
|
6095
|
+
}
|
|
6096
|
+
if (textNodes.length === 0) {
|
|
6097
|
+
return () => {
|
|
6098
|
+
if (!lineElement.textContent) {
|
|
6099
|
+
return { node: lineElement, offset: 0 };
|
|
6100
|
+
}
|
|
6101
|
+
return { node: lineElement, offset: lineElement.childNodes.length };
|
|
6102
|
+
};
|
|
6103
|
+
}
|
|
6104
|
+
return (offsetInLine) => {
|
|
6105
|
+
const clamped = Math.max(0, Math.min(offsetInLine, total));
|
|
6106
|
+
let low = 0;
|
|
6107
|
+
let high = cumulativeEnds.length - 1;
|
|
6108
|
+
while (low < high) {
|
|
6109
|
+
const mid = low + high >>> 1;
|
|
6110
|
+
if ((cumulativeEnds[mid] ?? 0) < clamped) {
|
|
6111
|
+
low = mid + 1;
|
|
6112
|
+
} else {
|
|
6113
|
+
high = mid;
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6116
|
+
const node = textNodes[low] ?? lineElement;
|
|
6117
|
+
const prevEnd = low > 0 ? cumulativeEnds[low - 1] ?? 0 : 0;
|
|
6118
|
+
return { node, offset: clamped - prevEnd };
|
|
6119
|
+
};
|
|
6120
|
+
}
|
|
5699
6121
|
function measureCharacterRect(params) {
|
|
5700
6122
|
if (params.lineLength <= 0) {
|
|
5701
6123
|
return null;
|
|
@@ -5708,101 +6130,155 @@ function measureCharacterRect(params) {
|
|
|
5708
6130
|
params.cursorToCodeUnit,
|
|
5709
6131
|
Math.min(params.offset + 1, params.lineLength)
|
|
5710
6132
|
);
|
|
5711
|
-
const startPosition = resolveDomPosition(
|
|
5712
|
-
const endPosition = resolveDomPosition(
|
|
5713
|
-
|
|
5714
|
-
range.
|
|
5715
|
-
range.
|
|
5716
|
-
const rects = range.getClientRects();
|
|
6133
|
+
const startPosition = params.resolveDomPosition(startCodeUnit);
|
|
6134
|
+
const endPosition = params.resolveDomPosition(endCodeUnit);
|
|
6135
|
+
params.range.setStart(startPosition.node, startPosition.offset);
|
|
6136
|
+
params.range.setEnd(endPosition.node, endPosition.offset);
|
|
6137
|
+
const rects = params.range.getClientRects();
|
|
5717
6138
|
if (rects.length > 0) {
|
|
5718
6139
|
return rects[0] ?? null;
|
|
5719
6140
|
}
|
|
5720
|
-
const rect = range.getBoundingClientRect();
|
|
6141
|
+
const rect = params.range.getBoundingClientRect();
|
|
5721
6142
|
if (rect.width === 0 && rect.height === 0) {
|
|
5722
6143
|
return null;
|
|
5723
6144
|
}
|
|
5724
6145
|
return rect;
|
|
5725
6146
|
}
|
|
5726
6147
|
function measureLineRows(params) {
|
|
6148
|
+
var _a, _b;
|
|
6149
|
+
const fallbackLineBox = toLayoutRect({
|
|
6150
|
+
rect: params.lineRect,
|
|
6151
|
+
containerRect: params.containerRect,
|
|
6152
|
+
scroll: params.scroll
|
|
6153
|
+
});
|
|
5727
6154
|
if (params.lineLength === 0) {
|
|
5728
|
-
const lineBox = toLayoutRect({
|
|
5729
|
-
rect: params.lineRect,
|
|
5730
|
-
containerRect: params.containerRect,
|
|
5731
|
-
scroll: params.scroll
|
|
5732
|
-
});
|
|
5733
6155
|
return [
|
|
5734
6156
|
{
|
|
5735
6157
|
startOffset: 0,
|
|
5736
6158
|
endOffset: 0,
|
|
5737
|
-
rect: { ...
|
|
6159
|
+
rect: { ...fallbackLineBox, width: 0 }
|
|
5738
6160
|
}
|
|
5739
6161
|
];
|
|
5740
6162
|
}
|
|
5741
|
-
const
|
|
5742
|
-
const
|
|
5743
|
-
const
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
);
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
const fullLineRects = groupDomRectsByRow(
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
const fallbackLineBox = toLayoutRect({
|
|
5753
|
-
rect: params.lineRect,
|
|
5754
|
-
containerRect: params.containerRect,
|
|
5755
|
-
scroll: params.scroll
|
|
5756
|
-
});
|
|
5757
|
-
const charRect = measureCharacterRect({
|
|
5758
|
-
lineElement: params.lineElement,
|
|
5759
|
-
offset: 0,
|
|
5760
|
-
lineLength: params.lineLength,
|
|
5761
|
-
cursorToCodeUnit: params.cursorToCodeUnit
|
|
5762
|
-
});
|
|
5763
|
-
const charWidth = (charRect == null ? void 0 : charRect.width) ?? 0;
|
|
5764
|
-
if (fullLineRects.length === 0 || charWidth <= 0) {
|
|
5765
|
-
const rows2 = [
|
|
6163
|
+
const WRAP_THRESHOLD_PX = 3;
|
|
6164
|
+
const resolvePosition = createDomPositionResolver(params.lineElement);
|
|
6165
|
+
const scratchRange = document.createRange();
|
|
6166
|
+
const topCache = /* @__PURE__ */ new Map();
|
|
6167
|
+
const fullLineStart = resolvePosition(0);
|
|
6168
|
+
const fullLineEnd = resolvePosition(params.codeUnitLength);
|
|
6169
|
+
scratchRange.setStart(fullLineStart.node, fullLineStart.offset);
|
|
6170
|
+
scratchRange.setEnd(fullLineEnd.node, fullLineEnd.offset);
|
|
6171
|
+
const fullLineRects = groupDomRectsByRow(Array.from(scratchRange.getClientRects()));
|
|
6172
|
+
if (fullLineRects.length === 0) {
|
|
6173
|
+
return [
|
|
5766
6174
|
{
|
|
5767
6175
|
startOffset: 0,
|
|
5768
6176
|
endOffset: params.lineLength,
|
|
5769
6177
|
rect: fallbackLineBox
|
|
5770
6178
|
}
|
|
5771
6179
|
];
|
|
5772
|
-
|
|
6180
|
+
}
|
|
6181
|
+
function offsetToTop(offset) {
|
|
6182
|
+
if (topCache.has(offset)) {
|
|
6183
|
+
return topCache.get(offset) ?? null;
|
|
6184
|
+
}
|
|
6185
|
+
const rect = measureCharacterRect({
|
|
6186
|
+
lineElement: params.lineElement,
|
|
6187
|
+
offset,
|
|
6188
|
+
lineLength: params.lineLength,
|
|
6189
|
+
cursorToCodeUnit: params.cursorToCodeUnit,
|
|
6190
|
+
resolveDomPosition: resolvePosition,
|
|
6191
|
+
range: scratchRange
|
|
6192
|
+
});
|
|
6193
|
+
const top = rect ? rect.top : null;
|
|
6194
|
+
topCache.set(offset, top);
|
|
6195
|
+
return top;
|
|
6196
|
+
}
|
|
6197
|
+
function findFirstMeasurableOffset(from) {
|
|
6198
|
+
for (let offset = Math.max(0, from); offset < params.lineLength; offset++) {
|
|
6199
|
+
if (offsetToTop(offset) !== null) {
|
|
6200
|
+
return offset;
|
|
6201
|
+
}
|
|
6202
|
+
}
|
|
6203
|
+
return null;
|
|
6204
|
+
}
|
|
6205
|
+
function findNextRowStartOffset(fromExclusive, rowTop) {
|
|
6206
|
+
const lastIndex = params.lineLength - 1;
|
|
6207
|
+
if (fromExclusive > lastIndex) {
|
|
6208
|
+
return null;
|
|
6209
|
+
}
|
|
6210
|
+
const isNewRowAt = (offset) => {
|
|
6211
|
+
const top = offsetToTop(offset);
|
|
6212
|
+
return top !== null && Math.abs(top - rowTop) > WRAP_THRESHOLD_PX;
|
|
6213
|
+
};
|
|
6214
|
+
let step = 1;
|
|
6215
|
+
let lastSame = fromExclusive - 1;
|
|
6216
|
+
let probe = fromExclusive;
|
|
6217
|
+
while (probe <= lastIndex) {
|
|
6218
|
+
if (isNewRowAt(probe)) {
|
|
6219
|
+
break;
|
|
6220
|
+
}
|
|
6221
|
+
lastSame = probe;
|
|
6222
|
+
probe += step;
|
|
6223
|
+
step *= 2;
|
|
6224
|
+
}
|
|
6225
|
+
if (probe > lastIndex) {
|
|
6226
|
+
probe = lastIndex;
|
|
6227
|
+
if (!isNewRowAt(probe)) {
|
|
6228
|
+
return null;
|
|
6229
|
+
}
|
|
6230
|
+
}
|
|
6231
|
+
let low = Math.max(fromExclusive, lastSame + 1);
|
|
6232
|
+
let high = probe;
|
|
6233
|
+
while (low < high) {
|
|
6234
|
+
const mid = low + high >>> 1;
|
|
6235
|
+
if (isNewRowAt(mid)) {
|
|
6236
|
+
high = mid;
|
|
6237
|
+
} else {
|
|
6238
|
+
low = mid + 1;
|
|
6239
|
+
}
|
|
6240
|
+
}
|
|
6241
|
+
return low < params.lineLength ? low : null;
|
|
6242
|
+
}
|
|
6243
|
+
const rows = [];
|
|
6244
|
+
const firstMeasurable = findFirstMeasurableOffset(0);
|
|
6245
|
+
if (firstMeasurable === null) {
|
|
6246
|
+
return [
|
|
5773
6247
|
{
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
top: fallbackLineBox.top,
|
|
5778
|
-
height: fallbackLineBox.height
|
|
5779
|
-
}
|
|
6248
|
+
startOffset: 0,
|
|
6249
|
+
endOffset: params.lineLength,
|
|
6250
|
+
rect: fallbackLineBox
|
|
5780
6251
|
}
|
|
5781
|
-
]
|
|
6252
|
+
];
|
|
5782
6253
|
}
|
|
5783
|
-
let
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
const
|
|
5789
|
-
const
|
|
5790
|
-
|
|
5791
|
-
|
|
6254
|
+
let currentRowStart = 0;
|
|
6255
|
+
let currentRowTop = ((_a = fullLineRects[0]) == null ? void 0 : _a.top) ?? params.lineRect.top;
|
|
6256
|
+
let searchFrom = firstMeasurable + 1;
|
|
6257
|
+
let rowIndex = 0;
|
|
6258
|
+
while (currentRowStart < params.lineLength) {
|
|
6259
|
+
const nextRowStart = findNextRowStartOffset(searchFrom, currentRowTop);
|
|
6260
|
+
const currentRowEnd = nextRowStart ?? params.lineLength;
|
|
6261
|
+
const domRect = fullLineRects[rowIndex] ?? params.lineRect;
|
|
6262
|
+
rows.push({
|
|
6263
|
+
startOffset: currentRowStart,
|
|
6264
|
+
endOffset: currentRowEnd,
|
|
5792
6265
|
rect: toLayoutRect({
|
|
5793
|
-
rect,
|
|
6266
|
+
rect: domRect,
|
|
5794
6267
|
containerRect: params.containerRect,
|
|
5795
6268
|
scroll: params.scroll
|
|
5796
6269
|
})
|
|
5797
|
-
};
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
6270
|
+
});
|
|
6271
|
+
if (nextRowStart === null) {
|
|
6272
|
+
break;
|
|
6273
|
+
}
|
|
6274
|
+
currentRowStart = nextRowStart;
|
|
6275
|
+
rowIndex += 1;
|
|
6276
|
+
const nextMeasurable = findFirstMeasurableOffset(currentRowStart);
|
|
6277
|
+
if (nextMeasurable === null) {
|
|
6278
|
+
break;
|
|
6279
|
+
}
|
|
6280
|
+
currentRowTop = ((_b = fullLineRects[rowIndex]) == null ? void 0 : _b.top) ?? currentRowTop;
|
|
6281
|
+
searchFrom = nextMeasurable + 1;
|
|
5806
6282
|
}
|
|
5807
6283
|
if (rows.length === 1) {
|
|
5808
6284
|
rows[0] = {
|
|
@@ -5874,6 +6350,44 @@ function measureLayoutModelFromDom(params) {
|
|
|
5874
6350
|
}
|
|
5875
6351
|
return buildLayoutModel(params.lines, measurer);
|
|
5876
6352
|
}
|
|
6353
|
+
function measureLayoutModelRangeFromDom(params) {
|
|
6354
|
+
const measurer = createDomLayoutMeasurer({
|
|
6355
|
+
root: params.root,
|
|
6356
|
+
container: params.container,
|
|
6357
|
+
lines: params.lines
|
|
6358
|
+
});
|
|
6359
|
+
if (!measurer) {
|
|
6360
|
+
return null;
|
|
6361
|
+
}
|
|
6362
|
+
const clampedStart = Math.max(0, Math.min(params.startLineIndex, params.lines.length - 1));
|
|
6363
|
+
const clampedEnd = Math.max(clampedStart, Math.min(params.endLineIndex, params.lines.length - 1));
|
|
6364
|
+
const lineOffsets = getLineOffsets(params.lines);
|
|
6365
|
+
let lineStartOffset = lineOffsets[clampedStart] ?? 0;
|
|
6366
|
+
const layouts = [];
|
|
6367
|
+
for (let lineIndex = clampedStart; lineIndex <= clampedEnd; lineIndex += 1) {
|
|
6368
|
+
const lineInfo = params.lines[lineIndex];
|
|
6369
|
+
if (!lineInfo) {
|
|
6370
|
+
continue;
|
|
6371
|
+
}
|
|
6372
|
+
const measurement = measurer.measureLine({
|
|
6373
|
+
lineIndex: lineInfo.lineIndex,
|
|
6374
|
+
lineText: lineInfo.text,
|
|
6375
|
+
lineLength: lineInfo.cursorLength,
|
|
6376
|
+
lineHasNewline: lineInfo.hasNewline,
|
|
6377
|
+
top: 0
|
|
6378
|
+
});
|
|
6379
|
+
layouts.push({
|
|
6380
|
+
lineIndex: lineInfo.lineIndex,
|
|
6381
|
+
lineStartOffset,
|
|
6382
|
+
lineLength: lineInfo.cursorLength,
|
|
6383
|
+
lineHasNewline: lineInfo.hasNewline,
|
|
6384
|
+
lineBox: measurement.lineBox,
|
|
6385
|
+
rows: measurement.rows
|
|
6386
|
+
});
|
|
6387
|
+
lineStartOffset += lineInfo.cursorLength + (lineInfo.hasNewline ? 1 : 0);
|
|
6388
|
+
}
|
|
6389
|
+
return { container: measurer.container, lines: layouts };
|
|
6390
|
+
}
|
|
5877
6391
|
function getLineElement(root, lineIndex) {
|
|
5878
6392
|
return root.querySelector(`[data-line-index="${lineIndex}"]`);
|
|
5879
6393
|
}
|
|
@@ -6067,7 +6581,7 @@ function computeSelectionRects(layout, selection, measurer) {
|
|
|
6067
6581
|
return rects;
|
|
6068
6582
|
}
|
|
6069
6583
|
function computeCaretRect(caret) {
|
|
6070
|
-
const height = caret.
|
|
6584
|
+
const height = caret.fontSize * 1.2;
|
|
6071
6585
|
const contentHeight = caret.lineRect.height > 0 ? Math.max(
|
|
6072
6586
|
0,
|
|
6073
6587
|
caret.lineRect.height - caret.padding.top - caret.padding.bottom
|
|
@@ -6089,7 +6603,17 @@ function getSelectionGeometry(params) {
|
|
|
6089
6603
|
end: selection.start,
|
|
6090
6604
|
affinity: selection.affinity
|
|
6091
6605
|
};
|
|
6092
|
-
const layout = shouldMeasureLayout(normalized) ?
|
|
6606
|
+
const layout = shouldMeasureLayout(normalized) ? (() => {
|
|
6607
|
+
const startLine2 = resolveOffsetToLine(docLines, normalized.start);
|
|
6608
|
+
const endLine = resolveOffsetToLine(docLines, normalized.end);
|
|
6609
|
+
return measureLayoutModelRangeFromDom({
|
|
6610
|
+
lines: docLines,
|
|
6611
|
+
root,
|
|
6612
|
+
container,
|
|
6613
|
+
startLineIndex: startLine2.lineIndex,
|
|
6614
|
+
endLineIndex: endLine.lineIndex
|
|
6615
|
+
});
|
|
6616
|
+
})() : null;
|
|
6093
6617
|
const containerRect = container.getBoundingClientRect();
|
|
6094
6618
|
const scroll = { top: container.scrollTop, left: container.scrollLeft };
|
|
6095
6619
|
const startLine = resolveOffsetToLine(docLines, normalized.start);
|
|
@@ -6121,6 +6645,7 @@ function getSelectionGeometry(params) {
|
|
|
6121
6645
|
scroll
|
|
6122
6646
|
}),
|
|
6123
6647
|
lineLength: lineInfo.cursorLength,
|
|
6648
|
+
fontSize: getComputedFontSize(lineElement),
|
|
6124
6649
|
padding: getComputedVerticalPadding(lineElement)
|
|
6125
6650
|
};
|
|
6126
6651
|
const caretRect = computeCaretRect(caretMeasurement);
|
|
@@ -6164,6 +6689,7 @@ function getSelectionGeometry(params) {
|
|
|
6164
6689
|
scroll
|
|
6165
6690
|
}),
|
|
6166
6691
|
lineLength: lineInfo.cursorLength,
|
|
6692
|
+
fontSize: getComputedFontSize(focusLineElement),
|
|
6167
6693
|
padding: getComputedVerticalPadding(focusLineElement)
|
|
6168
6694
|
};
|
|
6169
6695
|
focusRect = computeCaretRect(caretMeasurement);
|
|
@@ -6290,6 +6816,11 @@ function getComputedVerticalPadding(lineElement) {
|
|
|
6290
6816
|
bottom: Number.isFinite(bottom) ? bottom : 0
|
|
6291
6817
|
};
|
|
6292
6818
|
}
|
|
6819
|
+
function getComputedFontSize(lineElement) {
|
|
6820
|
+
const fontSize = window.getComputedStyle(lineElement).fontSize;
|
|
6821
|
+
const parsed = Number.parseFloat(fontSize);
|
|
6822
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 16;
|
|
6823
|
+
}
|
|
6293
6824
|
function rectRight(rect) {
|
|
6294
6825
|
return rect.left + rect.width;
|
|
6295
6826
|
}
|
|
@@ -6687,6 +7218,36 @@ function nextWordBreak(text, offset) {
|
|
|
6687
7218
|
}
|
|
6688
7219
|
return text.length;
|
|
6689
7220
|
}
|
|
7221
|
+
const MAX_HTML_INPUT_LENGTH = 5e5;
|
|
7222
|
+
const MAX_MARKDOWN_OUTPUT_LENGTH = 1e5;
|
|
7223
|
+
function isHTMLElement(node) {
|
|
7224
|
+
return node !== null && node.nodeType === Node.ELEMENT_NODE;
|
|
7225
|
+
}
|
|
7226
|
+
function isElement(node) {
|
|
7227
|
+
return node !== null && node.nodeType === Node.ELEMENT_NODE;
|
|
7228
|
+
}
|
|
7229
|
+
const CLEANUP_PATTERNS = {
|
|
7230
|
+
unescapeHeaders: /^\\(#{1,6})\s+/gm,
|
|
7231
|
+
unescapeBlockquote: /^\\>/gm,
|
|
7232
|
+
unescapeMarkdown: /\\([*_`~[\]])/g,
|
|
7233
|
+
unescapeListBullets: /^(\s*)\\([-*+])(\s+)/gm,
|
|
7234
|
+
unescapeListNumbers: /^(\s*)(\d+)\\\.(\s+)/gm,
|
|
7235
|
+
normalizeBullets: /^(\s*)[-*+](\s{2,})/gm,
|
|
7236
|
+
normalizeNumbers: /^[\s]*\d+\.[\s]+/gm,
|
|
7237
|
+
normalizeHeaders: /^(#{1,6})[\s]{2,}/gm,
|
|
7238
|
+
removeTrailingSpaces: /[ \t]+$/gm,
|
|
7239
|
+
headersInBlockquotes: /^>\s*(#{1,6}\s+.*)/gm,
|
|
7240
|
+
excessiveNewlines: /\n{3,}/g,
|
|
7241
|
+
interlacedTableGaps: /(\|[^\n]*\|)\s*\n\s*\n+\s*(\|[^\n]*\|)/g,
|
|
7242
|
+
complexTableGaps: /(\|[^\n]*\|)(\s*\n){2,}(\|[^\n]*\|)/g
|
|
7243
|
+
};
|
|
7244
|
+
const HTML_PREPROCESSING_PATTERNS = {
|
|
7245
|
+
removeStyleAndDataAttrs: /\s(?:style|data-[^=]*|id)="[^"]*"/gi,
|
|
7246
|
+
removeNonCodeClasses: /\sclass="(?![^"]*(?:language-|hljs))[^"]*"/gi,
|
|
7247
|
+
removeEmptyElements: /<(\w+)[^>]*>\s*<\/\1>/gi,
|
|
7248
|
+
normalizeSpaces: /[ \t]{2,}/g,
|
|
7249
|
+
reduceBlankLines: /\n\s*\n/g
|
|
7250
|
+
};
|
|
6690
7251
|
const turndownService = new TurndownService({
|
|
6691
7252
|
headingStyle: "atx",
|
|
6692
7253
|
bulletListMarker: "-",
|
|
@@ -6704,6 +7265,13 @@ turndownService.addRule("strikethrough", {
|
|
|
6704
7265
|
turndownService.addRule("codeBlock", {
|
|
6705
7266
|
filter: "pre",
|
|
6706
7267
|
replacement: (content, node) => {
|
|
7268
|
+
if (!isHTMLElement(node)) {
|
|
7269
|
+
return `
|
|
7270
|
+
\`\`\`
|
|
7271
|
+
${content}
|
|
7272
|
+
\`\`\`
|
|
7273
|
+
`;
|
|
7274
|
+
}
|
|
6707
7275
|
const codeElement = node.querySelector("code");
|
|
6708
7276
|
if (codeElement) {
|
|
6709
7277
|
const className = codeElement.className || "";
|
|
@@ -6722,12 +7290,308 @@ ${content}
|
|
|
6722
7290
|
`;
|
|
6723
7291
|
}
|
|
6724
7292
|
});
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
7293
|
+
turndownService.addRule("tableRow", {
|
|
7294
|
+
filter: "tr",
|
|
7295
|
+
replacement: (_content, node) => {
|
|
7296
|
+
var _a;
|
|
7297
|
+
if (!isHTMLElement(node)) {
|
|
7298
|
+
return "";
|
|
7299
|
+
}
|
|
7300
|
+
const isHeaderRow = ((_a = node.parentNode) == null ? void 0 : _a.nodeName) === "THEAD";
|
|
7301
|
+
const cells = Array.from(node.querySelectorAll("td, th"));
|
|
7302
|
+
const cellContents = cells.map((cell) => {
|
|
7303
|
+
const text = cell.textContent || "";
|
|
7304
|
+
return text.replace(/\|/g, "\\|").trim();
|
|
7305
|
+
});
|
|
7306
|
+
let result = "| " + cellContents.join(" | ") + " |\n";
|
|
7307
|
+
if (isHeaderRow) {
|
|
7308
|
+
const separators = cells.map((cell) => {
|
|
7309
|
+
const align = cell.getAttribute("align");
|
|
7310
|
+
if (align === "center") {
|
|
7311
|
+
return ":-------------:";
|
|
7312
|
+
}
|
|
7313
|
+
if (align === "right") {
|
|
7314
|
+
return "-------------:";
|
|
7315
|
+
}
|
|
7316
|
+
return "-------------";
|
|
7317
|
+
});
|
|
7318
|
+
result += "| " + separators.join(" | ") + " |\n";
|
|
7319
|
+
}
|
|
7320
|
+
return result;
|
|
7321
|
+
}
|
|
7322
|
+
});
|
|
7323
|
+
turndownService.addRule("taskList", {
|
|
7324
|
+
filter: (node) => {
|
|
7325
|
+
if (!isHTMLElement(node)) {
|
|
7326
|
+
return false;
|
|
7327
|
+
}
|
|
7328
|
+
if (node.nodeName !== "LI") {
|
|
7329
|
+
return false;
|
|
7330
|
+
}
|
|
7331
|
+
if (node.querySelector('input[type="checkbox"]') !== null) {
|
|
7332
|
+
return true;
|
|
7333
|
+
}
|
|
7334
|
+
const textContent = node.textContent || "";
|
|
7335
|
+
const hasCheckboxSymbols = /^[\s]*[☐☑✓✗[\]]/m.test(textContent) || /^[\s]*\[[ x]\]/m.test(textContent);
|
|
7336
|
+
const hasCheckboxClass = Boolean(
|
|
7337
|
+
node.className && (node.className.includes("task") || node.className.includes("checkbox") || node.className.includes("todo"))
|
|
7338
|
+
);
|
|
7339
|
+
return hasCheckboxSymbols || hasCheckboxClass;
|
|
7340
|
+
},
|
|
7341
|
+
replacement: (content, node) => {
|
|
7342
|
+
if (!isHTMLElement(node)) {
|
|
7343
|
+
return content;
|
|
7344
|
+
}
|
|
7345
|
+
const checkbox = node.querySelector('input[type="checkbox"]');
|
|
7346
|
+
const isCheckbox = checkbox instanceof HTMLInputElement && checkbox.type === "checkbox";
|
|
7347
|
+
let isChecked = false;
|
|
7348
|
+
if (isCheckbox) {
|
|
7349
|
+
isChecked = checkbox.checked;
|
|
7350
|
+
} else {
|
|
7351
|
+
const textContent2 = node.textContent || "";
|
|
7352
|
+
isChecked = /^[\s]*[☑✓✗]/.test(textContent2) || /^[\s]*\[x\]/i.test(textContent2);
|
|
7353
|
+
}
|
|
7354
|
+
const prefix = isChecked ? "- [x] " : "- [ ] ";
|
|
7355
|
+
let textContent = content;
|
|
7356
|
+
textContent = textContent.replace(/^\s*\[[ x]\]\s*/gi, "");
|
|
7357
|
+
textContent = textContent.replace(/^\s*[☐☑✓✗]\s*/g, "");
|
|
7358
|
+
textContent = textContent.replace(/^\s*\[[x ]\]\s*/gi, "");
|
|
7359
|
+
textContent = textContent.replace(/^\s*\\?\[[ x]\\?\]\s*/gi, "");
|
|
7360
|
+
textContent = textContent.replace(/\\?\[\\?\s*\\?\]\\?\s*/g, "");
|
|
7361
|
+
return prefix + textContent.trim() + "\n";
|
|
7362
|
+
}
|
|
7363
|
+
});
|
|
7364
|
+
turndownService.addRule("list", {
|
|
7365
|
+
filter: ["ul", "ol"],
|
|
7366
|
+
replacement: (content, node) => {
|
|
7367
|
+
const parent = node.parentNode;
|
|
7368
|
+
const isNested = isElement(parent) && parent.tagName === "LI";
|
|
7369
|
+
if (isNested) {
|
|
7370
|
+
return "\n" + content;
|
|
7371
|
+
}
|
|
7372
|
+
return "\n" + content + "\n";
|
|
7373
|
+
}
|
|
7374
|
+
});
|
|
7375
|
+
turndownService.addRule("blockquote", {
|
|
7376
|
+
filter: "blockquote",
|
|
7377
|
+
replacement: (content) => {
|
|
7378
|
+
const lines = content.trim().split("\n");
|
|
7379
|
+
const processedLines = lines.map((line) => {
|
|
7380
|
+
const trimmed = line.trim();
|
|
7381
|
+
if (!trimmed) {
|
|
7382
|
+
return ">";
|
|
7383
|
+
}
|
|
7384
|
+
const existingQuotes = trimmed.match(/^(>\s*)+/);
|
|
7385
|
+
if (existingQuotes) {
|
|
7386
|
+
return existingQuotes[0] + " " + trimmed;
|
|
7387
|
+
}
|
|
7388
|
+
return "> " + trimmed;
|
|
7389
|
+
});
|
|
7390
|
+
return "\n" + processedLines.join("\n") + "\n";
|
|
7391
|
+
}
|
|
7392
|
+
});
|
|
7393
|
+
turndownService.addRule("horizontalRule", {
|
|
7394
|
+
filter: "hr",
|
|
7395
|
+
replacement: () => "\n---\n"
|
|
7396
|
+
});
|
|
7397
|
+
turndownService.addRule("inlineCode", {
|
|
7398
|
+
filter: (node) => {
|
|
7399
|
+
const parent = node.parentNode;
|
|
7400
|
+
return node.nodeName === "CODE" && !(isElement(parent) && parent.tagName === "PRE");
|
|
7401
|
+
},
|
|
7402
|
+
replacement: (content) => {
|
|
7403
|
+
const backtickCount = Math.max(
|
|
7404
|
+
1,
|
|
7405
|
+
(content.match(/`+/g) || []).reduce(
|
|
7406
|
+
(max, match) => Math.max(max, match.length),
|
|
7407
|
+
0
|
|
7408
|
+
) + 1
|
|
7409
|
+
);
|
|
7410
|
+
const delimiter = "`".repeat(backtickCount);
|
|
7411
|
+
return delimiter + content + delimiter;
|
|
7412
|
+
}
|
|
7413
|
+
});
|
|
7414
|
+
turndownService.addRule("image", {
|
|
7415
|
+
filter: "img",
|
|
7416
|
+
replacement: (_content, node) => {
|
|
7417
|
+
if (!isHTMLElement(node)) {
|
|
7418
|
+
return "";
|
|
7419
|
+
}
|
|
7420
|
+
const src = node.getAttribute("src") || "";
|
|
7421
|
+
const alt = node.getAttribute("alt") || "";
|
|
7422
|
+
const title = node.getAttribute("title");
|
|
7423
|
+
if (title) {
|
|
7424
|
+
return ``;
|
|
7425
|
+
}
|
|
7426
|
+
return ``;
|
|
7427
|
+
}
|
|
7428
|
+
});
|
|
7429
|
+
turndownService.addRule("highlight", {
|
|
7430
|
+
filter: (node) => {
|
|
7431
|
+
var _a, _b;
|
|
7432
|
+
if (!isHTMLElement(node)) {
|
|
7433
|
+
return false;
|
|
7434
|
+
}
|
|
7435
|
+
return node.nodeName === "MARK" || node.nodeName === "SPAN" && (((_a = node.style) == null ? void 0 : _a.backgroundColor) === "yellow" || ((_b = node.className) == null ? void 0 : _b.includes("highlight")));
|
|
7436
|
+
},
|
|
7437
|
+
replacement: (content) => `==${content}==`
|
|
7438
|
+
});
|
|
7439
|
+
turndownService.addRule("headerWithId", {
|
|
7440
|
+
filter: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
7441
|
+
replacement: (content, node) => {
|
|
7442
|
+
if (!isHTMLElement(node)) {
|
|
7443
|
+
return content;
|
|
7444
|
+
}
|
|
7445
|
+
const rawLevel = parseInt(node.nodeName.charAt(1));
|
|
7446
|
+
const level = Math.min(rawLevel, 3);
|
|
7447
|
+
const hashes = "#".repeat(level);
|
|
7448
|
+
const id = node.getAttribute("id");
|
|
7449
|
+
if (id) {
|
|
7450
|
+
return `
|
|
7451
|
+
${hashes} ${content} {#${id}}
|
|
7452
|
+
`;
|
|
7453
|
+
}
|
|
7454
|
+
return `
|
|
7455
|
+
${hashes} ${content}
|
|
7456
|
+
`;
|
|
7457
|
+
}
|
|
7458
|
+
});
|
|
7459
|
+
function processTextNodes(element) {
|
|
7460
|
+
const spans = Array.from(element.querySelectorAll("span"));
|
|
7461
|
+
spans.forEach((span) => {
|
|
7462
|
+
var _a;
|
|
7463
|
+
const textContent = span.textContent || "";
|
|
7464
|
+
const textNode = document.createTextNode(textContent);
|
|
7465
|
+
(_a = span.parentNode) == null ? void 0 : _a.replaceChild(textNode, span);
|
|
7466
|
+
});
|
|
7467
|
+
}
|
|
7468
|
+
function detectSourceApp(html) {
|
|
7469
|
+
const detectionPatterns = {
|
|
7470
|
+
notion: ["notion-", "notranslate"],
|
|
7471
|
+
github: ["github.com", "js-file-line-container"],
|
|
7472
|
+
slack: ["slack-", "c-message"],
|
|
7473
|
+
"google-docs": ["docs.google.com", "kix-"]
|
|
7474
|
+
};
|
|
7475
|
+
for (const [app, patterns] of Object.entries(detectionPatterns)) {
|
|
7476
|
+
if (patterns.some((pattern) => html.includes(pattern))) {
|
|
7477
|
+
return app;
|
|
7478
|
+
}
|
|
7479
|
+
}
|
|
7480
|
+
return "unknown";
|
|
7481
|
+
}
|
|
7482
|
+
function preprocessForApp(html, app) {
|
|
7483
|
+
switch (app) {
|
|
7484
|
+
case "notion":
|
|
7485
|
+
return html.replace(/<div[^>]*class="[^"]*notion-[^"]*"[^>]*>/gi, "<div>").replace(/<span[^>]*class="[^"]*notion-[^"]*"[^>]*>/gi, "<span>").replace(/<details[^>]*>/gi, "<div>").replace(/<\/details>/gi, "</div>").replace(/<summary[^>]*>/gi, "<strong>").replace(/<\/summary>/gi, "</strong>");
|
|
7486
|
+
case "github":
|
|
7487
|
+
return html.replace(/<td[^>]*class="[^"]*blob-num[^"]*"[^>]*>.*?<\/td>/gi, "").replace(/<span[^>]*class="[^"]*pl-[^"]*"[^>]*>/gi, "<span>").replace(/<span[^>]*class="[^"]*highlight[^"]*"[^>]*>/gi, "<span>");
|
|
7488
|
+
case "slack":
|
|
7489
|
+
return html.replace(/<span[^>]*class="[^"]*c-member[^"]*"[^>]*>/gi, "<span>").replace(/<span[^>]*data-stringify-type="mention"[^>]*>/gi, "<span>").replace(
|
|
7490
|
+
/<span[^>]*class="[^"]*c-emoji[^"]*"[^>]*>([^<]*)<\/span>/gi,
|
|
7491
|
+
"$1"
|
|
7492
|
+
);
|
|
7493
|
+
case "google-docs":
|
|
7494
|
+
return html.replace(
|
|
7495
|
+
/<span[^>]*style="[^"]*font-weight:[^;"]*bold[^"]*"[^>]*>/gi,
|
|
7496
|
+
"<strong>"
|
|
7497
|
+
).replace(
|
|
7498
|
+
/<span[^>]*style="[^"]*font-style:[^;"]*italic[^"]*"[^>]*>/gi,
|
|
7499
|
+
"<em>"
|
|
7500
|
+
).replace(/<\/span>/gi, "").replace(/<p[^>]*style="[^"]*"[^>]*>/gi, "<p>");
|
|
7501
|
+
default:
|
|
7502
|
+
return html;
|
|
7503
|
+
}
|
|
7504
|
+
}
|
|
7505
|
+
function cleanupMarkdown(markdown) {
|
|
7506
|
+
return markdown.replace(CLEANUP_PATTERNS.unescapeHeaders, "$1 ").replace(CLEANUP_PATTERNS.unescapeBlockquote, ">").replace(CLEANUP_PATTERNS.unescapeMarkdown, "$1").replace(CLEANUP_PATTERNS.unescapeListBullets, "$1$2$3").replace(CLEANUP_PATTERNS.unescapeListNumbers, "$1$2.$3").replace(CLEANUP_PATTERNS.normalizeBullets, "$1- ").replace(CLEANUP_PATTERNS.normalizeNumbers, (match) => {
|
|
7507
|
+
var _a;
|
|
7508
|
+
const num = ((_a = match.match(/\d+/)) == null ? void 0 : _a[0]) || "1";
|
|
7509
|
+
return `${num}. `;
|
|
7510
|
+
}).replace(CLEANUP_PATTERNS.normalizeHeaders, "$1 ").replace(CLEANUP_PATTERNS.removeTrailingSpaces, "").replace(CLEANUP_PATTERNS.headersInBlockquotes, "\n$1").replace(CLEANUP_PATTERNS.excessiveNewlines, "\n\n").replace(CLEANUP_PATTERNS.interlacedTableGaps, "$1\n$2").replace(CLEANUP_PATTERNS.complexTableGaps, "$1\n$3").replace(/\*\*\[([^\]]+?)\]\(([^)]+?)\)\*\*/g, "[**$1**]($2)").replace(/__\[([^\]]+?)\]\(([^)]+?)\)__/g, "[**$1**]($2)").replace(/\*\[([^\]]+?)\]\(([^)]+?)\)\*/g, "[*$1*]($2)").replace(/_\[([^\]]+?)\]\(([^)]+?)\)_/g, "[*$1*]($2)").trim();
|
|
7511
|
+
}
|
|
7512
|
+
function preprocessHtml(html) {
|
|
7513
|
+
const sourceApp = detectSourceApp(html);
|
|
7514
|
+
let processedHtml = preprocessForApp(html, sourceApp);
|
|
7515
|
+
const parser = new DOMParser();
|
|
7516
|
+
const doc = parser.parseFromString(processedHtml, "text/html");
|
|
7517
|
+
if (doc.body) {
|
|
7518
|
+
processTextNodes(doc.body);
|
|
7519
|
+
processedHtml = doc.body.innerHTML;
|
|
7520
|
+
}
|
|
7521
|
+
return processedHtml.replace(HTML_PREPROCESSING_PATTERNS.removeStyleAndDataAttrs, "").replace(HTML_PREPROCESSING_PATTERNS.removeNonCodeClasses, "").replace(HTML_PREPROCESSING_PATTERNS.removeEmptyElements, "").replace(HTML_PREPROCESSING_PATTERNS.normalizeSpaces, " ").replace(HTML_PREPROCESSING_PATTERNS.reduceBlankLines, "\n\n").trim();
|
|
7522
|
+
}
|
|
7523
|
+
function sanitizeContent(content) {
|
|
7524
|
+
return content.replace(/<script[^>]*>.*?<\/script>/gi, "").replace(
|
|
7525
|
+
/<(?:iframe|object|embed)[^>]*>.*?<\/(?:iframe|object|embed)>/gi,
|
|
7526
|
+
""
|
|
7527
|
+
);
|
|
7528
|
+
}
|
|
7529
|
+
function limitContentLength(content, maxLength = MAX_MARKDOWN_OUTPUT_LENGTH) {
|
|
7530
|
+
return content.substring(0, maxLength);
|
|
7531
|
+
}
|
|
7532
|
+
function sanitizeMarkdown(markdown) {
|
|
7533
|
+
return limitContentLength(sanitizeContent(markdown));
|
|
7534
|
+
}
|
|
7535
|
+
function shouldProcessPaste(htmlContent) {
|
|
7536
|
+
if (htmlContent.length > MAX_HTML_INPUT_LENGTH) {
|
|
7537
|
+
console.warn("HTML content too large for paste processing");
|
|
7538
|
+
return false;
|
|
7539
|
+
}
|
|
7540
|
+
const hasFormatting = /<(?:strong|b|em|i|u|s|del|strike|code|pre|h[1-6]|blockquote|ul|ol|li|table|tr|td|th|a|img|mark|span|div)[\s>]/i.test(
|
|
7541
|
+
htmlContent
|
|
7542
|
+
);
|
|
7543
|
+
if (!hasFormatting) {
|
|
7544
|
+
return false;
|
|
7545
|
+
}
|
|
7546
|
+
if (/<img\s/i.test(htmlContent)) {
|
|
7547
|
+
return true;
|
|
7548
|
+
}
|
|
7549
|
+
const strippedContent = htmlContent.replace(/<[^>]*>/g, "").trim();
|
|
7550
|
+
if (!strippedContent || strippedContent.length < 3) {
|
|
7551
|
+
return false;
|
|
7552
|
+
}
|
|
7553
|
+
return true;
|
|
7554
|
+
}
|
|
7555
|
+
function normalizeListPrefixes(content) {
|
|
7556
|
+
const lines = content.split("\n");
|
|
7557
|
+
let currentListType = null;
|
|
7558
|
+
let currentNumber = 1;
|
|
7559
|
+
return lines.map((line) => {
|
|
7560
|
+
const match = line.match(/^(\s*)([-*+]|\d+\.)( +)(.*)$/);
|
|
7561
|
+
if (match) {
|
|
7562
|
+
const [, indent, marker, , listContent] = match;
|
|
7563
|
+
if (currentListType === null) {
|
|
7564
|
+
currentListType = /\d+\./.test(marker) ? "numbered" : "bullet";
|
|
7565
|
+
}
|
|
7566
|
+
const newMarker = currentListType === "bullet" ? "-" : `${currentNumber}.`;
|
|
7567
|
+
if (currentListType === "numbered") {
|
|
7568
|
+
currentNumber += 1;
|
|
7569
|
+
}
|
|
7570
|
+
return `${indent}${newMarker} ${listContent}`;
|
|
7571
|
+
}
|
|
7572
|
+
return line;
|
|
7573
|
+
}).join("\n");
|
|
7574
|
+
}
|
|
7575
|
+
function convertHtmlToMarkdown(html) {
|
|
7576
|
+
try {
|
|
7577
|
+
const processedHtml = preprocessHtml(html);
|
|
7578
|
+
const markdown = turndownService.turndown(processedHtml);
|
|
7579
|
+
return cleanupMarkdown(markdown);
|
|
7580
|
+
} catch (error) {
|
|
7581
|
+
console.error("Error converting HTML to markdown:", error);
|
|
6728
7582
|
return "";
|
|
6729
7583
|
}
|
|
6730
|
-
|
|
7584
|
+
}
|
|
7585
|
+
function htmlToMarkdownForPaste(htmlContent) {
|
|
7586
|
+
if (!shouldProcessPaste(htmlContent)) {
|
|
7587
|
+
return null;
|
|
7588
|
+
}
|
|
7589
|
+
const markdown = convertHtmlToMarkdown(htmlContent);
|
|
7590
|
+
if (!markdown.trim()) {
|
|
7591
|
+
return null;
|
|
7592
|
+
}
|
|
7593
|
+
const normalizedMarkdown = normalizeListPrefixes(markdown);
|
|
7594
|
+
return sanitizeMarkdown(normalizedMarkdown).replace(/\r\n?/g, "\n");
|
|
6731
7595
|
}
|
|
6732
7596
|
const defaultSelection = { start: 0, end: 0, affinity: "forward" };
|
|
6733
7597
|
const COMPOSITION_COMMIT_CLEAR_DELAY_MS = 50;
|
|
@@ -6757,10 +7621,13 @@ class CakeEngine {
|
|
|
6757
7621
|
this.caretBlinkTimeoutId = null;
|
|
6758
7622
|
this.overlayUpdateId = null;
|
|
6759
7623
|
this.scrollCaretIntoViewId = null;
|
|
7624
|
+
this.selectionRectElements = [];
|
|
7625
|
+
this.lastSelectionRects = null;
|
|
6760
7626
|
this.extensionsRoot = null;
|
|
6761
7627
|
this.placeholderRoot = null;
|
|
6762
7628
|
this.lastFocusRect = null;
|
|
6763
7629
|
this.verticalNavGoalX = null;
|
|
7630
|
+
this.lastRenderPerf = null;
|
|
6764
7631
|
this.history = {
|
|
6765
7632
|
undoStack: [],
|
|
6766
7633
|
redoStack: [],
|
|
@@ -6814,6 +7681,9 @@ class CakeEngine {
|
|
|
6814
7681
|
set state(value) {
|
|
6815
7682
|
this._state = value;
|
|
6816
7683
|
}
|
|
7684
|
+
getLastRenderPerf() {
|
|
7685
|
+
return this.lastRenderPerf;
|
|
7686
|
+
}
|
|
6817
7687
|
isEventTargetInContentRoot(target) {
|
|
6818
7688
|
if (target === this.container) {
|
|
6819
7689
|
return true;
|
|
@@ -6850,6 +7720,9 @@ class CakeEngine {
|
|
|
6850
7720
|
getSelection() {
|
|
6851
7721
|
return this.state.selection;
|
|
6852
7722
|
}
|
|
7723
|
+
getCursorLength() {
|
|
7724
|
+
return this.state.map.cursorLength;
|
|
7725
|
+
}
|
|
6853
7726
|
getFocusRect() {
|
|
6854
7727
|
return this.lastFocusRect;
|
|
6855
7728
|
}
|
|
@@ -6904,12 +7777,18 @@ class CakeEngine {
|
|
|
6904
7777
|
if (!this.isComposing) {
|
|
6905
7778
|
this.applySelection(this.state.selection);
|
|
6906
7779
|
}
|
|
7780
|
+
this.scheduleScrollCaretIntoView();
|
|
6907
7781
|
}
|
|
6908
7782
|
setValue({ value, selection }) {
|
|
6909
|
-
const
|
|
6910
|
-
if (
|
|
7783
|
+
const valueChanged = value !== this.state.source;
|
|
7784
|
+
if (!valueChanged && selection === void 0) {
|
|
7785
|
+
return;
|
|
7786
|
+
}
|
|
7787
|
+
if (!valueChanged && selection !== void 0) {
|
|
7788
|
+
this.setSelection(selection);
|
|
6911
7789
|
return;
|
|
6912
7790
|
}
|
|
7791
|
+
const nextSelection = selection ?? this.state.selection;
|
|
6913
7792
|
this.state = this.runtime.createState(value, nextSelection);
|
|
6914
7793
|
this.render();
|
|
6915
7794
|
}
|
|
@@ -6974,7 +7853,7 @@ class CakeEngine {
|
|
|
6974
7853
|
}
|
|
6975
7854
|
executeCommand(command) {
|
|
6976
7855
|
var _a;
|
|
6977
|
-
const shouldOpenLinkPopover =
|
|
7856
|
+
const shouldOpenLinkPopover = "openPopover" in command && command.openPopover === true;
|
|
6978
7857
|
const nextState = this.runtime.applyEdit(command, this.state);
|
|
6979
7858
|
if (nextState === this.state) {
|
|
6980
7859
|
return false;
|
|
@@ -7149,6 +8028,14 @@ class CakeEngine {
|
|
|
7149
8028
|
this.patchedCaretRangeFromPoint = null;
|
|
7150
8029
|
}
|
|
7151
8030
|
render() {
|
|
8031
|
+
const perfEnabled = this.container.dataset.cakePerf === "1";
|
|
8032
|
+
let perfStart = 0;
|
|
8033
|
+
let renderStart = 0;
|
|
8034
|
+
let renderAndMapMs = 0;
|
|
8035
|
+
let applySelectionMs = 0;
|
|
8036
|
+
if (perfEnabled) {
|
|
8037
|
+
perfStart = performance.now();
|
|
8038
|
+
}
|
|
7152
8039
|
if (!this.contentRoot) {
|
|
7153
8040
|
const containerPosition = window.getComputedStyle(
|
|
7154
8041
|
this.container
|
|
@@ -7164,16 +8051,40 @@ class CakeEngine {
|
|
|
7164
8051
|
this.container.replaceChildren(this.contentRoot, overlay, extensionsRoot);
|
|
7165
8052
|
this.attachDragListeners();
|
|
7166
8053
|
}
|
|
8054
|
+
if (perfEnabled) {
|
|
8055
|
+
renderStart = performance.now();
|
|
8056
|
+
}
|
|
7167
8057
|
const { content, map } = renderDocContent(
|
|
7168
8058
|
this.state.doc,
|
|
7169
8059
|
this.extensions,
|
|
7170
8060
|
this.contentRoot
|
|
7171
8061
|
);
|
|
7172
|
-
this.contentRoot.
|
|
8062
|
+
const existingChildren = Array.from(this.contentRoot.childNodes);
|
|
8063
|
+
const needsUpdate = content.length !== existingChildren.length || content.some((node, i) => node !== existingChildren[i]);
|
|
8064
|
+
if (needsUpdate) {
|
|
8065
|
+
this.contentRoot.replaceChildren(...content);
|
|
8066
|
+
}
|
|
7173
8067
|
this.domMap = map;
|
|
7174
|
-
|
|
7175
|
-
|
|
8068
|
+
if (perfEnabled) {
|
|
8069
|
+
renderAndMapMs = performance.now() - renderStart;
|
|
8070
|
+
}
|
|
8071
|
+
if (!this.isComposing && this.hasFocus()) {
|
|
8072
|
+
const selectionStart = perfEnabled ? performance.now() : 0;
|
|
7176
8073
|
this.applySelection(this.state.selection);
|
|
8074
|
+
if (perfEnabled) {
|
|
8075
|
+
applySelectionMs = performance.now() - selectionStart;
|
|
8076
|
+
}
|
|
8077
|
+
}
|
|
8078
|
+
if (perfEnabled) {
|
|
8079
|
+
const totalMs = performance.now() - perfStart;
|
|
8080
|
+
this.lastRenderPerf = {
|
|
8081
|
+
totalMs,
|
|
8082
|
+
renderAndMapMs,
|
|
8083
|
+
applySelectionMs,
|
|
8084
|
+
didUpdateDom: needsUpdate,
|
|
8085
|
+
blockCount: this.state.doc.blocks.length,
|
|
8086
|
+
runCount: map.runs.length
|
|
8087
|
+
};
|
|
7177
8088
|
}
|
|
7178
8089
|
this.updatePlaceholder();
|
|
7179
8090
|
this.scheduleOverlayUpdate();
|
|
@@ -7423,22 +8334,31 @@ class CakeEngine {
|
|
|
7423
8334
|
this.suppressSelectionChange = false;
|
|
7424
8335
|
return;
|
|
7425
8336
|
}
|
|
7426
|
-
const
|
|
7427
|
-
|
|
7428
|
-
if (
|
|
8337
|
+
const pendingHit = this.pendingClickHit;
|
|
8338
|
+
const selection = this.state.selection;
|
|
8339
|
+
if (pendingHit && selection.start !== selection.end) {
|
|
7429
8340
|
const newSelection = {
|
|
7430
|
-
start:
|
|
7431
|
-
end:
|
|
7432
|
-
affinity:
|
|
8341
|
+
start: pendingHit.cursorOffset,
|
|
8342
|
+
end: pendingHit.cursorOffset,
|
|
8343
|
+
affinity: pendingHit.affinity
|
|
7433
8344
|
};
|
|
8345
|
+
this.pendingClickHit = null;
|
|
8346
|
+
this.suppressSelectionChange = true;
|
|
7434
8347
|
this.state = this.runtime.updateSelection(this.state, newSelection, {
|
|
7435
8348
|
kind: "dom"
|
|
7436
8349
|
});
|
|
7437
8350
|
this.applySelection(this.state.selection);
|
|
7438
8351
|
(_b = this.onSelectionChange) == null ? void 0 : _b.call(this, this.state.selection);
|
|
7439
8352
|
this.scheduleOverlayUpdate();
|
|
8353
|
+
setTimeout(() => {
|
|
8354
|
+
this.suppressSelectionChange = false;
|
|
8355
|
+
}, 0);
|
|
8356
|
+
return;
|
|
7440
8357
|
}
|
|
7441
|
-
this.
|
|
8358
|
+
this.pendingClickHit = null;
|
|
8359
|
+
setTimeout(() => {
|
|
8360
|
+
this.suppressSelectionChange = false;
|
|
8361
|
+
}, 0);
|
|
7442
8362
|
return;
|
|
7443
8363
|
}
|
|
7444
8364
|
this.pendingClickHit = null;
|
|
@@ -7765,6 +8685,13 @@ class CakeEngine {
|
|
|
7765
8685
|
}
|
|
7766
8686
|
event.preventDefault();
|
|
7767
8687
|
clipboardData.setData("text/plain", text);
|
|
8688
|
+
const html = this.runtime.serializeSelectionToHtml(
|
|
8689
|
+
this.state,
|
|
8690
|
+
this.state.selection
|
|
8691
|
+
);
|
|
8692
|
+
if (html) {
|
|
8693
|
+
clipboardData.setData("text/html", html);
|
|
8694
|
+
}
|
|
7768
8695
|
}
|
|
7769
8696
|
handleCut(event) {
|
|
7770
8697
|
if (this.readOnly) {
|
|
@@ -7809,7 +8736,7 @@ class CakeEngine {
|
|
|
7809
8736
|
if (!command) {
|
|
7810
8737
|
continue;
|
|
7811
8738
|
}
|
|
7812
|
-
if (command
|
|
8739
|
+
if (isApplyEditCommand(command)) {
|
|
7813
8740
|
this.applyEdit(command);
|
|
7814
8741
|
} else {
|
|
7815
8742
|
this.executeCommand(command);
|
|
@@ -8329,6 +9256,39 @@ class CakeEngine {
|
|
|
8329
9256
|
return { start: target, end: target, affinity: direction };
|
|
8330
9257
|
}
|
|
8331
9258
|
const currentPos = selection.start;
|
|
9259
|
+
const currentAffinity = selection.affinity ?? "forward";
|
|
9260
|
+
const measurement = this.getLayoutForNavigation();
|
|
9261
|
+
if (measurement) {
|
|
9262
|
+
const { lines, layout } = measurement;
|
|
9263
|
+
const { rowStart, rowEnd } = getVisualRowBoundaries({
|
|
9264
|
+
lines,
|
|
9265
|
+
layout,
|
|
9266
|
+
offset: currentPos,
|
|
9267
|
+
affinity: currentAffinity
|
|
9268
|
+
});
|
|
9269
|
+
if (direction === "backward" && currentPos === rowStart && currentAffinity === "forward" && currentPos > 0) {
|
|
9270
|
+
const prevBoundaries = getVisualRowBoundaries({
|
|
9271
|
+
lines,
|
|
9272
|
+
layout,
|
|
9273
|
+
offset: currentPos,
|
|
9274
|
+
affinity: "backward"
|
|
9275
|
+
});
|
|
9276
|
+
if (prevBoundaries.rowEnd !== rowEnd || prevBoundaries.rowStart !== rowStart) {
|
|
9277
|
+
return { start: currentPos, end: currentPos, affinity: "backward" };
|
|
9278
|
+
}
|
|
9279
|
+
}
|
|
9280
|
+
if (direction === "forward" && currentPos === rowEnd && currentAffinity === "backward" && currentPos < this.state.map.cursorLength) {
|
|
9281
|
+
const nextBoundaries = getVisualRowBoundaries({
|
|
9282
|
+
lines,
|
|
9283
|
+
layout,
|
|
9284
|
+
offset: currentPos,
|
|
9285
|
+
affinity: "forward"
|
|
9286
|
+
});
|
|
9287
|
+
if (nextBoundaries.rowEnd !== rowEnd || nextBoundaries.rowStart !== rowStart) {
|
|
9288
|
+
return { start: currentPos, end: currentPos, affinity: "forward" };
|
|
9289
|
+
}
|
|
9290
|
+
}
|
|
9291
|
+
}
|
|
8332
9292
|
const nextPos = this.moveOffsetByChar(currentPos, direction);
|
|
8333
9293
|
if (nextPos === null) {
|
|
8334
9294
|
return null;
|
|
@@ -9018,6 +9978,7 @@ class CakeEngine {
|
|
|
9018
9978
|
}
|
|
9019
9979
|
this.overlayUpdateId = window.requestAnimationFrame(() => {
|
|
9020
9980
|
this.overlayUpdateId = null;
|
|
9981
|
+
this.updateExtensionsOverlayPosition();
|
|
9021
9982
|
this.updateSelectionOverlay();
|
|
9022
9983
|
});
|
|
9023
9984
|
}
|
|
@@ -9047,12 +10008,34 @@ class CakeEngine {
|
|
|
9047
10008
|
overlay.style.zIndex = "2";
|
|
9048
10009
|
const caret = document.createElement("div");
|
|
9049
10010
|
caret.className = "cake-caret";
|
|
10011
|
+
caret.style.position = "absolute";
|
|
9050
10012
|
caret.style.display = "none";
|
|
9051
10013
|
overlay.append(caret);
|
|
9052
10014
|
this.overlayRoot = overlay;
|
|
9053
10015
|
this.caretElement = caret;
|
|
10016
|
+
this.selectionRectElements = [];
|
|
10017
|
+
this.lastSelectionRects = null;
|
|
9054
10018
|
return overlay;
|
|
9055
10019
|
}
|
|
10020
|
+
selectionRectsEqual(prev, next) {
|
|
10021
|
+
if (!prev) {
|
|
10022
|
+
return false;
|
|
10023
|
+
}
|
|
10024
|
+
if (prev.length !== next.length) {
|
|
10025
|
+
return false;
|
|
10026
|
+
}
|
|
10027
|
+
for (let index = 0; index < prev.length; index += 1) {
|
|
10028
|
+
const a = prev[index];
|
|
10029
|
+
const b = next[index];
|
|
10030
|
+
if (!a || !b) {
|
|
10031
|
+
return false;
|
|
10032
|
+
}
|
|
10033
|
+
if (a.top !== b.top || a.left !== b.left || a.width !== b.width || a.height !== b.height) {
|
|
10034
|
+
return false;
|
|
10035
|
+
}
|
|
10036
|
+
}
|
|
10037
|
+
return true;
|
|
10038
|
+
}
|
|
9056
10039
|
ensureExtensionsRoot() {
|
|
9057
10040
|
if (this.extensionsRoot) {
|
|
9058
10041
|
return this.extensionsRoot;
|
|
@@ -9074,8 +10057,8 @@ class CakeEngine {
|
|
|
9074
10057
|
if (!this.extensionsRoot) {
|
|
9075
10058
|
return;
|
|
9076
10059
|
}
|
|
9077
|
-
const scrollTop = this.container.scrollTop;
|
|
9078
|
-
const scrollLeft = this.container.scrollLeft;
|
|
10060
|
+
const scrollTop = Math.max(0, this.container.scrollTop);
|
|
10061
|
+
const scrollLeft = Math.max(0, this.container.scrollLeft);
|
|
9079
10062
|
if (scrollTop === 0 && scrollLeft === 0) {
|
|
9080
10063
|
this.extensionsRoot.style.transform = "";
|
|
9081
10064
|
return;
|
|
@@ -9113,21 +10096,34 @@ class CakeEngine {
|
|
|
9113
10096
|
if (!this.overlayRoot || !this.caretElement) {
|
|
9114
10097
|
return;
|
|
9115
10098
|
}
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
10099
|
+
if (this.selectionRectsEqual(this.lastSelectionRects, rects)) {
|
|
10100
|
+
return;
|
|
10101
|
+
}
|
|
10102
|
+
this.lastSelectionRects = rects;
|
|
10103
|
+
while (this.selectionRectElements.length > rects.length) {
|
|
10104
|
+
const element = this.selectionRectElements.pop();
|
|
10105
|
+
element == null ? void 0 : element.remove();
|
|
10106
|
+
}
|
|
10107
|
+
if (this.selectionRectElements.length < rects.length) {
|
|
10108
|
+
const fragment = document.createDocumentFragment();
|
|
10109
|
+
while (this.selectionRectElements.length < rects.length) {
|
|
10110
|
+
const element = document.createElement("div");
|
|
10111
|
+
element.className = "cake-selection-rect";
|
|
10112
|
+
fragment.append(element);
|
|
10113
|
+
this.selectionRectElements.push(element);
|
|
10114
|
+
}
|
|
10115
|
+
this.overlayRoot.insertBefore(fragment, this.caretElement);
|
|
10116
|
+
}
|
|
10117
|
+
rects.forEach((rect, index) => {
|
|
10118
|
+
const element = this.selectionRectElements[index];
|
|
10119
|
+
if (!element) {
|
|
10120
|
+
return;
|
|
10121
|
+
}
|
|
9124
10122
|
element.style.top = `${rect.top}px`;
|
|
9125
10123
|
element.style.left = `${rect.left}px`;
|
|
9126
10124
|
element.style.width = `${rect.width}px`;
|
|
9127
10125
|
element.style.height = `${rect.height}px`;
|
|
9128
|
-
fragment.append(element);
|
|
9129
10126
|
});
|
|
9130
|
-
this.overlayRoot.insertBefore(fragment, this.caretElement);
|
|
9131
10127
|
}
|
|
9132
10128
|
updateCaret(position) {
|
|
9133
10129
|
if (!this.caretElement) {
|
|
@@ -9294,7 +10290,7 @@ class CakeEngine {
|
|
|
9294
10290
|
return { cursorOffset: cursor.cursorOffset, affinity: "backward" };
|
|
9295
10291
|
}
|
|
9296
10292
|
handlePointerDown(event) {
|
|
9297
|
-
var _a;
|
|
10293
|
+
var _a, _b, _c;
|
|
9298
10294
|
if (!this.isEventTargetInContentRoot(event.target)) {
|
|
9299
10295
|
return;
|
|
9300
10296
|
}
|
|
@@ -9359,13 +10355,51 @@ class CakeEngine {
|
|
|
9359
10355
|
}
|
|
9360
10356
|
}
|
|
9361
10357
|
}
|
|
9362
|
-
if (
|
|
9363
|
-
this.suppressSelectionChange = true;
|
|
10358
|
+
if (!event.shiftKey) {
|
|
9364
10359
|
const hit2 = this.hitTestFromClientPoint(event.clientX, event.clientY);
|
|
9365
|
-
if (hit2) {
|
|
10360
|
+
if (!hit2) {
|
|
10361
|
+
return;
|
|
10362
|
+
}
|
|
10363
|
+
if (selection.start !== selection.end) {
|
|
10364
|
+
const selStart2 = Math.min(selection.start, selection.end);
|
|
10365
|
+
const selEnd2 = Math.max(selection.start, selection.end);
|
|
10366
|
+
const clickedInsideSelection2 = hit2.cursorOffset >= selStart2 && hit2.cursorOffset <= selEnd2;
|
|
10367
|
+
if (clickedInsideSelection2) {
|
|
10368
|
+
this.suppressSelectionChange = false;
|
|
10369
|
+
this.blockTrustedTextDrag = true;
|
|
10370
|
+
this.pendingClickHit = hit2;
|
|
10371
|
+
} else {
|
|
10372
|
+
this.suppressSelectionChange = true;
|
|
10373
|
+
this.pendingClickHit = hit2;
|
|
10374
|
+
const newSelection = {
|
|
10375
|
+
start: hit2.cursorOffset,
|
|
10376
|
+
end: hit2.cursorOffset,
|
|
10377
|
+
affinity: hit2.affinity
|
|
10378
|
+
};
|
|
10379
|
+
this.state = this.runtime.updateSelection(this.state, newSelection, {
|
|
10380
|
+
kind: "dom"
|
|
10381
|
+
});
|
|
10382
|
+
this.applySelection(this.state.selection);
|
|
10383
|
+
(_b = this.onSelectionChange) == null ? void 0 : _b.call(this, this.state.selection);
|
|
10384
|
+
this.scheduleOverlayUpdate();
|
|
10385
|
+
return;
|
|
10386
|
+
}
|
|
10387
|
+
} else {
|
|
10388
|
+
this.suppressSelectionChange = true;
|
|
9366
10389
|
this.pendingClickHit = hit2;
|
|
10390
|
+
const newSelection = {
|
|
10391
|
+
start: hit2.cursorOffset,
|
|
10392
|
+
end: hit2.cursorOffset,
|
|
10393
|
+
affinity: hit2.affinity
|
|
10394
|
+
};
|
|
10395
|
+
this.state = this.runtime.updateSelection(this.state, newSelection, {
|
|
10396
|
+
kind: "dom"
|
|
10397
|
+
});
|
|
10398
|
+
this.applySelection(this.state.selection);
|
|
10399
|
+
(_c = this.onSelectionChange) == null ? void 0 : _c.call(this, this.state.selection);
|
|
10400
|
+
this.scheduleOverlayUpdate();
|
|
10401
|
+
return;
|
|
9367
10402
|
}
|
|
9368
|
-
return;
|
|
9369
10403
|
}
|
|
9370
10404
|
this.suppressSelectionChange = false;
|
|
9371
10405
|
const selStart = Math.min(selection.start, selection.end);
|
|
@@ -9477,10 +10511,6 @@ class CakeEngine {
|
|
|
9477
10511
|
}
|
|
9478
10512
|
handlePointerUp(event) {
|
|
9479
10513
|
this.blockTrustedTextDrag = false;
|
|
9480
|
-
if (this.pendingClickHit) {
|
|
9481
|
-
this.pendingClickHit = null;
|
|
9482
|
-
this.suppressSelectionChange = false;
|
|
9483
|
-
}
|
|
9484
10514
|
if (this.selectionDragState) {
|
|
9485
10515
|
if (event.pointerId !== this.selectionDragState.pointerId) {
|
|
9486
10516
|
return;
|
|
@@ -9980,7 +11010,7 @@ function findOffsetInTextNode(textNode, clientX, clientY) {
|
|
|
9980
11010
|
let closestDistance = Infinity;
|
|
9981
11011
|
let closestYDistance = Infinity;
|
|
9982
11012
|
let closestCaretX = 0;
|
|
9983
|
-
let
|
|
11013
|
+
let selectedViaRightEdge = false;
|
|
9984
11014
|
const rowInfo = /* @__PURE__ */ new Map();
|
|
9985
11015
|
const range = document.createRange();
|
|
9986
11016
|
let lastCharRect = null;
|
|
@@ -10049,15 +11079,28 @@ function findOffsetInTextNode(textNode, clientX, clientY) {
|
|
|
10049
11079
|
closestDistance = xDistance;
|
|
10050
11080
|
closestOffset = i;
|
|
10051
11081
|
closestCaretX = caretX;
|
|
10052
|
-
|
|
11082
|
+
bestRect.top;
|
|
10053
11083
|
closestYDistance = yDistance;
|
|
11084
|
+
selectedViaRightEdge = false;
|
|
11085
|
+
}
|
|
11086
|
+
if (i < text.length) {
|
|
11087
|
+
const rightEdgeX = bestRect.right;
|
|
11088
|
+
const rightEdgeDistance = Math.abs(clientX - rightEdgeX);
|
|
11089
|
+
if (rightEdgeDistance < closestDistance) {
|
|
11090
|
+
closestDistance = rightEdgeDistance;
|
|
11091
|
+
closestOffset = i + 1;
|
|
11092
|
+
closestCaretX = rightEdgeX;
|
|
11093
|
+
bestRect.top;
|
|
11094
|
+
closestYDistance = yDistance;
|
|
11095
|
+
selectedViaRightEdge = true;
|
|
11096
|
+
}
|
|
10054
11097
|
}
|
|
10055
11098
|
} else if (yDistance < closestYDistance || yDistance === closestYDistance && xDistance < closestDistance) {
|
|
10056
11099
|
closestYDistance = yDistance;
|
|
10057
11100
|
closestDistance = xDistance;
|
|
10058
11101
|
closestOffset = i;
|
|
10059
11102
|
closestCaretX = caretX;
|
|
10060
|
-
|
|
11103
|
+
bestRect.top;
|
|
10061
11104
|
}
|
|
10062
11105
|
}
|
|
10063
11106
|
let closestRow = null;
|
|
@@ -10082,11 +11125,11 @@ function findOffsetInTextNode(textNode, clientX, clientY) {
|
|
|
10082
11125
|
if (clientX < closestRow.left) {
|
|
10083
11126
|
closestOffset = closestRow.startOffset;
|
|
10084
11127
|
closestCaretX = closestRow.left;
|
|
10085
|
-
|
|
11128
|
+
closestRow.top;
|
|
10086
11129
|
} else if (clientX > closestRow.right) {
|
|
10087
|
-
closestOffset = closestRow.endOffset;
|
|
11130
|
+
closestOffset = Math.min(closestRow.endOffset + 1, text.length);
|
|
10088
11131
|
closestCaretX = closestRow.right;
|
|
10089
|
-
|
|
11132
|
+
closestRow.top;
|
|
10090
11133
|
} else if (clientY < closestRow.top || clientY > closestRow.bottom) {
|
|
10091
11134
|
let bestXDistance = Infinity;
|
|
10092
11135
|
const range2 = document.createRange();
|
|
@@ -10106,12 +11149,12 @@ function findOffsetInTextNode(textNode, clientX, clientY) {
|
|
|
10106
11149
|
bestXDistance = xDist;
|
|
10107
11150
|
closestOffset = i;
|
|
10108
11151
|
closestCaretX = rect.left;
|
|
10109
|
-
|
|
11152
|
+
closestRow.top;
|
|
10110
11153
|
}
|
|
10111
11154
|
}
|
|
10112
11155
|
}
|
|
10113
11156
|
}
|
|
10114
|
-
const pastRowEnd = closestRow !== null && clientX > closestRow.right
|
|
11157
|
+
const pastRowEnd = selectedViaRightEdge || closestRow !== null && clientX > closestRow.right;
|
|
10115
11158
|
if (Math.abs(clientX - closestCaretX) <= 2 && closestOffset < text.length && text[closestOffset] === "\n") {
|
|
10116
11159
|
closestOffset = Math.max(0, closestOffset - 1);
|
|
10117
11160
|
}
|
|
@@ -10564,8 +11607,9 @@ const CakeEditor = forwardRef(
|
|
|
10564
11607
|
const lastEmittedSelectionRef = useRef(null);
|
|
10565
11608
|
const [overlayRoot, setOverlayRoot] = useState(null);
|
|
10566
11609
|
const [contentRoot, setContentRoot] = useState(null);
|
|
11610
|
+
const baseExtensions = props.disableImageExtension ? bundledExtensionsWithoutImage : bundledExtensions;
|
|
10567
11611
|
const allExtensionsRef = useRef([
|
|
10568
|
-
...
|
|
11612
|
+
...baseExtensions,
|
|
10569
11613
|
...props.extensions ?? []
|
|
10570
11614
|
]);
|
|
10571
11615
|
useEffect(() => {
|
|
@@ -10723,6 +11767,10 @@ const CakeEditor = forwardRef(
|
|
|
10723
11767
|
}
|
|
10724
11768
|
return { start: selection.start, end: selection.end };
|
|
10725
11769
|
},
|
|
11770
|
+
getCursorLength: () => {
|
|
11771
|
+
var _a;
|
|
11772
|
+
return ((_a = engineRef.current) == null ? void 0 : _a.getCursorLength()) ?? 0;
|
|
11773
|
+
},
|
|
10726
11774
|
insertText: (text) => {
|
|
10727
11775
|
var _a;
|
|
10728
11776
|
(_a = engineRef.current) == null ? void 0 : _a.insertText(text);
|
|
@@ -10738,10 +11786,10 @@ const CakeEditor = forwardRef(
|
|
|
10738
11786
|
containerStyle.position = "relative";
|
|
10739
11787
|
}
|
|
10740
11788
|
const containerClassName = props.className ? `cake ${props.className}` : "cake";
|
|
10741
|
-
const overlayContext =
|
|
11789
|
+
const overlayContext = containerRef.current && contentRoot ? {
|
|
10742
11790
|
container: containerRef.current,
|
|
10743
11791
|
contentRoot,
|
|
10744
|
-
overlayRoot,
|
|
11792
|
+
overlayRoot: overlayRoot ?? void 0,
|
|
10745
11793
|
toOverlayRect: (rect) => {
|
|
10746
11794
|
var _a;
|
|
10747
11795
|
const containerRect = (_a = containerRef.current) == null ? void 0 : _a.getBoundingClientRect();
|
|
@@ -10776,10 +11824,17 @@ const CakeEditor = forwardRef(
|
|
|
10776
11824
|
}
|
|
10777
11825
|
const focus = selection.start === selection.end ? selection.start : Math.max(selection.start, selection.end);
|
|
10778
11826
|
return { start: focus, end: focus };
|
|
11827
|
+
},
|
|
11828
|
+
executeCommand: (command) => {
|
|
11829
|
+
var _a;
|
|
11830
|
+
return ((_a = engineRef.current) == null ? void 0 : _a.executeCommand(command)) ?? false;
|
|
10779
11831
|
}
|
|
10780
11832
|
} : null;
|
|
10781
|
-
|
|
10782
|
-
|
|
11833
|
+
const hasOverlayExtensions = allExtensionsRef.current.some(
|
|
11834
|
+
(ext) => ext.renderOverlay
|
|
11835
|
+
);
|
|
11836
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { position: "relative", height: "100%" }, children: [
|
|
11837
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
10783
11838
|
"div",
|
|
10784
11839
|
{
|
|
10785
11840
|
ref: containerRef,
|
|
@@ -10790,35 +11845,12 @@ const CakeEditor = forwardRef(
|
|
|
10790
11845
|
var _a;
|
|
10791
11846
|
(_a = props.onBlur) == null ? void 0 : _a.call(props, event.nativeEvent);
|
|
10792
11847
|
}
|
|
10793
|
-
}
|
|
10794
|
-
void 0,
|
|
10795
|
-
false,
|
|
10796
|
-
{
|
|
10797
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/react/CakeEditor.tsx",
|
|
10798
|
-
lineNumber: 351,
|
|
10799
|
-
columnNumber: 9
|
|
10800
|
-
},
|
|
10801
|
-
this
|
|
11848
|
+
}
|
|
10802
11849
|
),
|
|
10803
|
-
|
|
10804
|
-
/* @__PURE__ */
|
|
10805
|
-
(extension) => extension.renderOverlay ? /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(Fragment, { children: extension.renderOverlay(overlayContext) }, extension.name, false, {
|
|
10806
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/react/CakeEditor.tsx",
|
|
10807
|
-
lineNumber: 365,
|
|
10808
|
-
columnNumber: 21
|
|
10809
|
-
}, this) : null
|
|
10810
|
-
) }, void 0, false, {
|
|
10811
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/react/CakeEditor.tsx",
|
|
10812
|
-
lineNumber: 362,
|
|
10813
|
-
columnNumber: 15
|
|
10814
|
-
}, this),
|
|
10815
|
-
overlayRoot
|
|
11850
|
+
overlayContext && hasOverlayExtensions ? allExtensionsRef.current.map(
|
|
11851
|
+
(extension) => extension.renderOverlay ? /* @__PURE__ */ jsxRuntimeExports.jsx(Fragment, { children: extension.renderOverlay(overlayContext) }, extension.name) : null
|
|
10816
11852
|
) : null
|
|
10817
|
-
] }
|
|
10818
|
-
fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/react/CakeEditor.tsx",
|
|
10819
|
-
lineNumber: 350,
|
|
10820
|
-
columnNumber: 7
|
|
10821
|
-
}, this);
|
|
11853
|
+
] });
|
|
10822
11854
|
}
|
|
10823
11855
|
);
|
|
10824
11856
|
CakeEditor.displayName = "CakeEditor";
|
|
@@ -10828,6 +11860,7 @@ export {
|
|
|
10828
11860
|
blockquoteExtension,
|
|
10829
11861
|
boldExtension,
|
|
10830
11862
|
bundledExtensions,
|
|
11863
|
+
bundledExtensionsWithoutImage,
|
|
10831
11864
|
combinedEmphasisExtension,
|
|
10832
11865
|
headingExtension,
|
|
10833
11866
|
imageExtension,
|