@blankdotpage/cake 0.1.0

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.
Files changed (45) hide show
  1. package/README.md +54 -0
  2. package/dist/cake/clipboard.d.ts +1 -0
  3. package/dist/cake/core/mapping/cursor-source-map.d.ts +32 -0
  4. package/dist/cake/core/runtime.d.ts +121 -0
  5. package/dist/cake/core/types.d.ts +42 -0
  6. package/dist/cake/dom/dom-map.d.ts +21 -0
  7. package/dist/cake/dom/dom-selection.d.ts +4 -0
  8. package/dist/cake/dom/render.d.ts +14 -0
  9. package/dist/cake/dom/types.d.ts +10 -0
  10. package/dist/cake/engine/cake-engine.d.ts +208 -0
  11. package/dist/cake/engine/selection/selection-geometry-dom.d.ts +23 -0
  12. package/dist/cake/engine/selection/selection-geometry.d.ts +23 -0
  13. package/dist/cake/engine/selection/selection-layout-dom.d.ts +30 -0
  14. package/dist/cake/engine/selection/selection-layout.d.ts +54 -0
  15. package/dist/cake/engine/selection/selection-navigation.d.ts +21 -0
  16. package/dist/cake/engine/selection/visible-text.d.ts +4 -0
  17. package/dist/cake/extensions/blockquote/blockquote.d.ts +2 -0
  18. package/dist/cake/extensions/bold/bold.d.ts +2 -0
  19. package/dist/cake/extensions/bundles.d.ts +2 -0
  20. package/dist/cake/extensions/combined-emphasis/combined-emphasis.d.ts +2 -0
  21. package/dist/cake/extensions/heading/heading.d.ts +2 -0
  22. package/dist/cake/extensions/image/image.d.ts +2 -0
  23. package/dist/cake/extensions/index.d.ts +13 -0
  24. package/dist/cake/extensions/italic/italic.d.ts +2 -0
  25. package/dist/cake/extensions/link/link-popover.d.ts +10 -0
  26. package/dist/cake/extensions/link/link.d.ts +2 -0
  27. package/dist/cake/extensions/list/list-ast.d.ts +50 -0
  28. package/dist/cake/extensions/list/list.d.ts +2 -0
  29. package/dist/cake/extensions/overlay-types.d.ts +31 -0
  30. package/dist/cake/extensions/pipe-link/pipe-link.d.ts +2 -0
  31. package/dist/cake/extensions/scrollbar/index.d.ts +2 -0
  32. package/dist/cake/extensions/strikethrough/strikethrough.d.ts +2 -0
  33. package/dist/cake/extensions/types.d.ts +5 -0
  34. package/dist/cake/index.d.ts +1 -0
  35. package/dist/cake/react/CakeEditor.d.ts +8 -0
  36. package/dist/cake/shared/platform.d.ts +2 -0
  37. package/dist/cake/shared/segmenter.d.ts +12 -0
  38. package/dist/cake/shared/url.d.ts +2 -0
  39. package/dist/cake/shared/word-break.d.ts +18 -0
  40. package/dist/cake/test/harness.d.ts +53 -0
  41. package/dist/codemirror/markdown-commands.d.ts +12 -0
  42. package/dist/editor.d.ts +83 -0
  43. package/dist/index.d.ts +4 -0
  44. package/dist/index.js +92 -0
  45. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # 🍰 Cake
