@sobree/core 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 +106 -0
- package/dist/__vite-browser-external-DYxpcVy9.js +5 -0
- package/dist/__vite-browser-external-DYxpcVy9.js.map +1 -0
- package/dist/blob/cache.d.ts +69 -0
- package/dist/blob/fetch.d.ts +18 -0
- package/dist/blob/hash.d.ts +13 -0
- package/dist/blob/index.d.ts +33 -0
- package/dist/blob/memory.d.ts +2 -0
- package/dist/blob/types.d.ts +80 -0
- package/dist/createSobree.d.ts +132 -0
- package/dist/doc/api.d.ts +132 -0
- package/dist/doc/builders.d.ts +42 -0
- package/dist/doc/pageSetupBridge.d.ts +26 -0
- package/dist/doc/parts.d.ts +18 -0
- package/dist/doc/runs.d.ts +47 -0
- package/dist/doc/styles.d.ts +19 -0
- package/dist/doc/types.d.ts +800 -0
- package/dist/doc/walk.d.ts +30 -0
- package/dist/docx/export/contentTypes.d.ts +35 -0
- package/dist/docx/export/context.d.ts +59 -0
- package/dist/docx/export/document.d.ts +19 -0
- package/dist/docx/export/drawings.d.ts +10 -0
- package/dist/docx/export/headers.d.ts +19 -0
- package/dist/docx/export/index.d.ts +14 -0
- package/dist/docx/export/runs.d.ts +8 -0
- package/dist/docx/export/styles.d.ts +8 -0
- package/dist/docx/export/zip.d.ts +13 -0
- package/dist/docx/import/anchoredFrames.d.ts +34 -0
- package/dist/docx/import/comments.d.ts +3 -0
- package/dist/docx/import/document.d.ts +57 -0
- package/dist/docx/import/flowFrames.d.ts +11 -0
- package/dist/docx/import/footnotes.d.ts +3 -0
- package/dist/docx/import/headers.d.ts +50 -0
- package/dist/docx/import/index.d.ts +12 -0
- package/dist/docx/import/inlineFrames.d.ts +62 -0
- package/dist/docx/import/numbering.d.ts +2 -0
- package/dist/docx/import/paragraph.d.ts +24 -0
- package/dist/docx/import/paragraphs.d.ts +27 -0
- package/dist/docx/import/rels.d.ts +5 -0
- package/dist/docx/import/runs.d.ts +64 -0
- package/dist/docx/import/settings.d.ts +48 -0
- package/dist/docx/import/styles.d.ts +3 -0
- package/dist/docx/import/tables.d.ts +12 -0
- package/dist/docx/import/unzip.d.ts +13 -0
- package/dist/docx/shared/namespaces.d.ts +31 -0
- package/dist/docx/shared/pageSize.d.ts +27 -0
- package/dist/docx/shared/shading.d.ts +2 -0
- package/dist/docx/shared/units.d.ts +35 -0
- package/dist/docx/shared/xml.d.ts +29 -0
- package/dist/docx/types.d.ts +98 -0
- package/dist/editor/index.d.ts +1078 -0
- package/dist/editor/internal/blockRegistry.d.ts +91 -0
- package/dist/editor/internal/mutations.d.ts +63 -0
- package/dist/editor/internal/positionMap.d.ts +35 -0
- package/dist/editor/table.d.ts +96 -0
- package/dist/editor/view/docRenderer/anchorLayer.d.ts +26 -0
- package/dist/editor/view/docRenderer/block.d.ts +13 -0
- package/dist/editor/view/docRenderer/fontFallback.d.ts +28 -0
- package/dist/editor/view/docRenderer/index.d.ts +18 -0
- package/dist/editor/view/docRenderer/inline.d.ts +15 -0
- package/dist/editor/view/docRenderer/inlineFrame.d.ts +4 -0
- package/dist/editor/view/docRenderer/lists.d.ts +28 -0
- package/dist/editor/view/docRenderer/paragraph.d.ts +2 -0
- package/dist/editor/view/docRenderer/properties.d.ts +2 -0
- package/dist/editor/view/docRenderer/table.d.ts +15 -0
- package/dist/editor/view/docRenderer/units.d.ts +48 -0
- package/dist/editor/view/docSerialize/block.d.ts +14 -0
- package/dist/editor/view/docSerialize/index.d.ts +8 -0
- package/dist/editor/view/docSerialize/inline.d.ts +11 -0
- package/dist/editor/view/docSerialize/table.d.ts +12 -0
- package/dist/editor/view/imageResize.d.ts +16 -0
- package/dist/embed/floatingCorner.d.ts +44 -0
- package/dist/embed/viewport.d.ts +133 -0
- package/dist/fonts/embedAPI.d.ts +33 -0
- package/dist/fonts/emit.d.ts +24 -0
- package/dist/fonts/fontFaceRegistry.d.ts +20 -0
- package/dist/fonts/fsType.d.ts +36 -0
- package/dist/fonts/index.d.ts +19 -0
- package/dist/fonts/liveness.d.ts +2 -0
- package/dist/fonts/odttf.d.ts +33 -0
- package/dist/fonts/parse.d.ts +29 -0
- package/dist/fonts/types.d.ts +52 -0
- package/dist/headless.d.ts +168 -0
- package/dist/history/history.d.ts +100 -0
- package/dist/history/index.d.ts +4 -0
- package/dist/history/types.d.ts +54 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +10561 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown/parse.d.ts +6 -0
- package/dist/pagination/cost.d.ts +32 -0
- package/dist/pagination/index.d.ts +2 -0
- package/dist/pagination/paginate.d.ts +10 -0
- package/dist/pagination/postConditions.d.ts +10 -0
- package/dist/pagination/types.d.ts +94 -0
- package/dist/paperStack/pageSetup.d.ts +42 -0
- package/dist/paperStack/paginationAdapter/buildItems.d.ts +19 -0
- package/dist/paperStack/paginationAdapter/distribute.d.ts +23 -0
- package/dist/paperStack/paginationAdapter/index.d.ts +18 -0
- package/dist/paperStack/paginationAdapter/paragraphLines.d.ts +23 -0
- package/dist/paperStack/paginationAdapter/splitList.d.ts +19 -0
- package/dist/paperStack/paginationAdapter/splitParagraph.d.ts +21 -0
- package/dist/paperStack/paginationAdapter/types.d.ts +30 -0
- package/dist/paperStack/paper.d.ts +107 -0
- package/dist/paperStack/paperStack.d.ts +245 -0
- package/dist/plugin.d.ts +24 -0
- package/dist/plugins/marks.d.ts +49 -0
- package/dist/plugins/sections.d.ts +15 -0
- package/dist/presence/attach.d.ts +48 -0
- package/dist/presence/awareness.d.ts +28 -0
- package/dist/presence/index.d.ts +19 -0
- package/dist/presence/overlay.d.ts +28 -0
- package/dist/presence/state.d.ts +36 -0
- package/dist/sobree.d.ts +211 -0
- package/dist/tokens.css +144 -0
- package/dist/util/selection.d.ts +13 -0
- package/dist/ydoc/apply.d.ts +68 -0
- package/dist/ydoc/index.d.ts +18 -0
- package/dist/ydoc/project.d.ts +41 -0
- package/dist/ydoc/runs.d.ts +51 -0
- package/dist/ydoc/schema.d.ts +123 -0
- package/dist/ydoc/seed.d.ts +45 -0
- package/dist/ydoc/textDiff.d.ts +59 -0
- package/dist/zoneEdit/index.d.ts +22 -0
- package/package.json +61 -0
|
@@ -0,0 +1,1078 @@
|
|
|
1
|
+
import { BlobCache, BlobStore } from '../blob';
|
|
2
|
+
import { BlockRef, EditError, EditResult, InlinePosition, Range as ApiRange, Selection } from '../doc/api';
|
|
3
|
+
import { EmbedFontFaces, EmbedFontOptions } from '../fonts';
|
|
4
|
+
import { RunPropertiesPatch } from '../doc/runs';
|
|
5
|
+
import { Block, InlineRun, ParagraphAlignment, ParagraphProperties, SobreeDocument } from '../doc/types';
|
|
6
|
+
import { BlockRegistry } from './internal/blockRegistry';
|
|
7
|
+
import { countBlocks } from './internal/positionMap';
|
|
8
|
+
import { EditorTable } from './table';
|
|
9
|
+
import { History } from '../history';
|
|
10
|
+
import * as Y from "yjs";
|
|
11
|
+
export type { BlockRef, EditError, EditResult, InlinePosition, Selection };
|
|
12
|
+
export type ApiRangeType = ApiRange;
|
|
13
|
+
/**
|
|
14
|
+
* One logical tracked change — a maximal run of consecutive inline
|
|
15
|
+
* runs that all carry a `revision` marker by the same author.
|
|
16
|
+
* `getRevisions()` returns these; pass `range` straight to
|
|
17
|
+
* `acceptRevision` / `rejectRevision`.
|
|
18
|
+
*
|
|
19
|
+
* `kinds` is the set of revision types in the span: `["ins"]` or
|
|
20
|
+
* `["del"]` for a plain change, both for a delete-then-insert
|
|
21
|
+
* replacement (which accepts/rejects as a single unit).
|
|
22
|
+
*/
|
|
23
|
+
export interface RevisionSpan {
|
|
24
|
+
range: ApiRange;
|
|
25
|
+
author?: string;
|
|
26
|
+
kinds: ("ins" | "del")[];
|
|
27
|
+
/** ISO date of the span's first revision run, if recorded. */
|
|
28
|
+
date?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Discriminator between revision levels:
|
|
31
|
+
* `"inline"` (default for backwards compat) — the span covers
|
|
32
|
+
* `ins`/`del` text runs inside a block. Pass `range` to
|
|
33
|
+
* `acceptRevision` / `rejectRevision`.
|
|
34
|
+
* `"paragraph"` — the span flags the *paragraph mark* itself on
|
|
35
|
+
* `range.from.block`. The range covers offset `[0, length]` of
|
|
36
|
+
* the block so it still selects the right element for UIs, but
|
|
37
|
+
* accept/reject must go through `acceptParagraphRevision` /
|
|
38
|
+
* `rejectParagraphRevision`.
|
|
39
|
+
* `"format"` — the span flags a tracked format change
|
|
40
|
+
* (`<w:rPrChange>`) on contiguous runs by the same author.
|
|
41
|
+
* `kinds` always reports `["ins"]` (the marker is binary: a
|
|
42
|
+
* format change exists or not). Pass `range` to
|
|
43
|
+
* `acceptFormatRevision` / `rejectFormatRevision`.
|
|
44
|
+
*/
|
|
45
|
+
level?: "inline" | "paragraph" | "format";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Track-changes mode. When `enabled` is true, the editor reinterprets
|
|
49
|
+
* authoring mutations as tracked revisions rather than direct edits:
|
|
50
|
+
*
|
|
51
|
+
* - `insertRun` stamps `revision: { type: "ins", author }` on the
|
|
52
|
+
* inserted run instead of merging it in plainly.
|
|
53
|
+
* - `deleteRange` stamps `revision: { type: "del", author }` on the
|
|
54
|
+
* plain text runs in range instead of dropping them. A run that's
|
|
55
|
+
* already an `ins` *by the same author* is dropped instead — the
|
|
56
|
+
* author cancelling their own pending insert — and runs already
|
|
57
|
+
* carrying a peer's revision are left untouched (the API user must
|
|
58
|
+
* resolve those via `acceptRevision` / `rejectRevision` first).
|
|
59
|
+
* - All other mutations (`applyRunProperties`, block-level ops, etc.)
|
|
60
|
+
* pass through unchanged in this first cut; format-change tracking
|
|
61
|
+
* (`<w:rPrChange>`) and paragraph-mark tracking will land later.
|
|
62
|
+
*
|
|
63
|
+
* `author` is the human-readable name written into the revision
|
|
64
|
+
* marker. Optional — falls back to no `author` field, mirroring the
|
|
65
|
+
* Word semantics for anonymous-author tracked changes.
|
|
66
|
+
*
|
|
67
|
+
* This is the *authoring* side of the review feature. `getRevisions`
|
|
68
|
+
* / `acceptRevision` / `rejectRevision` are the *consumption* side
|
|
69
|
+
* and work the same regardless of this flag.
|
|
70
|
+
*/
|
|
71
|
+
export interface TrackChangesState {
|
|
72
|
+
enabled: boolean;
|
|
73
|
+
author?: string;
|
|
74
|
+
}
|
|
75
|
+
export type { CellRef, InsertAt, InsertColumnOpts, InsertRowOpts, MergeCellsOpts, } from './table';
|
|
76
|
+
/**
|
|
77
|
+
* Payload delivered to `change` subscribers. Plain data — safe to
|
|
78
|
+
* JSON-stringify and ship over a wire. `rawParts` is stripped from
|
|
79
|
+
* `document` so the payload never carries binary Uint8Arrays.
|
|
80
|
+
*/
|
|
81
|
+
export interface ChangePayload {
|
|
82
|
+
doc: SobreeDocument;
|
|
83
|
+
/**
|
|
84
|
+
* @deprecated Use `doc` instead. This alias is kept for backwards
|
|
85
|
+
* compatibility within the pre-1.0 line and will be removed before
|
|
86
|
+
* v1. Same reference as `doc`.
|
|
87
|
+
*/
|
|
88
|
+
document: SobreeDocument;
|
|
89
|
+
revision: number;
|
|
90
|
+
documentVersion: number;
|
|
91
|
+
}
|
|
92
|
+
/** Summary of a top-level block, for `getBlocks()` and list-style UIs. */
|
|
93
|
+
export interface BlockInfo {
|
|
94
|
+
/** Current position in the body (unstable across edits). */
|
|
95
|
+
index: number;
|
|
96
|
+
/** Stable id — pair with `version` to form a `BlockRef`. */
|
|
97
|
+
id: string;
|
|
98
|
+
/** Bumps on every modification of this block. */
|
|
99
|
+
version: number;
|
|
100
|
+
kind: Block["kind"];
|
|
101
|
+
styleId?: string;
|
|
102
|
+
alignment?: ParagraphAlignment;
|
|
103
|
+
/** Plain-text preview. */
|
|
104
|
+
text: string;
|
|
105
|
+
/** Total character-length of the block's content (see `runsLength`). */
|
|
106
|
+
length: number;
|
|
107
|
+
}
|
|
108
|
+
/** Outline entry — one per heading in document order. */
|
|
109
|
+
export interface OutlineItem {
|
|
110
|
+
level: number;
|
|
111
|
+
text: string;
|
|
112
|
+
blockIndex: number;
|
|
113
|
+
block: BlockRef;
|
|
114
|
+
}
|
|
115
|
+
export type ParagraphPropertiesPatch = {
|
|
116
|
+
[K in keyof ParagraphProperties]?: ParagraphProperties[K] | undefined;
|
|
117
|
+
};
|
|
118
|
+
export type WrapTag = "sup" | "sub" | "strong" | "em" | "u" | "s" | "mark";
|
|
119
|
+
export type EditorEvent = "change" | "selection" | "keydown" | "track-changes-change";
|
|
120
|
+
export type EditorEventPayload = {
|
|
121
|
+
change: ChangePayload;
|
|
122
|
+
selection: SelectionPayload;
|
|
123
|
+
keydown: KeyDownPayload;
|
|
124
|
+
"track-changes-change": TrackChangesState;
|
|
125
|
+
};
|
|
126
|
+
export type Unsubscribe = () => void;
|
|
127
|
+
/**
|
|
128
|
+
* Payload delivered to `selection` subscribers. Fires whenever the live
|
|
129
|
+
* DOM selection changes — typing, clicking, arrow-key navigation, focus
|
|
130
|
+
* loss, programmatic restore. Subscribers should subscribe through the
|
|
131
|
+
* editor rather than `document.addEventListener("selectionchange")`
|
|
132
|
+
* directly so cleanup is centralised and the editor can later add
|
|
133
|
+
* dedup / throttling.
|
|
134
|
+
*
|
|
135
|
+
* `selection` is the model shape (`null` when focus is outside the
|
|
136
|
+
* editor). The convenience fields below mirror what `EditorSelection`
|
|
137
|
+
* exposes for ergonomics — read whichever one you need.
|
|
138
|
+
*/
|
|
139
|
+
export interface SelectionPayload {
|
|
140
|
+
selection: Selection;
|
|
141
|
+
range: ApiRange | null;
|
|
142
|
+
caret: InlinePosition | null;
|
|
143
|
+
block: BlockRef | null;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Payload delivered to `keydown` subscribers. Fires for every key press
|
|
147
|
+
* inside the editor host. The editor binds NO shortcuts itself —
|
|
148
|
+
* plugins map keys to API calls via `preventDefault()` (stops the
|
|
149
|
+
* browser's default action) and `stopPropagation()` (stops the chain
|
|
150
|
+
* of remaining subscribers). Subscribers fire in registration order.
|
|
151
|
+
*/
|
|
152
|
+
export interface KeyDownPayload {
|
|
153
|
+
/** `KeyboardEvent.key` — `"b"`, `"Enter"`, `"ArrowLeft"`, … (lowercased for letters). */
|
|
154
|
+
key: string;
|
|
155
|
+
/** `KeyboardEvent.code` — `"KeyB"`, `"Enter"`, `"ArrowLeft"`, … (layout-independent). */
|
|
156
|
+
code: string;
|
|
157
|
+
ctrl: boolean;
|
|
158
|
+
shift: boolean;
|
|
159
|
+
alt: boolean;
|
|
160
|
+
meta: boolean;
|
|
161
|
+
/** Stop the browser's default for this key (insertion, navigation, …). */
|
|
162
|
+
preventDefault(): void;
|
|
163
|
+
/** Stop further subscribers from receiving this key. */
|
|
164
|
+
stopPropagation(): void;
|
|
165
|
+
/** Underlying DOM event — for advanced needs (`isComposing`, repeat, …). */
|
|
166
|
+
originalEvent: KeyboardEvent;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* A registered command — a named, callable unit of editor work that
|
|
170
|
+
* plugins coordinate around. Same definition gets reached by a
|
|
171
|
+
* keyboard shortcut, a toolbar click, a programmatic call from an
|
|
172
|
+
* agent, or an MCP request.
|
|
173
|
+
*
|
|
174
|
+
* The `run` function is the one place the work happens. `isActive` and
|
|
175
|
+
* `isAvailable` let UI plugins paint toggle / disabled state without
|
|
176
|
+
* understanding what the command actually does.
|
|
177
|
+
*/
|
|
178
|
+
export interface CommandDefinition<Args = void> {
|
|
179
|
+
/** Dotted, namespaced — `"mark.toggle.bold"`, `"section.insertBreak"`, … */
|
|
180
|
+
name: string;
|
|
181
|
+
/** Short human label for tooltips / command palettes. */
|
|
182
|
+
title?: string;
|
|
183
|
+
/** Perform the work. Should be idempotent w.r.t. selection (a second
|
|
184
|
+
* invocation on an already-bold selection clears bold, etc.). */
|
|
185
|
+
run: (args: Args) => void;
|
|
186
|
+
/** True when the command represents an active state (mark already on,
|
|
187
|
+
* block is already a heading, …). Drives toolbar `is-active`. */
|
|
188
|
+
isActive?: () => boolean;
|
|
189
|
+
/** False when the command can't run (e.g. selection is wrong shape).
|
|
190
|
+
* Defaults to true. Drives toolbar `disabled`. */
|
|
191
|
+
isAvailable?: () => boolean;
|
|
192
|
+
}
|
|
193
|
+
/** Snapshot of one registered command — what `commands.list()` returns. */
|
|
194
|
+
export interface CommandSnapshot {
|
|
195
|
+
name: string;
|
|
196
|
+
title: string;
|
|
197
|
+
isActive: boolean;
|
|
198
|
+
isAvailable: boolean;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Registry every plugin uses to talk to every other plugin. The
|
|
202
|
+
* editor owns it; plugins register commands on attach and unregister
|
|
203
|
+
* on detach. Keyboard plugins, toolbar plugins, and a future MCP
|
|
204
|
+
* adapter all share the same dispatch path: `editor.commands.execute(name)`.
|
|
205
|
+
*/
|
|
206
|
+
export interface CommandBus {
|
|
207
|
+
/** Register a command. Returns an unsubscribe that removes it. */
|
|
208
|
+
register<Args = void>(def: CommandDefinition<Args>): () => void;
|
|
209
|
+
/** Run a registered command. No-op (with a warning) if unknown. */
|
|
210
|
+
execute<Args = void>(name: string, args?: Args): void;
|
|
211
|
+
/** Snapshot every registered command — for command palettes,
|
|
212
|
+
* toolbars rendering toggle states, accessibility audits. */
|
|
213
|
+
list(): CommandSnapshot[];
|
|
214
|
+
/** Whether the named command is currently registered. */
|
|
215
|
+
has(name: string): boolean;
|
|
216
|
+
}
|
|
217
|
+
export interface EditorOptions {
|
|
218
|
+
initialDocument?: SobreeDocument;
|
|
219
|
+
changeDebounceMs?: number;
|
|
220
|
+
/**
|
|
221
|
+
* Elements whose children are editable blocks, in document order. Called
|
|
222
|
+
* fresh each time — the list can grow/shrink (e.g. during pagination).
|
|
223
|
+
*/
|
|
224
|
+
contentHosts?: () => HTMLElement[];
|
|
225
|
+
/**
|
|
226
|
+
* Y.Doc backing the document. Optional — if absent, the editor creates
|
|
227
|
+
* one internally. Embedders pass their own when they need to attach
|
|
228
|
+
* a provider (`y-websocket`, `y-indexeddb`, `y-webrtc`, …) for
|
|
229
|
+
* persistence or collaboration.
|
|
230
|
+
*
|
|
231
|
+
* When supplied, the editor checks whether the Y.Doc already has body
|
|
232
|
+
* content. If empty, it seeds from `initialDocument`. If non-empty
|
|
233
|
+
* (Phase 2+: a peer joined an active room), the existing Y.Doc state
|
|
234
|
+
* wins and `initialDocument` is ignored. See `editor.ydoc` for the
|
|
235
|
+
* public escape hatch.
|
|
236
|
+
*/
|
|
237
|
+
ydoc?: Y.Doc;
|
|
238
|
+
/**
|
|
239
|
+
* Optional content-hashed `BlobStore` for binary parts (images, fonts).
|
|
240
|
+
*
|
|
241
|
+
* Without one (default): bytes live inline in the Y.Doc's `parts`
|
|
242
|
+
* Y.Map and replicate to every peer through Y updates. Fine for
|
|
243
|
+
* small docs.
|
|
244
|
+
*
|
|
245
|
+
* With one (Phase 3.2+): the editor hashes binary parts, uploads
|
|
246
|
+
* the bytes to the store, and writes only the hash into the Y.Doc's
|
|
247
|
+
* `partRefs` Y.Map. Y updates stay small regardless of image size.
|
|
248
|
+
* The editor maintains a local `BlobCache` that synchronously serves
|
|
249
|
+
* already-fetched bytes to the renderer; `editor.ensurePartsLoaded()`
|
|
250
|
+
* is the async hook for explicit pre-fetching (e.g. before
|
|
251
|
+
* `toDocx()`).
|
|
252
|
+
*
|
|
253
|
+
* See `@sobree/core/blob` for the interface + reference impls
|
|
254
|
+
* (`inMemoryBlobStore`, `fetchBlobStore`).
|
|
255
|
+
*/
|
|
256
|
+
blobStore?: BlobStore;
|
|
257
|
+
/**
|
|
258
|
+
* Initial track-changes mode. When omitted, the editor starts in
|
|
259
|
+
* direct-edit mode (`{ enabled: false }`) and embedders can flip it
|
|
260
|
+
* later via `editor.setTrackChanges`. See `TrackChangesState`.
|
|
261
|
+
*/
|
|
262
|
+
trackChanges?: TrackChangesState;
|
|
263
|
+
}
|
|
264
|
+
export { runsLength } from '../doc/runs';
|
|
265
|
+
export type { RunPropertiesPatch };
|
|
266
|
+
/**
|
|
267
|
+
* Public editor surface.
|
|
268
|
+
*
|
|
269
|
+
* Two entry points for every operation:
|
|
270
|
+
* - Core methods take `BlockRef` / `InlinePosition` / `Range` and
|
|
271
|
+
* return `EditResult`. These are the wire-callable API — same
|
|
272
|
+
* contract for in-process toolbars, headless Y peers (HeadlessSobree),
|
|
273
|
+
* and future MCP wrappers.
|
|
274
|
+
* - "AtSelection" sugar reads the live DOM selection, builds the
|
|
275
|
+
* position/range for you, and delegates to the core. Use these in
|
|
276
|
+
* in-process UI code.
|
|
277
|
+
*
|
|
278
|
+
* Mutations enforce optimistic locking via block `version` numbers.
|
|
279
|
+
* Conflicts return `{ ok: false, error: { code: "optimistic-lock", … } }`
|
|
280
|
+
* rather than throwing.
|
|
281
|
+
*/
|
|
282
|
+
export declare class Editor {
|
|
283
|
+
readonly host: HTMLElement;
|
|
284
|
+
readonly selection: EditorSelection;
|
|
285
|
+
/**
|
|
286
|
+
* Ergonomic table operations — row/column insert-delete, cell merge /
|
|
287
|
+
* unmerge, cell-level properties. Every method returns an `EditResult`
|
|
288
|
+
* and inherits optimistic-lock checking via `replaceBlock`.
|
|
289
|
+
*/
|
|
290
|
+
readonly table: EditorTable;
|
|
291
|
+
/**
|
|
292
|
+
* Named-command registry — the coordination point between plugins.
|
|
293
|
+
* Plugins register commands on attach and unregister on detach;
|
|
294
|
+
* keyboard / toolbar / agent / MCP all dispatch through `execute()`.
|
|
295
|
+
*/
|
|
296
|
+
readonly commands: CommandBus;
|
|
297
|
+
/**
|
|
298
|
+
* Y.Doc backing the document. The Editor's `this.doc` field is a
|
|
299
|
+
* cached projection of this Y.Doc — every local mutation mirrors
|
|
300
|
+
* into the Y.Doc inside a transact (`origin: "local"`). Embedders
|
|
301
|
+
* read this for escape-hatch wiring (providers, dev tools, custom
|
|
302
|
+
* persistence).
|
|
303
|
+
*
|
|
304
|
+
* Phase 1a: Y.Doc is a faithful mirror of `this.doc` but the Editor
|
|
305
|
+
* still treats `this.doc` as the in-memory truth. Phase 1b inverts
|
|
306
|
+
* this — Y.Doc becomes the truth and `this.doc` becomes a pure
|
|
307
|
+
* cache invalidated by Y observers (which is what enables Phase 2
|
|
308
|
+
* providers to drive the editor from outside).
|
|
309
|
+
*/
|
|
310
|
+
readonly ydoc: Y.Doc;
|
|
311
|
+
/**
|
|
312
|
+
* Optional content-hashed blob layer (Phase 3.2+). When set, binary
|
|
313
|
+
* parts (images, fonts) go through this rather than living inline
|
|
314
|
+
* in the Y.Doc. `null` means "use the inline parts path" — today's
|
|
315
|
+
* default. See `EditorOptions.blobStore`.
|
|
316
|
+
*/
|
|
317
|
+
readonly blobStore: BlobStore | null;
|
|
318
|
+
/**
|
|
319
|
+
* Local cache for blob bytes — synchronously fetchable for the
|
|
320
|
+
* renderer, populated by `blobStore.put` (local writes) or
|
|
321
|
+
* `blobStore.get` (background fetches). `null` when no blobStore
|
|
322
|
+
* is configured.
|
|
323
|
+
*/
|
|
324
|
+
readonly blobCache: BlobCache | null;
|
|
325
|
+
/** Most recent projection's partRefs map (path → hash). Used by
|
|
326
|
+
* `ensurePartsLoaded` and by the `onResolved` callback to know
|
|
327
|
+
* which paths reference a freshly-arrived blob. */
|
|
328
|
+
private lastPartRefs;
|
|
329
|
+
/**
|
|
330
|
+
* Part paths whose bytes are in flight to the BlobStore — the
|
|
331
|
+
* editor wrote them synchronously to `doc.rawParts` (so the local
|
|
332
|
+
* renderer has them), but the async hash + upload + partRef write
|
|
333
|
+
* is still pending. Mirror skips these so they don't end up inline
|
|
334
|
+
* in the Y.Doc.
|
|
335
|
+
*/
|
|
336
|
+
private readonly pendingPartRefMigrations;
|
|
337
|
+
/** Listener that re-projects + re-renders on remote Y.Doc updates.
|
|
338
|
+
* Removed on `destroy()`. */
|
|
339
|
+
private ydocUpdateListener;
|
|
340
|
+
private doc;
|
|
341
|
+
private readonly registry;
|
|
342
|
+
private readonly debounceMs;
|
|
343
|
+
private readonly getContentHosts;
|
|
344
|
+
private debounceHandle;
|
|
345
|
+
private detachImageResize;
|
|
346
|
+
private inputListener;
|
|
347
|
+
private beforeInputListener;
|
|
348
|
+
private pasteListener;
|
|
349
|
+
private dragOverListener;
|
|
350
|
+
private dropListener;
|
|
351
|
+
private revision;
|
|
352
|
+
private readonly listeners;
|
|
353
|
+
/**
|
|
354
|
+
* Authoring mode for revisions. Off by default — mutations apply
|
|
355
|
+
* plainly. On, `insertRun` / `deleteRange` route through
|
|
356
|
+
* `applyTrackChangesToInsert` / `applyTrackChangesToDelete`. See the
|
|
357
|
+
* `TrackChangesState` type docblock for the full semantics.
|
|
358
|
+
*/
|
|
359
|
+
private trackChanges;
|
|
360
|
+
/**
|
|
361
|
+
* One-shot warning set for tracked-mode beforeinput types we don't
|
|
362
|
+
* (yet) route through the API — paragraph splits, paste, IME, etc.
|
|
363
|
+
* We let the browser handle them untracked rather than swallowing
|
|
364
|
+
* the keystroke, but log the inputType the first time we see it so
|
|
365
|
+
* the gap is visible during development.
|
|
366
|
+
*/
|
|
367
|
+
private trackedInputWarned;
|
|
368
|
+
/**
|
|
369
|
+
* Active IME composition state (`compositionstart` → `compositionend`).
|
|
370
|
+
* `null` outside composition or in non-tracked mode.
|
|
371
|
+
*
|
|
372
|
+
* Snapshot-then-restore is the only practical way to track IME-typed
|
|
373
|
+
* text: we can't intercept `beforeinput` during composition without
|
|
374
|
+
* breaking input methods on most platforms, so we let the browser
|
|
375
|
+
* mutate the DOM natively during composition, then on `compositionend`
|
|
376
|
+
* we roll back to the pre-composition AST and re-insert the final
|
|
377
|
+
* composed string (`event.data`) through `insertRun` — which stamps
|
|
378
|
+
* the `revision: ins` marker per `TrackChangesState`.
|
|
379
|
+
*/
|
|
380
|
+
private composition;
|
|
381
|
+
private compositionStartListener;
|
|
382
|
+
private compositionEndListener;
|
|
383
|
+
/** Document-level `selectionchange` listener. Funnels every cursor
|
|
384
|
+
* movement (typing, click, arrows, programmatic restore) into the
|
|
385
|
+
* `selection` event so plugins don't each register their own. */
|
|
386
|
+
private selectionChangeListener;
|
|
387
|
+
/** Host-level `keydown` listener. Funnels every key press into the
|
|
388
|
+
* `keydown` event for plugins (mark shortcuts, navigation, etc.). */
|
|
389
|
+
private keydownListener;
|
|
390
|
+
/** Cached last-seen per-block JSON strings, for diff-based version bumps. */
|
|
391
|
+
private lastSerialisedBlocks;
|
|
392
|
+
/** Tracks `@font-face` registrations for the document's embedded fonts. */
|
|
393
|
+
private readonly fontFaces;
|
|
394
|
+
/**
|
|
395
|
+
* Undo / redo orchestrator. Snapshots the document at every commit
|
|
396
|
+
* + at the start of each typing session (coalesced). Memory-efficient:
|
|
397
|
+
* snapshots store doc references, not deep copies — the immutable-block
|
|
398
|
+
* model means consecutive snapshots share most blocks. Public API is
|
|
399
|
+
* `editor.history.undo() / redo() / clear() / depth() / on(...)`.
|
|
400
|
+
*/
|
|
401
|
+
readonly history: History;
|
|
402
|
+
/**
|
|
403
|
+
* True when DOM mutations since the last sync were user-driven (typing,
|
|
404
|
+
* paste, drag-drop image). False right after we render from AST — the
|
|
405
|
+
* DOM is then a projection of `this.doc`, and reading it back can't
|
|
406
|
+
* tell us anything the AST doesn't already know, while losing any
|
|
407
|
+
* fidelity the serializer drops (column widths, vAlign, …). `getDocument`
|
|
408
|
+
* and `emitChangeNow` sync only when this flag is set.
|
|
409
|
+
*/
|
|
410
|
+
private domDirty;
|
|
411
|
+
constructor(host: HTMLElement, options?: EditorOptions);
|
|
412
|
+
/**
|
|
413
|
+
* Re-project the Y.Doc into `this.doc`, sync the BlockRegistry to
|
|
414
|
+
* the projected ids, re-render the DOM, and fire `change`. Called
|
|
415
|
+
* when a remote provider applies an update we didn't initiate.
|
|
416
|
+
*/
|
|
417
|
+
private adoptYDocState;
|
|
418
|
+
/**
|
|
419
|
+
* Resolve any `partRefs` hashes that are currently cached into the
|
|
420
|
+
* provided document's `rawParts`. In-place mutation — the document
|
|
421
|
+
* is owned by the caller. Missing hashes stay out of `rawParts`;
|
|
422
|
+
* the renderer handles missing parts gracefully (placeholder).
|
|
423
|
+
*/
|
|
424
|
+
private resolveCachedPartRefsInto;
|
|
425
|
+
/**
|
|
426
|
+
* Callback fired by the BlobCache when a background fetch lands.
|
|
427
|
+
* Walks `lastPartRefs` to find which paths reference this hash,
|
|
428
|
+
* patches `this.doc.rawParts`, and re-renders so the user sees
|
|
429
|
+
* the part appear.
|
|
430
|
+
*/
|
|
431
|
+
private onBlobResolved;
|
|
432
|
+
/**
|
|
433
|
+
* Wait for every currently-referenced binary part to be available
|
|
434
|
+
* in the local cache. Useful before `toDocx()` so the exported
|
|
435
|
+
* file contains all images / fonts.
|
|
436
|
+
*
|
|
437
|
+
* Returns a resolved Promise immediately when no `blobStore` is
|
|
438
|
+
* configured (today's default — bytes are always inline).
|
|
439
|
+
*/
|
|
440
|
+
ensurePartsLoaded(): Promise<void>;
|
|
441
|
+
/**
|
|
442
|
+
* Current document as SobreeDocument.
|
|
443
|
+
*
|
|
444
|
+
* Syncs from the DOM only if the user typed since the last render. If
|
|
445
|
+
* the latest change came from an API call, returns the in-memory AST
|
|
446
|
+
* verbatim — the DOM is a projection of it and reading back would
|
|
447
|
+
* throw away properties the renderer doesn't surface.
|
|
448
|
+
*/
|
|
449
|
+
getDocument(): SobreeDocument;
|
|
450
|
+
/** Replace the document. Fires `change` synchronously. */
|
|
451
|
+
setDocument(doc: SobreeDocument): void;
|
|
452
|
+
/**
|
|
453
|
+
* Internal apply path shared by `setDocument` and any other
|
|
454
|
+
* full-replace caller. The Y.Doc mirror produces tracked Y
|
|
455
|
+
* operations that Y.UndoManager turns into a single stack item.
|
|
456
|
+
*/
|
|
457
|
+
private applyDocument;
|
|
458
|
+
/**
|
|
459
|
+
* Drop entries from `rawParts` that nothing in the AST references.
|
|
460
|
+
* Useful after deleting images (or font embeds — Phase 3) to keep the
|
|
461
|
+
* in-memory doc lean. Idempotent; reports the keys that were removed.
|
|
462
|
+
*
|
|
463
|
+
* Not auto-invoked — `exportDocx` already filters at packaging time,
|
|
464
|
+
* so callers only need this when they're keeping the doc around
|
|
465
|
+
* in-memory across many edits.
|
|
466
|
+
*/
|
|
467
|
+
pruneUnusedParts(): {
|
|
468
|
+
kept: number;
|
|
469
|
+
pruned: string[];
|
|
470
|
+
};
|
|
471
|
+
/**
|
|
472
|
+
* Embed a TTF/OTF font into the document. Thin wrapper around
|
|
473
|
+
* `embedFontIntoDoc()` from the fonts module — handles the
|
|
474
|
+
* setDocument round so the renderer + `@font-face` registry pick
|
|
475
|
+
* up the new face automatically.
|
|
476
|
+
*
|
|
477
|
+
* Refuses (with a warning) when the font's OS/2 `fsType` field
|
|
478
|
+
* marks it as restricted, unless `opts.allowRestricted` is true.
|
|
479
|
+
* Pass any subset of {regular, bold, italic, boldItalic}; missing
|
|
480
|
+
* faces are simply not embedded.
|
|
481
|
+
*/
|
|
482
|
+
embedFont(name: string, faces: EmbedFontFaces, opts?: EmbedFontOptions): {
|
|
483
|
+
warnings: string[];
|
|
484
|
+
};
|
|
485
|
+
/**
|
|
486
|
+
* Drop a font declaration by name. The associated font parts in
|
|
487
|
+
* `rawParts` aren't immediately removed — call `pruneUnusedParts()`
|
|
488
|
+
* (or just export) to GC them.
|
|
489
|
+
*/
|
|
490
|
+
removeEmbeddedFont(name: string): void;
|
|
491
|
+
/** Monotonic counter bumped on each `change` event. */
|
|
492
|
+
getRevision(): number;
|
|
493
|
+
/** Monotonic document-wide version (bumps on any mutation). */
|
|
494
|
+
getDocumentVersion(): number;
|
|
495
|
+
/** Render current document to an HTML string. */
|
|
496
|
+
toHtml(): string;
|
|
497
|
+
getBlocks(): BlockInfo[];
|
|
498
|
+
getBlock(index: number): BlockInfo;
|
|
499
|
+
/** Same summary, looked up by stable id. Returns `null` if unknown. */
|
|
500
|
+
getBlockById(id: string): BlockInfo | null;
|
|
501
|
+
getOutline(): OutlineItem[];
|
|
502
|
+
/** Replace the block at `target`'s index with `block`. */
|
|
503
|
+
replaceBlock(target: BlockRef, block: Block): EditResult<BlockRef>;
|
|
504
|
+
/**
|
|
505
|
+
* Insert `block` before the target block. Returns the new ref.
|
|
506
|
+
*
|
|
507
|
+
* In track-changes mode (see `setTrackChanges`), if `block` is a
|
|
508
|
+
* paragraph it gets stamped with `revision: { type: "ins", author }`
|
|
509
|
+
* on its properties — the same paragraph-mark semantics as
|
|
510
|
+
* `splitBlock`. Non-paragraph blocks (table, section_break) don't
|
|
511
|
+
* carry the marker in v1 and insert plain.
|
|
512
|
+
*/
|
|
513
|
+
insertBlockBefore(target: BlockRef, block: Block): EditResult<BlockRef>;
|
|
514
|
+
/**
|
|
515
|
+
* Insert `block` after the target block. Returns the new ref.
|
|
516
|
+
* Tracked-mode behaviour matches `insertBlockBefore`.
|
|
517
|
+
*/
|
|
518
|
+
insertBlockAfter(target: BlockRef, block: Block): EditResult<BlockRef>;
|
|
519
|
+
/**
|
|
520
|
+
* Delete the target block.
|
|
521
|
+
*
|
|
522
|
+
* In track-changes mode, paragraph blocks aren't removed — their
|
|
523
|
+
* `properties.revision` is stamped `del` (the renderer shows the
|
|
524
|
+
* paragraph mark with a strikethrough ¶ glyph; the body text stays
|
|
525
|
+
* visible). If the paragraph carries the *current author's* pending
|
|
526
|
+
* `ins` marker (a paragraph the user themselves just created), the
|
|
527
|
+
* block is removed outright — cancelling an un-committed insert,
|
|
528
|
+
* matching the inline `deleteRange` semantics. Non-paragraph blocks
|
|
529
|
+
* (tables, section breaks) bypass tracking in v1 — they remove plainly.
|
|
530
|
+
*/
|
|
531
|
+
deleteBlock(target: BlockRef): EditResult<void>;
|
|
532
|
+
/**
|
|
533
|
+
* Stamp `revision: ins` on a paragraph block if tracked mode is on
|
|
534
|
+
* and the block doesn't already carry one. Helper for
|
|
535
|
+
* `insertBlockBefore` / `insertBlockAfter`. Non-paragraph blocks
|
|
536
|
+
* pass through unchanged.
|
|
537
|
+
*/
|
|
538
|
+
private stampInsertedBlockIfTracked;
|
|
539
|
+
/** Merge a patch into each target paragraph's properties. */
|
|
540
|
+
applyBlockProperties(targets: BlockRef[], patch: ParagraphPropertiesPatch): EditResult<void>;
|
|
541
|
+
/**
|
|
542
|
+
* Apply run-level properties across `range`.
|
|
543
|
+
*
|
|
544
|
+
* In track-changes mode (see `setTrackChanges`), each text run in
|
|
545
|
+
* `range` that doesn't already carry a `revisionFormat` snapshot
|
|
546
|
+
* gets one — capturing the run's properties *before* the patch is
|
|
547
|
+
* applied. Repeated tracked edits don't overwrite the snapshot, so
|
|
548
|
+
* a reject always returns the run to its pre-tracking state.
|
|
549
|
+
* Non-text runs and runs with no concrete properties pass through
|
|
550
|
+
* unchanged.
|
|
551
|
+
*/
|
|
552
|
+
applyRunProperties(range: ApiRange, patch: RunPropertiesPatch, opts?: {
|
|
553
|
+
expect?: Record<string, number>;
|
|
554
|
+
}): EditResult<void>;
|
|
555
|
+
/** Wrap the runs in `range` with semantic formatting. */
|
|
556
|
+
wrapRange(range: ApiRange, tag: WrapTag, opts?: {
|
|
557
|
+
expect?: Record<string, number>;
|
|
558
|
+
}): EditResult<void>;
|
|
559
|
+
/**
|
|
560
|
+
* Insert a run at `at`. Splits the run list at the offset.
|
|
561
|
+
*
|
|
562
|
+
* In track-changes mode (see `setTrackChanges`), the run is stamped
|
|
563
|
+
* with `revision: { type: "ins", author }` before insertion (unless
|
|
564
|
+
* it already carries a `revision` — caller-provided revisions are
|
|
565
|
+
* never overwritten).
|
|
566
|
+
*/
|
|
567
|
+
insertRun(at: InlinePosition, run: InlineRun): EditResult<BlockRef>;
|
|
568
|
+
/**
|
|
569
|
+
* Split a paragraph at `at`. The runs before the offset stay on the
|
|
570
|
+
* original block; the runs after move into a fresh paragraph that's
|
|
571
|
+
* inserted immediately after. The new block inherits the original
|
|
572
|
+
* paragraph's properties (alignment, style, indent, …) so the visual
|
|
573
|
+
* shape of the split is what the user expects from pressing Enter.
|
|
574
|
+
*
|
|
575
|
+
* In track-changes mode (see `setTrackChanges`), the new paragraph's
|
|
576
|
+
* `properties.revision` is stamped `{ type: "ins", author }` — the
|
|
577
|
+
* "this paragraph break is a tracked insert" marker. The original
|
|
578
|
+
* paragraph is left clean; only the *new* paragraph carries the mark,
|
|
579
|
+
* mirroring how Word stores `<w:rPr><w:ins/></w:rPr>` inside `<w:pPr>`.
|
|
580
|
+
*
|
|
581
|
+
* `at.offset` is clamped to `[0, block-length]`. A split at offset 0
|
|
582
|
+
* inserts an empty paragraph *before* the cursor; a split at the
|
|
583
|
+
* block's full length inserts an empty paragraph *after*.
|
|
584
|
+
*
|
|
585
|
+
* Returns the ref of the *new* (second) block — callers typically
|
|
586
|
+
* place the caret at its offset 0.
|
|
587
|
+
*/
|
|
588
|
+
splitBlock(at: InlinePosition): EditResult<BlockRef>;
|
|
589
|
+
/**
|
|
590
|
+
* Insert an image at `at`. The bytes are stored in `doc.rawParts` under
|
|
591
|
+
* a fresh `word/media/imageN.{ext}` path; a `DrawingRun` referencing
|
|
592
|
+
* that path is inserted at the position.
|
|
593
|
+
*
|
|
594
|
+
* When a `blobStore` is configured (Phase 3.2+), the bytes also
|
|
595
|
+
* migrate in the background: hashed, uploaded to the store, and a
|
|
596
|
+
* `partRefs[partPath] = hash` entry written to the Y.Doc. Once the
|
|
597
|
+
* migration lands, the Y.Doc's inline `parts[partPath]` is cleared —
|
|
598
|
+
* so the bytes ride the side-channel, not the Y update stream.
|
|
599
|
+
* The local renderer keeps reading `doc.rawParts[partPath]`
|
|
600
|
+
* throughout (the value is stable from the moment `insertImage`
|
|
601
|
+
* returns).
|
|
602
|
+
*/
|
|
603
|
+
insertImage(at: InlinePosition, bytes: Uint8Array, opts: {
|
|
604
|
+
mime: string;
|
|
605
|
+
widthPx?: number;
|
|
606
|
+
heightPx?: number;
|
|
607
|
+
altText?: string;
|
|
608
|
+
}): EditResult<BlockRef>;
|
|
609
|
+
/**
|
|
610
|
+
* Delete the content inside `range`. Supports both single-block and
|
|
611
|
+
* cross-paragraph ranges.
|
|
612
|
+
*
|
|
613
|
+
* In track-changes mode (see `setTrackChanges`), the deletion is
|
|
614
|
+
* *recorded* rather than applied:
|
|
615
|
+
* - plain runs are stamped with `revision: { type: "del", author }`;
|
|
616
|
+
* - a run already marked as the same author's pending `ins` is
|
|
617
|
+
* dropped (the author cancelling their own un-committed insert
|
|
618
|
+
* — no audit trail because it was never committed);
|
|
619
|
+
* - runs carrying a peer's revision (any author other than the
|
|
620
|
+
* current one) are left untouched: the API user should resolve
|
|
621
|
+
* those via `acceptRevision` / `rejectRevision` first.
|
|
622
|
+
*
|
|
623
|
+
* **Cross-paragraph behaviour**: in tracked mode every paragraph
|
|
624
|
+
* *after* the first one in the range gets its paragraph-mark
|
|
625
|
+
* stamped `del` too, so `acceptAllRevisions` later collapses the
|
|
626
|
+
* range into a single paragraph. In non-tracked mode the
|
|
627
|
+
* intermediate paragraphs are removed outright and the first +
|
|
628
|
+
* last blocks merge into one.
|
|
629
|
+
*/
|
|
630
|
+
deleteRange(range: ApiRange, opts?: {
|
|
631
|
+
expect?: Record<string, number>;
|
|
632
|
+
}): EditResult<void>;
|
|
633
|
+
/**
|
|
634
|
+
* Tracked cross-paragraph delete. Walks each paragraph in the range:
|
|
635
|
+
* stamps `del` on the affected runs (first-block tail / intermediate
|
|
636
|
+
* full / last-block head), and on every paragraph *after the first*
|
|
637
|
+
* stamps the paragraph-mark `del` so `acceptAllRevisions` later
|
|
638
|
+
* merges them all into the first block. Single commit.
|
|
639
|
+
*/
|
|
640
|
+
private deleteRangeAcrossBlocksTracked;
|
|
641
|
+
/**
|
|
642
|
+
* Non-tracked cross-paragraph delete. Keeps the head of the first
|
|
643
|
+
* block + the tail of the last block, splices them into the first
|
|
644
|
+
* block as one paragraph, and removes everything in between.
|
|
645
|
+
*/
|
|
646
|
+
private deleteRangeAcrossBlocksPlain;
|
|
647
|
+
/**
|
|
648
|
+
* Read the current track-changes mode. Defaults to `{ enabled: false }`
|
|
649
|
+
* — the editor mutates the document plainly. See `setTrackChanges`.
|
|
650
|
+
*
|
|
651
|
+
* Returns a fresh copy each call, so callers can mutate the returned
|
|
652
|
+
* object without affecting editor state.
|
|
653
|
+
*/
|
|
654
|
+
getTrackChanges(): TrackChangesState;
|
|
655
|
+
/**
|
|
656
|
+
* Switch authoring mode. When `enabled`, subsequent `insertRun` and
|
|
657
|
+
* `deleteRange` calls produce tracked revisions instead of direct
|
|
658
|
+
* mutations (see `TrackChangesState`'s docblock for the full rules).
|
|
659
|
+
*
|
|
660
|
+
* Fires `track-changes-change` if the state actually changes
|
|
661
|
+
* (idempotent — re-setting the same enabled+author is a no-op).
|
|
662
|
+
* Listeners receive the new state; embedders typically forward it
|
|
663
|
+
* to a toolbar pill / mode badge.
|
|
664
|
+
*/
|
|
665
|
+
setTrackChanges(state: TrackChangesState): void;
|
|
666
|
+
/**
|
|
667
|
+
* Route a tracked-mode `beforeinput` event through the typed API so
|
|
668
|
+
* the resulting runs carry revision markers. Returns `true` if the
|
|
669
|
+
* event was consumed (caller should `preventDefault`), `false` to
|
|
670
|
+
* let the browser handle it natively (untracked).
|
|
671
|
+
*
|
|
672
|
+
* **Handled inputTypes** (the 95% case — typing and deleting text):
|
|
673
|
+
*
|
|
674
|
+
* - `insertText`, `insertReplacementText` — typed characters,
|
|
675
|
+
* dictation / autocomplete replacements.
|
|
676
|
+
* - `deleteContentBackward`, `deleteContentForward` — Backspace
|
|
677
|
+
* and Delete keys, including over a selection.
|
|
678
|
+
* - `deleteWordBackward`, `deleteWordForward` — Option-Backspace
|
|
679
|
+
* style word deletions; the browser collapses to the right
|
|
680
|
+
* selection range before firing, we just delete it.
|
|
681
|
+
* - `deleteByCut` — Cmd-X. The clipboard side has already been
|
|
682
|
+
* populated by the browser; we mark the source range deleted.
|
|
683
|
+
*
|
|
684
|
+
* **Unhandled** (return `false`, fall through, warn-once):
|
|
685
|
+
*
|
|
686
|
+
* - `insertParagraph` / `insertLineBreak` — block split / soft
|
|
687
|
+
* break. Word tracks these as `<w:ins>` on the paragraph mark;
|
|
688
|
+
* the corresponding AST mutation (split-block as a tracked op)
|
|
689
|
+
* hasn't landed yet.
|
|
690
|
+
* - `insertFromPaste` — paste is handled at the `paste` event level
|
|
691
|
+
* in `onPaste` (not here), where tracked-mode plain-text paste is
|
|
692
|
+
* already routed through `insertRun` / `splitBlock`. Rich-paste
|
|
693
|
+
* (HTML) in tracked mode falls back to plain text by design.
|
|
694
|
+
* - `insertCompositionText` — handled separately via
|
|
695
|
+
* `compositionstart` / `compositionend` listeners
|
|
696
|
+
* (`handleCompositionStart` / `handleCompositionEnd`). We let the
|
|
697
|
+
* IME render natively during composition, then on end we restore
|
|
698
|
+
* the pre-composition AST and re-insert the final composed string
|
|
699
|
+
* through this `insertRun` path.
|
|
700
|
+
*
|
|
701
|
+
* Falling through means the keystroke still works (no broken UX in
|
|
702
|
+
* tracked mode), it just doesn't get a revision marker. The console
|
|
703
|
+
* warning makes the gap visible.
|
|
704
|
+
*
|
|
705
|
+
* Caret restoration: after every routed mutation we set the model
|
|
706
|
+
* selection to where the caret would have ended up on the equivalent
|
|
707
|
+
* direct-edit operation. Because tracked-mode `deleteRange` leaves
|
|
708
|
+
* the runs in place (marked `del`), offsets don't shift — caret math
|
|
709
|
+
* is straightforward.
|
|
710
|
+
*/
|
|
711
|
+
private handleTrackedInput;
|
|
712
|
+
/**
|
|
713
|
+
* Snapshot the pre-composition AST + caret so `handleCompositionEnd`
|
|
714
|
+
* can roll back the browser's native IME mutations and re-insert the
|
|
715
|
+
* final composed string through the tracked-mode `insertRun`. No-op
|
|
716
|
+
* when tracked mode is off — IME falls through to the browser as
|
|
717
|
+
* always (untracked, but functional).
|
|
718
|
+
*/
|
|
719
|
+
private handleCompositionStart;
|
|
720
|
+
/**
|
|
721
|
+
* Commit a tracked IME composition. Restores the AST to its
|
|
722
|
+
* pre-composition snapshot, re-renders, then inserts `event.data`
|
|
723
|
+
* through `insertRun` at the captured caret — so the final composed
|
|
724
|
+
* string lands as a tracked `ins` instead of as plain text from the
|
|
725
|
+
* browser's native IME commit.
|
|
726
|
+
*
|
|
727
|
+
* Bails out (and clears state) if tracked mode was toggled off
|
|
728
|
+
* mid-composition or the snapshot is missing; the browser's native
|
|
729
|
+
* commit then stands as-is (untracked, but functional).
|
|
730
|
+
*/
|
|
731
|
+
private handleCompositionEnd;
|
|
732
|
+
/**
|
|
733
|
+
* Resolve the position to insert at when the user types over the
|
|
734
|
+
* current selection. For a caret, that's the caret itself; for a
|
|
735
|
+
* range, we delete the range first (which in tracked mode marks the
|
|
736
|
+
* runs `del` but keeps them in place — so the `from` offset is still
|
|
737
|
+
* the right insertion point afterwards). Returns `null` if the
|
|
738
|
+
* selection spans blocks or the delete failed.
|
|
739
|
+
*/
|
|
740
|
+
private markedRangeForReplace;
|
|
741
|
+
/**
|
|
742
|
+
* Range a Backspace-style key should delete. Range-selection wins
|
|
743
|
+
* if there is one (just delete the selection). Otherwise step one
|
|
744
|
+
* character left of the caret — at offset 0 we have nothing to do
|
|
745
|
+
* (and Word does nothing in that position too, in v1; cross-block
|
|
746
|
+
* backspace is a follow-up).
|
|
747
|
+
*/
|
|
748
|
+
private rangeForBackwardDelete;
|
|
749
|
+
/** Forward-delete equivalent of `rangeForBackwardDelete`. */
|
|
750
|
+
private rangeForForwardDelete;
|
|
751
|
+
/** Re-lookup the block by id to get a fresh `BlockRef` (current version). */
|
|
752
|
+
private refreshedPosition;
|
|
753
|
+
/** Place the caret at `(blockId, offset)` using a fresh block ref. */
|
|
754
|
+
private placeCaret;
|
|
755
|
+
/**
|
|
756
|
+
* True when the current DOM selection's caret sits inside an `<ins>`,
|
|
757
|
+
* `<del>`, or `<span.sobree-revision-format>` wrapper — the markup
|
|
758
|
+
* the renderer emits for tracked revisions. Used by `beforeinput` in
|
|
759
|
+
* mode-off to detect when we have to take over the insert path: if
|
|
760
|
+
* we don't, the browser's contenteditable inserts the new character
|
|
761
|
+
* INSIDE the wrapper and the post-input DOM-sync stamps it with the
|
|
762
|
+
* wrapper's revision marker (an edit the user explicitly opted out
|
|
763
|
+
* of tracking).
|
|
764
|
+
*
|
|
765
|
+
* Returns `false` for a normal caret in plain text, in a `<strong>`
|
|
766
|
+
* / `<em>` / etc. — those wrappers *should* inherit (formatting),
|
|
767
|
+
* unlike revision wrappers which encode "edit history."
|
|
768
|
+
*/
|
|
769
|
+
private caretInsideRevisionWrapper;
|
|
770
|
+
/**
|
|
771
|
+
* Accept the tracked changes inside `range`: insertions become
|
|
772
|
+
* permanent (the revision marker is stripped, text kept), deletions
|
|
773
|
+
* are applied (the deleted text is dropped). Runs in `range` with no
|
|
774
|
+
* revision marker pass through untouched, so it's safe to pass a
|
|
775
|
+
* range slightly wider than the change.
|
|
776
|
+
*/
|
|
777
|
+
acceptRevision(range: ApiRange, opts?: {
|
|
778
|
+
expect?: Record<string, number>;
|
|
779
|
+
}): EditResult<void>;
|
|
780
|
+
/**
|
|
781
|
+
* Reject the tracked changes inside `range`: insertions are removed
|
|
782
|
+
* (the inserted text is dropped), deletions are restored (the
|
|
783
|
+
* revision marker is stripped, deleted text kept). The inverse of
|
|
784
|
+
* `acceptRevision`.
|
|
785
|
+
*/
|
|
786
|
+
rejectRevision(range: ApiRange, opts?: {
|
|
787
|
+
expect?: Record<string, number>;
|
|
788
|
+
}): EditResult<void>;
|
|
789
|
+
/**
|
|
790
|
+
* Accept tracked format changes inside `range`: each text run with a
|
|
791
|
+
* `revisionFormat` snapshot drops the snapshot; the current
|
|
792
|
+
* `properties` stay. Runs without one pass through.
|
|
793
|
+
*/
|
|
794
|
+
acceptFormatRevision(range: ApiRange, opts?: {
|
|
795
|
+
expect?: Record<string, number>;
|
|
796
|
+
}): EditResult<void>;
|
|
797
|
+
/**
|
|
798
|
+
* Reject tracked format changes inside `range`: each run reverts its
|
|
799
|
+
* `properties` to `revisionFormat.before`. Inverse of
|
|
800
|
+
* `acceptFormatRevision`.
|
|
801
|
+
*/
|
|
802
|
+
rejectFormatRevision(range: ApiRange, opts?: {
|
|
803
|
+
expect?: Record<string, number>;
|
|
804
|
+
}): EditResult<void>;
|
|
805
|
+
/**
|
|
806
|
+
* Accept the paragraph-mark revision on `target`.
|
|
807
|
+
*
|
|
808
|
+
* Per the semantics in `ParagraphProperties.revision`'s docblock:
|
|
809
|
+
* - `ins` → strip the marker; the paragraph break stays permanent.
|
|
810
|
+
* - `del` → merge this paragraph's content into the *previous*
|
|
811
|
+
* paragraph; the paragraph break is consumed.
|
|
812
|
+
*
|
|
813
|
+
* Returns `range-empty`-coded failure if the block has no paragraph
|
|
814
|
+
* revision, and `invalid-state` if a `del` accept would require
|
|
815
|
+
* merging with a non-paragraph (table, section break) — those merges
|
|
816
|
+
* aren't well-defined yet.
|
|
817
|
+
*/
|
|
818
|
+
acceptParagraphRevision(target: BlockRef): EditResult<void>;
|
|
819
|
+
/**
|
|
820
|
+
* Reject the paragraph-mark revision on `target`.
|
|
821
|
+
* - `ins` → merge this paragraph into the *previous* one (the split
|
|
822
|
+
* introduced by the tracked Enter is undone).
|
|
823
|
+
* - `del` → strip the marker; the paragraph break stays.
|
|
824
|
+
*/
|
|
825
|
+
rejectParagraphRevision(target: BlockRef): EditResult<void>;
|
|
826
|
+
/**
|
|
827
|
+
* Concatenate `body[index]`'s runs onto the end of `body[index-1]`
|
|
828
|
+
* and remove `body[index]`. The previous block must be a paragraph;
|
|
829
|
+
* otherwise we bail with `invalid-state`. Used by accept/reject of
|
|
830
|
+
* paragraph-mark revisions where the decision means "this paragraph
|
|
831
|
+
* break should not exist".
|
|
832
|
+
*/
|
|
833
|
+
private mergeWithPrevious;
|
|
834
|
+
/**
|
|
835
|
+
* Strip the `revision` marker from `body[index]`'s paragraph
|
|
836
|
+
* properties, leaving the block (and its content) in place.
|
|
837
|
+
* Fallback for merge-impossible cases — see `mergeWithPrevious`'s
|
|
838
|
+
* `index <= 0` branch and `applyAllRevisions`' second pass.
|
|
839
|
+
*/
|
|
840
|
+
private stripParagraphMarker;
|
|
841
|
+
/**
|
|
842
|
+
* Flag `body[index]`'s paragraph break as a tracked deletion —
|
|
843
|
+
* stamp `properties.revision = { type: "del", author }`. Used by
|
|
844
|
+
* `handleTrackedInput` for the Backspace-at-start-of-paragraph
|
|
845
|
+
* keystroke.
|
|
846
|
+
*
|
|
847
|
+
* Two short-circuits:
|
|
848
|
+
* - If the paragraph already carries the *same author's* pending
|
|
849
|
+
* `ins` (the user is backspacing into a split they themselves
|
|
850
|
+
* just made), drop the marker and merge into the previous
|
|
851
|
+
* paragraph — cancelling an un-committed insert rather than
|
|
852
|
+
* layering del on top of ins.
|
|
853
|
+
* - If the paragraph carries some OTHER revision (peer's ins, an
|
|
854
|
+
* existing del), leave it alone with a no-op success. The
|
|
855
|
+
* reviewer should resolve those via accept/reject first.
|
|
856
|
+
*/
|
|
857
|
+
private markParagraphBreakForDelete;
|
|
858
|
+
/**
|
|
859
|
+
* Enumerate every logical tracked change in the document.
|
|
860
|
+
*
|
|
861
|
+
* Consecutive revision-bearing runs by the same author coalesce into
|
|
862
|
+
* one `RevisionSpan` — so a delete-then-insert replacement is one
|
|
863
|
+
* span, and two unrelated insertions in a paragraph (separated by
|
|
864
|
+
* plain text) are two. Each span's `range` carries fresh, versioned
|
|
865
|
+
* `BlockRef`s, ready to hand to `acceptRevision` / `rejectRevision`.
|
|
866
|
+
*
|
|
867
|
+
* Call after each `change` — the ranges are positional, so re-query
|
|
868
|
+
* rather than caching across edits.
|
|
869
|
+
*/
|
|
870
|
+
getRevisions(): RevisionSpan[];
|
|
871
|
+
/**
|
|
872
|
+
* Walk one paragraph and append its revision spans to `out`. Used by
|
|
873
|
+
* `getRevisions` for both top-level paragraphs (where `ref` is the
|
|
874
|
+
* paragraph's own BlockRef) and for paragraphs inside table cells
|
|
875
|
+
* (where `ref` is the *containing table's* BlockRef as a sentinel —
|
|
876
|
+
* cell paragraphs don't have their own registry entry yet).
|
|
877
|
+
*
|
|
878
|
+
* Emits the same three-level span shape as the inline walker:
|
|
879
|
+
* paragraph-mark first, then coalesced inline ins/del spans, then
|
|
880
|
+
* coalesced format-change spans.
|
|
881
|
+
*/
|
|
882
|
+
private collectParagraphRevisions;
|
|
883
|
+
/**
|
|
884
|
+
* Accept every tracked change in the document. With `opts.author`,
|
|
885
|
+
* accept only that author's changes. One commit for the whole sweep.
|
|
886
|
+
*/
|
|
887
|
+
acceptAllRevisions(opts?: {
|
|
888
|
+
author?: string;
|
|
889
|
+
}): EditResult<void>;
|
|
890
|
+
/** Reject every tracked change (optionally filtered by author). */
|
|
891
|
+
rejectAllRevisions(opts?: {
|
|
892
|
+
author?: string;
|
|
893
|
+
}): EditResult<void>;
|
|
894
|
+
private applyAllRevisions;
|
|
895
|
+
/**
|
|
896
|
+
* Walk a table's cell paragraphs and apply the accept/reject
|
|
897
|
+
* decision to inline + format + paragraph-mark revisions inside.
|
|
898
|
+
* Returns `{ next, changed }` so the caller knows whether to bump
|
|
899
|
+
* the table block. Paragraph-mark del within a cell falls back to
|
|
900
|
+
* strip-the-marker rather than attempting a cross-cell-paragraph
|
|
901
|
+
* merge — that structural edit is out of v1 scope for tables.
|
|
902
|
+
*/
|
|
903
|
+
private sweepTableCellRevisions;
|
|
904
|
+
/** Mark comment `id` resolved (`Comment.done = true`). */
|
|
905
|
+
resolveComment(id: number): EditResult<void>;
|
|
906
|
+
/** Re-open a resolved comment `id` (`Comment.done = false`). */
|
|
907
|
+
reopenComment(id: number): EditResult<void>;
|
|
908
|
+
private setCommentDone;
|
|
909
|
+
setBlockPropertiesAtSelection(patch: ParagraphPropertiesPatch): EditResult<void>;
|
|
910
|
+
setRunPropertiesAtSelection(patch: RunPropertiesPatch): EditResult<void>;
|
|
911
|
+
wrapSelection(tag: WrapTag): EditResult<void>;
|
|
912
|
+
insertImageAtSelection(bytes: Uint8Array, opts: {
|
|
913
|
+
mime: string;
|
|
914
|
+
widthPx?: number;
|
|
915
|
+
heightPx?: number;
|
|
916
|
+
altText?: string;
|
|
917
|
+
}): EditResult<BlockRef>;
|
|
918
|
+
/**
|
|
919
|
+
* Unwrap span ancestors intersecting the selection, up to the block.
|
|
920
|
+
* Best-effort DOM-level cleanup — preserves the current in-place UX
|
|
921
|
+
* without re-rendering.
|
|
922
|
+
*/
|
|
923
|
+
clearInlineFormattingAtSelection(): void;
|
|
924
|
+
on<E extends EditorEvent>(event: E, cb: (p: EditorEventPayload[E]) => void): Unsubscribe;
|
|
925
|
+
destroy(): void;
|
|
926
|
+
/** @internal */
|
|
927
|
+
_hosts(): HTMLElement[];
|
|
928
|
+
/** @internal */
|
|
929
|
+
_registry(): BlockRegistry;
|
|
930
|
+
/** @internal */
|
|
931
|
+
_blockElementAt(index: number): HTMLElement | null;
|
|
932
|
+
/**
|
|
933
|
+
* Parallel array of live block ids (same length as `doc.body`), used
|
|
934
|
+
* by the renderer to stamp `data-block-id` onto every block element.
|
|
935
|
+
* Lets external tools (block tools, embedders) locate a block's DOM
|
|
936
|
+
* element after the body is re-rendered from scratch.
|
|
937
|
+
*/
|
|
938
|
+
private blockIdsArray;
|
|
939
|
+
private checkRefs;
|
|
940
|
+
private checkRange;
|
|
941
|
+
/**
|
|
942
|
+
* Apply a runs transform to the runs covered by `range`. Returns
|
|
943
|
+
* `EditResult<void>`. Handles both single- and multi-block ranges.
|
|
944
|
+
* Assumes locks have already been checked.
|
|
945
|
+
*/
|
|
946
|
+
private mutateRunsInRange;
|
|
947
|
+
/**
|
|
948
|
+
* Apply a mutation to `this.doc`, update the registry, re-render, fire
|
|
949
|
+
* change. Returns the affected refs (post-bump).
|
|
950
|
+
*/
|
|
951
|
+
private commit;
|
|
952
|
+
/**
|
|
953
|
+
* Ensure `this.doc` reflects the latest edits. If the DOM has been
|
|
954
|
+
* dirtied by user typing / paste / drop, pull the latest content out
|
|
955
|
+
* of it and bump affected block versions. If the last mutation came
|
|
956
|
+
* from the API, the AST is already current — skip the (lossy)
|
|
957
|
+
* DOM-to-AST round-trip.
|
|
958
|
+
*/
|
|
959
|
+
private ensureCurrent;
|
|
960
|
+
private syncFromDom;
|
|
961
|
+
/**
|
|
962
|
+
* Schedule a DOM-driven change emit. Called from the `input` listener
|
|
963
|
+
* when the user types — the DOM is the source of truth and we sync the
|
|
964
|
+
* AST from it before notifying listeners.
|
|
965
|
+
*/
|
|
966
|
+
private scheduleChange;
|
|
967
|
+
/**
|
|
968
|
+
* Emit a `change` event using the current in-memory AST verbatim. Do
|
|
969
|
+
* NOT sync from DOM — callers that need a DOM sync should call it
|
|
970
|
+
* explicitly (user-typing path does). API mutations have already
|
|
971
|
+
* rendered their AST into the DOM and must not let the lossy DOM-read
|
|
972
|
+
* overwrite properties the renderer doesn't surface
|
|
973
|
+
* (column widths, verticalAlign, table properties, …).
|
|
974
|
+
*/
|
|
975
|
+
private emitChangeNow;
|
|
976
|
+
/**
|
|
977
|
+
* Snapshot of the live block ids in body order — used both as the
|
|
978
|
+
* input to `applyDocumentToYDoc` (so each Y.Map carries its stable
|
|
979
|
+
* id) and as the `blockIdsArray()` the renderer uses to set the
|
|
980
|
+
* `data-block-id` attribute.
|
|
981
|
+
*/
|
|
982
|
+
private allBlockIds;
|
|
983
|
+
/**
|
|
984
|
+
* Mirror the current `this.doc` into the Y.Doc as a single
|
|
985
|
+
* transaction. The diff is performed by `applyDocumentToYDoc`,
|
|
986
|
+
* which matches blocks by id so concurrent edits to *different*
|
|
987
|
+
* blocks merge cleanly via the Y.Array CRDT.
|
|
988
|
+
*
|
|
989
|
+
* Origin is `"local"` so a future Y observer can distinguish locally-
|
|
990
|
+
* driven mutations (already rendered) from remote ones (need re-render).
|
|
991
|
+
*/
|
|
992
|
+
private mirrorToYDoc;
|
|
993
|
+
/**
|
|
994
|
+
* Returns the set of part paths that mirror should NOT write
|
|
995
|
+
* inline — they're (or will soon be) tracked via the partRefs
|
|
996
|
+
* Y.Map instead. Returns `undefined` when there's nothing to skip
|
|
997
|
+
* (the common no-BlobStore case) so the mirror takes its
|
|
998
|
+
* fastest path.
|
|
999
|
+
*/
|
|
1000
|
+
private computePartPathSkipSet;
|
|
1001
|
+
/**
|
|
1002
|
+
* Background migrate inline part bytes into the BlobStore. Called
|
|
1003
|
+
* by mutators (`insertImage`, `embedFont`) when a `BlobStore` is
|
|
1004
|
+
* configured. The local `doc.rawParts` keeps its inline copy so
|
|
1005
|
+
* the renderer stays synchronous; the Y.Doc gets a `partRefs`
|
|
1006
|
+
* entry referencing the BlobStore content hash, and any stale
|
|
1007
|
+
* `parts` entry is deleted.
|
|
1008
|
+
*
|
|
1009
|
+
* Robust against errors: an upload failure logs and leaves the
|
|
1010
|
+
* path in the pending set so a future call can retry. The local
|
|
1011
|
+
* renderer is unaffected (bytes are still in `doc.rawParts`).
|
|
1012
|
+
*/
|
|
1013
|
+
private migratePartToBlobStore;
|
|
1014
|
+
/**
|
|
1015
|
+
* Compose the current selection into a {@link SelectionPayload} and
|
|
1016
|
+
* dispatch to subscribers. Called from the document-level
|
|
1017
|
+
* `selectionchange` listener attached in the constructor; safe to fire
|
|
1018
|
+
* even when no subscribers exist (the early-return keeps it cheap).
|
|
1019
|
+
*/
|
|
1020
|
+
private fireSelection;
|
|
1021
|
+
/**
|
|
1022
|
+
* Normalise a DOM `KeyboardEvent` into a {@link KeyDownPayload} and
|
|
1023
|
+
* dispatch to subscribers in registration order. Subscribers can
|
|
1024
|
+
* `preventDefault()` (browser default) and / or `stopPropagation()`
|
|
1025
|
+
* (further subscribers). The editor itself binds no shortcuts —
|
|
1026
|
+
* everything goes through plugins.
|
|
1027
|
+
*/
|
|
1028
|
+
private fireKeyDown;
|
|
1029
|
+
private summariseBlock;
|
|
1030
|
+
private onPaste;
|
|
1031
|
+
/**
|
|
1032
|
+
* Insert `text` at the current selection in track-changes mode, with
|
|
1033
|
+
* each `\n` becoming a `splitBlock`. Used by `onPaste` for plain-text
|
|
1034
|
+
* paste; could be reused for tracked drop (a follow-up). Splits the
|
|
1035
|
+
* line list once up-front and walks it so each insertRun lands at the
|
|
1036
|
+
* caret of the *current* paragraph (which may be a fresh one from a
|
|
1037
|
+
* preceding splitBlock).
|
|
1038
|
+
*/
|
|
1039
|
+
private pasteTrackedText;
|
|
1040
|
+
private onDragOver;
|
|
1041
|
+
private onDrop;
|
|
1042
|
+
private insertImageFromFile;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Read and write the caret / selection in model terms. Lives on
|
|
1046
|
+
* `editor.selection`. Wraps `window.getSelection()` so callers never
|
|
1047
|
+
* touch the DOM directly.
|
|
1048
|
+
*/
|
|
1049
|
+
export declare class EditorSelection {
|
|
1050
|
+
private readonly editor;
|
|
1051
|
+
constructor(editor: Editor);
|
|
1052
|
+
/** Current selection as a model `Selection`. Returns `null` when focus is outside. */
|
|
1053
|
+
get(): Selection;
|
|
1054
|
+
/** Apply a model selection to the DOM. */
|
|
1055
|
+
set(sel: Selection): boolean;
|
|
1056
|
+
/** Shortcut: current selection as a `Range`, or `null` when collapsed/absent. */
|
|
1057
|
+
currentRange(): ApiRange | null;
|
|
1058
|
+
/** Shortcut: the caret position (collapses a range to its `from`). */
|
|
1059
|
+
currentCaret(): InlinePosition | null;
|
|
1060
|
+
/** Shortcut: ref of the block containing the caret. */
|
|
1061
|
+
currentBlock(): BlockRef | null;
|
|
1062
|
+
/** Legacy: current block index (for code still using indices). */
|
|
1063
|
+
currentBlockIndex(): number | null;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Default {@link CommandBus} implementation. Plain in-memory map; no
|
|
1067
|
+
* editor coupling beyond the closure plugins use when registering.
|
|
1068
|
+
* Replacing it would mean swapping a field on Editor — the rest of
|
|
1069
|
+
* the surface stays the same.
|
|
1070
|
+
*/
|
|
1071
|
+
export declare class EditorCommands implements CommandBus {
|
|
1072
|
+
private readonly commands;
|
|
1073
|
+
register<Args = void>(def: CommandDefinition<Args>): () => void;
|
|
1074
|
+
execute<Args = void>(name: string, args?: Args): void;
|
|
1075
|
+
list(): CommandSnapshot[];
|
|
1076
|
+
has(name: string): boolean;
|
|
1077
|
+
}
|
|
1078
|
+
export { countBlocks };
|