@sofereditor/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.
@@ -0,0 +1,601 @@
1
+ import * as Y from 'yjs';
2
+ export { Y };
3
+
4
+ /**
5
+ * Document-level page configuration: physical size and margins.
6
+ *
7
+ * Everything is stored in **CSS pixels @ 96dpi** for consistency with the
8
+ * pagination engine (`packages/react/src/usePagination.ts` reads these
9
+ * directly). Conversion helpers (`mmToPx`, `pxToMm`) round-trip with millimeter
10
+ * inputs from the UI and the OOXML twip values used by docx import/export.
11
+ */
12
+ type PagePreset = "a4" | "letter" | "oficio" | "custom";
13
+ interface PageSettings {
14
+ /** Page width in CSS px. */
15
+ width: number;
16
+ /** Page height in CSS px. */
17
+ height: number;
18
+ marginTop: number;
19
+ marginBottom: number;
20
+ marginLeft: number;
21
+ marginRight: number;
22
+ /**
23
+ * The preset whose size the geometry matches, with the ±0.5mm tolerance used
24
+ * by `detectPreset`. "custom" when the dimensions don't match any preset.
25
+ * This is purely a UI hint — it's recomputed from width/height when needed.
26
+ */
27
+ preset?: PagePreset;
28
+ }
29
+ /** Page sizes in CSS px @ 96dpi. */
30
+ declare const PAGE_PRESETS: {
31
+ readonly a4: {
32
+ readonly width: 794;
33
+ readonly height: 1123;
34
+ };
35
+ readonly letter: {
36
+ readonly width: 816;
37
+ readonly height: 1056;
38
+ };
39
+ readonly oficio: {
40
+ readonly width: 816;
41
+ readonly height: 1248;
42
+ };
43
+ };
44
+ /** Margin presets in CSS px. Match common Word defaults. */
45
+ declare const MARGIN_PRESETS: {
46
+ readonly normal: 96;
47
+ readonly narrow: 48;
48
+ readonly wide: 192;
49
+ };
50
+ declare const DEFAULT_PAGE_SETTINGS: PageSettings;
51
+ declare function mmToPx(mm: number): number;
52
+ declare function pxToMm(px: number): number;
53
+ /**
54
+ * Detect which preset a (width, height) pair matches. Tolerance is ±0.5mm to
55
+ * absorb rounding from the twips↔px conversion used by docx round-trip.
56
+ */
57
+ declare function detectPreset(width: number, height: number): PagePreset;
58
+ /** Fill missing fields with the default. Useful when reading from a Y.Map that may be partially populated. */
59
+ declare function withDefaults(partial: Partial<PageSettings> | undefined): PageSettings;
60
+
61
+ /**
62
+ * Public type definitions for the document model.
63
+ * The runtime representation lives in Y.js (see document.ts);
64
+ * these types describe its shape for consumers.
65
+ */
66
+ type BlockType = "paragraph" | "heading" | "blockquote" | "codeBlock" | "listItem" | "table";
67
+ type AlignValue = "left" | "center" | "right" | "justify";
68
+ type DirValue = "ltr" | "rtl";
69
+ type ListKind = "bullet" | "ordered";
70
+ /** Maximum supported list nesting (0 = top level). */
71
+ declare const MAX_LIST_LEVEL = 5;
72
+ interface BlockAttrs {
73
+ align?: AlignValue;
74
+ dir?: DirValue;
75
+ /** Only meaningful when `type === "heading"`. 1..6. */
76
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
77
+ /** Only meaningful when `type === "codeBlock"`. Free-form (CSS-style language hint). */
78
+ language?: string;
79
+ /** Only meaningful when `type === "listItem"`. */
80
+ listKind?: ListKind;
81
+ /** Only meaningful when `type === "listItem"`. 0..MAX_LIST_LEVEL. */
82
+ listLevel?: number;
83
+ /** Only meaningful when `type === "table"`. */
84
+ rows?: number;
85
+ /** Only meaningful when `type === "table"`. */
86
+ cols?: number;
87
+ /**
88
+ * Only meaningful when `type === "table"`. Per-column widths in CSS px.
89
+ * Length should equal `cols` when set; missing or short arrays fall back to
90
+ * an auto distribution at render time.
91
+ */
92
+ colWidths?: number[];
93
+ }
94
+ interface CellAttrs {
95
+ /**
96
+ * Number of rows this cell spans, starting at its row. Default 1.
97
+ * Only meaningful on REAL cells (not covered).
98
+ */
99
+ rowspan?: number;
100
+ /** Default 1. Only meaningful on REAL cells. */
101
+ colspan?: number;
102
+ /**
103
+ * `true` iff this cell is occluded by an owning cell with a `rowspan`/`colspan`
104
+ * spanning into this position. Covered cells do not render their own `<td>`,
105
+ * never accept input, and are skipped by Tab navigation.
106
+ *
107
+ * The cells array keeps `rows * cols` entries to preserve the row-major index
108
+ * scheme — covered cells are placeholders, not gaps.
109
+ */
110
+ covered?: true;
111
+ }
112
+ interface Position {
113
+ blockIndex: number;
114
+ /**
115
+ * When the caret is inside a table cell, identifies which cell within the
116
+ * table at `blockIndex`. Flat row-major index: `row * cols + col`.
117
+ * `undefined` means the position addresses the block's own text (or the block
118
+ * itself, for atomic blocks). Tables have no own text — every caret position
119
+ * inside a table block carries a defined `cellIndex`.
120
+ */
121
+ cellIndex?: number;
122
+ offset: number;
123
+ }
124
+ interface Selection {
125
+ anchor: Position;
126
+ focus: Position;
127
+ }
128
+ type MarkName = "bold" | "italic" | "underline" | "strike" | "color" | "fontFamily" | "fontSize" | "link" | "comment";
129
+ interface LinkAttr {
130
+ href: string;
131
+ title?: string;
132
+ }
133
+ interface CommentAttr {
134
+ /** Identificador estável compartilhado com o backend (ProvaDocumentoComentario.markId). */
135
+ markId: string;
136
+ }
137
+ interface MarkAttrs {
138
+ bold?: true;
139
+ italic?: true;
140
+ underline?: true;
141
+ strike?: true;
142
+ color?: string;
143
+ fontFamily?: string;
144
+ fontSize?: string;
145
+ link?: LinkAttr;
146
+ comment?: CommentAttr;
147
+ }
148
+ type MarkValue<K extends MarkName = MarkName> = NonNullable<MarkAttrs[K]>;
149
+ /**
150
+ * Visual layout mode for an image embed.
151
+ *
152
+ * - `inline` (default): part of the text run, flows like a character.
153
+ * - `wrap-left` / `wrap-right`: image floats to the named side of the anchor
154
+ * paragraph (Word-style "wrap square"). Text in the paragraph wraps around it.
155
+ * - `behind`: image is absolutely positioned beneath the text (z-index < text).
156
+ * - `front`: image is absolutely positioned above the text (z-index > text).
157
+ *
158
+ * In every non-inline mode the embed remains in the Y.Text run at its offset —
159
+ * the offset is its **anchor** (it follows the paragraph during pagination
160
+ * and re-flows). Visual position is offset from the anchor block's content box.
161
+ */
162
+ type ImageLayout = "inline" | "wrap-left" | "wrap-right" | "behind" | "front";
163
+ interface ImageEmbed {
164
+ type: "image";
165
+ /** Data URL (base64) — self-contained so the embed travels with the Y.Doc. */
166
+ src: string;
167
+ /** Rendered CSS px (display size — may differ from intrinsic dimensions). */
168
+ width: number;
169
+ height: number;
170
+ /** When set (inline mode only), the image renders `display: block` with the matching margin auto. */
171
+ align?: "left" | "center" | "right";
172
+ /** Visual layout. Defaults to "inline" when omitted. */
173
+ layout?: ImageLayout;
174
+ /**
175
+ * Horizontal offset in CSS px from the anchor block's content box.
176
+ * - wrap-left: extra left margin from the paragraph's start edge.
177
+ * - wrap-right: extra right margin from the paragraph's end edge.
178
+ * - behind / front: `left` relative to the anchor block's padding-box.
179
+ * Ignored for `inline`.
180
+ */
181
+ offsetX?: number;
182
+ /**
183
+ * Vertical offset in CSS px from the anchor block's content box.
184
+ * - wrap-left / wrap-right: extra top margin pushing the float down inside
185
+ * the paragraph.
186
+ * - behind / front: `top` relative to the anchor block's padding-box.
187
+ * Ignored for `inline`.
188
+ */
189
+ offsetY?: number;
190
+ /**
191
+ * Optional plain-text caption. Renders inside a `<figcaption>` beneath the
192
+ * image in any layout — the figure wraps the image so the caption travels
193
+ * with floats/anchors. Plain text only; rich marks are out of scope (we
194
+ * never want a sub-editor inside an inline embed).
195
+ */
196
+ caption?: string;
197
+ /**
198
+ * Text alignment for the caption. Defaults to "center" (Word/Google Docs
199
+ * default for figure captions). Only meaningful when `caption` is set.
200
+ * Note: DOCX export emits captions inside the host paragraph, so this
201
+ * setting only affects the editor and HTML/PDF exports — Word displays the
202
+ * caption using the paragraph's own alignment.
203
+ */
204
+ captionAlign?: "left" | "center" | "right";
205
+ }
206
+ type InsertContent = string | ImageEmbed;
207
+ declare function isImageEmbed(v: unknown): v is ImageEmbed;
208
+ interface DeltaOp {
209
+ insert: InsertContent;
210
+ attributes?: MarkAttrs;
211
+ }
212
+ interface SerializedCell {
213
+ /** Plain concatenated text. */
214
+ text: string;
215
+ /** Delta with marks. */
216
+ delta: DeltaOp[];
217
+ attrs: CellAttrs;
218
+ }
219
+ interface SerializedBlock {
220
+ type: BlockType;
221
+ /** Plain concatenated text (no marks). Kept for ergonomic equality checks. */
222
+ text: string;
223
+ /** Yjs delta — used by the renderer to draw mark-aware spans. */
224
+ delta: DeltaOp[];
225
+ /** Block-level attributes (align, dir, heading level, code language…). */
226
+ attrs: BlockAttrs;
227
+ /** Only present when `type === "table"`. Flat row-major list, length = rows * cols. */
228
+ cells?: SerializedCell[];
229
+ }
230
+
231
+ /**
232
+ * Document JSON shape used by `EditorDocument.toJSON` / `fromJSON`, the
233
+ * exporters and the importer. Until Fase 9.5 this was a bare `SerializedBlock[]`;
234
+ * Fase 9.6 promoted it to an object so per-document settings (page size,
235
+ * margins) can travel with the content.
236
+ *
237
+ * `fromJSON` still accepts a legacy array for backwards compatibility — see
238
+ * `isLegacySerializedDocument`.
239
+ */
240
+ interface SerializedDocument {
241
+ blocks: SerializedBlock[];
242
+ /** Page settings. Optional; omitted means default A4 + 1" margins. */
243
+ pageSettings?: PageSettings;
244
+ }
245
+ /** A `SerializedDocument` produced before Fase 9.6 — just an array of blocks. */
246
+ type LegacySerializedDocument = SerializedBlock[];
247
+ declare function isLegacySerializedDocument(v: SerializedDocument | LegacySerializedDocument): v is LegacySerializedDocument;
248
+
249
+ interface BlockSpec {
250
+ type: BlockType;
251
+ defaultAttrs: BlockAttrs;
252
+ }
253
+ declare const BLOCK_SPECS: Record<BlockType, BlockSpec>;
254
+ declare function isKnownBlockType(t: string): t is BlockType;
255
+ declare function defaultAttrsFor(type: BlockType): BlockAttrs;
256
+
257
+ /**
258
+ * Y.js-backed document.
259
+ *
260
+ * Storage layout:
261
+ * ydoc.getArray("blocks") : Y.Array<Y.Map>
262
+ * each Y.Map ("block"):
263
+ * - type: string ("paragraph" | "heading" | "blockquote" | "codeBlock" | "listItem" | "table")
264
+ * - text: Y.Text (NOT present on tables)
265
+ * - attrs: Y.Map<unknown> (align, dir, level, language… plus rows/cols for tables)
266
+ * - cells: Y.Array<Y.Map> (only on tables, length = rows * cols, row-major)
267
+ * each cell map: { text: Y.Text, attrs: Y.Map<unknown> }
268
+ *
269
+ * Block-level attrs live inside a nested Y.Map so concurrent edits (e.g. two users
270
+ * changing alignment at once) reconcile through Yjs' last-write-wins per key.
271
+ */
272
+ declare class EditorDocument {
273
+ readonly ydoc: Y.Doc;
274
+ readonly blocks: Y.Array<Y.Map<unknown>>;
275
+ /** Document-level settings (page size, margins). See `getPageSettings`. */
276
+ readonly docSettings: Y.Map<unknown>;
277
+ constructor(ydoc?: Y.Doc);
278
+ /**
279
+ * Returns the current page settings. Missing fields fall back to
280
+ * `DEFAULT_PAGE_SETTINGS`. The returned object is a fresh snapshot — mutating
281
+ * it does not write back to the Y.Doc. Use `setPageSettings` for that.
282
+ */
283
+ getPageSettings(): PageSettings;
284
+ /**
285
+ * Merge `partial` into the docSettings Y.Map. Runs in a `ydoc.transact` with
286
+ * origin `"pageSettings"` (NOT tracked by the UndoManager, mirroring the
287
+ * `"import"` convention from Fase 9.5).
288
+ */
289
+ setPageSettings(partial: Partial<PageSettings>): void;
290
+ /** Listen for page-settings changes. Returns an unsubscribe function. */
291
+ observePageSettings(cb: () => void): () => void;
292
+ getBlock(index: number): Y.Map<unknown> | undefined;
293
+ getBlockText(index: number): Y.Text | undefined;
294
+ getBlockType(index: number): BlockType | undefined;
295
+ getBlockAttrsMap(index: number): Y.Map<unknown> | undefined;
296
+ getBlockAttrs(index: number): BlockAttrs;
297
+ blockCount(): number;
298
+ isTable(index: number): boolean;
299
+ /** Returns rows/cols read from the block's attrs map. `{0,0}` for non-table blocks. */
300
+ getTableSize(index: number): {
301
+ rows: number;
302
+ cols: number;
303
+ };
304
+ getCells(index: number): Y.Array<Y.Map<unknown>> | undefined;
305
+ getCell(blockIndex: number, cellIndex: number): Y.Map<unknown> | undefined;
306
+ getCellText(blockIndex: number, cellIndex: number): Y.Text | undefined;
307
+ getCellAttrsMap(blockIndex: number, cellIndex: number): Y.Map<unknown> | undefined;
308
+ getCellAttrs(blockIndex: number, cellIndex: number): CellAttrs;
309
+ /**
310
+ * Returns the editable Y.Text targeted by a position:
311
+ * - When `cellIndex` is set: the cell's text (or the owner's text if the
312
+ * target cell is covered by a span — we treat the covered position as
313
+ * addressing the owner).
314
+ * - Otherwise: the block's own text.
315
+ */
316
+ textAt(blockIndex: number, cellIndex: number | undefined): Y.Text | undefined;
317
+ /**
318
+ * Returns the "real" (non-covered) cell index for a target cell. When the
319
+ * target is already a real cell, returns it unchanged. When covered, walks
320
+ * up-and-left to find the owner.
321
+ *
322
+ * The walk is robust to oversized or non-rectangular spans by skipping
323
+ * candidates whose declared span doesn't actually cover the target.
324
+ */
325
+ realCellIndex(blockIndex: number, cellIndex: number): number | undefined;
326
+ /**
327
+ * Replace the document contents with a `SerializedDocument`. Runs in a single
328
+ * `ydoc.transact` with origin `"import"` so the UndoManager can group or skip
329
+ * the operation as one step.
330
+ *
331
+ * Reuses `createBlock` / `createTableBlock` so every block ends up with the
332
+ * exact same Y.js shape produced by the rest of the editor.
333
+ */
334
+ loadFromJSON(serialized: SerializedDocument | LegacySerializedDocument): void;
335
+ /** Build a fresh `EditorDocument` from a `SerializedDocument`. */
336
+ static fromJSON(serialized: SerializedDocument | LegacySerializedDocument): EditorDocument;
337
+ toJSON(): SerializedDocument;
338
+ }
339
+ declare function createBlock(type: BlockType, initialText?: string, attrs?: BlockAttrs): Y.Map<unknown>;
340
+ /**
341
+ * Build a table block with `rows × cols` empty cells.
342
+ * Cells are stored in row-major order: `cell[row * cols + col]`.
343
+ */
344
+ declare function createTableBlock(rows: number, cols: number): Y.Map<unknown>;
345
+ declare function createCell(attrs?: CellAttrs): Y.Map<unknown>;
346
+ /** Coerce a `rowspan`/`colspan` attribute value into a positive integer. */
347
+ declare function spanOf(value: unknown): number;
348
+
349
+ declare function isCollapsed(sel: Selection): boolean;
350
+ declare function positionsEqual(a: Position, b: Position): boolean;
351
+ declare function comparePositions(a: Position, b: Position): number;
352
+ declare function orderedRange(sel: Selection): {
353
+ start: Position;
354
+ end: Position;
355
+ };
356
+ declare function collapsedSelection(pos: Position): Selection;
357
+ /**
358
+ * Two positions are in the same "editable run" (same Y.Text) when they share
359
+ * the block index AND the cellIndex (treating absent and present-with-undefined
360
+ * the same way: a non-table position has cellIndex === undefined for both).
361
+ */
362
+ declare function sameTextRun(a: Position, b: Position): boolean;
363
+
364
+ /**
365
+ * Returns the marks observed across `[start, end)`. For each mark name encountered:
366
+ * - if every covered character has the same value, returns that value
367
+ * - if values differ OR some characters lack the mark, returns "mixed"
368
+ */
369
+ declare function getMarksInRange(yText: Y.Text, start: number, end: number): Partial<Record<MarkName, MarkAttrs[MarkName] | "mixed">>;
370
+ declare function isMarkUniformInRange(yText: Y.Text, start: number, end: number, name: MarkName, value?: MarkAttrs[MarkName]): boolean;
371
+ /** Total number of inserted characters. Embeds (non-string inserts) count as 1. */
372
+ declare function deltaLength(delta: DeltaOp[]): number;
373
+ /**
374
+ * Return the subset of `delta` covering `[start, end)` characters.
375
+ * Embeds are atomic length-1 ops: included whole if their slot intersects the range.
376
+ */
377
+ declare function sliceDelta(delta: DeltaOp[], start: number, end: number): DeltaOp[];
378
+
379
+ declare const COMMAND_ORIGIN: unique symbol;
380
+ interface CommandContext {
381
+ doc: EditorDocument;
382
+ getSelection: () => Selection;
383
+ setSelection: (sel: Selection) => void;
384
+ }
385
+ /**
386
+ * Insert text at the current selection.
387
+ *
388
+ * `marks` is an explicit override map applied to the freshly inserted range.
389
+ * - Entry value: apply that mark value.
390
+ * - Entry value `null`: clear that mark (used when the UI just toggled it off
391
+ * with a collapsed caret and the next typed char must NOT inherit the
392
+ * surrounding run's attribute).
393
+ * - When `marks` is undefined, Y.Text's default inheritance applies (good for
394
+ * normal typing inside a marked run).
395
+ */
396
+ declare function insertText(ctx: CommandContext, text: string, marks?: Partial<Record<MarkName, MarkAttrs[MarkName] | null>>): void;
397
+ declare function insertParagraph(ctx: CommandContext): void;
398
+ declare function deleteBackward(ctx: CommandContext): void;
399
+ declare function deleteForward(ctx: CommandContext): void;
400
+ /**
401
+ * Toggle a mark across the current selection.
402
+ * - If the selection is collapsed: no-op (the UI layer handles pendingMarks).
403
+ * - Boolean marks (bold/italic/underline/strike): if every character already has
404
+ * the mark, remove it; otherwise apply.
405
+ * - Parametric marks (color, fontFamily, fontSize, link): require `value` to apply;
406
+ * when uniform with that value across the range, remove instead (acts like toggle).
407
+ */
408
+ declare function toggleMark(ctx: CommandContext, name: MarkName, value?: MarkAttrs[MarkName]): void;
409
+ declare function setMark(ctx: CommandContext, name: MarkName, value: MarkAttrs[MarkName]): void;
410
+ declare function removeMark(ctx: CommandContext, name: MarkName): void;
411
+ /**
412
+ * Change the type of every block covered by the current selection.
413
+ * When `attrs` is provided, merges those on top of the new type's defaults.
414
+ * Preserves `Y.Text` content (and therefore all inline marks) and the selection.
415
+ */
416
+ declare function setBlockType(ctx: CommandContext, type: BlockType, attrs?: BlockAttrs): void;
417
+ /**
418
+ * Set a single attribute on every block covered by the selection.
419
+ * Passing `value === null` deletes the key.
420
+ */
421
+ declare function setBlockAttr<K extends keyof BlockAttrs>(ctx: CommandContext, key: K, value: BlockAttrs[K] | null): void;
422
+ /**
423
+ * Convert every block in the selection to/from a `listItem` of `kind`.
424
+ * If every covered block is already a `listItem` of `kind` → convert them all to paragraphs.
425
+ * Otherwise → convert them all to `listItem` (keeping existing `listLevel` when already
426
+ * a listItem, defaulting to 0 otherwise).
427
+ */
428
+ declare function toggleList(ctx: CommandContext, kind: ListKind): void;
429
+ /** Increase nesting of every listItem in the selection (clamped to MAX_LIST_LEVEL). */
430
+ declare function indentList(ctx: CommandContext): void;
431
+ /**
432
+ * Decrease nesting of every listItem in the selection.
433
+ * When level was 0, demote to paragraph (escape from the list).
434
+ */
435
+ declare function dedentList(ctx: CommandContext): void;
436
+ /**
437
+ * Enter behavior inside a listItem.
438
+ * - Empty listItem at level > 0 → dedent (stays a listItem, one level out).
439
+ * - Empty listItem at level 0 → convert to paragraph (exit the list).
440
+ * - Non-empty → split: tail moves to a new listItem with same kind/level.
441
+ *
442
+ * Returns `true` when it handled the event; `false` to let the default `insertParagraph` run.
443
+ */
444
+ declare function splitListItem(ctx: CommandContext): boolean;
445
+ /**
446
+ * Insert a fresh `rows × cols` table after the block currently holding the
447
+ * caret (or at the end of the document if the selection is somehow invalid),
448
+ * and place the caret in the table's first cell.
449
+ *
450
+ * Sub-phase 4.1 doesn't try to be clever: it does NOT replace an empty
451
+ * preceding paragraph. Callers can clean that up themselves; an extra empty
452
+ * paragraph above a table is a small price for predictable behavior.
453
+ */
454
+ declare function insertTable(ctx: CommandContext, rows: number, cols: number): void;
455
+ /** Remove the entire table at `blockIndex` and place the caret at the start of the next block (or the previous one if removal would leave the doc empty). */
456
+ declare function deleteTable(ctx: CommandContext, blockIndex: number): void;
457
+ /**
458
+ * Sub-phase 4.1 has no rowspan/colspan, so row/col operations are pure splice
459
+ * on the flat `cells` array. The flat index of `(r, c)` is `r * cols + c`.
460
+ */
461
+ interface TableLocation {
462
+ blockIndex: number;
463
+ row: number;
464
+ col: number;
465
+ }
466
+ /**
467
+ * Derive `{row, col}` from a position inside a table — always pointing at the
468
+ * owner of the targeted cell (covered cells redirect to their owner). Returns
469
+ * null when the position isn't inside a table cell.
470
+ */
471
+ declare function tableLocationOf(doc: EditorDocument, pos: Position): TableLocation | null;
472
+ /**
473
+ * Insert a fresh row above (`where === "before"`) or below (`"after"`) the
474
+ * given row. Spans that previously crossed the insertion boundary are extended
475
+ * by 1; the corresponding columns of the new row become `covered: true`.
476
+ */
477
+ declare function insertTableRow(ctx: CommandContext, blockIndex: number, row: number, where: "before" | "after"): void;
478
+ /**
479
+ * Remove `row` from the table. Spans that pass through the row are shortened
480
+ * by one; spans that START at the row are removed AND the cell at row R+1 (in
481
+ * each affected column) is promoted to be the new owner with `rowspan - 1`.
482
+ * When it was the last row, deletes the whole table.
483
+ */
484
+ declare function deleteTableRow(ctx: CommandContext, blockIndex: number, row: number): void;
485
+ /**
486
+ * Insert a fresh column to the left (`"before"`) or right (`"after"`) of `col`.
487
+ * Spans crossing the insertion column have `colspan += 1`; the new column's
488
+ * cells in covered rows are themselves `covered: true`.
489
+ *
490
+ * Also resizes `colWidths` (if defined) by inserting a default width.
491
+ */
492
+ declare function insertTableColumn(ctx: CommandContext, blockIndex: number, col: number, where: "before" | "after"): void;
493
+ /**
494
+ * Remove `col` from the table. Spans that pass through the column are
495
+ * shortened by one; spans that START at the column with `colspan > 1` get
496
+ * their owner promoted from (r, C+1) to take over with `colspan - 1`.
497
+ *
498
+ * Also shrinks `colWidths` (if defined). When it was the last column, deletes
499
+ * the whole table.
500
+ */
501
+ declare function deleteTableColumn(ctx: CommandContext, blockIndex: number, col: number): void;
502
+ interface TableRect {
503
+ blockIndex: number;
504
+ /** Inclusive bounds in OWNER coordinates. */
505
+ top: number;
506
+ left: number;
507
+ bottom: number;
508
+ right: number;
509
+ }
510
+ /**
511
+ * Interpret a `Selection` as a rectangular cell range when:
512
+ * - Both endpoints carry `cellIndex`.
513
+ * - They live in the same table block.
514
+ * - They point to DIFFERENT owner cells.
515
+ *
516
+ * The naive bounds are then EXPANDED until every span fully fits inside —
517
+ * essential so that operations like `mergeSelection` can never produce a
518
+ * non-rectangular shape, and so visual highlights always cover whole spans.
519
+ */
520
+ declare function tableRectSelection(doc: EditorDocument, sel: Selection): TableRect | null;
521
+ /**
522
+ * Grow a candidate rect until every owner whose span starts within the rect
523
+ * fits entirely, and every covered cell inside it has its owner inside too.
524
+ * Converges in O(rows × cols) iterations in the worst case (each pass either
525
+ * grows a bound or terminates).
526
+ */
527
+ declare function expandTableRect(doc: EditorDocument, blockIndex: number, initTop: number, initLeft: number, initBottom: number, initRight: number): TableRect;
528
+ /** Enumerate every cell-index (real or covered) contained in `rect`. */
529
+ declare function cellsInRect(doc: EditorDocument, rect: TableRect): number[];
530
+ /**
531
+ * Merge every cell in the current rectangular selection into one owner at the
532
+ * rect's top-left. The owner's text is the concatenation of all non-empty
533
+ * inner texts (space-separated, mark-preserving). Returns true on success, false
534
+ * when there is no rect selection.
535
+ */
536
+ declare function mergeSelection(ctx: CommandContext): boolean;
537
+ /**
538
+ * Clears the text of every cell in the current rectangular selection (no
539
+ * structural mutation — spans stay intact). Returns the collapsed selection
540
+ * at the rect's top-left when something was cleared, otherwise null.
541
+ */
542
+ declare function clearRectCells(ctx: CommandContext): Selection | null;
543
+ /**
544
+ * Merge the cell at `(row, col)` with its immediate right neighbor. The right
545
+ * neighbor must be a real cell with matching `rowspan` (otherwise the merge
546
+ * would form a non-rectangle). Returns true on success, false on shape
547
+ * mismatch or when there is no neighbor.
548
+ *
549
+ * After: the owner's `colspan` grows by the neighbor's `colspan`; the
550
+ * neighbor and all its formerly-covered cells in the merged rectangle are
551
+ * marked `covered: true`. The neighbor's text is appended to the owner with a
552
+ * single space delimiter (when both sides had content).
553
+ */
554
+ declare function mergeRight(ctx: CommandContext, blockIndex: number, row: number, col: number): boolean;
555
+ /** Merge with the immediate bottom neighbor. Mirror of `mergeRight`. */
556
+ declare function mergeDown(ctx: CommandContext, blockIndex: number, row: number, col: number): boolean;
557
+ /**
558
+ * Split a merged cell back into a `rowspan × colspan` grid of independent real
559
+ * cells. Each formerly-covered cell becomes empty and real. Returns true when
560
+ * something was split. No-op (returns false) on a cell with no spans.
561
+ */
562
+ declare function splitCell(ctx: CommandContext, blockIndex: number, row: number, col: number): boolean;
563
+ /** Set the width (in CSS px) of a specific column. Auto-initializes `colWidths`. */
564
+ declare function setColumnWidth(ctx: CommandContext, blockIndex: number, col: number, widthPx: number): void;
565
+ /**
566
+ * Move the caret to the next cell of the same table (Tab). At the last cell of
567
+ * the last row, creates a new row and lands in its first cell. Returns true
568
+ * when handled. Caret must be inside a table for this to act.
569
+ */
570
+ declare function moveToNextCell(ctx: CommandContext): boolean;
571
+ /** Move the caret to the previous cell (Shift+Tab). At the first cell, no-op (returns true). */
572
+ declare function moveToPrevCell(ctx: CommandContext): boolean;
573
+ /**
574
+ * Insert an inline image embed at the current selection. Embeds occupy 1 char
575
+ * in offset arithmetic (Y.Text treats them as length-1 inserts).
576
+ */
577
+ declare function insertImage(ctx: CommandContext, embed: ImageEmbed): void;
578
+ /**
579
+ * Update attributes of an existing image embed at `(blockIndex, embedOffset)`.
580
+ * Implementation: delete the 1-char slot and re-insert the merged embed in the
581
+ * SAME transaction — Y.UndoManager coalesces delete+insert in one transact, so
582
+ * undo treats it as a single step.
583
+ *
584
+ * If `newOffset` is provided and differs from `embedOffset`, the embed is moved
585
+ * to that offset within the same block/cell. Used by wrap-left/wrap-right
586
+ * layouts which must anchor the image at the block start so floated text wraps
587
+ * around it (CSS float only flows text that comes AFTER the float in DOM order).
588
+ */
589
+ declare function setImageAttrs(ctx: CommandContext, blockIndex: number, embedOffset: number, partial: Partial<ImageEmbed>, cellIndex?: number, newOffset?: number): void;
590
+ declare function isCommandOrigin(origin: unknown): boolean;
591
+ declare const TRACKED_ORIGINS: Set<unknown>;
592
+
593
+ declare class EditorHistory {
594
+ readonly undoManager: Y.UndoManager;
595
+ constructor(doc: EditorDocument);
596
+ undo(): boolean;
597
+ redo(): boolean;
598
+ destroy(): void;
599
+ }
600
+
601
+ export { type AlignValue, BLOCK_SPECS, type BlockAttrs, type BlockType, COMMAND_ORIGIN, type CellAttrs, type CommandContext, type CommentAttr, DEFAULT_PAGE_SETTINGS, type DeltaOp, type DirValue, EditorDocument, EditorHistory, type ImageEmbed, type ImageLayout, type InsertContent, type LegacySerializedDocument, type LinkAttr, type ListKind, MARGIN_PRESETS, MAX_LIST_LEVEL, type MarkAttrs, type MarkName, type MarkValue, PAGE_PRESETS, type PagePreset, type PageSettings, type Position, type Selection, type SerializedBlock, type SerializedCell, type SerializedDocument, TRACKED_ORIGINS, type TableLocation, type TableRect, cellsInRect, clearRectCells, collapsedSelection, comparePositions, createBlock, createCell, createTableBlock, dedentList, defaultAttrsFor, deleteBackward, deleteForward, deleteTable, deleteTableColumn, deleteTableRow, deltaLength, detectPreset, expandTableRect, getMarksInRange, indentList, insertImage, insertParagraph, insertTable, insertTableColumn, insertTableRow, insertText, isCollapsed, isCommandOrigin, isImageEmbed, isKnownBlockType, isLegacySerializedDocument, isMarkUniformInRange, mergeDown, mergeRight, mergeSelection, mmToPx, moveToNextCell, moveToPrevCell, orderedRange, positionsEqual, pxToMm, removeMark, sameTextRun, setBlockAttr, setBlockType, setColumnWidth, setImageAttrs, setMark, sliceDelta, spanOf, splitCell, splitListItem, tableLocationOf, tableRectSelection, toggleList, toggleMark, withDefaults };