creo-edit 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.
- package/LICENSE +21 -0
- package/README.md +169 -0
- package/dist/clipboard/drop.d.ts +17 -0
- package/dist/clipboard/htmlParser.d.ts +18 -0
- package/dist/clipboard/htmlSerializer.d.ts +9 -0
- package/dist/commands/imageCommands.d.ts +32 -0
- package/dist/commands/insertCommands.d.ts +33 -0
- package/dist/commands/listCommands.d.ts +19 -0
- package/dist/commands/markCommands.d.ts +21 -0
- package/dist/commands/navigationCommands.d.ts +27 -0
- package/dist/commands/structuralCommands.d.ts +22 -0
- package/dist/commands/textCommands.d.ts +18 -0
- package/dist/controller/history.d.ts +31 -0
- package/dist/controller/navigation.d.ts +18 -0
- package/dist/controller/selection.d.ts +25 -0
- package/dist/controller/wordBoundary.d.ts +25 -0
- package/dist/createEditor.d.ts +252 -0
- package/dist/dom/anchorMap.d.ts +27 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +7833 -0
- package/dist/index.js.map +72 -0
- package/dist/input/keymap.d.ts +47 -0
- package/dist/input/mobile.d.ts +13 -0
- package/dist/input/nativeInput.d.ts +27 -0
- package/dist/markdown/serialize.d.ts +2 -0
- package/dist/model/blockText.d.ts +42 -0
- package/dist/model/cellAccess.d.ts +2 -0
- package/dist/model/doc.d.ts +45 -0
- package/dist/model/fractional.d.ts +57 -0
- package/dist/model/rebalance.d.ts +12 -0
- package/dist/model/types.d.ts +133 -0
- package/dist/plugin/anchorCodec.d.ts +18 -0
- package/dist/plugin/atomic.d.ts +2 -0
- package/dist/plugin/builtin.d.ts +10 -0
- package/dist/plugin/decorations.d.ts +35 -0
- package/dist/plugin/htmlCodec.d.ts +8 -0
- package/dist/plugin/keymapMatch.d.ts +2 -0
- package/dist/plugin/registry.d.ts +29 -0
- package/dist/plugin/runsAt.d.ts +9 -0
- package/dist/plugin/serializeCodec.d.ts +6 -0
- package/dist/plugin/triggers.d.ts +33 -0
- package/dist/plugin/types.d.ts +188 -0
- package/dist/plugins/add-block/index.d.ts +17 -0
- package/dist/plugins/calendar/index.d.ts +7 -0
- package/dist/plugins/calendar/view.d.ts +29 -0
- package/dist/plugins/cells/codecs.d.ts +6 -0
- package/dist/plugins/cells/commands.d.ts +5 -0
- package/dist/plugins/cells/controls.d.ts +3 -0
- package/dist/plugins/cells/htmlCodec.d.ts +6 -0
- package/dist/plugins/cells/index.d.ts +3 -0
- package/dist/plugins/cells/views.d.ts +27 -0
- package/dist/plugins/drag-handle/index.d.ts +6 -0
- package/dist/plugins/infinite-scroll/index.d.ts +44 -0
- package/dist/plugins/md-shortcuts/index.d.ts +2 -0
- package/dist/plugins/search/engine.d.ts +33 -0
- package/dist/plugins/search/highlight.d.ts +13 -0
- package/dist/plugins/search/index.d.ts +5 -0
- package/dist/plugins/search/navigate.d.ts +15 -0
- package/dist/plugins/search/styles.d.ts +1 -0
- package/dist/plugins/search/types.d.ts +73 -0
- package/dist/plugins/search/ui.d.ts +2 -0
- package/dist/plugins/slash/index.d.ts +12 -0
- package/dist/plugins/slash/items.d.ts +21 -0
- package/dist/plugins/slash/menu.d.ts +17 -0
- package/dist/plugins/styles.css +233 -0
- package/dist/render/DocView.d.ts +7 -0
- package/dist/render/InlineRunsView.d.ts +7 -0
- package/dist/render/blocks/CodeBlockView.d.ts +7 -0
- package/dist/render/blocks/HeadingView.d.ts +7 -0
- package/dist/render/blocks/ImageView.d.ts +8 -0
- package/dist/render/blocks/ListItemView.d.ts +7 -0
- package/dist/render/blocks/ParagraphView.d.ts +7 -0
- package/dist/virtual/VirtualDoc.d.ts +28 -0
- package/dist/virtual/heightIndex.d.ts +36 -0
- package/package.json +53 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Mark } from "../model/types";
|
|
2
|
+
import type { SetBlockTypePayload } from "../commands/structuralCommands";
|
|
3
|
+
/**
|
|
4
|
+
* Cross-platform "mod" key — ⌘ on macOS, Ctrl elsewhere.
|
|
5
|
+
*
|
|
6
|
+
* The detection is conservative: we look at navigator.platform first; in
|
|
7
|
+
* test / SSR environments (no navigator) we default to non-mac.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isMac(): boolean;
|
|
10
|
+
export type KeymapHit = {
|
|
11
|
+
kind: "toggleMark";
|
|
12
|
+
mark: Mark;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "setBlockType";
|
|
15
|
+
payload: SetBlockTypePayload;
|
|
16
|
+
} | {
|
|
17
|
+
kind: "indent";
|
|
18
|
+
} | {
|
|
19
|
+
kind: "outdent";
|
|
20
|
+
} | {
|
|
21
|
+
kind: "undo";
|
|
22
|
+
} | {
|
|
23
|
+
kind: "redo";
|
|
24
|
+
} | {
|
|
25
|
+
kind: "selectAll";
|
|
26
|
+
} | {
|
|
27
|
+
kind: "moveWord";
|
|
28
|
+
direction: -1 | 1;
|
|
29
|
+
extend: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
kind: "moveLineEdge";
|
|
32
|
+
direction: -1 | 1;
|
|
33
|
+
extend: boolean;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "moveDocEdge";
|
|
36
|
+
direction: -1 | 1;
|
|
37
|
+
extend: boolean;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Match a keyboard event against the default chord table. Returns the hit if
|
|
41
|
+
* the key is a known chord, else `null` so the caller can fall through to
|
|
42
|
+
* navigation / text editing.
|
|
43
|
+
*
|
|
44
|
+
* The matcher is intentionally event-oriented rather than string-oriented;
|
|
45
|
+
* it sidesteps the "Ctrl+Shift+K" parsing rabbit-hole.
|
|
46
|
+
*/
|
|
47
|
+
export declare function matchKeymap(e: KeyboardEvent): KeymapHit | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Store } from "creo";
|
|
2
|
+
import type { DocState, Selection } from "../model/types";
|
|
3
|
+
export declare function isCoarsePointer(): boolean;
|
|
4
|
+
/** Test-only override (clears the cache so the next read re-detects). */
|
|
5
|
+
export declare function __resetCoarsePointerCache(): void;
|
|
6
|
+
export type ViewportStores = {
|
|
7
|
+
selStore: Store<Selection>;
|
|
8
|
+
docStore: Store<DocState>;
|
|
9
|
+
};
|
|
10
|
+
export type ViewportHandle = {
|
|
11
|
+
destroy: () => void;
|
|
12
|
+
};
|
|
13
|
+
export declare function attachVisualViewport(root: HTMLElement, stores: ViewportStores): ViewportHandle;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Store } from "creo";
|
|
2
|
+
import type { DocState, Selection } from "../model/types";
|
|
3
|
+
import type { DispatchableCommand } from "../createEditor";
|
|
4
|
+
import { type UploadFn } from "../commands/imageCommands";
|
|
5
|
+
import type { Registry } from "../plugin/registry";
|
|
6
|
+
import type { TriggerManager } from "../plugin/triggers";
|
|
7
|
+
export type NativeInputStores = {
|
|
8
|
+
docStore: Store<DocState>;
|
|
9
|
+
selStore: Store<Selection>;
|
|
10
|
+
};
|
|
11
|
+
export type NativeInputOptions = {
|
|
12
|
+
dispatch: (cmd: DispatchableCommand) => void;
|
|
13
|
+
undo: () => void;
|
|
14
|
+
redo: () => void;
|
|
15
|
+
selectAll: () => void;
|
|
16
|
+
/** Optional upload hook for pasted / dropped image files. */
|
|
17
|
+
uploadImage?: UploadFn;
|
|
18
|
+
/** Plugin registry — keymap matching, command dispatch for plugin chords. */
|
|
19
|
+
registry: Registry;
|
|
20
|
+
/** Trigger manager — watches text insertion + key events for plugin
|
|
21
|
+
* triggers (slash commands, mentions, etc.). */
|
|
22
|
+
triggers: TriggerManager;
|
|
23
|
+
};
|
|
24
|
+
export type NativeInputHandle = {
|
|
25
|
+
destroy: () => void;
|
|
26
|
+
};
|
|
27
|
+
export declare function attachNativeInput(root: HTMLElement, stores: NativeInputStores, options: NativeInputOptions): NativeInputHandle;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Block, InlineRun, Mark } from "./types";
|
|
2
|
+
/** Blocks whose content is a single InlineRun[] (paragraphs, headings, list items). */
|
|
3
|
+
export type TextBearingBlock = Extract<Block, {
|
|
4
|
+
runs: InlineRun[];
|
|
5
|
+
}>;
|
|
6
|
+
export declare function isTextBearing(block: Block): block is TextBearingBlock;
|
|
7
|
+
/** Total length of plain text across all runs of a text-bearing block. */
|
|
8
|
+
export declare function runsLength(runs: InlineRun[]): number;
|
|
9
|
+
export declare function blockTextLength(block: TextBearingBlock): number;
|
|
10
|
+
/**
|
|
11
|
+
* Normalize: drop empty runs and merge adjacent runs with identical marks.
|
|
12
|
+
* Always returns a fresh array.
|
|
13
|
+
*/
|
|
14
|
+
export declare function normalizeRuns(runs: InlineRun[]): InlineRun[];
|
|
15
|
+
/**
|
|
16
|
+
* Find the run + local offset that owns the given character position.
|
|
17
|
+
* `offset` is clamped to [0, runsLength].
|
|
18
|
+
*
|
|
19
|
+
* The "boundary" rule: if `offset` lands exactly between two runs, we report
|
|
20
|
+
* `runIndex` = the index of the run that *ends* at that boundary (so callers
|
|
21
|
+
* inserting text inherit the LEFT run's marks by default).
|
|
22
|
+
*/
|
|
23
|
+
export type RunPos = {
|
|
24
|
+
runIndex: number;
|
|
25
|
+
localOffset: number;
|
|
26
|
+
/** Aggregate length of all runs strictly before runIndex. */
|
|
27
|
+
prefixLen: number;
|
|
28
|
+
};
|
|
29
|
+
export declare function locateRun(runs: InlineRun[], offset: number): RunPos;
|
|
30
|
+
/** Marks the next inserted character should inherit at `offset`. */
|
|
31
|
+
export declare function marksAt(runs: InlineRun[], offset: number): ReadonlySet<Mark> | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Insert `text` (with optional `marks`) at character `offset`.
|
|
34
|
+
* Returns a new runs array.
|
|
35
|
+
*/
|
|
36
|
+
export declare function insertText(runs: InlineRun[], offset: number, text: string, marks?: ReadonlySet<Mark>): InlineRun[];
|
|
37
|
+
/** Delete characters in [start, end). Returns a new runs array. */
|
|
38
|
+
export declare function deleteRange(runs: InlineRun[], start: number, end: number): InlineRun[];
|
|
39
|
+
/** Split runs at offset into [left, right] arrays. */
|
|
40
|
+
export declare function splitRunsAt(runs: InlineRun[], offset: number): [InlineRun[], InlineRun[]];
|
|
41
|
+
/** Concatenate two runs arrays, normalizing the seam. */
|
|
42
|
+
export declare function concatRuns(a: InlineRun[], b: InlineRun[]): InlineRun[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Block, BlockId, BlockSpec, DocState, FracIndex } from "./types";
|
|
2
|
+
/** Cheap, monotonically increasing block id with a per-session prefix. */
|
|
3
|
+
export declare function newBlockId(): BlockId;
|
|
4
|
+
export declare function emptyDoc(): DocState;
|
|
5
|
+
/** Build a DocState from a list of blocks (will assign indices if missing). */
|
|
6
|
+
export declare function docFromBlocks(blocks: BlockSpec[]): DocState;
|
|
7
|
+
/**
|
|
8
|
+
* Returns the insertion index in `order` such that the new key sorts at that
|
|
9
|
+
* position. Pure binary search by `Block.index`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function findInsertionPos(doc: DocState, index: FracIndex): number;
|
|
12
|
+
/** Returns the position of `id` in `order`, or -1 if not found. */
|
|
13
|
+
export declare function findPos(doc: DocState, id: BlockId): number;
|
|
14
|
+
/**
|
|
15
|
+
* Insert `block` between the blocks at positions [pos-1] and [pos].
|
|
16
|
+
* The new block's `index` is overwritten with a fractional key in that gap.
|
|
17
|
+
*/
|
|
18
|
+
export declare function insertAt(doc: DocState, pos: number, block: BlockSpec): DocState;
|
|
19
|
+
/** Insert a block whose `index` has already been assigned. */
|
|
20
|
+
export declare function insertWithIndex(doc: DocState, block: Block): DocState;
|
|
21
|
+
/** Insert `block` immediately after `afterId` (or at the start if null). */
|
|
22
|
+
export declare function insertAfter(doc: DocState, afterId: BlockId | null, block: BlockSpec): DocState;
|
|
23
|
+
/** Replace the block with the same id. */
|
|
24
|
+
export declare function updateBlock(doc: DocState, block: Block): DocState;
|
|
25
|
+
/** Remove a block. */
|
|
26
|
+
export declare function removeBlock(doc: DocState, id: BlockId): DocState;
|
|
27
|
+
/**
|
|
28
|
+
* Remove multiple blocks in one pass. Single allocation of the new order
|
|
29
|
+
* array — avoids the O(M*N) cost of calling removeBlock M times in a loop.
|
|
30
|
+
*/
|
|
31
|
+
export declare function removeBlocks(doc: DocState, ids: Iterable<BlockId>): DocState;
|
|
32
|
+
/**
|
|
33
|
+
* Bulk-insert: drop `n` new blocks evenly spaced into the gap before `pos`.
|
|
34
|
+
* Faster than calling insertAt repeatedly because we compute all keys at once.
|
|
35
|
+
*
|
|
36
|
+
* Builds the new `order` array in a single pass (O(N + M) where N = current
|
|
37
|
+
* doc size, M = inserted block count) instead of splicing in a loop. Splicing
|
|
38
|
+
* inside a loop is O(N) per call → O(M*N) overall, which hurts catastrophically
|
|
39
|
+
* for large pastes (e.g. ~10k paragraphs from a long document).
|
|
40
|
+
*/
|
|
41
|
+
export declare function insertManyAt(doc: DocState, pos: number, blocks: BlockSpec[]): DocState;
|
|
42
|
+
export declare function maybeRebalance(doc: DocState): DocState;
|
|
43
|
+
export declare function iterBlocks(doc: DocState): IterableIterator<Block>;
|
|
44
|
+
export declare function getBlock(doc: DocState, id: BlockId): Block | undefined;
|
|
45
|
+
export declare function blockAt(doc: DocState, pos: number): Block | undefined;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fractional indexing over a base-62 alphabet.
|
|
3
|
+
*
|
|
4
|
+
* Alphabet ordering (string compare matches numeric order):
|
|
5
|
+
* '0'..'9' < 'A'..'Z' < 'a'..'z' → 62 symbols.
|
|
6
|
+
*
|
|
7
|
+
* A key is a non-empty string of alphabet characters; lexicographic comparison
|
|
8
|
+
* yields the same ordering as the rationals they represent. The first symbol's
|
|
9
|
+
* value is the most-significant fractional digit.
|
|
10
|
+
*
|
|
11
|
+
* `generateBetween(a, b)` returns a key strictly between `a` and `b`. `null`
|
|
12
|
+
* means "no bound" — use it for the document's first or last position.
|
|
13
|
+
*
|
|
14
|
+
* `generateN(a, b, n)` produces `n` evenly-spaced keys between `a` and `b` in
|
|
15
|
+
* O(n + log span); used by bulk-paste so we don't pay O(n²) re-midpointing.
|
|
16
|
+
*/
|
|
17
|
+
declare function ord(c: string): number;
|
|
18
|
+
declare function chr(n: number): string;
|
|
19
|
+
/** Random base-62 character — used as a tail to keep keys distinct. */
|
|
20
|
+
declare function randomChar(): string;
|
|
21
|
+
/** Strip trailing MIN_CHAR ('0') noise from a key (those bits are zeros). */
|
|
22
|
+
declare function trimZeros(s: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Generate a key strictly between `a` and `b`.
|
|
25
|
+
* - If a==null, the new key is < b.
|
|
26
|
+
* - If b==null, the new key is > a.
|
|
27
|
+
* - Both null → returns "U" (mid-range).
|
|
28
|
+
*/
|
|
29
|
+
export declare function generateBetween(a: string | null, b: string | null): string;
|
|
30
|
+
/**
|
|
31
|
+
* Generate `n` evenly-spaced keys strictly between `a` and `b`.
|
|
32
|
+
* Returns them in ascending order.
|
|
33
|
+
*
|
|
34
|
+
* O(n) total — far cheaper than calling generateBetween repeatedly with
|
|
35
|
+
* a moving cursor (which would degrade by binary descent each step).
|
|
36
|
+
*/
|
|
37
|
+
export declare function generateN(a: string | null, b: string | null, n: number): string[];
|
|
38
|
+
/** Soft cap; once any key exceeds this length, schedule a rebalance. */
|
|
39
|
+
export declare const REBALANCE_THRESHOLD = 32;
|
|
40
|
+
/** Returns true if any key in the sorted list exceeds REBALANCE_THRESHOLD. */
|
|
41
|
+
export declare function needsRebalance(keys: string[]): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Produce a fresh, evenly-spaced sequence of N keys, replacing an existing
|
|
44
|
+
* sorted sequence. Used by the doc layer when keys grow past threshold.
|
|
45
|
+
*/
|
|
46
|
+
export declare function rebalance(count: number): string[];
|
|
47
|
+
export declare const __internal: {
|
|
48
|
+
ALPHABET: string;
|
|
49
|
+
BASE: number;
|
|
50
|
+
MIN_CHAR: string;
|
|
51
|
+
MAX_CHAR: string;
|
|
52
|
+
ord: typeof ord;
|
|
53
|
+
chr: typeof chr;
|
|
54
|
+
randomChar: typeof randomChar;
|
|
55
|
+
trimZeros: typeof trimZeros;
|
|
56
|
+
};
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Store } from "creo";
|
|
2
|
+
import type { DocState } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Watch a docStore and schedule a microtask rebalance whenever any
|
|
5
|
+
* fractional index grows past the soft threshold.
|
|
6
|
+
*
|
|
7
|
+
* Rebalance assigns fresh, evenly-spaced indices to every block (preserving
|
|
8
|
+
* order). It's O(n) and runs at most once per microtask, so worst-case a
|
|
9
|
+
* 600k-block doc rebalances in ~50ms — and only ever triggers under
|
|
10
|
+
* adversarial insertion patterns.
|
|
11
|
+
*/
|
|
12
|
+
export declare function attachAutoRebalance(docStore: Store<DocState>): () => void;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export type BlockId = string;
|
|
2
|
+
export type FracIndex = string;
|
|
3
|
+
export type Mark = "b" | "i" | "u" | "s" | "code";
|
|
4
|
+
export type InlineRun = {
|
|
5
|
+
text: string;
|
|
6
|
+
marks?: ReadonlySet<Mark>;
|
|
7
|
+
};
|
|
8
|
+
export type ParagraphBlock = {
|
|
9
|
+
id: BlockId;
|
|
10
|
+
index: FracIndex;
|
|
11
|
+
type: "p";
|
|
12
|
+
runs: InlineRun[];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Code-block — like a paragraph, but rendered monospaced inside a <pre>.
|
|
16
|
+
* Enter inside the block inserts a newline character (it does NOT split
|
|
17
|
+
* the block) — the editor treats the whole code block as a single
|
|
18
|
+
* multi-line region. `lang` is an optional language hint preserved across
|
|
19
|
+
* markdown / HTML round-trips; the editor itself doesn't syntax-highlight.
|
|
20
|
+
*/
|
|
21
|
+
export type CodeBlock = {
|
|
22
|
+
id: BlockId;
|
|
23
|
+
index: FracIndex;
|
|
24
|
+
type: "code";
|
|
25
|
+
runs: InlineRun[];
|
|
26
|
+
lang?: string;
|
|
27
|
+
};
|
|
28
|
+
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
29
|
+
export type HeadingBlock = {
|
|
30
|
+
id: BlockId;
|
|
31
|
+
index: FracIndex;
|
|
32
|
+
type: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
|
33
|
+
runs: InlineRun[];
|
|
34
|
+
};
|
|
35
|
+
export type ListItemBlock = {
|
|
36
|
+
id: BlockId;
|
|
37
|
+
index: FracIndex;
|
|
38
|
+
type: "li";
|
|
39
|
+
ordered: boolean;
|
|
40
|
+
depth: 0 | 1 | 2 | 3;
|
|
41
|
+
runs: InlineRun[];
|
|
42
|
+
};
|
|
43
|
+
export type ImageBlock = {
|
|
44
|
+
id: BlockId;
|
|
45
|
+
index: FracIndex;
|
|
46
|
+
type: "img";
|
|
47
|
+
src: string;
|
|
48
|
+
alt?: string;
|
|
49
|
+
width?: number;
|
|
50
|
+
height?: number;
|
|
51
|
+
};
|
|
52
|
+
export type TableBlock = {
|
|
53
|
+
id: BlockId;
|
|
54
|
+
index: FracIndex;
|
|
55
|
+
type: "table";
|
|
56
|
+
rows: number;
|
|
57
|
+
cols: number;
|
|
58
|
+
cells: InlineRun[][][];
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Multi-column layout — N side-by-side columns of inline runs. Internal
|
|
62
|
+
* cursor path = [colIndex, charOffset]. Same model shape as a 1-row
|
|
63
|
+
* table but rendered as flex columns instead of an HTML <table>.
|
|
64
|
+
*/
|
|
65
|
+
export type ColumnsBlock = {
|
|
66
|
+
id: BlockId;
|
|
67
|
+
index: FracIndex;
|
|
68
|
+
type: "columns";
|
|
69
|
+
cols: number;
|
|
70
|
+
/** cells[c] is the inline-runs sequence for column c. */
|
|
71
|
+
cells: InlineRun[][];
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Calendar — non-editable atomic block. Renders one row per day starting
|
|
75
|
+
* at `date` for `days` consecutive days. Path encoding: `[side]` (0
|
|
76
|
+
* = before, 1 = after) like image. Built as a first-party demo of the
|
|
77
|
+
* `isAtomic` plugin contract — third-party plugins follow the same shape.
|
|
78
|
+
*/
|
|
79
|
+
export type CalendarBlock = {
|
|
80
|
+
id: BlockId;
|
|
81
|
+
index: FracIndex;
|
|
82
|
+
type: "calendar";
|
|
83
|
+
/** Anchor date in ISO YYYY-MM-DD form. */
|
|
84
|
+
date: string;
|
|
85
|
+
/** Number of consecutive days to render starting at `date`. */
|
|
86
|
+
days: number;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* DateMarker — slim non-editable atomic block. One line, e.g.
|
|
90
|
+
* "Wednesday, 2 Sep." Used to break up a journal-style document into
|
|
91
|
+
* day sections that the user can write between.
|
|
92
|
+
*/
|
|
93
|
+
export type DateMarkerBlock = {
|
|
94
|
+
id: BlockId;
|
|
95
|
+
index: FracIndex;
|
|
96
|
+
type: "date-marker";
|
|
97
|
+
/** Anchor date in ISO YYYY-MM-DD form. */
|
|
98
|
+
date: string;
|
|
99
|
+
};
|
|
100
|
+
export type Block = ParagraphBlock | HeadingBlock | ListItemBlock | CodeBlock | ImageBlock | TableBlock | ColumnsBlock | CalendarBlock | DateMarkerBlock;
|
|
101
|
+
export type BlockType = Block["type"];
|
|
102
|
+
/**
|
|
103
|
+
* Distributive `Omit` — applies Omit to each member of a union.
|
|
104
|
+
* Plain `Omit<Union, K>` collapses the union into its intersection of fields,
|
|
105
|
+
* which loses the discriminated-union shape.
|
|
106
|
+
*/
|
|
107
|
+
export type DistOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : never;
|
|
108
|
+
/** A block missing its fractional `index` — used by callers that hand the doc
|
|
109
|
+
* layer un-indexed blocks for insertion. */
|
|
110
|
+
export type BlockSpec = DistOmit<Block, "index">;
|
|
111
|
+
export type DocState = {
|
|
112
|
+
byId: Map<BlockId, Block>;
|
|
113
|
+
order: BlockId[];
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Anchor — points to a position inside a block.
|
|
117
|
+
* - For text-bearing blocks (p / h1..h6 / li): path = [charOffset]
|
|
118
|
+
* - For tables: path = [row, col, charOffset]
|
|
119
|
+
* - For images: path = [side] side: 0 = before, 1 = after
|
|
120
|
+
*/
|
|
121
|
+
export type Anchor = {
|
|
122
|
+
blockId: BlockId;
|
|
123
|
+
path: number[];
|
|
124
|
+
offset: number;
|
|
125
|
+
};
|
|
126
|
+
export type Selection = {
|
|
127
|
+
kind: "caret";
|
|
128
|
+
at: Anchor;
|
|
129
|
+
} | {
|
|
130
|
+
kind: "range";
|
|
131
|
+
anchor: Anchor;
|
|
132
|
+
focus: Anchor;
|
|
133
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AnchorCodec, DomPoint } from "./types";
|
|
2
|
+
export declare function registerAnchorCodec(type: string, codec: AnchorCodec): void;
|
|
3
|
+
export declare function getAnchorCodec(type: string): AnchorCodec | null;
|
|
4
|
+
/** Visible-character offset from start of `scopeEl` to (hitNode, localOffset). */
|
|
5
|
+
export declare function offsetWithinScope(scopeEl: HTMLElement, hitNode: Node, localOffset: number): number;
|
|
6
|
+
/** Walk descendant text nodes to find the (node, offset) at `charOffset`. */
|
|
7
|
+
export declare function findTextPoint(scopeEl: HTMLElement, charOffset: number): DomPoint;
|
|
8
|
+
/** Default codec — text-bearing blocks land here when they don't register
|
|
9
|
+
* their own. Path encoding: [charOffset]. */
|
|
10
|
+
export declare const defaultTextCodec: AnchorCodec;
|
|
11
|
+
export declare const codeBlockCodec: AnchorCodec;
|
|
12
|
+
export declare const atomicCodec: AnchorCodec;
|
|
13
|
+
export declare const imageCodec: AnchorCodec;
|
|
14
|
+
export declare function findOwningBlockEl(node: Node): HTMLElement | null;
|
|
15
|
+
/** Pluggable anchor → which is just looking up the registered codec.
|
|
16
|
+
* Centralized here so anchorMap.ts and other consumers share one path. */
|
|
17
|
+
export declare function lookupAnchorCodec(kind: string): AnchorCodec | null;
|
|
18
|
+
export type { DomPoint };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EditorPlugin } from "./types";
|
|
2
|
+
import { cellsPlugin } from "../plugins/cells";
|
|
3
|
+
export declare const paragraphPlugin: EditorPlugin;
|
|
4
|
+
export declare const headingPlugin: EditorPlugin;
|
|
5
|
+
export declare const listPlugin: EditorPlugin;
|
|
6
|
+
export declare const codeBlockPlugin: EditorPlugin;
|
|
7
|
+
export declare const imagePlugin: EditorPlugin;
|
|
8
|
+
export { cellsPlugin };
|
|
9
|
+
/** All built-in plugins, in registration order. */
|
|
10
|
+
export declare const defaultPlugins: EditorPlugin[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Store } from "creo";
|
|
2
|
+
import type { BlockId, DocState } from "../model/types";
|
|
3
|
+
import type { Registry } from "./registry";
|
|
4
|
+
export type DecorationManagerOptions = {
|
|
5
|
+
registry: Registry;
|
|
6
|
+
docStore: Store<DocState>;
|
|
7
|
+
/** Editor root (the contentEditable div). The decoration layer mounts as
|
|
8
|
+
* a sibling, positioned to overlay the same screen rect. */
|
|
9
|
+
editorRoot: HTMLElement;
|
|
10
|
+
};
|
|
11
|
+
export declare class DecorationManager {
|
|
12
|
+
private opts;
|
|
13
|
+
private layer;
|
|
14
|
+
private mounted;
|
|
15
|
+
private rafQueued;
|
|
16
|
+
private rafSyncRequested;
|
|
17
|
+
private unsub;
|
|
18
|
+
private resizeObserver;
|
|
19
|
+
/** Hovered block id — surfaced to decorations via dataset on the layer
|
|
20
|
+
* so they can style themselves with sibling CSS or read it directly. */
|
|
21
|
+
private hoveredBlockId;
|
|
22
|
+
constructor(opts: DecorationManagerOptions);
|
|
23
|
+
destroy(): void;
|
|
24
|
+
/** Currently hovered block id (or null). Decorations read this via
|
|
25
|
+
* `manager.hoveredBlock()` to decide their own visibility. */
|
|
26
|
+
hoveredBlock(): BlockId | null;
|
|
27
|
+
private scheduleSync;
|
|
28
|
+
private schedulePosition;
|
|
29
|
+
private queueFlush;
|
|
30
|
+
private sync;
|
|
31
|
+
private mountDecoration;
|
|
32
|
+
private position;
|
|
33
|
+
private onPointerMove;
|
|
34
|
+
private onPointerLeave;
|
|
35
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Block, BlockSpec } from "../model/types";
|
|
2
|
+
import type { HtmlBlockCodec, HtmlParseCtx } from "./types";
|
|
3
|
+
type ParserFn = (el: HTMLElement, ctx: HtmlParseCtx) => BlockSpec | null;
|
|
4
|
+
type SerializerFn = (b: Block) => string;
|
|
5
|
+
export declare function registerHtmlBlockCodec(type: string, codec: HtmlBlockCodec): void;
|
|
6
|
+
export declare function getHtmlParserForTag(tag: string): ParserFn | null;
|
|
7
|
+
export declare function getHtmlSerializer(type: string): SerializerFn | null;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CommandCtx, CommandDef, DecorationDef, EditorPlugin, KeymapDef, TriggerDef } from "./types";
|
|
2
|
+
export declare class Registry {
|
|
3
|
+
readonly commands: Map<string, CommandDef<unknown>>;
|
|
4
|
+
readonly keymap: KeymapDef[];
|
|
5
|
+
readonly triggers: TriggerDef[];
|
|
6
|
+
readonly decorations: DecorationDef[];
|
|
7
|
+
readonly coalescePrefixes: Set<string>;
|
|
8
|
+
/** Set of all known block type discriminators (for fast existence checks). */
|
|
9
|
+
readonly knownBlockTypes: Set<string>;
|
|
10
|
+
install(plugin: EditorPlugin): void;
|
|
11
|
+
/**
|
|
12
|
+
* Look up a command by t and run it; returns false if the command is
|
|
13
|
+
* unknown OR if the command itself returned false (signaling "did not
|
|
14
|
+
* apply"). Used by both the editor dispatch path and the keymap matcher.
|
|
15
|
+
*/
|
|
16
|
+
runCommand(t: string, payload: unknown, ctx: CommandCtx): boolean;
|
|
17
|
+
/** Should the given history tag coalesce with prior matching tags? */
|
|
18
|
+
shouldCoalesce(tag: string): boolean;
|
|
19
|
+
}
|
|
20
|
+
import type { PublicView } from "creo";
|
|
21
|
+
import type { Block } from "../model/types";
|
|
22
|
+
export declare function registerView(type: string, v: PublicView<{
|
|
23
|
+
block: Block;
|
|
24
|
+
key?: string;
|
|
25
|
+
}, void>): void;
|
|
26
|
+
export declare function getView(type: string): PublicView<{
|
|
27
|
+
block: Block;
|
|
28
|
+
key?: string;
|
|
29
|
+
}, void> | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Anchor, Block } from "../model/types";
|
|
2
|
+
import type { RunsCtx } from "./types";
|
|
3
|
+
type RunsAtFn = (b: Block, a: Anchor) => RunsCtx | null;
|
|
4
|
+
export declare function registerRunsAt(type: string, fn: RunsAtFn): void;
|
|
5
|
+
export declare function runsAt(block: Block, anchor: Anchor): RunsCtx | null;
|
|
6
|
+
/** Total length of the runs container at `anchor` — convenience helper used
|
|
7
|
+
* by both text commands and the IME composition diff. */
|
|
8
|
+
export declare function runsLengthAt(block: Block, anchor: Anchor): number;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Block, BlockId, BlockSpec } from "../model/types";
|
|
2
|
+
import type { SerializeCodec } from "./types";
|
|
3
|
+
export declare function registerSerializeCodec(type: string, codec: SerializeCodec): void;
|
|
4
|
+
export declare function getSerializeCodec(type: string): SerializeCodec | null;
|
|
5
|
+
export declare function serializeBlock(b: Block): unknown | null;
|
|
6
|
+
export declare function deserializeBlock(type: string, s: unknown, id: BlockId): BlockSpec | null;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Store } from "creo";
|
|
2
|
+
import type { DocState, Selection } from "../model/types";
|
|
3
|
+
import type { DispatchableCommand } from "../createEditor";
|
|
4
|
+
import type { Registry } from "./registry";
|
|
5
|
+
export type TriggerManagerOptions = {
|
|
6
|
+
registry: Registry;
|
|
7
|
+
docStore: Store<DocState>;
|
|
8
|
+
selStore: Store<Selection>;
|
|
9
|
+
dispatch: (cmd: DispatchableCommand) => void;
|
|
10
|
+
};
|
|
11
|
+
export declare class TriggerManager {
|
|
12
|
+
private opts;
|
|
13
|
+
private active;
|
|
14
|
+
private unsubSel;
|
|
15
|
+
private unsubDoc;
|
|
16
|
+
constructor(opts: TriggerManagerOptions);
|
|
17
|
+
destroy(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Re-derive the active trigger's state from the current doc + selection.
|
|
20
|
+
* Closes the menu if any of:
|
|
21
|
+
* - caret left the trigger's block
|
|
22
|
+
* - caret moved before the trigger char position
|
|
23
|
+
* - the trigger char at the start position is no longer there
|
|
24
|
+
* Otherwise, recomputes the live query string and notifies the controller.
|
|
25
|
+
*/
|
|
26
|
+
private reconcile;
|
|
27
|
+
/** Called by nativeInput AFTER a successful insertText dispatch. */
|
|
28
|
+
onTextInserted(text: string): void;
|
|
29
|
+
/** Called by nativeInput at the top of onKeyDown. Returns true if consumed. */
|
|
30
|
+
handleKeyDown(e: KeyboardEvent): boolean;
|
|
31
|
+
isActive(): boolean;
|
|
32
|
+
close(): void;
|
|
33
|
+
}
|