2
+
3
+ Cake is a small, extension-first, markdown-based editor toolkit. It’s designed
4
+ to give you a reliable plain-text source of truth while still supporting rich
5
+ behaviors (bold, links, lists, images, overlays) through extensions. The project
6
+ includes a core runtime, a DOM/engine layer, and a thin React wrapper.
7
+
8
+ ## What it does
9
+
10
+ - **Edits plain text with markdown semantics**: the underlying value is always
11
+ a markdown string, not a proprietary document model.
12
+ - **Runs on a runtime+engine split**: the runtime parses/serializes and applies
13
+ edit commands; the engine handles DOM rendering, selection, and events.
14
+ - **Extensible by design**: features are extensions (inline, block, overlay)
15
+ that can add syntax, behavior, and UI without changing the core.
16
+
17
+ ## How it’s different from other editors
18
+
19
+ - **No hidden document model**: Cake never stores a separate rich‑text AST.
20
+ The markdown string is the source of truth.
21
+ - **Extension-first architecture**: rich features are optional and removable,
22
+ rather than baked into a monolith.
23
+ - **Tested at the behavior level**: includes Vitest unit + browser tests to
24
+ validate parsing, selection, and DOM behavior.
25
+
26
+ ## When to use it
27
+
28
+ - You want **markdown as the canonical format**.
29
+ - You need **custom editing features** without forking a large editor.
30
+ - You prefer a **small surface area** and predictable behavior.
31
+
32
+ If you need a full WYSIWYG document editor with complex layout, tables, or
33
+ collaborative editing out of the box, Cake likely isn’t the right fit.
34
+
35
+ ## Development
36
+
37
+ ```bash
38
+ npm install
39
+ npm run test
40
+ npm run build
41
+ ```
42
+
43
+ ## Demo
44
+
45
+ ```bash
46
+ npm --workspace demo install
47
+ npm run demo
48
+ ```
49
+
50
+ ## Library usage
51
+
52
+ ```tsx
53
+ import { CakeEditor, defaultEditorSettings } from "@blankdotpage/cake";
54
+ ```
@@ -0,0 +1 @@
1
+ export declare function htmlToMarkdownForPaste(html: string): string;
@@ -0,0 +1,32 @@
1
+ import type { Affinity } from "../types";
2
+ export type CursorBoundary = {
3
+ sourceBackward: number;
4
+ sourceForward: number;
5
+ };
6
+ export type CursorSourceMap = {
7
+ cursorLength: number;
8
+ boundaries: CursorBoundary[];
9
+ cursorToSource(cursorOffset: number, affinity: Affinity): number;
10
+ sourceToCursor(sourceOffset: number, bias: Affinity): {
11
+ cursorOffset: number;
12
+ affinity: Affinity;
13
+ };
14
+ };
15
+ export declare function createCursorSourceMap(boundaries: CursorBoundary[], cursorLength: number): CursorSourceMap;
16
+ export declare class CursorSourceBuilder {
17
+ private sourceParts;
18
+ private boundaries;
19
+ private cursorLength;
20
+ private sourceLengthValue;
21
+ appendSourceOnly(text: string): void;
22
+ appendText(text: string): void;
23
+ appendCursorAtom(sourceText: string, cursorUnits?: number): void;
24
+ appendSerialized(serialized: {
25
+ source: string;
26
+ map: CursorSourceMap;
27
+ }): void;
28
+ build(): {
29
+ source: string;
30
+ map: CursorSourceMap;
31
+ };
32
+ }
@@ -0,0 +1,121 @@
1
+ import type { Block, Doc, Inline, Selection } from "./types";
2
+ import type { ReactElement } from "react";
3
+ import { type CursorSourceMap } from "./mapping/cursor-source-map";
4
+ import type { DomRenderContext } from "../dom/types";
5
+ import type { OverlayExtensionContext } from "../extensions/overlay-types";
6
+ type BlockParseResult = {
7
+ block: Block;
8
+ nextPos: number;
9
+ };
10
+ type InlineParseResult = {
11
+ inline: Inline;
12
+ nextPos: number;
13
+ };
14
+ export type ParseBlockResult = BlockParseResult | null;
15
+ export type ParseInlineResult = InlineParseResult | null;
16
+ export type SerializeBlockResult = {
17
+ source: string;
18
+ map: CursorSourceMap;
19
+ };
20
+ export type SerializeInlineResult = {
21
+ source: string;
22
+ map: CursorSourceMap;
23
+ };
24
+ export type EditCommand = {
25
+ type: "insert";
26
+ text: string;
27
+ } | {
28
+ type: "insert-line-break";
29
+ } | {
30
+ type: "delete-backward";
31
+ } | {
32
+ type: "delete-forward";
33
+ } | {
34
+ type: "indent";
35
+ } | {
36
+ type: "outdent";
37
+ } | {
38
+ type: "toggle-bullet-list";
39
+ } | {
40
+ type: "toggle-numbered-list";
41
+ } | {
42
+ type: "toggle-inline";
43
+ marker: string;
44
+ } | {
45
+ type: "wrap-link";
46
+ url?: string;
47
+ openPopover?: boolean;
48
+ };
49
+ export type EditResult = {
50
+ source: string;
51
+ selection: Selection;
52
+ };
53
+ export type KeyBinding = {
54
+ key: string;
55
+ meta?: boolean;
56
+ ctrl?: boolean;
57
+ alt?: boolean;
58
+ shift?: boolean;
59
+ command: EditCommand | ((state: RuntimeState) => EditCommand | null);
60
+ };
61
+ export type InlineWrapperAffinity = {
62
+ kind: string;
63
+ /**
64
+ * Whether the wrapper should be considered "active" when the caret is at its
65
+ * end boundary. For v1 parity, links are non-inclusive; emphasis is inclusive.
66
+ */
67
+ inclusive: boolean;
68
+ };
69
+ export type ExtensionContext = {
70
+ parseInline: (source: string, start: number, end: number) => Inline[];
71
+ serializeInline: (inline: Inline) => SerializeInlineResult;
72
+ serializeBlock: (block: Block) => SerializeBlockResult;
73
+ };
74
+ export type CakeExtension = {
75
+ name: string;
76
+ inlineWrapperAffinity?: InlineWrapperAffinity[];
77
+ /**
78
+ * Declares which inline wrapper kind a `toggle-inline` marker corresponds to.
79
+ * This keeps the core runtime syntax-agnostic while still allowing extension-
80
+ * specific toggle behavior at wrapper boundaries.
81
+ */
82
+ toggleInline?: {
83
+ kind: string;
84
+ markers: string[];
85
+ };
86
+ parseBlock?: (source: string, start: number, context: ExtensionContext) => ParseBlockResult;
87
+ parseInline?: (source: string, start: number, end: number, context: ExtensionContext) => ParseInlineResult;
88
+ serializeBlock?: (block: Block, context: ExtensionContext) => SerializeBlockResult | null;
89
+ serializeInline?: (inline: Inline, context: ExtensionContext) => SerializeInlineResult | null;
90
+ normalizeBlock?: (block: Block) => Block | null;
91
+ normalizeInline?: (inline: Inline) => Inline | null;
92
+ onEdit?: (command: EditCommand, state: RuntimeState) => EditResult | null;
93
+ onPasteText?: (text: string, state: RuntimeState) => EditCommand | null;
94
+ keybindings?: KeyBinding[];
95
+ renderInline?: (inline: Inline, context: DomRenderContext) => Node | Node[] | null;
96
+ renderBlock?: (block: Block, context: DomRenderContext) => Node | Node[] | null;
97
+ renderOverlay?: (context: OverlayExtensionContext) => ReactElement | null;
98
+ };
99
+ export type RuntimeState = {
100
+ source: string;
101
+ selection: Selection;
102
+ map: CursorSourceMap;
103
+ doc: Doc;
104
+ runtime: Runtime;
105
+ };
106
+ export type Runtime = {
107
+ extensions: CakeExtension[];
108
+ parse(source: string): Doc;
109
+ serialize(doc: Doc): {
110
+ source: string;
111
+ map: CursorSourceMap;
112
+ };
113
+ createState(source: string, selection?: Selection): RuntimeState;
114
+ updateSelection(state: RuntimeState, selection: Selection, options?: {
115
+ kind?: "dom" | "keyboard" | "programmatic";
116
+ }): RuntimeState;
117
+ serializeSelection(state: RuntimeState, selection: Selection): string;
118
+ applyEdit(command: EditCommand, state: RuntimeState): RuntimeState;
119
+ };
120
+ export declare function createRuntime(extensions: CakeExtension[]): Runtime;
121
+ export {};
@@ -0,0 +1,42 @@
1
+ export type Affinity = "backward" | "forward";
2
+ export type Selection = {
3
+ start: number;
4
+ end: number;
5
+ affinity?: Affinity;
6
+ };
7
+ export type Doc = {
8
+ type: "doc";
9
+ blocks: Block[];
10
+ };
11
+ export type Block = ParagraphBlock | BlockWrapperBlock | AtomBlock;
12
+ export type ParagraphBlock = {
13
+ type: "paragraph";
14
+ content: Inline[];
15
+ };
16
+ export type BlockWrapperBlock = {
17
+ type: "block-wrapper";
18
+ kind: string;
19
+ blocks: Block[];
20
+ data?: Record<string, unknown>;
21
+ };
22
+ export type AtomBlock = {
23
+ type: "block-atom";
24
+ kind: string;
25
+ data?: Record<string, unknown>;
26
+ };
27
+ export type Inline = TextInline | InlineWrapper | InlineAtom;
28
+ export type TextInline = {
29
+ type: "text";
30
+ text: string;
31
+ };
32
+ export type InlineWrapper = {
33
+ type: "inline-wrapper";
34
+ kind: string;
35
+ children: Inline[];
36
+ data?: Record<string, unknown>;
37
+ };
38
+ export type InlineAtom = {
39
+ type: "inline-atom";
40
+ kind: string;
41
+ data?: Record<string, unknown>;
42
+ };
@@ -0,0 +1,21 @@
1
+ import type { Affinity } from "../core/types";
2
+ export type TextRun = {
3
+ node: Text;
4
+ cursorStart: number;
5
+ cursorEnd: number;
6
+ boundaryOffsets: number[];
7
+ };
8
+ export type DomPoint = {
9
+ node: Text;
10
+ offset: number;
11
+ };
12
+ export type DomMap = {
13
+ runs: TextRun[];
14
+ domAtCursor(cursorOffset: number, affinity: Affinity): DomPoint | null;
15
+ cursorAtDom(node: Text, offset: number): {
16
+ cursorOffset: number;
17
+ affinity: Affinity;
18
+ } | null;
19
+ };
20
+ export declare function createTextRun(node: Text, cursorStart: number): TextRun;
21
+ export declare function createDomMap(runs: TextRun[]): DomMap;
@@ -0,0 +1,4 @@
1
+ import type { Selection } from "../core/types";
2
+ import type { DomMap } from "./dom-map";
3
+ export declare function applyDomSelection(selection: Selection, map: DomMap): void;
4
+ export declare function readDomSelection(map: DomMap): Selection | null;
@@ -0,0 +1,14 @@
1
+ import type { Doc, Inline } from "../core/types";
2
+ import type { CakeExtension } from "../core/runtime";
3
+ import { createDomMap } from "./dom-map";
4
+ export type RenderResult = {
5
+ root: HTMLElement;
6
+ map: ReturnType<typeof createDomMap>;
7
+ };
8
+ export type RenderContentResult = {
9
+ content: Node[];
10
+ map: ReturnType<typeof createDomMap>;
11
+ };
12
+ export declare function renderDocContent(doc: Doc, extensions: CakeExtension[], _root?: HTMLElement): RenderContentResult;
13
+ export declare function renderDoc(doc: Doc, extensions: CakeExtension[]): RenderResult;
14
+ export declare function mergeInlineForRender(inlines: Inline[]): Inline[];
@@ -0,0 +1,10 @@
1
+ import type { Block, Inline } from "../core/types";
2
+ import type { TextRun } from "./dom-map";
3
+ export type DomRenderContext = {
4
+ renderInline: (inline: Inline) => Node[];
5
+ renderBlock: (block: Block) => Node[];
6
+ renderBlocks: (blocks: Block[]) => Node[];
7
+ createTextRun: (node: Text) => TextRun;
8
+ getLineIndex: () => number;
9
+ incrementLineIndex: () => void;
10
+ };
@@ -0,0 +1,208 @@
1
+ import type { Selection } from "../core/types";
2
+ import { type CakeExtension, type EditCommand } from "../core/runtime";
3
+ import type { SelectionRect } from "./selection/selection-geometry";
4
+ type EngineOptions = {
5
+ container: HTMLElement;
6
+ value: string;
7
+ selection?: Selection;
8
+ extensions?: CakeExtension[];
9
+ onChange?: (value: string, selection: Selection) => void;
10
+ onSelectionChange?: (selection: Selection) => void;
11
+ readOnly?: boolean;
12
+ spellCheckEnabled?: boolean;
13
+ };
14
+ export declare class CakeEngine {
15
+ private container;
16
+ private runtime;
17
+ private extensions;
18
+ private _state;
19
+ private originalCaretRangeFromPoint;
20
+ private patchedCaretRangeFromPoint;
21
+ private get state();
22
+ private set state(value);
23
+ private contentRoot;
24
+ private domMap;
25
+ private isApplyingSelection;
26
+ private isComposing;
27
+ private beforeInputHandled;
28
+ private beforeInputResetId;
29
+ private keydownHandledBeforeInput;
30
+ private suppressSelectionChange;
31
+ private suppressSelectionChangeResetId;
32
+ private ignoreTouchNativeSelectionUntil;
33
+ private blockTrustedTextDrag;
34
+ private selectedAtomicLineIndex;
35
+ private lastAppliedSelection;
36
+ private compositionCommit;
37
+ private compositionCommitTimeoutId;
38
+ private overlayRoot;
39
+ private caretElement;
40
+ private caretBlinkTimeoutId;
41
+ private overlayUpdateId;
42
+ private scrollCaretIntoViewId;
43
+ private onChange?;
44
+ private onSelectionChange?;
45
+ private readOnly;
46
+ private spellCheckEnabled;
47
+ private extensionsRoot;
48
+ private placeholderRoot;
49
+ private lastFocusRect;
50
+ private verticalNavGoalX;
51
+ private history;
52
+ private pendingClickHit;
53
+ private isEventTargetInContentRoot;
54
+ private handleBeforeInputBound;
55
+ private handleInputBound;
56
+ private handleCompositionStartBound;
57
+ private handleCompositionEndBound;
58
+ private handleSelectionChangeBound;
59
+ private handleScrollBound;
60
+ private handleResizeBound;
61
+ private handleClickBound;
62
+ private handleKeyDownBound;
63
+ private handlePasteBound;
64
+ private handleCopyBound;
65
+ private handleCutBound;
66
+ private handlePointerDownBound;
67
+ private handlePointerMoveBound;
68
+ private handlePointerUpBound;
69
+ private handleDragStartBound;
70
+ private handleDragOverBound;
71
+ private handleDropBound;
72
+ private handleDragEndBound;
73
+ private dragState;
74
+ private dropIndicator;
75
+ private textDragState;
76
+ private selectionDragState;
77
+ private pointerDownPosition;
78
+ private hasMovedSincePointerDown;
79
+ constructor(options: EngineOptions);
80
+ destroy(): void;
81
+ setReadOnly(readOnly: boolean): void;
82
+ setSpellCheckEnabled(enabled: boolean): void;
83
+ getValue(): string;
84
+ getSelection(): Selection;
85
+ getFocusRect(): SelectionRect | null;
86
+ getContainer(): HTMLElement;
87
+ getContentRoot(): HTMLElement | null;
88
+ getOverlayRoot(): HTMLDivElement | null;
89
+ syncPlaceholder(): void;
90
+ insertText(text: string): void;
91
+ replaceText(oldText: string, newText: string): void;
92
+ setSelection(selection: Selection): void;
93
+ setValue({ value, selection }: {
94
+ value: string;
95
+ selection?: Selection;
96
+ }): void;
97
+ focus(selection?: Selection): void;
98
+ blur(): void;
99
+ hasFocus(): boolean;
100
+ selectAll(): void;
101
+ undo(): void;
102
+ redo(): void;
103
+ canUndo(): boolean;
104
+ canRedo(): boolean;
105
+ executeCommand(command: EditCommand): boolean;
106
+ private attachListeners;
107
+ private attachDragListeners;
108
+ private detachDragListeners;
109
+ private detachListeners;
110
+ private installCaretRangeFromPointShim;
111
+ private uninstallCaretRangeFromPointShim;
112
+ private render;
113
+ private isEmptyParagraphDoc;
114
+ private updatePlaceholder;
115
+ private syncPlaceholderPadding;
116
+ private updateContentRootAttributes;
117
+ private applySelection;
118
+ private handleSelectionChange;
119
+ private syncSelectionFromDom;
120
+ private adjustSelectionForAtomicBlocks;
121
+ private getAtomicBlockSelectionFromClick;
122
+ private handleClick;
123
+ private handleKeyDown;
124
+ private resolveExtensionKeybinding;
125
+ private handleCopy;
126
+ private handleCut;
127
+ private handlePaste;
128
+ private handleBeforeInput;
129
+ private applyInputIntent;
130
+ private handleInput;
131
+ private handleCompositionStart;
132
+ private handleCompositionEnd;
133
+ private resolveBeforeInputIntent;
134
+ private selectionFromTargetRangesWithStatus;
135
+ private cursorFromDom;
136
+ private applyEdit;
137
+ private handleDeleteAtomicBlockSelection;
138
+ private handleBackspaceAfterAtomicBlock;
139
+ private recordHistory;
140
+ private applySelectionUpdate;
141
+ private getLayoutForNavigation;
142
+ private moveSelectionByChar;
143
+ private moveOffsetByChar;
144
+ private moveSelectionVertically;
145
+ private moveSelectionToVisualRowStart;
146
+ private moveSelectionToVisualRowEnd;
147
+ private extendSelectionToVisualRowStart;
148
+ private extendSelectionToVisualRowEnd;
149
+ private extendSelectionToDocumentStart;
150
+ private extendSelectionToDocumentEnd;
151
+ private extendFullLineSelectionByLine;
152
+ private moveSelectionByWord;
153
+ private extendSelectionByWord;
154
+ private moveOffsetByWord;
155
+ private deleteToVisualRowStart;
156
+ private handleIndent;
157
+ private handleOutdent;
158
+ private readDomText;
159
+ /**
160
+ * Reconcile DOM text changes with the model while preserving formatting markers.
161
+ * Used when external agents (IME, Grammarly) modify the DOM directly.
162
+ *
163
+ * Strategy:
164
+ * 1. Get visible text from DOM (what was modified)
165
+ * 2. Get visible text from model (what we had)
166
+ * 3. Find the minimal diff (common prefix/suffix)
167
+ * 4. Map the changed region from cursor space to source space
168
+ * 5. Replace the corresponding source region, preserving markers
169
+ */
170
+ private reconcileDomChanges;
171
+ private resolveCollapsedReconcileAffinity;
172
+ private markBeforeInputHandled;
173
+ private suppressSelectionChangeForTick;
174
+ private markCompositionCommit;
175
+ private clearCompositionCommit;
176
+ private handleScroll;
177
+ private handleResize;
178
+ private openLinkPopoverForSelection;
179
+ private scheduleOverlayUpdate;
180
+ private flushOverlayUpdate;
181
+ private ensureOverlayRoot;
182
+ private ensureExtensionsRoot;
183
+ private updateExtensionsOverlayPosition;
184
+ private updateSelectionOverlay;
185
+ private syncSelectionRects;
186
+ private updateCaret;
187
+ private markCaretActive;
188
+ private stopCaretBlink;
189
+ private clearCaretBlinkTimer;
190
+ private scheduleScrollCaretIntoView;
191
+ private scrollCaretIntoView;
192
+ private hitTestFromClientPoint;
193
+ private handlePointerDown;
194
+ private handlePointerMove;
195
+ private handlePointerUp;
196
+ private showDropIndicator;
197
+ private hideDropIndicator;
198
+ private findClosestLineByY;
199
+ private calculateDropLineIndex;
200
+ private detectFullLineSelection;
201
+ private moveLines;
202
+ private handleDragStart;
203
+ private handleDragOver;
204
+ private handleDrop;
205
+ private handleDragEnd;
206
+ private dataTransferHasImageFile;
207
+ }
208
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { Selection } from "../../core/types";
2
+ import { type LineInfo } from "./selection-layout";
3
+ import { type SelectionRect } from "./selection-geometry";
4
+ export type SelectionGeometry = {
5
+ selectionRects: SelectionRect[];
6
+ caretRect: SelectionRect | null;
7
+ focusRect: SelectionRect | null;
8
+ };
9
+ export declare function getSelectionGeometry(params: {
10
+ root: HTMLElement;
11
+ container: HTMLElement;
12
+ docLines: LineInfo[];
13
+ selection: Selection;
14
+ }): SelectionGeometry;
15
+ export declare function getCaretRect(params: {
16
+ lineElement: HTMLElement;
17
+ lineInfo: LineInfo;
18
+ offsetInLine: number;
19
+ affinity?: "forward" | "backward";
20
+ }): {
21
+ rect: DOMRect;
22
+ lineRect: DOMRect;
23
+ } | null;
@@ -0,0 +1,23 @@
1
+ import type { Selection } from "../../core/types";
2
+ import type { LayoutModel, LayoutRect } from "./selection-layout";
3
+ export type SelectionRect = {
4
+ top: number;
5
+ left: number;
6
+ width: number;
7
+ height: number;
8
+ };
9
+ export type SelectionCaretMeasurement = {
10
+ lineRect: LayoutRect;
11
+ caretRect: LayoutRect;
12
+ lineLength: number;
13
+ lineHeight: number | null;
14
+ fontSize: number | null;
15
+ padding: {
16
+ top: number;
17
+ bottom: number;
18
+ };
19
+ nearestTextCaretHeight: number | null;
20
+ };
21
+ export type OffsetToXMeasurer = (lineIndex: number, offsetInLine: number) => number | null;
22
+ export declare function computeSelectionRects(layout: LayoutModel, selection: Selection, measurer?: OffsetToXMeasurer): SelectionRect[];
23
+ export declare function computeCaretRect(caret: SelectionCaretMeasurement): SelectionRect | null;
@@ -0,0 +1,30 @@
1
+ import type { LayoutMeasurer, LayoutModel, LayoutRect, LineInfo } from "./selection-layout";
2
+ export declare function toLayoutRect(params: {
3
+ rect: DOMRect;
4
+ containerRect: DOMRect;
5
+ scroll: {
6
+ top: number;
7
+ left: number;
8
+ };
9
+ }): LayoutRect;
10
+ export declare function createDomLayoutMeasurer(params: {
11
+ root: HTMLElement;
12
+ container: HTMLElement;
13
+ lines: LineInfo[];
14
+ }): LayoutMeasurer | null;
15
+ export declare function measureLayoutModelFromDom(params: {
16
+ lines: LineInfo[];
17
+ root: HTMLElement;
18
+ container: HTMLElement;
19
+ }): LayoutModel | null;
20
+ export declare function getLineElement(root: HTMLElement, lineIndex: number): HTMLElement | null;
21
+ export declare function resolveDomPosition(lineElement: HTMLElement, offsetInLine: number): {
22
+ node: Node;
23
+ offset: number;
24
+ };
25
+ export declare function createOffsetToXMeasurer(params: {
26
+ root: HTMLElement;
27
+ container: HTMLElement;
28
+ lines: LineInfo[];
29
+ }): (lineIndex: number, offsetInLine: number) => number | null;
30
+ export declare function cursorOffsetToDomOffset(cursorToCodeUnit: number[], offset: number): number;
@@ -0,0 +1,54 @@
1
+ import type { Doc } from "../../core/types";
2
+ export type LayoutRect = {
3
+ top: number;
4
+ left: number;
5
+ width: number;
6
+ height: number;
7
+ };
8
+ export type LayoutRow = {
9
+ startOffset: number;
10
+ endOffset: number;
11
+ rect: LayoutRect;
12
+ };
13
+ export type LineLayout = {
14
+ lineIndex: number;
15
+ lineStartOffset: number;
16
+ lineLength: number;
17
+ lineHasNewline: boolean;
18
+ lineBox: LayoutRect;
19
+ rows: LayoutRow[];
20
+ };
21
+ export type LayoutModel = {
22
+ container: LayoutRect;
23
+ lines: LineLayout[];
24
+ };
25
+ export type LineMeasurementInput = {
26
+ lineIndex: number;
27
+ lineText: string;
28
+ lineLength: number;
29
+ lineHasNewline: boolean;
30
+ top: number;
31
+ };
32
+ export type LineMeasurement = {
33
+ lineBox: LayoutRect;
34
+ rows: LayoutRow[];
35
+ };
36
+ export type LayoutMeasurer = {
37
+ container: LayoutRect;
38
+ measureLine: (input: LineMeasurementInput) => LineMeasurement;
39
+ };
40
+ export type LineInfo = {
41
+ lineIndex: number;
42
+ text: string;
43
+ cursorLength: number;
44
+ hasNewline: boolean;
45
+ cursorToCodeUnit: number[];
46
+ isAtomic: boolean;
47
+ };
48
+ export declare function buildLayoutModel(lines: LineInfo[], measurer: LayoutMeasurer): LayoutModel;
49
+ export declare function getDocLines(doc: Doc): LineInfo[];
50
+ export declare function getLineOffsets(lines: LineInfo[]): number[];
51
+ export declare function resolveOffsetToLine(lines: LineInfo[], offset: number): {
52
+ lineIndex: number;
53
+ offsetInLine: number;
54
+ };
@@ -0,0 +1,21 @@
1
+ import type { Selection } from "../../core/types";
2
+ import type { LayoutModel, LineInfo } from "./selection-layout";
3
+ type Affinity = "forward" | "backward";
4
+ export type VerticalNavigationResult = {
5
+ selection: Selection;
6
+ goalX: number;
7
+ };
8
+ export declare function moveSelectionVertically(params: {
9
+ lines: LineInfo[];
10
+ layout: LayoutModel;
11
+ selection: Selection;
12
+ direction: "up" | "down";
13
+ goalX: number | null;
14
+ focusRowIndex?: number;
15
+ hitTestCursorAt?: (x: number, y: number) => {
16
+ cursorOffset: number;
17
+ affinity: Affinity;
18
+ caretTop?: number;
19
+ } | null;
20
+ }): VerticalNavigationResult | null;
21
+ export {};
@@ -0,0 +1,4 @@
1
+ import { type LineInfo } from "./selection-layout";
2
+ export declare function getVisibleText(lines: LineInfo[]): string;
3
+ export declare function visibleOffsetToCursorOffset(lines: LineInfo[], visibleOffset: number): number | null;
4
+ export declare function cursorOffsetToVisibleOffset(lines: LineInfo[], cursorOffset: number): number;
@@ -0,0 +1,2 @@
1
+ import type { CakeExtension } from "../../core/runtime";
2
+ export declare const blockquoteExtension: CakeExtension;
@@ -0,0 +1,2 @@
1
+ import type { CakeExtension } from "../../core/runtime";
2
+ export declare const boldExtension: CakeExtension;