@blankdotpage/cake 0.1.4 → 0.1.6
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/engine/cake-engine.d.ts +5 -6
- package/dist/index.cjs +105 -233
- package/dist/index.js +105 -233
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { type CakeExtension, type EditCommand } from "../core/runtime";
|
|
|
3
3
|
import type { SelectionRect } from "./selection/selection-geometry";
|
|
4
4
|
type EngineOptions = {
|
|
5
5
|
container: HTMLElement;
|
|
6
|
+
contentRoot?: HTMLElement;
|
|
6
7
|
value: string;
|
|
7
8
|
selection?: Selection;
|
|
8
9
|
extensions?: CakeExtension[];
|
|
@@ -24,8 +25,6 @@ export declare class CakeEngine {
|
|
|
24
25
|
private runtime;
|
|
25
26
|
private extensions;
|
|
26
27
|
private _state;
|
|
27
|
-
private originalCaretRangeFromPoint;
|
|
28
|
-
private patchedCaretRangeFromPoint;
|
|
29
28
|
private get state();
|
|
30
29
|
private set state(value);
|
|
31
30
|
private contentRoot;
|
|
@@ -56,6 +55,7 @@ export declare class CakeEngine {
|
|
|
56
55
|
private spellCheckEnabled;
|
|
57
56
|
private extensionsRoot;
|
|
58
57
|
private placeholderRoot;
|
|
58
|
+
private resizeObserver;
|
|
59
59
|
private lastFocusRect;
|
|
60
60
|
private verticalNavGoalX;
|
|
61
61
|
private lastRenderPerf;
|
|
@@ -89,6 +89,7 @@ export declare class CakeEngine {
|
|
|
89
89
|
private pointerDownPosition;
|
|
90
90
|
private hasMovedSincePointerDown;
|
|
91
91
|
private lastTouchTime;
|
|
92
|
+
private isTouchDevice;
|
|
92
93
|
constructor(options: EngineOptions);
|
|
93
94
|
destroy(): void;
|
|
94
95
|
setReadOnly(readOnly: boolean): void;
|
|
@@ -99,7 +100,7 @@ export declare class CakeEngine {
|
|
|
99
100
|
getFocusRect(): SelectionRect | null;
|
|
100
101
|
getContainer(): HTMLElement;
|
|
101
102
|
getContentRoot(): HTMLElement | null;
|
|
102
|
-
getOverlayRoot(): HTMLDivElement
|
|
103
|
+
getOverlayRoot(): HTMLDivElement;
|
|
103
104
|
syncPlaceholder(): void;
|
|
104
105
|
insertText(text: string): void;
|
|
105
106
|
replaceText(oldText: string, newText: string): void;
|
|
@@ -121,12 +122,10 @@ export declare class CakeEngine {
|
|
|
121
122
|
private attachDragListeners;
|
|
122
123
|
private detachDragListeners;
|
|
123
124
|
private detachListeners;
|
|
124
|
-
private installCaretRangeFromPointShim;
|
|
125
|
-
private uninstallCaretRangeFromPointShim;
|
|
126
125
|
private render;
|
|
127
126
|
private isEmptyParagraphDoc;
|
|
128
127
|
private updatePlaceholder;
|
|
129
|
-
private
|
|
128
|
+
private syncPlaceholderPosition;
|
|
130
129
|
private updateContentRootAttributes;
|
|
131
130
|
private applySelection;
|
|
132
131
|
private handleSelectionChange;
|
package/dist/index.cjs
CHANGED
|
@@ -2870,13 +2870,11 @@ function renderDocContent(doc, extensions, root) {
|
|
|
2870
2870
|
return "unknown";
|
|
2871
2871
|
}
|
|
2872
2872
|
function getElementKey(element) {
|
|
2873
|
-
if (element.
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
if (lineKind && lineKind !== blockType) {
|
|
2877
|
-
return lineKind;
|
|
2873
|
+
if (element.classList.contains("cake-line")) {
|
|
2874
|
+
if (element.hasAttribute("data-block-atom")) {
|
|
2875
|
+
return `block-atom:${element.getAttribute("data-block-atom")}`;
|
|
2878
2876
|
}
|
|
2879
|
-
return
|
|
2877
|
+
return "paragraph";
|
|
2880
2878
|
}
|
|
2881
2879
|
if (element.hasAttribute("data-block-wrapper")) {
|
|
2882
2880
|
return `block-wrapper:${element.getAttribute("data-block-wrapper")}`;
|
|
@@ -2902,11 +2900,13 @@ function renderDocContent(doc, extensions, root) {
|
|
|
2902
2900
|
if (element.classList.contains("cake-text")) {
|
|
2903
2901
|
return "text";
|
|
2904
2902
|
}
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2903
|
+
for (const cls of Array.from(element.classList)) {
|
|
2904
|
+
if (cls.startsWith("cake-inline--")) {
|
|
2905
|
+
return `inline-wrapper:${cls.slice("cake-inline--".length)}`;
|
|
2906
|
+
}
|
|
2907
|
+
if (cls.startsWith("cake-inline-atom--")) {
|
|
2908
|
+
return `inline-atom:${cls.slice("cake-inline-atom--".length)}`;
|
|
2909
|
+
}
|
|
2910
2910
|
}
|
|
2911
2911
|
return "unknown";
|
|
2912
2912
|
}
|
|
@@ -2943,11 +2943,13 @@ function renderDocContent(doc, extensions, root) {
|
|
|
2943
2943
|
if (inline.type === "inline-wrapper") {
|
|
2944
2944
|
const canReuse = existing && existing instanceof HTMLSpanElement && getInlineElementKey(existing) === getInlineKey(inline);
|
|
2945
2945
|
if (canReuse) {
|
|
2946
|
+
existing.removeAttribute("data-inline");
|
|
2947
|
+
existing.classList.add("cake-inline", `cake-inline--${inline.kind}`);
|
|
2946
2948
|
reconcileInlineChildren(existing, inline.children);
|
|
2947
2949
|
return [existing];
|
|
2948
2950
|
}
|
|
2949
2951
|
const element = document.createElement("span");
|
|
2950
|
-
element.
|
|
2952
|
+
element.classList.add("cake-inline", `cake-inline--${inline.kind}`);
|
|
2951
2953
|
for (const child of inline.children) {
|
|
2952
2954
|
for (const node of reconcileInline(child, null)) {
|
|
2953
2955
|
element.append(node);
|
|
@@ -2958,6 +2960,11 @@ function renderDocContent(doc, extensions, root) {
|
|
|
2958
2960
|
if (inline.type === "inline-atom") {
|
|
2959
2961
|
const canReuse = existing && existing instanceof HTMLSpanElement && getInlineElementKey(existing) === getInlineKey(inline);
|
|
2960
2962
|
if (canReuse) {
|
|
2963
|
+
existing.removeAttribute("data-inline-atom");
|
|
2964
|
+
existing.classList.add(
|
|
2965
|
+
"cake-inline-atom",
|
|
2966
|
+
`cake-inline-atom--${inline.kind}`
|
|
2967
|
+
);
|
|
2961
2968
|
const textNode = existing.firstChild;
|
|
2962
2969
|
if (textNode instanceof Text) {
|
|
2963
2970
|
createTextRun$1(textNode);
|
|
@@ -2965,7 +2972,10 @@ function renderDocContent(doc, extensions, root) {
|
|
|
2965
2972
|
}
|
|
2966
2973
|
}
|
|
2967
2974
|
const element = document.createElement("span");
|
|
2968
|
-
element.
|
|
2975
|
+
element.classList.add(
|
|
2976
|
+
"cake-inline-atom",
|
|
2977
|
+
`cake-inline-atom--${inline.kind}`
|
|
2978
|
+
);
|
|
2969
2979
|
const node = document.createTextNode(" ");
|
|
2970
2980
|
createTextRun$1(node);
|
|
2971
2981
|
element.append(node);
|
|
@@ -3008,6 +3018,13 @@ function renderDocContent(doc, extensions, root) {
|
|
|
3008
3018
|
context.incrementLineIndex();
|
|
3009
3019
|
if (canReuse) {
|
|
3010
3020
|
existing.setAttribute("data-line-index", String(currentLineIndex));
|
|
3021
|
+
existing.removeAttribute("data-block");
|
|
3022
|
+
delete existing.dataset.lineKind;
|
|
3023
|
+
delete existing.dataset.headingLevel;
|
|
3024
|
+
delete existing.dataset.headingPlaceholder;
|
|
3025
|
+
existing.removeAttribute("aria-placeholder");
|
|
3026
|
+
existing.className = "cake-line";
|
|
3027
|
+
existing.removeAttribute("style");
|
|
3011
3028
|
if (block.content.length === 0) {
|
|
3012
3029
|
const firstChild = existing.firstChild;
|
|
3013
3030
|
if (firstChild instanceof Text && existing.querySelector("br")) {
|
|
@@ -3028,10 +3045,8 @@ function renderDocContent(doc, extensions, root) {
|
|
|
3028
3045
|
return [existing];
|
|
3029
3046
|
}
|
|
3030
3047
|
const element = document.createElement("div");
|
|
3031
|
-
element.setAttribute("data-block", "paragraph");
|
|
3032
3048
|
element.setAttribute("data-line-index", String(currentLineIndex));
|
|
3033
3049
|
element.classList.add("cake-line");
|
|
3034
|
-
element.dataset.lineKind = "paragraph";
|
|
3035
3050
|
if (block.content.length === 0) {
|
|
3036
3051
|
const textNode = document.createTextNode("");
|
|
3037
3052
|
createTextRun$1(textNode);
|
|
@@ -4639,18 +4654,16 @@ const headingExtension = defineExtension({
|
|
|
4639
4654
|
const level = typeof ((_a = block.data) == null ? void 0 : _a.level) === "number" ? block.data.level : 1;
|
|
4640
4655
|
const normalizedLevel = Math.max(1, Math.min(3, level));
|
|
4641
4656
|
const lineElement = document.createElement("div");
|
|
4642
|
-
lineElement.setAttribute("data-block", "paragraph");
|
|
4643
4657
|
lineElement.setAttribute("data-line-index", String(context.getLineIndex()));
|
|
4644
4658
|
lineElement.classList.add(
|
|
4645
4659
|
"cake-line",
|
|
4646
4660
|
"is-heading",
|
|
4647
4661
|
`is-heading-${normalizedLevel}`
|
|
4648
4662
|
);
|
|
4649
|
-
lineElement.dataset.lineKind = "heading";
|
|
4650
|
-
lineElement.dataset.headingLevel = String(normalizedLevel);
|
|
4651
4663
|
context.incrementLineIndex();
|
|
4652
4664
|
const paragraph = block.blocks[0];
|
|
4653
4665
|
if ((paragraph == null ? void 0 : paragraph.type) === "paragraph" && paragraph.content.length > 0) {
|
|
4666
|
+
lineElement.removeAttribute("aria-placeholder");
|
|
4654
4667
|
const mergedContent = mergeInlineForRender(paragraph.content);
|
|
4655
4668
|
for (const inline of mergedContent) {
|
|
4656
4669
|
for (const node of context.renderInline(inline)) {
|
|
@@ -4658,7 +4671,10 @@ const headingExtension = defineExtension({
|
|
|
4658
4671
|
}
|
|
4659
4672
|
}
|
|
4660
4673
|
} else {
|
|
4661
|
-
lineElement.
|
|
4674
|
+
lineElement.setAttribute(
|
|
4675
|
+
"aria-placeholder",
|
|
4676
|
+
`Heading ${normalizedLevel}`
|
|
4677
|
+
);
|
|
4662
4678
|
const node = document.createTextNode("");
|
|
4663
4679
|
context.createTextRun(node);
|
|
4664
4680
|
lineElement.append(node);
|
|
@@ -5665,7 +5681,6 @@ const listExtension = defineExtension({
|
|
|
5665
5681
|
return null;
|
|
5666
5682
|
}
|
|
5667
5683
|
const element = document.createElement("div");
|
|
5668
|
-
element.setAttribute("data-block", "paragraph");
|
|
5669
5684
|
element.setAttribute("data-line-index", String(context.getLineIndex()));
|
|
5670
5685
|
element.classList.add("cake-line", "is-list");
|
|
5671
5686
|
context.incrementLineIndex();
|
|
@@ -7601,8 +7616,6 @@ const HISTORY_GROUPING_INTERVAL_MS = 500;
|
|
|
7601
7616
|
const MAX_UNDO_STACK_SIZE = 100;
|
|
7602
7617
|
class CakeEngine {
|
|
7603
7618
|
constructor(options) {
|
|
7604
|
-
this.originalCaretRangeFromPoint = null;
|
|
7605
|
-
this.patchedCaretRangeFromPoint = null;
|
|
7606
7619
|
this.contentRoot = null;
|
|
7607
7620
|
this.domMap = null;
|
|
7608
7621
|
this.isApplyingSelection = false;
|
|
@@ -7627,6 +7640,7 @@ class CakeEngine {
|
|
|
7627
7640
|
this.lastSelectionRects = null;
|
|
7628
7641
|
this.extensionsRoot = null;
|
|
7629
7642
|
this.placeholderRoot = null;
|
|
7643
|
+
this.resizeObserver = null;
|
|
7630
7644
|
this.lastFocusRect = null;
|
|
7631
7645
|
this.verticalNavGoalX = null;
|
|
7632
7646
|
this.lastRenderPerf = null;
|
|
@@ -7664,6 +7678,7 @@ class CakeEngine {
|
|
|
7664
7678
|
this.hasMovedSincePointerDown = false;
|
|
7665
7679
|
this.lastTouchTime = 0;
|
|
7666
7680
|
this.container = options.container;
|
|
7681
|
+
this.contentRoot = options.contentRoot ?? null;
|
|
7667
7682
|
this.extensions = options.extensions ?? bundledExtensions;
|
|
7668
7683
|
this.runtime = createRuntime(this.extensions);
|
|
7669
7684
|
this.state = this.runtime.createState(
|
|
@@ -7676,7 +7691,6 @@ class CakeEngine {
|
|
|
7676
7691
|
this.spellCheckEnabled = options.spellCheckEnabled ?? true;
|
|
7677
7692
|
this.render();
|
|
7678
7693
|
this.attachListeners();
|
|
7679
|
-
this.installCaretRangeFromPointShim();
|
|
7680
7694
|
}
|
|
7681
7695
|
get state() {
|
|
7682
7696
|
return this._state;
|
|
@@ -7696,9 +7710,13 @@ class CakeEngine {
|
|
|
7696
7710
|
}
|
|
7697
7711
|
return target instanceof Node && (target === this.contentRoot || this.contentRoot.contains(target));
|
|
7698
7712
|
}
|
|
7713
|
+
// Detect if this is a touch-primary device (mobile/tablet)
|
|
7714
|
+
// We check for touch support AND coarse pointer to exclude laptops with touchscreens
|
|
7715
|
+
isTouchDevice() {
|
|
7716
|
+
return "ontouchstart" in window && window.matchMedia("(pointer: coarse)").matches;
|
|
7717
|
+
}
|
|
7699
7718
|
destroy() {
|
|
7700
7719
|
this.detachListeners();
|
|
7701
|
-
this.uninstallCaretRangeFromPointShim();
|
|
7702
7720
|
this.clearCaretBlinkTimer();
|
|
7703
7721
|
if (this.overlayUpdateId !== null) {
|
|
7704
7722
|
window.cancelAnimationFrame(this.overlayUpdateId);
|
|
@@ -7736,7 +7754,7 @@ class CakeEngine {
|
|
|
7736
7754
|
return this.contentRoot;
|
|
7737
7755
|
}
|
|
7738
7756
|
getOverlayRoot() {
|
|
7739
|
-
return this.
|
|
7757
|
+
return this.ensureExtensionsRoot();
|
|
7740
7758
|
}
|
|
7741
7759
|
// Placeholder text is provided by the caller via the container's
|
|
7742
7760
|
// `data-placeholder` attribute (set by the React wrapper).
|
|
@@ -7890,6 +7908,11 @@ class CakeEngine {
|
|
|
7890
7908
|
);
|
|
7891
7909
|
this.container.addEventListener("scroll", this.handleScrollBound);
|
|
7892
7910
|
window.addEventListener("resize", this.handleResizeBound);
|
|
7911
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
7912
|
+
this.syncPlaceholderPosition();
|
|
7913
|
+
this.scheduleOverlayUpdate();
|
|
7914
|
+
});
|
|
7915
|
+
this.resizeObserver.observe(this.container);
|
|
7893
7916
|
this.container.addEventListener("click", this.handleClickBound);
|
|
7894
7917
|
this.container.addEventListener("keydown", this.handleKeyDownBound);
|
|
7895
7918
|
this.container.addEventListener("paste", this.handlePasteBound);
|
|
@@ -7921,6 +7944,7 @@ class CakeEngine {
|
|
|
7921
7944
|
this.contentRoot.removeEventListener("dragend", this.handleDragEndBound);
|
|
7922
7945
|
}
|
|
7923
7946
|
detachListeners() {
|
|
7947
|
+
var _a;
|
|
7924
7948
|
this.container.removeEventListener(
|
|
7925
7949
|
"beforeinput",
|
|
7926
7950
|
this.handleBeforeInputBound
|
|
@@ -7940,6 +7964,8 @@ class CakeEngine {
|
|
|
7940
7964
|
);
|
|
7941
7965
|
this.container.removeEventListener("scroll", this.handleScrollBound);
|
|
7942
7966
|
window.removeEventListener("resize", this.handleResizeBound);
|
|
7967
|
+
(_a = this.resizeObserver) == null ? void 0 : _a.disconnect();
|
|
7968
|
+
this.resizeObserver = null;
|
|
7943
7969
|
this.container.removeEventListener("click", this.handleClickBound);
|
|
7944
7970
|
this.container.removeEventListener("keydown", this.handleKeyDownBound);
|
|
7945
7971
|
this.container.removeEventListener("paste", this.handlePasteBound);
|
|
@@ -7956,80 +7982,6 @@ class CakeEngine {
|
|
|
7956
7982
|
this.container.removeEventListener("pointerup", this.handlePointerUpBound);
|
|
7957
7983
|
this.detachDragListeners();
|
|
7958
7984
|
}
|
|
7959
|
-
installCaretRangeFromPointShim() {
|
|
7960
|
-
const doc = document;
|
|
7961
|
-
if (typeof doc.caretRangeFromPoint !== "function") {
|
|
7962
|
-
return;
|
|
7963
|
-
}
|
|
7964
|
-
if (this.patchedCaretRangeFromPoint) {
|
|
7965
|
-
return;
|
|
7966
|
-
}
|
|
7967
|
-
this.originalCaretRangeFromPoint = doc.caretRangeFromPoint.bind(document);
|
|
7968
|
-
const patched = (x, y) => {
|
|
7969
|
-
var _a;
|
|
7970
|
-
const original = this.originalCaretRangeFromPoint;
|
|
7971
|
-
const range = original ? original(x, y) : null;
|
|
7972
|
-
const startNode = (range == null ? void 0 : range.startContainer) ?? null;
|
|
7973
|
-
const startLine = startNode instanceof HTMLElement ? startNode.closest("[data-line-index]") : (_a = startNode == null ? void 0 : startNode.parentElement) == null ? void 0 : _a.closest("[data-line-index]");
|
|
7974
|
-
if (startLine) {
|
|
7975
|
-
return range;
|
|
7976
|
-
}
|
|
7977
|
-
const rect = this.container.getBoundingClientRect();
|
|
7978
|
-
const isInside = x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
|
|
7979
|
-
if (!isInside || !this.domMap) {
|
|
7980
|
-
return range;
|
|
7981
|
-
}
|
|
7982
|
-
const docAny = document;
|
|
7983
|
-
let node = null;
|
|
7984
|
-
let offset = 0;
|
|
7985
|
-
if (typeof docAny.caretPositionFromPoint === "function") {
|
|
7986
|
-
const pos = docAny.caretPositionFromPoint(x, y);
|
|
7987
|
-
node = (pos == null ? void 0 : pos.offsetNode) ?? null;
|
|
7988
|
-
offset = (pos == null ? void 0 : pos.offset) ?? 0;
|
|
7989
|
-
} else if (original) {
|
|
7990
|
-
const fallback = original(x, y);
|
|
7991
|
-
node = (fallback == null ? void 0 : fallback.startContainer) ?? null;
|
|
7992
|
-
offset = (fallback == null ? void 0 : fallback.startOffset) ?? 0;
|
|
7993
|
-
}
|
|
7994
|
-
let resolved = null;
|
|
7995
|
-
if (node instanceof Text) {
|
|
7996
|
-
resolved = { node, offset };
|
|
7997
|
-
} else if (node instanceof Element) {
|
|
7998
|
-
resolved = resolveTextPoint(node, offset);
|
|
7999
|
-
}
|
|
8000
|
-
if (!resolved) {
|
|
8001
|
-
return range;
|
|
8002
|
-
}
|
|
8003
|
-
const cursor = this.domMap.cursorAtDom(resolved.node, resolved.offset);
|
|
8004
|
-
if (!cursor) {
|
|
8005
|
-
return range;
|
|
8006
|
-
}
|
|
8007
|
-
const domPoint = this.domMap.domAtCursor(
|
|
8008
|
-
cursor.cursorOffset,
|
|
8009
|
-
cursor.affinity
|
|
8010
|
-
);
|
|
8011
|
-
if (!domPoint) {
|
|
8012
|
-
return range;
|
|
8013
|
-
}
|
|
8014
|
-
const fixed = document.createRange();
|
|
8015
|
-
fixed.setStart(domPoint.node, domPoint.offset);
|
|
8016
|
-
fixed.setEnd(domPoint.node, domPoint.offset);
|
|
8017
|
-
return fixed;
|
|
8018
|
-
};
|
|
8019
|
-
this.patchedCaretRangeFromPoint = patched;
|
|
8020
|
-
doc.caretRangeFromPoint = patched;
|
|
8021
|
-
}
|
|
8022
|
-
uninstallCaretRangeFromPointShim() {
|
|
8023
|
-
if (!this.originalCaretRangeFromPoint || !this.patchedCaretRangeFromPoint) {
|
|
8024
|
-
return;
|
|
8025
|
-
}
|
|
8026
|
-
const doc = document;
|
|
8027
|
-
if (doc.caretRangeFromPoint === this.patchedCaretRangeFromPoint) {
|
|
8028
|
-
doc.caretRangeFromPoint = this.originalCaretRangeFromPoint;
|
|
8029
|
-
}
|
|
8030
|
-
this.originalCaretRangeFromPoint = null;
|
|
8031
|
-
this.patchedCaretRangeFromPoint = null;
|
|
8032
|
-
}
|
|
8033
7985
|
render() {
|
|
8034
7986
|
const perfEnabled = this.container.dataset.cakePerf === "1";
|
|
8035
7987
|
let perfStart = 0;
|
|
@@ -8048,10 +8000,32 @@ class CakeEngine {
|
|
|
8048
8000
|
}
|
|
8049
8001
|
this.contentRoot = document.createElement("div");
|
|
8050
8002
|
this.contentRoot.className = "cake-content";
|
|
8051
|
-
|
|
8003
|
+
}
|
|
8004
|
+
if (this.isTouchDevice()) {
|
|
8005
|
+
this.contentRoot.classList.add("cake-touch-mode");
|
|
8006
|
+
}
|
|
8007
|
+
this.updateContentRootAttributes();
|
|
8008
|
+
if (!this.overlayRoot) {
|
|
8052
8009
|
const overlay = this.ensureOverlayRoot();
|
|
8053
|
-
const extensionsRoot = this.
|
|
8054
|
-
this.container.
|
|
8010
|
+
const extensionsRoot = this.extensionsRoot;
|
|
8011
|
+
const existingContainerChildren = Array.from(this.container.childNodes);
|
|
8012
|
+
const isCakeManagedContainerChild = (node) => node instanceof Element && (node.classList.contains("cake-content") || node.classList.contains("cake-selection-overlay") || node.classList.contains("cake-extension-overlay") || node.classList.contains("cake-placeholder"));
|
|
8013
|
+
const preservedContainerChildren = existingContainerChildren.filter(
|
|
8014
|
+
(node) => !isCakeManagedContainerChild(node)
|
|
8015
|
+
);
|
|
8016
|
+
if (this.contentRoot.parentElement === this.container) {
|
|
8017
|
+
this.container.append(overlay);
|
|
8018
|
+
if (extensionsRoot && !extensionsRoot.isConnected) {
|
|
8019
|
+
this.container.append(extensionsRoot);
|
|
8020
|
+
}
|
|
8021
|
+
} else {
|
|
8022
|
+
this.container.replaceChildren(
|
|
8023
|
+
this.contentRoot,
|
|
8024
|
+
overlay,
|
|
8025
|
+
...extensionsRoot ? [extensionsRoot] : [],
|
|
8026
|
+
...preservedContainerChildren
|
|
8027
|
+
);
|
|
8028
|
+
}
|
|
8055
8029
|
this.attachDragListeners();
|
|
8056
8030
|
}
|
|
8057
8031
|
if (perfEnabled) {
|
|
@@ -8063,9 +8037,12 @@ class CakeEngine {
|
|
|
8063
8037
|
this.contentRoot
|
|
8064
8038
|
);
|
|
8065
8039
|
const existingChildren = Array.from(this.contentRoot.childNodes);
|
|
8066
|
-
const
|
|
8040
|
+
const isManagedChild = (node) => node instanceof Element && node.hasAttribute("data-line-index");
|
|
8041
|
+
const existingManagedChildren = existingChildren.filter(isManagedChild);
|
|
8042
|
+
const preservedChildren = existingChildren.filter((node) => !isManagedChild(node));
|
|
8043
|
+
const needsUpdate = content.length !== existingManagedChildren.length || content.some((node, i) => node !== existingManagedChildren[i]);
|
|
8067
8044
|
if (needsUpdate) {
|
|
8068
|
-
this.contentRoot.replaceChildren(...content);
|
|
8045
|
+
this.contentRoot.replaceChildren(...content, ...preservedChildren);
|
|
8069
8046
|
}
|
|
8070
8047
|
this.domMap = map;
|
|
8071
8048
|
if (perfEnabled) {
|
|
@@ -8118,6 +8095,8 @@ class CakeEngine {
|
|
|
8118
8095
|
if (!this.placeholderRoot) {
|
|
8119
8096
|
this.placeholderRoot = document.createElement("div");
|
|
8120
8097
|
this.placeholderRoot.className = "cake-placeholder";
|
|
8098
|
+
this.placeholderRoot.style.position = "absolute";
|
|
8099
|
+
this.placeholderRoot.style.pointerEvents = "none";
|
|
8121
8100
|
}
|
|
8122
8101
|
if (!shouldShow) {
|
|
8123
8102
|
if (this.placeholderRoot.isConnected) {
|
|
@@ -8127,20 +8106,21 @@ class CakeEngine {
|
|
|
8127
8106
|
return;
|
|
8128
8107
|
}
|
|
8129
8108
|
this.placeholderRoot.textContent = placeholderText ?? "";
|
|
8130
|
-
this.syncPlaceholderPadding();
|
|
8131
8109
|
if (!this.placeholderRoot.isConnected) {
|
|
8132
8110
|
this.container.prepend(this.placeholderRoot);
|
|
8133
8111
|
}
|
|
8112
|
+
this.syncPlaceholderPosition();
|
|
8134
8113
|
}
|
|
8135
|
-
|
|
8136
|
-
if (!this.placeholderRoot) {
|
|
8114
|
+
syncPlaceholderPosition() {
|
|
8115
|
+
if (!this.placeholderRoot || !this.contentRoot) {
|
|
8137
8116
|
return;
|
|
8138
8117
|
}
|
|
8139
|
-
const
|
|
8140
|
-
|
|
8141
|
-
this.placeholderRoot.style.
|
|
8142
|
-
this.placeholderRoot.style.
|
|
8143
|
-
this.placeholderRoot.style.
|
|
8118
|
+
const containerRect = this.container.getBoundingClientRect();
|
|
8119
|
+
const contentRect = this.contentRoot.getBoundingClientRect();
|
|
8120
|
+
this.placeholderRoot.style.top = `${contentRect.top - containerRect.top}px`;
|
|
8121
|
+
this.placeholderRoot.style.left = `${contentRect.left - containerRect.left}px`;
|
|
8122
|
+
this.placeholderRoot.style.width = `${contentRect.width}px`;
|
|
8123
|
+
this.placeholderRoot.style.height = `${contentRect.height}px`;
|
|
8144
8124
|
}
|
|
8145
8125
|
updateContentRootAttributes() {
|
|
8146
8126
|
if (!this.contentRoot) {
|
|
@@ -8750,81 +8730,21 @@ class CakeEngine {
|
|
|
8750
8730
|
}
|
|
8751
8731
|
handleBeforeInput(event) {
|
|
8752
8732
|
if (this.readOnly || this.isComposing || event.isComposing) {
|
|
8753
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText") {
|
|
8754
|
-
console.log("[SPELLCHECK] beforeinput ignored (readonly/composing)", {
|
|
8755
|
-
inputType: event.inputType,
|
|
8756
|
-
data: event.data,
|
|
8757
|
-
cancelable: event.cancelable,
|
|
8758
|
-
isComposing: this.isComposing,
|
|
8759
|
-
eventIsComposing: event.isComposing,
|
|
8760
|
-
readOnly: this.readOnly
|
|
8761
|
-
});
|
|
8762
|
-
}
|
|
8763
8733
|
return;
|
|
8764
8734
|
}
|
|
8765
8735
|
if (!this.isEventTargetInContentRoot(event.target)) {
|
|
8766
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText") {
|
|
8767
|
-
console.log(
|
|
8768
|
-
"[SPELLCHECK] beforeinput ignored (target outside editor)",
|
|
8769
|
-
{
|
|
8770
|
-
inputType: event.inputType,
|
|
8771
|
-
data: event.data,
|
|
8772
|
-
cancelable: event.cancelable,
|
|
8773
|
-
target: event.target
|
|
8774
|
-
}
|
|
8775
|
-
);
|
|
8776
|
-
}
|
|
8777
8736
|
return;
|
|
8778
8737
|
}
|
|
8779
8738
|
if (this.keydownHandledBeforeInput) {
|
|
8780
8739
|
this.keydownHandledBeforeInput = false;
|
|
8781
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText") {
|
|
8782
|
-
console.log(
|
|
8783
|
-
"[SPELLCHECK] beforeinput skipped (keydownHandledBeforeInput)",
|
|
8784
|
-
{
|
|
8785
|
-
inputType: event.inputType,
|
|
8786
|
-
data: event.data,
|
|
8787
|
-
cancelable: event.cancelable
|
|
8788
|
-
}
|
|
8789
|
-
);
|
|
8790
|
-
}
|
|
8791
8740
|
event.preventDefault();
|
|
8792
8741
|
return;
|
|
8793
8742
|
}
|
|
8794
8743
|
const intent = this.resolveBeforeInputIntent(event);
|
|
8795
8744
|
if (!intent) {
|
|
8796
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText") {
|
|
8797
|
-
console.log("[SPELLCHECK] beforeinput: no intent", {
|
|
8798
|
-
inputType: event.inputType,
|
|
8799
|
-
data: event.data,
|
|
8800
|
-
cancelable: event.cancelable
|
|
8801
|
-
});
|
|
8802
|
-
}
|
|
8803
8745
|
return;
|
|
8804
8746
|
}
|
|
8805
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText" && intent.type === "replace-text") {
|
|
8806
|
-
const selection = this.state.selection;
|
|
8807
|
-
const focus = selection.start === selection.end ? selection.start : Math.max(selection.start, selection.end);
|
|
8808
|
-
const preview = this.state.source.slice(
|
|
8809
|
-
Math.max(0, focus - 24),
|
|
8810
|
-
Math.min(this.state.source.length, focus + 24)
|
|
8811
|
-
);
|
|
8812
|
-
console.log("[SPELLCHECK] beforeinput: resolved intent", {
|
|
8813
|
-
inputType: event.inputType,
|
|
8814
|
-
data: event.data,
|
|
8815
|
-
cancelable: event.cancelable,
|
|
8816
|
-
intent,
|
|
8817
|
-
currentSelection: this.state.selection,
|
|
8818
|
-
sourcePreviewAroundFocus: preview
|
|
8819
|
-
});
|
|
8820
|
-
}
|
|
8821
8747
|
event.preventDefault();
|
|
8822
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText" && intent.type === "replace-text") {
|
|
8823
|
-
console.log("[SPELLCHECK] beforeinput: after preventDefault", {
|
|
8824
|
-
inputType: event.inputType,
|
|
8825
|
-
defaultPrevented: event.defaultPrevented
|
|
8826
|
-
});
|
|
8827
|
-
}
|
|
8828
8748
|
this.markBeforeInputHandled();
|
|
8829
8749
|
this.suppressSelectionChangeForTick();
|
|
8830
8750
|
this.applyInputIntent(intent);
|
|
@@ -8871,22 +8791,9 @@ class CakeEngine {
|
|
|
8871
8791
|
return;
|
|
8872
8792
|
}
|
|
8873
8793
|
if (!this.isEventTargetInContentRoot(event.target)) {
|
|
8874
|
-
if (event.inputType === "insertReplacementText") {
|
|
8875
|
-
console.log("[SPELLCHECK] input ignored (target outside editor)", {
|
|
8876
|
-
inputType: event.inputType,
|
|
8877
|
-
data: event.data,
|
|
8878
|
-
target: event.target
|
|
8879
|
-
});
|
|
8880
|
-
}
|
|
8881
8794
|
return;
|
|
8882
8795
|
}
|
|
8883
8796
|
if (this.beforeInputHandled) {
|
|
8884
|
-
if (event.inputType === "insertReplacementText") {
|
|
8885
|
-
console.log("[SPELLCHECK] input ignored (handled via beforeinput)", {
|
|
8886
|
-
inputType: event.inputType,
|
|
8887
|
-
data: event.data
|
|
8888
|
-
});
|
|
8889
|
-
}
|
|
8890
8797
|
return;
|
|
8891
8798
|
}
|
|
8892
8799
|
if (this.compositionCommit && event.inputType === "insertText") {
|
|
@@ -8998,57 +8905,20 @@ class CakeEngine {
|
|
|
8998
8905
|
return null;
|
|
8999
8906
|
}
|
|
9000
8907
|
selectionFromTargetRangesWithStatus(event) {
|
|
9001
|
-
const debug = event.inputType === "insertReplacementText";
|
|
9002
8908
|
if (!event.getTargetRanges) {
|
|
9003
|
-
if (debug) {
|
|
9004
|
-
console.log("[SPELLCHECK][targetRanges] missing getTargetRanges()", {
|
|
9005
|
-
inputType: event.inputType,
|
|
9006
|
-
data: event.data,
|
|
9007
|
-
cancelable: event.cancelable
|
|
9008
|
-
});
|
|
9009
|
-
}
|
|
9010
8909
|
return { status: "none" };
|
|
9011
8910
|
}
|
|
9012
8911
|
const ranges = event.getTargetRanges();
|
|
9013
8912
|
if (!ranges || ranges.length === 0) {
|
|
9014
|
-
if (debug) {
|
|
9015
|
-
console.log("[SPELLCHECK][targetRanges] no ranges", {
|
|
9016
|
-
inputType: event.inputType,
|
|
9017
|
-
data: event.data,
|
|
9018
|
-
cancelable: event.cancelable
|
|
9019
|
-
});
|
|
9020
|
-
}
|
|
9021
8913
|
return { status: "none" };
|
|
9022
8914
|
}
|
|
9023
8915
|
const range = ranges[0];
|
|
9024
|
-
if (debug || event.inputType === "insertText") {
|
|
9025
|
-
console.log("[SPELLCHECK][targetRanges] raw range", {
|
|
9026
|
-
inputType: event.inputType,
|
|
9027
|
-
data: event.data,
|
|
9028
|
-
cancelable: event.cancelable,
|
|
9029
|
-
startContainer: range.startContainer instanceof Element ? range.startContainer.tagName : range.startContainer.nodeName,
|
|
9030
|
-
startOffset: range.startOffset,
|
|
9031
|
-
endContainer: range.endContainer instanceof Element ? range.endContainer.tagName : range.endContainer.nodeName,
|
|
9032
|
-
endOffset: range.endOffset,
|
|
9033
|
-
startContained: this.container.contains(range.startContainer),
|
|
9034
|
-
endContained: this.container.contains(range.endContainer)
|
|
9035
|
-
});
|
|
9036
|
-
}
|
|
9037
8916
|
if (!this.container.contains(range.startContainer) || !this.container.contains(range.endContainer)) {
|
|
9038
8917
|
return { status: "invalid" };
|
|
9039
8918
|
}
|
|
9040
8919
|
const start = this.cursorFromDom(range.startContainer, range.startOffset);
|
|
9041
8920
|
const end = this.cursorFromDom(range.endContainer, range.endOffset);
|
|
9042
8921
|
if (!start || !end) {
|
|
9043
|
-
if (debug || event.inputType === "insertText") {
|
|
9044
|
-
console.log("[SPELLCHECK][targetRanges] cursorFromDom failed", {
|
|
9045
|
-
inputType: event.inputType,
|
|
9046
|
-
data: event.data,
|
|
9047
|
-
cancelable: event.cancelable,
|
|
9048
|
-
start,
|
|
9049
|
-
end
|
|
9050
|
-
});
|
|
9051
|
-
}
|
|
9052
8922
|
return { status: "invalid" };
|
|
9053
8923
|
}
|
|
9054
8924
|
const affinity = start.cursorOffset === end.cursorOffset ? end.affinity : "forward";
|
|
@@ -9799,9 +9669,7 @@ class CakeEngine {
|
|
|
9799
9669
|
return this.state.source;
|
|
9800
9670
|
}
|
|
9801
9671
|
const blocks = Array.from(
|
|
9802
|
-
this.contentRoot.querySelectorAll(
|
|
9803
|
-
'[data-block="paragraph"]'
|
|
9804
|
-
)
|
|
9672
|
+
this.contentRoot.querySelectorAll(".cake-line")
|
|
9805
9673
|
);
|
|
9806
9674
|
if (blocks.length === 0) {
|
|
9807
9675
|
return this.contentRoot.textContent ?? "";
|
|
@@ -10054,6 +9922,9 @@ class CakeEngine {
|
|
|
10054
9922
|
root.style.zIndex = "50";
|
|
10055
9923
|
root.style.overflow = "hidden";
|
|
10056
9924
|
this.extensionsRoot = root;
|
|
9925
|
+
if (this.overlayRoot && !root.isConnected) {
|
|
9926
|
+
this.container.append(root);
|
|
9927
|
+
}
|
|
10057
9928
|
return root;
|
|
10058
9929
|
}
|
|
10059
9930
|
updateExtensionsOverlayPosition() {
|
|
@@ -10075,8 +9946,13 @@ class CakeEngine {
|
|
|
10075
9946
|
if (!this.overlayRoot || !this.contentRoot) {
|
|
10076
9947
|
return;
|
|
10077
9948
|
}
|
|
9949
|
+
if (!this.hasFocus()) {
|
|
9950
|
+
this.updateCaret(null);
|
|
9951
|
+
this.syncSelectionRects([]);
|
|
9952
|
+
return;
|
|
9953
|
+
}
|
|
10078
9954
|
const isRecentTouch = Date.now() - this.lastTouchTime < 2e3;
|
|
10079
|
-
if (isRecentTouch) {
|
|
9955
|
+
if (this.isTouchDevice() || isRecentTouch) {
|
|
10080
9956
|
this.contentRoot.classList.add("cake-touch-mode");
|
|
10081
9957
|
this.updateCaret(null);
|
|
10082
9958
|
this.syncSelectionRects([]);
|
|
@@ -11614,13 +11490,15 @@ const CakeEditor = require$$0.forwardRef(
|
|
|
11614
11490
|
const onSelectionChangeRef = require$$0.useRef(props.onSelectionChange);
|
|
11615
11491
|
const lastEmittedValueRef = require$$0.useRef(null);
|
|
11616
11492
|
const lastEmittedSelectionRef = require$$0.useRef(null);
|
|
11617
|
-
const [overlayRoot, setOverlayRoot] = require$$0.useState(null);
|
|
11618
11493
|
const [contentRoot, setContentRoot] = require$$0.useState(null);
|
|
11619
11494
|
const baseExtensions = props.disableImageExtension ? bundledExtensionsWithoutImage : bundledExtensions;
|
|
11620
11495
|
const allExtensionsRef = require$$0.useRef([
|
|
11621
11496
|
...baseExtensions,
|
|
11622
11497
|
...props.extensions ?? []
|
|
11623
11498
|
]);
|
|
11499
|
+
const hasOverlayExtensions = allExtensionsRef.current.some(
|
|
11500
|
+
(ext) => ext.renderOverlay
|
|
11501
|
+
);
|
|
11624
11502
|
require$$0.useEffect(() => {
|
|
11625
11503
|
onChangeRef.current = props.onChange;
|
|
11626
11504
|
onSelectionChangeRef.current = props.onSelectionChange;
|
|
@@ -11665,12 +11543,10 @@ const CakeEditor = require$$0.forwardRef(
|
|
|
11665
11543
|
}
|
|
11666
11544
|
});
|
|
11667
11545
|
engineRef.current = engine;
|
|
11668
|
-
setOverlayRoot(engine.getOverlayRoot());
|
|
11669
11546
|
setContentRoot(engine.getContentRoot());
|
|
11670
11547
|
return () => {
|
|
11671
11548
|
engine.destroy();
|
|
11672
11549
|
engineRef.current = null;
|
|
11673
|
-
setOverlayRoot(null);
|
|
11674
11550
|
setContentRoot(null);
|
|
11675
11551
|
};
|
|
11676
11552
|
}, []);
|
|
@@ -11798,7 +11674,6 @@ const CakeEditor = require$$0.forwardRef(
|
|
|
11798
11674
|
const overlayContext = containerRef.current && contentRoot ? {
|
|
11799
11675
|
container: containerRef.current,
|
|
11800
11676
|
contentRoot,
|
|
11801
|
-
overlayRoot: overlayRoot ?? void 0,
|
|
11802
11677
|
toOverlayRect: (rect) => {
|
|
11803
11678
|
var _a;
|
|
11804
11679
|
const containerRect = (_a = containerRef.current) == null ? void 0 : _a.getBoundingClientRect();
|
|
@@ -11839,9 +11714,6 @@ const CakeEditor = require$$0.forwardRef(
|
|
|
11839
11714
|
return ((_a = engineRef.current) == null ? void 0 : _a.executeCommand(command)) ?? false;
|
|
11840
11715
|
}
|
|
11841
11716
|
} : null;
|
|
11842
|
-
const hasOverlayExtensions = allExtensionsRef.current.some(
|
|
11843
|
-
(ext) => ext.renderOverlay
|
|
11844
|
-
);
|
|
11845
11717
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { position: "relative", height: "100%" }, children: [
|
|
11846
11718
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
11847
11719
|
"div",
|
package/dist/index.js
CHANGED
|
@@ -2868,13 +2868,11 @@ function renderDocContent(doc, extensions, root) {
|
|
|
2868
2868
|
return "unknown";
|
|
2869
2869
|
}
|
|
2870
2870
|
function getElementKey(element) {
|
|
2871
|
-
if (element.
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
if (lineKind && lineKind !== blockType) {
|
|
2875
|
-
return lineKind;
|
|
2871
|
+
if (element.classList.contains("cake-line")) {
|
|
2872
|
+
if (element.hasAttribute("data-block-atom")) {
|
|
2873
|
+
return `block-atom:${element.getAttribute("data-block-atom")}`;
|
|
2876
2874
|
}
|
|
2877
|
-
return
|
|
2875
|
+
return "paragraph";
|
|
2878
2876
|
}
|
|
2879
2877
|
if (element.hasAttribute("data-block-wrapper")) {
|
|
2880
2878
|
return `block-wrapper:${element.getAttribute("data-block-wrapper")}`;
|
|
@@ -2900,11 +2898,13 @@ function renderDocContent(doc, extensions, root) {
|
|
|
2900
2898
|
if (element.classList.contains("cake-text")) {
|
|
2901
2899
|
return "text";
|
|
2902
2900
|
}
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2901
|
+
for (const cls of Array.from(element.classList)) {
|
|
2902
|
+
if (cls.startsWith("cake-inline--")) {
|
|
2903
|
+
return `inline-wrapper:${cls.slice("cake-inline--".length)}`;
|
|
2904
|
+
}
|
|
2905
|
+
if (cls.startsWith("cake-inline-atom--")) {
|
|
2906
|
+
return `inline-atom:${cls.slice("cake-inline-atom--".length)}`;
|
|
2907
|
+
}
|
|
2908
2908
|
}
|
|
2909
2909
|
return "unknown";
|
|
2910
2910
|
}
|
|
@@ -2941,11 +2941,13 @@ function renderDocContent(doc, extensions, root) {
|
|
|
2941
2941
|
if (inline.type === "inline-wrapper") {
|
|
2942
2942
|
const canReuse = existing && existing instanceof HTMLSpanElement && getInlineElementKey(existing) === getInlineKey(inline);
|
|
2943
2943
|
if (canReuse) {
|
|
2944
|
+
existing.removeAttribute("data-inline");
|
|
2945
|
+
existing.classList.add("cake-inline", `cake-inline--${inline.kind}`);
|
|
2944
2946
|
reconcileInlineChildren(existing, inline.children);
|
|
2945
2947
|
return [existing];
|
|
2946
2948
|
}
|
|
2947
2949
|
const element = document.createElement("span");
|
|
2948
|
-
element.
|
|
2950
|
+
element.classList.add("cake-inline", `cake-inline--${inline.kind}`);
|
|
2949
2951
|
for (const child of inline.children) {
|
|
2950
2952
|
for (const node of reconcileInline(child, null)) {
|
|
2951
2953
|
element.append(node);
|
|
@@ -2956,6 +2958,11 @@ function renderDocContent(doc, extensions, root) {
|
|
|
2956
2958
|
if (inline.type === "inline-atom") {
|
|
2957
2959
|
const canReuse = existing && existing instanceof HTMLSpanElement && getInlineElementKey(existing) === getInlineKey(inline);
|
|
2958
2960
|
if (canReuse) {
|
|
2961
|
+
existing.removeAttribute("data-inline-atom");
|
|
2962
|
+
existing.classList.add(
|
|
2963
|
+
"cake-inline-atom",
|
|
2964
|
+
`cake-inline-atom--${inline.kind}`
|
|
2965
|
+
);
|
|
2959
2966
|
const textNode = existing.firstChild;
|
|
2960
2967
|
if (textNode instanceof Text) {
|
|
2961
2968
|
createTextRun$1(textNode);
|
|
@@ -2963,7 +2970,10 @@ function renderDocContent(doc, extensions, root) {
|
|
|
2963
2970
|
}
|
|
2964
2971
|
}
|
|
2965
2972
|
const element = document.createElement("span");
|
|
2966
|
-
element.
|
|
2973
|
+
element.classList.add(
|
|
2974
|
+
"cake-inline-atom",
|
|
2975
|
+
`cake-inline-atom--${inline.kind}`
|
|
2976
|
+
);
|
|
2967
2977
|
const node = document.createTextNode(" ");
|
|
2968
2978
|
createTextRun$1(node);
|
|
2969
2979
|
element.append(node);
|
|
@@ -3006,6 +3016,13 @@ function renderDocContent(doc, extensions, root) {
|
|
|
3006
3016
|
context.incrementLineIndex();
|
|
3007
3017
|
if (canReuse) {
|
|
3008
3018
|
existing.setAttribute("data-line-index", String(currentLineIndex));
|
|
3019
|
+
existing.removeAttribute("data-block");
|
|
3020
|
+
delete existing.dataset.lineKind;
|
|
3021
|
+
delete existing.dataset.headingLevel;
|
|
3022
|
+
delete existing.dataset.headingPlaceholder;
|
|
3023
|
+
existing.removeAttribute("aria-placeholder");
|
|
3024
|
+
existing.className = "cake-line";
|
|
3025
|
+
existing.removeAttribute("style");
|
|
3009
3026
|
if (block.content.length === 0) {
|
|
3010
3027
|
const firstChild = existing.firstChild;
|
|
3011
3028
|
if (firstChild instanceof Text && existing.querySelector("br")) {
|
|
@@ -3026,10 +3043,8 @@ function renderDocContent(doc, extensions, root) {
|
|
|
3026
3043
|
return [existing];
|
|
3027
3044
|
}
|
|
3028
3045
|
const element = document.createElement("div");
|
|
3029
|
-
element.setAttribute("data-block", "paragraph");
|
|
3030
3046
|
element.setAttribute("data-line-index", String(currentLineIndex));
|
|
3031
3047
|
element.classList.add("cake-line");
|
|
3032
|
-
element.dataset.lineKind = "paragraph";
|
|
3033
3048
|
if (block.content.length === 0) {
|
|
3034
3049
|
const textNode = document.createTextNode("");
|
|
3035
3050
|
createTextRun$1(textNode);
|
|
@@ -4637,18 +4652,16 @@ const headingExtension = defineExtension({
|
|
|
4637
4652
|
const level = typeof ((_a = block.data) == null ? void 0 : _a.level) === "number" ? block.data.level : 1;
|
|
4638
4653
|
const normalizedLevel = Math.max(1, Math.min(3, level));
|
|
4639
4654
|
const lineElement = document.createElement("div");
|
|
4640
|
-
lineElement.setAttribute("data-block", "paragraph");
|
|
4641
4655
|
lineElement.setAttribute("data-line-index", String(context.getLineIndex()));
|
|
4642
4656
|
lineElement.classList.add(
|
|
4643
4657
|
"cake-line",
|
|
4644
4658
|
"is-heading",
|
|
4645
4659
|
`is-heading-${normalizedLevel}`
|
|
4646
4660
|
);
|
|
4647
|
-
lineElement.dataset.lineKind = "heading";
|
|
4648
|
-
lineElement.dataset.headingLevel = String(normalizedLevel);
|
|
4649
4661
|
context.incrementLineIndex();
|
|
4650
4662
|
const paragraph = block.blocks[0];
|
|
4651
4663
|
if ((paragraph == null ? void 0 : paragraph.type) === "paragraph" && paragraph.content.length > 0) {
|
|
4664
|
+
lineElement.removeAttribute("aria-placeholder");
|
|
4652
4665
|
const mergedContent = mergeInlineForRender(paragraph.content);
|
|
4653
4666
|
for (const inline of mergedContent) {
|
|
4654
4667
|
for (const node of context.renderInline(inline)) {
|
|
@@ -4656,7 +4669,10 @@ const headingExtension = defineExtension({
|
|
|
4656
4669
|
}
|
|
4657
4670
|
}
|
|
4658
4671
|
} else {
|
|
4659
|
-
lineElement.
|
|
4672
|
+
lineElement.setAttribute(
|
|
4673
|
+
"aria-placeholder",
|
|
4674
|
+
`Heading ${normalizedLevel}`
|
|
4675
|
+
);
|
|
4660
4676
|
const node = document.createTextNode("");
|
|
4661
4677
|
context.createTextRun(node);
|
|
4662
4678
|
lineElement.append(node);
|
|
@@ -5663,7 +5679,6 @@ const listExtension = defineExtension({
|
|
|
5663
5679
|
return null;
|
|
5664
5680
|
}
|
|
5665
5681
|
const element = document.createElement("div");
|
|
5666
|
-
element.setAttribute("data-block", "paragraph");
|
|
5667
5682
|
element.setAttribute("data-line-index", String(context.getLineIndex()));
|
|
5668
5683
|
element.classList.add("cake-line", "is-list");
|
|
5669
5684
|
context.incrementLineIndex();
|
|
@@ -7599,8 +7614,6 @@ const HISTORY_GROUPING_INTERVAL_MS = 500;
|
|
|
7599
7614
|
const MAX_UNDO_STACK_SIZE = 100;
|
|
7600
7615
|
class CakeEngine {
|
|
7601
7616
|
constructor(options) {
|
|
7602
|
-
this.originalCaretRangeFromPoint = null;
|
|
7603
|
-
this.patchedCaretRangeFromPoint = null;
|
|
7604
7617
|
this.contentRoot = null;
|
|
7605
7618
|
this.domMap = null;
|
|
7606
7619
|
this.isApplyingSelection = false;
|
|
@@ -7625,6 +7638,7 @@ class CakeEngine {
|
|
|
7625
7638
|
this.lastSelectionRects = null;
|
|
7626
7639
|
this.extensionsRoot = null;
|
|
7627
7640
|
this.placeholderRoot = null;
|
|
7641
|
+
this.resizeObserver = null;
|
|
7628
7642
|
this.lastFocusRect = null;
|
|
7629
7643
|
this.verticalNavGoalX = null;
|
|
7630
7644
|
this.lastRenderPerf = null;
|
|
@@ -7662,6 +7676,7 @@ class CakeEngine {
|
|
|
7662
7676
|
this.hasMovedSincePointerDown = false;
|
|
7663
7677
|
this.lastTouchTime = 0;
|
|
7664
7678
|
this.container = options.container;
|
|
7679
|
+
this.contentRoot = options.contentRoot ?? null;
|
|
7665
7680
|
this.extensions = options.extensions ?? bundledExtensions;
|
|
7666
7681
|
this.runtime = createRuntime(this.extensions);
|
|
7667
7682
|
this.state = this.runtime.createState(
|
|
@@ -7674,7 +7689,6 @@ class CakeEngine {
|
|
|
7674
7689
|
this.spellCheckEnabled = options.spellCheckEnabled ?? true;
|
|
7675
7690
|
this.render();
|
|
7676
7691
|
this.attachListeners();
|
|
7677
|
-
this.installCaretRangeFromPointShim();
|
|
7678
7692
|
}
|
|
7679
7693
|
get state() {
|
|
7680
7694
|
return this._state;
|
|
@@ -7694,9 +7708,13 @@ class CakeEngine {
|
|
|
7694
7708
|
}
|
|
7695
7709
|
return target instanceof Node && (target === this.contentRoot || this.contentRoot.contains(target));
|
|
7696
7710
|
}
|
|
7711
|
+
// Detect if this is a touch-primary device (mobile/tablet)
|
|
7712
|
+
// We check for touch support AND coarse pointer to exclude laptops with touchscreens
|
|
7713
|
+
isTouchDevice() {
|
|
7714
|
+
return "ontouchstart" in window && window.matchMedia("(pointer: coarse)").matches;
|
|
7715
|
+
}
|
|
7697
7716
|
destroy() {
|
|
7698
7717
|
this.detachListeners();
|
|
7699
|
-
this.uninstallCaretRangeFromPointShim();
|
|
7700
7718
|
this.clearCaretBlinkTimer();
|
|
7701
7719
|
if (this.overlayUpdateId !== null) {
|
|
7702
7720
|
window.cancelAnimationFrame(this.overlayUpdateId);
|
|
@@ -7734,7 +7752,7 @@ class CakeEngine {
|
|
|
7734
7752
|
return this.contentRoot;
|
|
7735
7753
|
}
|
|
7736
7754
|
getOverlayRoot() {
|
|
7737
|
-
return this.
|
|
7755
|
+
return this.ensureExtensionsRoot();
|
|
7738
7756
|
}
|
|
7739
7757
|
// Placeholder text is provided by the caller via the container's
|
|
7740
7758
|
// `data-placeholder` attribute (set by the React wrapper).
|
|
@@ -7888,6 +7906,11 @@ class CakeEngine {
|
|
|
7888
7906
|
);
|
|
7889
7907
|
this.container.addEventListener("scroll", this.handleScrollBound);
|
|
7890
7908
|
window.addEventListener("resize", this.handleResizeBound);
|
|
7909
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
7910
|
+
this.syncPlaceholderPosition();
|
|
7911
|
+
this.scheduleOverlayUpdate();
|
|
7912
|
+
});
|
|
7913
|
+
this.resizeObserver.observe(this.container);
|
|
7891
7914
|
this.container.addEventListener("click", this.handleClickBound);
|
|
7892
7915
|
this.container.addEventListener("keydown", this.handleKeyDownBound);
|
|
7893
7916
|
this.container.addEventListener("paste", this.handlePasteBound);
|
|
@@ -7919,6 +7942,7 @@ class CakeEngine {
|
|
|
7919
7942
|
this.contentRoot.removeEventListener("dragend", this.handleDragEndBound);
|
|
7920
7943
|
}
|
|
7921
7944
|
detachListeners() {
|
|
7945
|
+
var _a;
|
|
7922
7946
|
this.container.removeEventListener(
|
|
7923
7947
|
"beforeinput",
|
|
7924
7948
|
this.handleBeforeInputBound
|
|
@@ -7938,6 +7962,8 @@ class CakeEngine {
|
|
|
7938
7962
|
);
|
|
7939
7963
|
this.container.removeEventListener("scroll", this.handleScrollBound);
|
|
7940
7964
|
window.removeEventListener("resize", this.handleResizeBound);
|
|
7965
|
+
(_a = this.resizeObserver) == null ? void 0 : _a.disconnect();
|
|
7966
|
+
this.resizeObserver = null;
|
|
7941
7967
|
this.container.removeEventListener("click", this.handleClickBound);
|
|
7942
7968
|
this.container.removeEventListener("keydown", this.handleKeyDownBound);
|
|
7943
7969
|
this.container.removeEventListener("paste", this.handlePasteBound);
|
|
@@ -7954,80 +7980,6 @@ class CakeEngine {
|
|
|
7954
7980
|
this.container.removeEventListener("pointerup", this.handlePointerUpBound);
|
|
7955
7981
|
this.detachDragListeners();
|
|
7956
7982
|
}
|
|
7957
|
-
installCaretRangeFromPointShim() {
|
|
7958
|
-
const doc = document;
|
|
7959
|
-
if (typeof doc.caretRangeFromPoint !== "function") {
|
|
7960
|
-
return;
|
|
7961
|
-
}
|
|
7962
|
-
if (this.patchedCaretRangeFromPoint) {
|
|
7963
|
-
return;
|
|
7964
|
-
}
|
|
7965
|
-
this.originalCaretRangeFromPoint = doc.caretRangeFromPoint.bind(document);
|
|
7966
|
-
const patched = (x, y) => {
|
|
7967
|
-
var _a;
|
|
7968
|
-
const original = this.originalCaretRangeFromPoint;
|
|
7969
|
-
const range = original ? original(x, y) : null;
|
|
7970
|
-
const startNode = (range == null ? void 0 : range.startContainer) ?? null;
|
|
7971
|
-
const startLine = startNode instanceof HTMLElement ? startNode.closest("[data-line-index]") : (_a = startNode == null ? void 0 : startNode.parentElement) == null ? void 0 : _a.closest("[data-line-index]");
|
|
7972
|
-
if (startLine) {
|
|
7973
|
-
return range;
|
|
7974
|
-
}
|
|
7975
|
-
const rect = this.container.getBoundingClientRect();
|
|
7976
|
-
const isInside = x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
|
|
7977
|
-
if (!isInside || !this.domMap) {
|
|
7978
|
-
return range;
|
|
7979
|
-
}
|
|
7980
|
-
const docAny = document;
|
|
7981
|
-
let node = null;
|
|
7982
|
-
let offset = 0;
|
|
7983
|
-
if (typeof docAny.caretPositionFromPoint === "function") {
|
|
7984
|
-
const pos = docAny.caretPositionFromPoint(x, y);
|
|
7985
|
-
node = (pos == null ? void 0 : pos.offsetNode) ?? null;
|
|
7986
|
-
offset = (pos == null ? void 0 : pos.offset) ?? 0;
|
|
7987
|
-
} else if (original) {
|
|
7988
|
-
const fallback = original(x, y);
|
|
7989
|
-
node = (fallback == null ? void 0 : fallback.startContainer) ?? null;
|
|
7990
|
-
offset = (fallback == null ? void 0 : fallback.startOffset) ?? 0;
|
|
7991
|
-
}
|
|
7992
|
-
let resolved = null;
|
|
7993
|
-
if (node instanceof Text) {
|
|
7994
|
-
resolved = { node, offset };
|
|
7995
|
-
} else if (node instanceof Element) {
|
|
7996
|
-
resolved = resolveTextPoint(node, offset);
|
|
7997
|
-
}
|
|
7998
|
-
if (!resolved) {
|
|
7999
|
-
return range;
|
|
8000
|
-
}
|
|
8001
|
-
const cursor = this.domMap.cursorAtDom(resolved.node, resolved.offset);
|
|
8002
|
-
if (!cursor) {
|
|
8003
|
-
return range;
|
|
8004
|
-
}
|
|
8005
|
-
const domPoint = this.domMap.domAtCursor(
|
|
8006
|
-
cursor.cursorOffset,
|
|
8007
|
-
cursor.affinity
|
|
8008
|
-
);
|
|
8009
|
-
if (!domPoint) {
|
|
8010
|
-
return range;
|
|
8011
|
-
}
|
|
8012
|
-
const fixed = document.createRange();
|
|
8013
|
-
fixed.setStart(domPoint.node, domPoint.offset);
|
|
8014
|
-
fixed.setEnd(domPoint.node, domPoint.offset);
|
|
8015
|
-
return fixed;
|
|
8016
|
-
};
|
|
8017
|
-
this.patchedCaretRangeFromPoint = patched;
|
|
8018
|
-
doc.caretRangeFromPoint = patched;
|
|
8019
|
-
}
|
|
8020
|
-
uninstallCaretRangeFromPointShim() {
|
|
8021
|
-
if (!this.originalCaretRangeFromPoint || !this.patchedCaretRangeFromPoint) {
|
|
8022
|
-
return;
|
|
8023
|
-
}
|
|
8024
|
-
const doc = document;
|
|
8025
|
-
if (doc.caretRangeFromPoint === this.patchedCaretRangeFromPoint) {
|
|
8026
|
-
doc.caretRangeFromPoint = this.originalCaretRangeFromPoint;
|
|
8027
|
-
}
|
|
8028
|
-
this.originalCaretRangeFromPoint = null;
|
|
8029
|
-
this.patchedCaretRangeFromPoint = null;
|
|
8030
|
-
}
|
|
8031
7983
|
render() {
|
|
8032
7984
|
const perfEnabled = this.container.dataset.cakePerf === "1";
|
|
8033
7985
|
let perfStart = 0;
|
|
@@ -8046,10 +7998,32 @@ class CakeEngine {
|
|
|
8046
7998
|
}
|
|
8047
7999
|
this.contentRoot = document.createElement("div");
|
|
8048
8000
|
this.contentRoot.className = "cake-content";
|
|
8049
|
-
|
|
8001
|
+
}
|
|
8002
|
+
if (this.isTouchDevice()) {
|
|
8003
|
+
this.contentRoot.classList.add("cake-touch-mode");
|
|
8004
|
+
}
|
|
8005
|
+
this.updateContentRootAttributes();
|
|
8006
|
+
if (!this.overlayRoot) {
|
|
8050
8007
|
const overlay = this.ensureOverlayRoot();
|
|
8051
|
-
const extensionsRoot = this.
|
|
8052
|
-
this.container.
|
|
8008
|
+
const extensionsRoot = this.extensionsRoot;
|
|
8009
|
+
const existingContainerChildren = Array.from(this.container.childNodes);
|
|
8010
|
+
const isCakeManagedContainerChild = (node) => node instanceof Element && (node.classList.contains("cake-content") || node.classList.contains("cake-selection-overlay") || node.classList.contains("cake-extension-overlay") || node.classList.contains("cake-placeholder"));
|
|
8011
|
+
const preservedContainerChildren = existingContainerChildren.filter(
|
|
8012
|
+
(node) => !isCakeManagedContainerChild(node)
|
|
8013
|
+
);
|
|
8014
|
+
if (this.contentRoot.parentElement === this.container) {
|
|
8015
|
+
this.container.append(overlay);
|
|
8016
|
+
if (extensionsRoot && !extensionsRoot.isConnected) {
|
|
8017
|
+
this.container.append(extensionsRoot);
|
|
8018
|
+
}
|
|
8019
|
+
} else {
|
|
8020
|
+
this.container.replaceChildren(
|
|
8021
|
+
this.contentRoot,
|
|
8022
|
+
overlay,
|
|
8023
|
+
...extensionsRoot ? [extensionsRoot] : [],
|
|
8024
|
+
...preservedContainerChildren
|
|
8025
|
+
);
|
|
8026
|
+
}
|
|
8053
8027
|
this.attachDragListeners();
|
|
8054
8028
|
}
|
|
8055
8029
|
if (perfEnabled) {
|
|
@@ -8061,9 +8035,12 @@ class CakeEngine {
|
|
|
8061
8035
|
this.contentRoot
|
|
8062
8036
|
);
|
|
8063
8037
|
const existingChildren = Array.from(this.contentRoot.childNodes);
|
|
8064
|
-
const
|
|
8038
|
+
const isManagedChild = (node) => node instanceof Element && node.hasAttribute("data-line-index");
|
|
8039
|
+
const existingManagedChildren = existingChildren.filter(isManagedChild);
|
|
8040
|
+
const preservedChildren = existingChildren.filter((node) => !isManagedChild(node));
|
|
8041
|
+
const needsUpdate = content.length !== existingManagedChildren.length || content.some((node, i) => node !== existingManagedChildren[i]);
|
|
8065
8042
|
if (needsUpdate) {
|
|
8066
|
-
this.contentRoot.replaceChildren(...content);
|
|
8043
|
+
this.contentRoot.replaceChildren(...content, ...preservedChildren);
|
|
8067
8044
|
}
|
|
8068
8045
|
this.domMap = map;
|
|
8069
8046
|
if (perfEnabled) {
|
|
@@ -8116,6 +8093,8 @@ class CakeEngine {
|
|
|
8116
8093
|
if (!this.placeholderRoot) {
|
|
8117
8094
|
this.placeholderRoot = document.createElement("div");
|
|
8118
8095
|
this.placeholderRoot.className = "cake-placeholder";
|
|
8096
|
+
this.placeholderRoot.style.position = "absolute";
|
|
8097
|
+
this.placeholderRoot.style.pointerEvents = "none";
|
|
8119
8098
|
}
|
|
8120
8099
|
if (!shouldShow) {
|
|
8121
8100
|
if (this.placeholderRoot.isConnected) {
|
|
@@ -8125,20 +8104,21 @@ class CakeEngine {
|
|
|
8125
8104
|
return;
|
|
8126
8105
|
}
|
|
8127
8106
|
this.placeholderRoot.textContent = placeholderText ?? "";
|
|
8128
|
-
this.syncPlaceholderPadding();
|
|
8129
8107
|
if (!this.placeholderRoot.isConnected) {
|
|
8130
8108
|
this.container.prepend(this.placeholderRoot);
|
|
8131
8109
|
}
|
|
8110
|
+
this.syncPlaceholderPosition();
|
|
8132
8111
|
}
|
|
8133
|
-
|
|
8134
|
-
if (!this.placeholderRoot) {
|
|
8112
|
+
syncPlaceholderPosition() {
|
|
8113
|
+
if (!this.placeholderRoot || !this.contentRoot) {
|
|
8135
8114
|
return;
|
|
8136
8115
|
}
|
|
8137
|
-
const
|
|
8138
|
-
|
|
8139
|
-
this.placeholderRoot.style.
|
|
8140
|
-
this.placeholderRoot.style.
|
|
8141
|
-
this.placeholderRoot.style.
|
|
8116
|
+
const containerRect = this.container.getBoundingClientRect();
|
|
8117
|
+
const contentRect = this.contentRoot.getBoundingClientRect();
|
|
8118
|
+
this.placeholderRoot.style.top = `${contentRect.top - containerRect.top}px`;
|
|
8119
|
+
this.placeholderRoot.style.left = `${contentRect.left - containerRect.left}px`;
|
|
8120
|
+
this.placeholderRoot.style.width = `${contentRect.width}px`;
|
|
8121
|
+
this.placeholderRoot.style.height = `${contentRect.height}px`;
|
|
8142
8122
|
}
|
|
8143
8123
|
updateContentRootAttributes() {
|
|
8144
8124
|
if (!this.contentRoot) {
|
|
@@ -8748,81 +8728,21 @@ class CakeEngine {
|
|
|
8748
8728
|
}
|
|
8749
8729
|
handleBeforeInput(event) {
|
|
8750
8730
|
if (this.readOnly || this.isComposing || event.isComposing) {
|
|
8751
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText") {
|
|
8752
|
-
console.log("[SPELLCHECK] beforeinput ignored (readonly/composing)", {
|
|
8753
|
-
inputType: event.inputType,
|
|
8754
|
-
data: event.data,
|
|
8755
|
-
cancelable: event.cancelable,
|
|
8756
|
-
isComposing: this.isComposing,
|
|
8757
|
-
eventIsComposing: event.isComposing,
|
|
8758
|
-
readOnly: this.readOnly
|
|
8759
|
-
});
|
|
8760
|
-
}
|
|
8761
8731
|
return;
|
|
8762
8732
|
}
|
|
8763
8733
|
if (!this.isEventTargetInContentRoot(event.target)) {
|
|
8764
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText") {
|
|
8765
|
-
console.log(
|
|
8766
|
-
"[SPELLCHECK] beforeinput ignored (target outside editor)",
|
|
8767
|
-
{
|
|
8768
|
-
inputType: event.inputType,
|
|
8769
|
-
data: event.data,
|
|
8770
|
-
cancelable: event.cancelable,
|
|
8771
|
-
target: event.target
|
|
8772
|
-
}
|
|
8773
|
-
);
|
|
8774
|
-
}
|
|
8775
8734
|
return;
|
|
8776
8735
|
}
|
|
8777
8736
|
if (this.keydownHandledBeforeInput) {
|
|
8778
8737
|
this.keydownHandledBeforeInput = false;
|
|
8779
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText") {
|
|
8780
|
-
console.log(
|
|
8781
|
-
"[SPELLCHECK] beforeinput skipped (keydownHandledBeforeInput)",
|
|
8782
|
-
{
|
|
8783
|
-
inputType: event.inputType,
|
|
8784
|
-
data: event.data,
|
|
8785
|
-
cancelable: event.cancelable
|
|
8786
|
-
}
|
|
8787
|
-
);
|
|
8788
|
-
}
|
|
8789
8738
|
event.preventDefault();
|
|
8790
8739
|
return;
|
|
8791
8740
|
}
|
|
8792
8741
|
const intent = this.resolveBeforeInputIntent(event);
|
|
8793
8742
|
if (!intent) {
|
|
8794
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText") {
|
|
8795
|
-
console.log("[SPELLCHECK] beforeinput: no intent", {
|
|
8796
|
-
inputType: event.inputType,
|
|
8797
|
-
data: event.data,
|
|
8798
|
-
cancelable: event.cancelable
|
|
8799
|
-
});
|
|
8800
|
-
}
|
|
8801
8743
|
return;
|
|
8802
8744
|
}
|
|
8803
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText" && intent.type === "replace-text") {
|
|
8804
|
-
const selection = this.state.selection;
|
|
8805
|
-
const focus = selection.start === selection.end ? selection.start : Math.max(selection.start, selection.end);
|
|
8806
|
-
const preview = this.state.source.slice(
|
|
8807
|
-
Math.max(0, focus - 24),
|
|
8808
|
-
Math.min(this.state.source.length, focus + 24)
|
|
8809
|
-
);
|
|
8810
|
-
console.log("[SPELLCHECK] beforeinput: resolved intent", {
|
|
8811
|
-
inputType: event.inputType,
|
|
8812
|
-
data: event.data,
|
|
8813
|
-
cancelable: event.cancelable,
|
|
8814
|
-
intent,
|
|
8815
|
-
currentSelection: this.state.selection,
|
|
8816
|
-
sourcePreviewAroundFocus: preview
|
|
8817
|
-
});
|
|
8818
|
-
}
|
|
8819
8745
|
event.preventDefault();
|
|
8820
|
-
if (event.inputType === "insertReplacementText" || event.inputType === "insertText" && intent.type === "replace-text") {
|
|
8821
|
-
console.log("[SPELLCHECK] beforeinput: after preventDefault", {
|
|
8822
|
-
inputType: event.inputType,
|
|
8823
|
-
defaultPrevented: event.defaultPrevented
|
|
8824
|
-
});
|
|
8825
|
-
}
|
|
8826
8746
|
this.markBeforeInputHandled();
|
|
8827
8747
|
this.suppressSelectionChangeForTick();
|
|
8828
8748
|
this.applyInputIntent(intent);
|
|
@@ -8869,22 +8789,9 @@ class CakeEngine {
|
|
|
8869
8789
|
return;
|
|
8870
8790
|
}
|
|
8871
8791
|
if (!this.isEventTargetInContentRoot(event.target)) {
|
|
8872
|
-
if (event.inputType === "insertReplacementText") {
|
|
8873
|
-
console.log("[SPELLCHECK] input ignored (target outside editor)", {
|
|
8874
|
-
inputType: event.inputType,
|
|
8875
|
-
data: event.data,
|
|
8876
|
-
target: event.target
|
|
8877
|
-
});
|
|
8878
|
-
}
|
|
8879
8792
|
return;
|
|
8880
8793
|
}
|
|
8881
8794
|
if (this.beforeInputHandled) {
|
|
8882
|
-
if (event.inputType === "insertReplacementText") {
|
|
8883
|
-
console.log("[SPELLCHECK] input ignored (handled via beforeinput)", {
|
|
8884
|
-
inputType: event.inputType,
|
|
8885
|
-
data: event.data
|
|
8886
|
-
});
|
|
8887
|
-
}
|
|
8888
8795
|
return;
|
|
8889
8796
|
}
|
|
8890
8797
|
if (this.compositionCommit && event.inputType === "insertText") {
|
|
@@ -8996,57 +8903,20 @@ class CakeEngine {
|
|
|
8996
8903
|
return null;
|
|
8997
8904
|
}
|
|
8998
8905
|
selectionFromTargetRangesWithStatus(event) {
|
|
8999
|
-
const debug = event.inputType === "insertReplacementText";
|
|
9000
8906
|
if (!event.getTargetRanges) {
|
|
9001
|
-
if (debug) {
|
|
9002
|
-
console.log("[SPELLCHECK][targetRanges] missing getTargetRanges()", {
|
|
9003
|
-
inputType: event.inputType,
|
|
9004
|
-
data: event.data,
|
|
9005
|
-
cancelable: event.cancelable
|
|
9006
|
-
});
|
|
9007
|
-
}
|
|
9008
8907
|
return { status: "none" };
|
|
9009
8908
|
}
|
|
9010
8909
|
const ranges = event.getTargetRanges();
|
|
9011
8910
|
if (!ranges || ranges.length === 0) {
|
|
9012
|
-
if (debug) {
|
|
9013
|
-
console.log("[SPELLCHECK][targetRanges] no ranges", {
|
|
9014
|
-
inputType: event.inputType,
|
|
9015
|
-
data: event.data,
|
|
9016
|
-
cancelable: event.cancelable
|
|
9017
|
-
});
|
|
9018
|
-
}
|
|
9019
8911
|
return { status: "none" };
|
|
9020
8912
|
}
|
|
9021
8913
|
const range = ranges[0];
|
|
9022
|
-
if (debug || event.inputType === "insertText") {
|
|
9023
|
-
console.log("[SPELLCHECK][targetRanges] raw range", {
|
|
9024
|
-
inputType: event.inputType,
|
|
9025
|
-
data: event.data,
|
|
9026
|
-
cancelable: event.cancelable,
|
|
9027
|
-
startContainer: range.startContainer instanceof Element ? range.startContainer.tagName : range.startContainer.nodeName,
|
|
9028
|
-
startOffset: range.startOffset,
|
|
9029
|
-
endContainer: range.endContainer instanceof Element ? range.endContainer.tagName : range.endContainer.nodeName,
|
|
9030
|
-
endOffset: range.endOffset,
|
|
9031
|
-
startContained: this.container.contains(range.startContainer),
|
|
9032
|
-
endContained: this.container.contains(range.endContainer)
|
|
9033
|
-
});
|
|
9034
|
-
}
|
|
9035
8914
|
if (!this.container.contains(range.startContainer) || !this.container.contains(range.endContainer)) {
|
|
9036
8915
|
return { status: "invalid" };
|
|
9037
8916
|
}
|
|
9038
8917
|
const start = this.cursorFromDom(range.startContainer, range.startOffset);
|
|
9039
8918
|
const end = this.cursorFromDom(range.endContainer, range.endOffset);
|
|
9040
8919
|
if (!start || !end) {
|
|
9041
|
-
if (debug || event.inputType === "insertText") {
|
|
9042
|
-
console.log("[SPELLCHECK][targetRanges] cursorFromDom failed", {
|
|
9043
|
-
inputType: event.inputType,
|
|
9044
|
-
data: event.data,
|
|
9045
|
-
cancelable: event.cancelable,
|
|
9046
|
-
start,
|
|
9047
|
-
end
|
|
9048
|
-
});
|
|
9049
|
-
}
|
|
9050
8920
|
return { status: "invalid" };
|
|
9051
8921
|
}
|
|
9052
8922
|
const affinity = start.cursorOffset === end.cursorOffset ? end.affinity : "forward";
|
|
@@ -9797,9 +9667,7 @@ class CakeEngine {
|
|
|
9797
9667
|
return this.state.source;
|
|
9798
9668
|
}
|
|
9799
9669
|
const blocks = Array.from(
|
|
9800
|
-
this.contentRoot.querySelectorAll(
|
|
9801
|
-
'[data-block="paragraph"]'
|
|
9802
|
-
)
|
|
9670
|
+
this.contentRoot.querySelectorAll(".cake-line")
|
|
9803
9671
|
);
|
|
9804
9672
|
if (blocks.length === 0) {
|
|
9805
9673
|
return this.contentRoot.textContent ?? "";
|
|
@@ -10052,6 +9920,9 @@ class CakeEngine {
|
|
|
10052
9920
|
root.style.zIndex = "50";
|
|
10053
9921
|
root.style.overflow = "hidden";
|
|
10054
9922
|
this.extensionsRoot = root;
|
|
9923
|
+
if (this.overlayRoot && !root.isConnected) {
|
|
9924
|
+
this.container.append(root);
|
|
9925
|
+
}
|
|
10055
9926
|
return root;
|
|
10056
9927
|
}
|
|
10057
9928
|
updateExtensionsOverlayPosition() {
|
|
@@ -10073,8 +9944,13 @@ class CakeEngine {
|
|
|
10073
9944
|
if (!this.overlayRoot || !this.contentRoot) {
|
|
10074
9945
|
return;
|
|
10075
9946
|
}
|
|
9947
|
+
if (!this.hasFocus()) {
|
|
9948
|
+
this.updateCaret(null);
|
|
9949
|
+
this.syncSelectionRects([]);
|
|
9950
|
+
return;
|
|
9951
|
+
}
|
|
10076
9952
|
const isRecentTouch = Date.now() - this.lastTouchTime < 2e3;
|
|
10077
|
-
if (isRecentTouch) {
|
|
9953
|
+
if (this.isTouchDevice() || isRecentTouch) {
|
|
10078
9954
|
this.contentRoot.classList.add("cake-touch-mode");
|
|
10079
9955
|
this.updateCaret(null);
|
|
10080
9956
|
this.syncSelectionRects([]);
|
|
@@ -11612,13 +11488,15 @@ const CakeEditor = forwardRef(
|
|
|
11612
11488
|
const onSelectionChangeRef = useRef(props.onSelectionChange);
|
|
11613
11489
|
const lastEmittedValueRef = useRef(null);
|
|
11614
11490
|
const lastEmittedSelectionRef = useRef(null);
|
|
11615
|
-
const [overlayRoot, setOverlayRoot] = useState(null);
|
|
11616
11491
|
const [contentRoot, setContentRoot] = useState(null);
|
|
11617
11492
|
const baseExtensions = props.disableImageExtension ? bundledExtensionsWithoutImage : bundledExtensions;
|
|
11618
11493
|
const allExtensionsRef = useRef([
|
|
11619
11494
|
...baseExtensions,
|
|
11620
11495
|
...props.extensions ?? []
|
|
11621
11496
|
]);
|
|
11497
|
+
const hasOverlayExtensions = allExtensionsRef.current.some(
|
|
11498
|
+
(ext) => ext.renderOverlay
|
|
11499
|
+
);
|
|
11622
11500
|
useEffect(() => {
|
|
11623
11501
|
onChangeRef.current = props.onChange;
|
|
11624
11502
|
onSelectionChangeRef.current = props.onSelectionChange;
|
|
@@ -11663,12 +11541,10 @@ const CakeEditor = forwardRef(
|
|
|
11663
11541
|
}
|
|
11664
11542
|
});
|
|
11665
11543
|
engineRef.current = engine;
|
|
11666
|
-
setOverlayRoot(engine.getOverlayRoot());
|
|
11667
11544
|
setContentRoot(engine.getContentRoot());
|
|
11668
11545
|
return () => {
|
|
11669
11546
|
engine.destroy();
|
|
11670
11547
|
engineRef.current = null;
|
|
11671
|
-
setOverlayRoot(null);
|
|
11672
11548
|
setContentRoot(null);
|
|
11673
11549
|
};
|
|
11674
11550
|
}, []);
|
|
@@ -11796,7 +11672,6 @@ const CakeEditor = forwardRef(
|
|
|
11796
11672
|
const overlayContext = containerRef.current && contentRoot ? {
|
|
11797
11673
|
container: containerRef.current,
|
|
11798
11674
|
contentRoot,
|
|
11799
|
-
overlayRoot: overlayRoot ?? void 0,
|
|
11800
11675
|
toOverlayRect: (rect) => {
|
|
11801
11676
|
var _a;
|
|
11802
11677
|
const containerRect = (_a = containerRef.current) == null ? void 0 : _a.getBoundingClientRect();
|
|
@@ -11837,9 +11712,6 @@ const CakeEditor = forwardRef(
|
|
|
11837
11712
|
return ((_a = engineRef.current) == null ? void 0 : _a.executeCommand(command)) ?? false;
|
|
11838
11713
|
}
|
|
11839
11714
|
} : null;
|
|
11840
|
-
const hasOverlayExtensions = allExtensionsRef.current.some(
|
|
11841
|
-
(ext) => ext.renderOverlay
|
|
11842
|
-
);
|
|
11843
11715
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { position: "relative", height: "100%" }, children: [
|
|
11844
11716
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
11845
11717
|
"div",
|