@meta-1/editor 1.0.7 → 1.0.9

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.
@@ -1,85 +1,80 @@
1
- import type { Editor } from "@tiptap/react"
2
- import type { Node } from "@tiptap/pm/model"
3
- import type { Command } from "@tiptap/pm/state"
4
- import { Selection, type EditorState, type Transaction } from "@tiptap/pm/state"
5
- import type { FindNodeResult, Rect } from "@tiptap/pm/tables"
1
+ import type { Node } from "@tiptap/pm/model";
2
+ import type { Command } from "@tiptap/pm/state";
3
+ import { type EditorState, Selection, type Transaction } from "@tiptap/pm/state";
4
+ import type { FindNodeResult, Rect } from "@tiptap/pm/tables";
6
5
  import {
7
- TableMap,
8
6
  CellSelection,
7
+ cellAround,
9
8
  findTable,
9
+ isInTable,
10
10
  selectedRect,
11
- cellAround,
12
11
  selectionCell,
13
- isInTable,
14
- } from "@tiptap/pm/tables"
15
- import { Mapping } from "@tiptap/pm/transform"
12
+ TableMap,
13
+ } from "@tiptap/pm/tables";
14
+ import { Mapping } from "@tiptap/pm/transform";
15
+ import type { Editor } from "@tiptap/react";
16
16
 
17
- export const RESIZE_MIN_WIDTH = 35
18
- export const EMPTY_CELL_WIDTH = 120
19
- export const EMPTY_CELL_HEIGHT = 40
17
+ export const RESIZE_MIN_WIDTH = 35;
18
+ export const EMPTY_CELL_WIDTH = 120;
19
+ export const EMPTY_CELL_HEIGHT = 40;
20
20
 
21
- export type Orientation = "row" | "column"
21
+ export type Orientation = "row" | "column";
22
22
  export interface CellInfo extends FindNodeResult {
23
- row: number
24
- column: number
23
+ row: number;
24
+ column: number;
25
25
  }
26
26
 
27
27
  export type CellCoordinates = {
28
- row: number
29
- col: number
30
- }
28
+ row: number;
29
+ col: number;
30
+ };
31
31
 
32
- export type SelectionReturnMode = "state" | "transaction" | "dispatch"
32
+ export type SelectionReturnMode = "state" | "transaction" | "dispatch";
33
33
 
34
- export type BaseSelectionOptions = { mode?: SelectionReturnMode }
34
+ export type BaseSelectionOptions = { mode?: SelectionReturnMode };
35
35
  export type DispatchSelectionOptions = {
36
- mode: "dispatch"
37
- dispatch: (tr: Transaction) => void
38
- }
39
- export type TransactionSelectionOptions = { mode: "transaction" }
40
- export type StateSelectionOptions = { mode?: "state" }
36
+ mode: "dispatch";
37
+ dispatch: (tr: Transaction) => void;
38
+ };
39
+ export type TransactionSelectionOptions = { mode: "transaction" };
40
+ export type StateSelectionOptions = { mode?: "state" };
41
41
 
42
42
  export type TableInfo = {
43
- map: TableMap
44
- } & FindNodeResult
43
+ map: TableMap;
44
+ } & FindNodeResult;
45
45
 
46
46
  // ============================================================================
47
47
  // HELPER CONSTANTS & UTILITIES
48
48
  // ============================================================================
49
49
 
50
- const EMPTY_CELLS_RESULT = { cells: [], mergedCells: [] }
50
+ const EMPTY_CELLS_RESULT = { cells: [], mergedCells: [] };
51
51
 
52
52
  export function isHTMLElement(n: unknown): n is HTMLElement {
53
- return n instanceof HTMLElement
53
+ return n instanceof HTMLElement;
54
54
  }
55
55
 
56
56
  export type DomCellAroundResult =
57
57
  | {
58
- type: "cell"
59
- domNode: HTMLElement
60
- tbodyNode: HTMLTableSectionElement | null
58
+ type: "cell";
59
+ domNode: HTMLElement;
60
+ tbodyNode: HTMLTableSectionElement | null;
61
61
  }
62
62
  | {
63
- type: "wrapper"
64
- domNode: HTMLElement
65
- tbodyNode: HTMLTableSectionElement | null
66
- }
63
+ type: "wrapper";
64
+ domNode: HTMLElement;
65
+ tbodyNode: HTMLTableSectionElement | null;
66
+ };
67
67
 
68
- export function safeClosest<T extends Element>(
69
- start: Element | null,
70
- selector: string
71
- ): T | null {
72
- return (start?.closest?.(selector) as T | null) ?? null
68
+ export function safeClosest<T extends Element>(start: Element | null, selector: string): T | null {
69
+ return (start?.closest?.(selector) as T | null) ?? null;
73
70
  }
74
71
 
75
72
  /**
76
73
  * Walk up from an element until we find a TD/TH or the table wrapper.
77
74
  * Returns the found element plus its tbody (if present).
78
75
  */
79
- export function domCellAround(
80
- target: Element
81
- ): DomCellAroundResult | undefined {
82
- let current: Element | null = target
76
+ export function domCellAround(target: Element): DomCellAroundResult | undefined {
77
+ let current: Element | null = target;
83
78
 
84
79
  while (
85
80
  current &&
@@ -87,41 +82,39 @@ export function domCellAround(
87
82
  current.tagName !== "TH" &&
88
83
  !current.classList.contains("tableWrapper")
89
84
  ) {
90
- if (current.classList.contains("ProseMirror")) return undefined
91
- current = isHTMLElement(current.parentNode)
92
- ? (current.parentNode as Element)
93
- : null
85
+ if (current.classList.contains("ProseMirror")) return undefined;
86
+ current = isHTMLElement(current.parentNode) ? (current.parentNode as Element) : null;
94
87
  }
95
88
 
96
- if (!current) return undefined
89
+ if (!current) return undefined;
97
90
 
98
91
  if (current.tagName === "TD" || current.tagName === "TH") {
99
92
  return {
100
93
  type: "cell",
101
94
  domNode: current as HTMLElement,
102
95
  tbodyNode: safeClosest<HTMLTableSectionElement>(current, "tbody"),
103
- }
96
+ };
104
97
  }
105
98
 
106
99
  return {
107
100
  type: "wrapper",
108
101
  domNode: current as HTMLElement,
109
102
  tbodyNode: (current as HTMLElement).querySelector("tbody"),
110
- }
103
+ };
111
104
  }
112
105
 
113
106
  /**
114
107
  * Clamps a value between min and max bounds
115
108
  */
116
109
  export function clamp(value: number, min: number, max: number): number {
117
- return Math.max(min, Math.min(value, max))
110
+ return Math.max(min, Math.min(value, max));
118
111
  }
119
112
 
120
113
  /**
121
114
  * Validates if row/col indices are within table bounds
122
115
  */
123
116
  function isWithinBounds(row: number, col: number, map: TableMap): boolean {
124
- return row >= 0 && row < map.height && col >= 0 && col < map.width
117
+ return row >= 0 && row < map.height && col >= 0 && col < map.width;
125
118
  }
126
119
 
127
120
  /**
@@ -131,34 +124,29 @@ function resolveOrientationIndex(
131
124
  state: EditorState,
132
125
  table: TableInfo,
133
126
  orientation: Orientation,
134
- providedIndex?: number
127
+ providedIndex?: number,
135
128
  ): number | null {
136
129
  if (typeof providedIndex === "number") {
137
- return providedIndex
130
+ return providedIndex;
138
131
  }
139
132
 
140
133
  if (state.selection instanceof CellSelection) {
141
- const rect = selectedRect(state)
142
- return orientation === "row" ? rect.top : rect.left
134
+ const rect = selectedRect(state);
135
+ return orientation === "row" ? rect.top : rect.left;
143
136
  }
144
137
 
145
- const $cell = cellAround(state.selection.$anchor) ?? selectionCell(state)
146
- if (!$cell) return null
138
+ const $cell = cellAround(state.selection.$anchor) ?? selectionCell(state);
139
+ if (!$cell) return null;
147
140
 
148
- const rel = $cell.pos - table.start
149
- const rect = table.map.findCell(rel)
150
- return orientation === "row" ? rect.top : rect.left
141
+ const rel = $cell.pos - table.start;
142
+ const rect = table.map.findCell(rel);
143
+ return orientation === "row" ? rect.top : rect.left;
151
144
  }
152
145
 
153
146
  /**
154
147
  * Creates a CellInfo object from position data
155
148
  */
156
- function createCellInfo(
157
- row: number,
158
- column: number,
159
- cellPos: number,
160
- cellNode: Node
161
- ): CellInfo {
149
+ function createCellInfo(row: number, column: number, cellPos: number, cellNode: Node): CellInfo {
162
150
  return {
163
151
  row,
164
152
  column,
@@ -166,17 +154,17 @@ function createCellInfo(
166
154
  node: cellNode,
167
155
  start: cellPos + 1,
168
156
  depth: cellNode ? cellNode.content.size : 0,
169
- }
157
+ };
170
158
  }
171
159
 
172
160
  /**
173
161
  * Checks if a cell is merged (has colspan or rowspan > 1)
174
162
  */
175
163
  export function isCellMerged(node: Node | null): boolean {
176
- if (!node) return false
177
- const colspan = node.attrs.colspan ?? 1
178
- const rowspan = node.attrs.rowspan ?? 1
179
- return colspan > 1 || rowspan > 1
164
+ if (!node) return false;
165
+ const colspan = node.attrs.colspan ?? 1;
166
+ const rowspan = node.attrs.rowspan ?? 1;
167
+ return colspan > 1 || rowspan > 1;
180
168
  }
181
169
 
182
170
  /**
@@ -186,110 +174,100 @@ function collectCells(
186
174
  editor: Editor | null,
187
175
  orientation: Orientation,
188
176
  index?: number,
189
- tablePos?: number
177
+ tablePos?: number,
190
178
  ): { cells: CellInfo[]; mergedCells: CellInfo[] } {
191
- if (!editor) return EMPTY_CELLS_RESULT
179
+ if (!editor) return EMPTY_CELLS_RESULT;
192
180
 
193
- const { state } = editor
194
- const table = getTable(editor, tablePos)
195
- if (!table) return EMPTY_CELLS_RESULT
181
+ const { state } = editor;
182
+ const table = getTable(editor, tablePos);
183
+ if (!table) return EMPTY_CELLS_RESULT;
196
184
 
197
- const tableStart = table.start
198
- const tableNode = table.node
199
- const map = table.map
185
+ const tableStart = table.start;
186
+ const tableNode = table.node;
187
+ const map = table.map;
200
188
 
201
- const resolvedIndex = resolveOrientationIndex(
202
- state,
203
- table,
204
- orientation,
205
- index
206
- )
207
- if (resolvedIndex === null) return EMPTY_CELLS_RESULT
189
+ const resolvedIndex = resolveOrientationIndex(state, table, orientation, index);
190
+ if (resolvedIndex === null) return EMPTY_CELLS_RESULT;
208
191
 
209
192
  // Bounds check
210
- const maxIndex = orientation === "row" ? map.height : map.width
193
+ const maxIndex = orientation === "row" ? map.height : map.width;
211
194
  if (resolvedIndex < 0 || resolvedIndex >= maxIndex) {
212
- return EMPTY_CELLS_RESULT
195
+ return EMPTY_CELLS_RESULT;
213
196
  }
214
197
 
215
- const cells: CellInfo[] = []
216
- const mergedCells: CellInfo[] = []
217
- const seenMerged = new Set<number>()
198
+ const cells: CellInfo[] = [];
199
+ const mergedCells: CellInfo[] = [];
200
+ const seenMerged = new Set<number>();
218
201
 
219
- const iterationCount = orientation === "row" ? map.width : map.height
202
+ const iterationCount = orientation === "row" ? map.width : map.height;
220
203
 
221
204
  for (let i = 0; i < iterationCount; i++) {
222
- const row = orientation === "row" ? resolvedIndex : i
223
- const col = orientation === "row" ? i : resolvedIndex
224
- const cellIndex = row * map.width + col
225
- const mapCell = map.map[cellIndex]
205
+ const row = orientation === "row" ? resolvedIndex : i;
206
+ const col = orientation === "row" ? i : resolvedIndex;
207
+ const cellIndex = row * map.width + col;
208
+ const mapCell = map.map[cellIndex];
226
209
 
227
- if (mapCell === undefined) continue
210
+ if (mapCell === undefined) continue;
228
211
 
229
- const cellPos = tableStart + mapCell
230
- const cellNode = tableNode.nodeAt(mapCell)
231
- if (!cellNode) continue
212
+ const cellPos = tableStart + mapCell;
213
+ const cellNode = tableNode.nodeAt(mapCell);
214
+ if (!cellNode) continue;
232
215
 
233
- const cell = createCellInfo(row, col, cellPos, cellNode)
216
+ const cell = createCellInfo(row, col, cellPos, cellNode);
234
217
 
235
218
  if (isCellMerged(cellNode)) {
236
219
  if (!seenMerged.has(cellPos)) {
237
- mergedCells.push(cell)
238
- seenMerged.add(cellPos)
220
+ mergedCells.push(cell);
221
+ seenMerged.add(cellPos);
239
222
  }
240
223
  }
241
224
 
242
- cells.push(cell)
225
+ cells.push(cell);
243
226
  }
244
227
 
245
- return { cells, mergedCells }
228
+ return { cells, mergedCells };
246
229
  }
247
230
 
248
231
  /**
249
232
  * Generic function to count empty cells from the end of a row or column
250
233
  */
251
- function countEmptyCellsFromEnd(
252
- editor: Editor,
253
- tablePos: number,
254
- orientation: Orientation
255
- ): number {
256
- const table = getTable(editor, tablePos)
257
- if (!table) return 0
234
+ function countEmptyCellsFromEnd(editor: Editor, tablePos: number, orientation: Orientation): number {
235
+ const table = getTable(editor, tablePos);
236
+ if (!table) return 0;
258
237
 
259
- const { doc } = editor.state
260
- const maxIndex = orientation === "row" ? table.map.height : table.map.width
238
+ const { doc } = editor.state;
239
+ const maxIndex = orientation === "row" ? table.map.height : table.map.width;
261
240
 
262
- let emptyCount = 0
241
+ let emptyCount = 0;
263
242
  for (let idx = maxIndex - 1; idx >= 0; idx--) {
264
- const seen = new Set<number>()
265
- let isLineEmpty = true
243
+ const seen = new Set<number>();
244
+ let isLineEmpty = true;
266
245
 
267
- const iterationCount =
268
- orientation === "row" ? table.map.width : table.map.height
246
+ const iterationCount = orientation === "row" ? table.map.width : table.map.height;
269
247
 
270
248
  for (let i = 0; i < iterationCount; i++) {
271
- const row = orientation === "row" ? idx : i
272
- const col = orientation === "row" ? i : idx
273
- const rel = table.map.positionAt(row, col, table.node)
249
+ const row = orientation === "row" ? idx : i;
250
+ const col = orientation === "row" ? i : idx;
251
+ const rel = table.map.positionAt(row, col, table.node);
274
252
 
275
- if (seen.has(rel)) continue
276
- seen.add(rel)
253
+ if (seen.has(rel)) continue;
254
+ seen.add(rel);
277
255
 
278
- const abs = tablePos + 1 + rel
279
- const cell = doc.nodeAt(abs)
280
- if (!cell) continue
256
+ const abs = tablePos + 1 + rel;
257
+ const cell = doc.nodeAt(abs);
258
+ if (!cell) continue;
281
259
 
282
260
  if (!isCellEmpty(cell)) {
283
- isLineEmpty = false
284
- break
261
+ isLineEmpty = false;
262
+ break;
285
263
  }
286
264
  }
287
265
 
288
- if (isLineEmpty) emptyCount++
289
- else break
266
+ if (isLineEmpty) emptyCount++;
267
+ else break;
290
268
  }
291
269
 
292
- return emptyCount
270
+ return emptyCount;
293
271
  }
294
272
 
295
273
  /**
@@ -307,34 +285,34 @@ function countEmptyCellsFromEnd(
307
285
  * If no table is found, returns null.
308
286
  */
309
287
  export function getTable(editor: Editor | null, tablePos?: number) {
310
- if (!editor) return null
288
+ if (!editor) return null;
311
289
 
312
- let table = null
290
+ let table: FindNodeResult | null | undefined = null;
313
291
 
314
292
  if (typeof tablePos === "number") {
315
- const tableNode = editor.state.doc.nodeAt(tablePos)
293
+ const tableNode = editor.state.doc.nodeAt(tablePos);
316
294
  if (tableNode?.type.name === "table") {
317
295
  table = {
318
296
  node: tableNode,
319
297
  pos: tablePos,
320
298
  start: tablePos + 1,
321
299
  depth: editor.state.doc.resolve(tablePos).depth,
322
- }
300
+ };
323
301
  }
324
302
  }
325
303
 
326
304
  if (!table) {
327
- const { state } = editor
328
- const $from = state.doc.resolve(state.selection.from)
329
- table = findTable($from)
305
+ const { state } = editor;
306
+ const $from = state.doc.resolve(state.selection.from);
307
+ table = findTable($from);
330
308
  }
331
309
 
332
- if (!table) return null
310
+ if (!table) return null;
333
311
 
334
- const tableMap = TableMap.get(table.node)
335
- if (!tableMap) return null
312
+ const tableMap = TableMap.get(table.node);
313
+ if (!tableMap) return null;
336
314
 
337
- return { ...table, map: tableMap }
315
+ return { ...table, map: tableMap };
338
316
  }
339
317
 
340
318
  /**
@@ -343,50 +321,47 @@ export function getTable(editor: Editor | null, tablePos?: number) {
343
321
  * @returns true if the selection is inside a table cell; false otherwise
344
322
  */
345
323
  export function isSelectionInCell(state: EditorState): boolean {
346
- const { selection } = state
347
- const $from = selection.$from
324
+ const { selection } = state;
325
+ const $from = selection.$from;
348
326
 
349
327
  for (let depth = $from.depth; depth > 0; depth--) {
350
- const node = $from.node(depth)
328
+ const node = $from.node(depth);
351
329
  if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
352
- return true
330
+ return true;
353
331
  }
354
332
  }
355
333
 
356
- return false
334
+ return false;
357
335
  }
358
336
 
359
337
  /**
360
338
  * Cells overlap a rectangle if any of the cells in the rectangle are merged
361
339
  * with cells outside the rectangle.
362
340
  */
363
- export function cellsOverlapRectangle(
364
- { width, height, map }: TableMap,
365
- rect: Rect
366
- ) {
341
+ export function cellsOverlapRectangle({ width, height, map }: TableMap, rect: Rect) {
367
342
  let indexTop = rect.top * width + rect.left,
368
- indexLeft = indexTop
343
+ indexLeft = indexTop;
369
344
  let indexBottom = (rect.bottom - 1) * width + rect.left,
370
- indexRight = indexTop + (rect.right - rect.left - 1)
345
+ indexRight = indexTop + (rect.right - rect.left - 1);
371
346
  for (let i = rect.top; i < rect.bottom; i++) {
372
347
  if (
373
- (rect.left > 0 && map[indexLeft] == map[indexLeft - 1]) ||
374
- (rect.right < width && map[indexRight] == map[indexRight + 1])
348
+ (rect.left > 0 && map[indexLeft] === map[indexLeft - 1]) ||
349
+ (rect.right < width && map[indexRight] === map[indexRight + 1])
375
350
  )
376
- return true
377
- indexLeft += width
378
- indexRight += width
351
+ return true;
352
+ indexLeft += width;
353
+ indexRight += width;
379
354
  }
380
355
  for (let i = rect.left; i < rect.right; i++) {
381
356
  if (
382
- (rect.top > 0 && map[indexTop] == map[indexTop - width]) ||
383
- (rect.bottom < height && map[indexBottom] == map[indexBottom + width])
357
+ (rect.top > 0 && map[indexTop] === map[indexTop - width]) ||
358
+ (rect.bottom < height && map[indexBottom] === map[indexBottom + width])
384
359
  )
385
- return true
386
- indexTop++
387
- indexBottom++
360
+ return true;
361
+ indexTop++;
362
+ indexBottom++;
388
363
  }
389
- return false
364
+ return false;
390
365
  }
391
366
 
392
367
  /**
@@ -396,36 +371,36 @@ export function cellsOverlapRectangle(
396
371
  * @returns True if the selection was successfully restored, false otherwise
397
372
  */
398
373
  export function runPreservingCursor(editor: Editor, fn: () => void): boolean {
399
- const view = editor.view
400
- const startSel = view.state.selection
401
- const bookmark = startSel.getBookmark()
374
+ const view = editor.view;
375
+ const startSel = view.state.selection;
376
+ const bookmark = startSel.getBookmark();
402
377
 
403
- const mapping = new Mapping()
404
- const originalDispatch = view.dispatch
378
+ const mapping = new Mapping();
379
+ const originalDispatch = view.dispatch;
405
380
 
406
381
  view.dispatch = (tr) => {
407
- mapping.appendMapping(tr.mapping)
408
- originalDispatch(tr)
409
- }
382
+ mapping.appendMapping(tr.mapping);
383
+ originalDispatch(tr);
384
+ };
410
385
 
411
386
  try {
412
- fn()
387
+ fn();
413
388
  } finally {
414
- view.dispatch = originalDispatch
389
+ view.dispatch = originalDispatch;
415
390
  }
416
391
 
417
392
  try {
418
- const sel = bookmark.map(mapping).resolve(view.state.doc)
419
- view.dispatch(view.state.tr.setSelection(sel))
420
- return true
393
+ const sel = bookmark.map(mapping).resolve(view.state.doc);
394
+ view.dispatch(view.state.tr.setSelection(sel));
395
+ return true;
421
396
  } catch {
422
397
  // Fallback: if the exact spot vanished (e.g., cell deleted),
423
398
  // go to the nearest valid position.
424
- const mappedPos = mapping.map(startSel.from, -1)
425
- const clamped = clamp(mappedPos, 0, view.state.doc.content.size)
426
- const near = Selection.near(view.state.doc.resolve(clamped), -1)
427
- view.dispatch(view.state.tr.setSelection(near))
428
- return false
399
+ const mappedPos = mapping.map(startSel.from, -1);
400
+ const clamped = clamp(mappedPos, 0, view.state.doc.content.size);
401
+ const near = Selection.near(view.state.doc.resolve(clamped), -1);
402
+ view.dispatch(view.state.tr.setSelection(near));
403
+ return false;
429
404
  }
430
405
  }
431
406
 
@@ -443,22 +418,22 @@ export function runPreservingCursor(editor: Editor, fn: () => void): boolean {
443
418
  * @returns true if the cell is empty; false otherwise
444
419
  */
445
420
  export function isCellEmpty(cellNode: Node): boolean {
446
- if (cellNode.childCount === 0) return true
421
+ if (cellNode.childCount === 0) return true;
447
422
 
448
- let isEmpty = true
423
+ let isEmpty = true;
449
424
  cellNode.descendants((n) => {
450
425
  if (n.isText && n.text?.trim()) {
451
- isEmpty = false
452
- return false
426
+ isEmpty = false;
427
+ return false;
453
428
  }
454
429
  if (n.isLeaf && !n.isText) {
455
- isEmpty = false
456
- return false
430
+ isEmpty = false;
431
+ return false;
457
432
  }
458
- return true
459
- })
433
+ return true;
434
+ });
460
435
 
461
- return isEmpty
436
+ return isEmpty;
462
437
  }
463
438
 
464
439
  /**
@@ -475,36 +450,36 @@ export function getTableSelectionType(
475
450
  editor: Editor | null,
476
451
  index?: number,
477
452
  orientation?: Orientation,
478
- tablePos?: number
453
+ tablePos?: number,
479
454
  ): { orientation: Orientation; index: number } | null {
480
455
  if (typeof index === "number" && orientation) {
481
- return { orientation, index }
456
+ return { orientation, index };
482
457
  }
483
458
 
484
- if (!editor) return null
459
+ if (!editor) return null;
485
460
 
486
- const { state } = editor
461
+ const { state } = editor;
487
462
 
488
- const table = getTable(editor, tablePos)
489
- if (!table) return null
463
+ const table = getTable(editor, tablePos);
464
+ if (!table) return null;
490
465
 
491
466
  if (state.selection instanceof CellSelection) {
492
- const rect = selectedRect(state)
493
- const width = rect.right - rect.left
494
- const height = rect.bottom - rect.top
467
+ const rect = selectedRect(state);
468
+ const width = rect.right - rect.left;
469
+ const height = rect.bottom - rect.top;
495
470
 
496
471
  if (height === 1 && width >= 1) {
497
- return { orientation: "row", index: rect.top }
472
+ return { orientation: "row", index: rect.top };
498
473
  }
499
474
 
500
475
  if (width === 1 && height >= 1) {
501
- return { orientation: "column", index: rect.left }
476
+ return { orientation: "column", index: rect.left };
502
477
  }
503
478
 
504
- return null
479
+ return null;
505
480
  }
506
481
 
507
- return null
482
+ return null;
508
483
  }
509
484
 
510
485
  /**
@@ -523,13 +498,13 @@ export function getRowOrColumnCells(
523
498
  editor: Editor | null,
524
499
  index?: number,
525
500
  orientation?: Orientation,
526
- tablePos?: number
501
+ tablePos?: number,
527
502
  ): {
528
- cells: CellInfo[]
529
- mergedCells: CellInfo[]
530
- index?: number
531
- orientation?: Orientation
532
- tablePos?: number
503
+ cells: CellInfo[];
504
+ mergedCells: CellInfo[];
505
+ index?: number;
506
+ orientation?: Orientation;
507
+ tablePos?: number;
533
508
  } {
534
509
  const emptyResult = {
535
510
  cells: [],
@@ -537,36 +512,29 @@ export function getRowOrColumnCells(
537
512
  index: undefined,
538
513
  orientation: undefined,
539
514
  tablePos: undefined,
540
- }
515
+ };
541
516
 
542
517
  if (!editor) {
543
- return emptyResult
518
+ return emptyResult;
544
519
  }
545
520
 
546
- if (
547
- typeof index !== "number" &&
548
- !(editor.state.selection instanceof CellSelection)
549
- ) {
550
- return emptyResult
521
+ if (typeof index !== "number" && !(editor.state.selection instanceof CellSelection)) {
522
+ return emptyResult;
551
523
  }
552
524
 
553
- let finalIndex = index
554
- let finalOrientation = orientation
525
+ let finalIndex = index;
526
+ let finalOrientation = orientation;
555
527
 
556
- if (
557
- typeof finalIndex !== "number" ||
558
- !finalOrientation ||
559
- !["row", "column"].includes(finalOrientation)
560
- ) {
561
- const selectionType = getTableSelectionType(editor)
562
- if (!selectionType) return emptyResult
528
+ if (typeof finalIndex !== "number" || !finalOrientation || !["row", "column"].includes(finalOrientation)) {
529
+ const selectionType = getTableSelectionType(editor);
530
+ if (!selectionType) return emptyResult;
563
531
 
564
- finalIndex = selectionType.index
565
- finalOrientation = selectionType.orientation
532
+ finalIndex = selectionType.index;
533
+ finalOrientation = selectionType.orientation;
566
534
  }
567
535
 
568
- const result = collectCells(editor, finalOrientation, finalIndex, tablePos)
569
- return { ...result, index: finalIndex, orientation: finalOrientation }
536
+ const result = collectCells(editor, finalOrientation, finalIndex, tablePos);
537
+ return { ...result, index: finalIndex, orientation: finalOrientation };
570
538
  }
571
539
 
572
540
  /**
@@ -577,9 +545,9 @@ export function getRowOrColumnCells(
577
545
  export function getRowCells(
578
546
  editor: Editor | null,
579
547
  rowIndex?: number,
580
- tablePos?: number
548
+ tablePos?: number,
581
549
  ): { cells: CellInfo[]; mergedCells: CellInfo[] } {
582
- return collectCells(editor, "row", rowIndex, tablePos)
550
+ return collectCells(editor, "row", rowIndex, tablePos);
583
551
  }
584
552
 
585
553
  /**
@@ -590,9 +558,9 @@ export function getRowCells(
590
558
  export function getColumnCells(
591
559
  editor: Editor | null,
592
560
  columnIndex?: number,
593
- tablePos?: number
561
+ tablePos?: number,
594
562
  ): { cells: CellInfo[]; mergedCells: CellInfo[] } {
595
- return collectCells(editor, "column", columnIndex, tablePos)
563
+ return collectCells(editor, "column", columnIndex, tablePos);
596
564
  }
597
565
 
598
566
  /**
@@ -610,60 +578,48 @@ export function updateSelectionAfterAction(
610
578
  editor: Editor,
611
579
  orientation: Orientation,
612
580
  newIndex: number,
613
- tablePos?: number
581
+ tablePos?: number,
614
582
  ): void {
615
583
  try {
616
- const table = getTable(editor, tablePos)
617
- if (!table) return
584
+ const table = getTable(editor, tablePos);
585
+ if (!table) return;
618
586
 
619
- const { state } = editor
620
- const { map } = table
587
+ const { state } = editor;
588
+ const { map } = table;
621
589
 
622
590
  if (orientation === "row") {
623
591
  if (newIndex >= 0 && newIndex < map.height) {
624
- const startCol = 0
625
- const endCol = map.width - 1
626
-
627
- const startCellPos =
628
- table.start + map.positionAt(newIndex, startCol, table.node)
629
- const endCellPos =
630
- table.start + map.positionAt(newIndex, endCol, table.node)
631
-
632
- const $start = state.doc.resolve(startCellPos)
633
- const $end = state.doc.resolve(endCellPos)
634
-
635
- const newSelection = CellSelection.create(
636
- state.doc,
637
- $start.pos,
638
- $end.pos
639
- )
640
- const tr = state.tr.setSelection(newSelection)
641
- editor.view.dispatch(tr)
592
+ const startCol = 0;
593
+ const endCol = map.width - 1;
594
+
595
+ const startCellPos = table.start + map.positionAt(newIndex, startCol, table.node);
596
+ const endCellPos = table.start + map.positionAt(newIndex, endCol, table.node);
597
+
598
+ const $start = state.doc.resolve(startCellPos);
599
+ const $end = state.doc.resolve(endCellPos);
600
+
601
+ const newSelection = CellSelection.create(state.doc, $start.pos, $end.pos);
602
+ const tr = state.tr.setSelection(newSelection);
603
+ editor.view.dispatch(tr);
642
604
  }
643
605
  } else if (orientation === "column") {
644
606
  if (newIndex >= 0 && newIndex < map.width) {
645
- const startRow = 0
646
- const endRow = map.height - 1
647
-
648
- const startCellPos =
649
- table.start + map.positionAt(startRow, newIndex, table.node)
650
- const endCellPos =
651
- table.start + map.positionAt(endRow, newIndex, table.node)
652
-
653
- const $start = state.doc.resolve(startCellPos)
654
- const $end = state.doc.resolve(endCellPos)
655
-
656
- const newSelection = CellSelection.create(
657
- state.doc,
658
- $start.pos,
659
- $end.pos
660
- )
661
- const tr = state.tr.setSelection(newSelection)
662
- editor.view.dispatch(tr)
607
+ const startRow = 0;
608
+ const endRow = map.height - 1;
609
+
610
+ const startCellPos = table.start + map.positionAt(startRow, newIndex, table.node);
611
+ const endCellPos = table.start + map.positionAt(endRow, newIndex, table.node);
612
+
613
+ const $start = state.doc.resolve(startCellPos);
614
+ const $end = state.doc.resolve(endCellPos);
615
+
616
+ const newSelection = CellSelection.create(state.doc, $start.pos, $end.pos);
617
+ const tr = state.tr.setSelection(newSelection);
618
+ editor.view.dispatch(tr);
663
619
  }
664
620
  }
665
621
  } catch (error) {
666
- console.warn("Failed to update selection after move:", error)
622
+ console.warn("Failed to update selection after move:", error);
667
623
  }
668
624
  }
669
625
 
@@ -674,50 +630,42 @@ export function updateSelectionAfterAction(
674
630
  *
675
631
  * @public
676
632
  */
677
- export function setCellAttr(attrs: Record<string, unknown>): Command
678
- export function setCellAttr(name: string, value: unknown): Command
679
- export function setCellAttr(
680
- nameOrAttrs: string | Record<string, unknown>,
681
- value?: unknown
682
- ): Command {
683
- return function (state, dispatch) {
684
- if (!isInTable(state)) return false
685
- const $cell = selectionCell(state)
686
-
687
- const attrs =
688
- typeof nameOrAttrs === "string" ? { [nameOrAttrs]: value } : nameOrAttrs
633
+ export function setCellAttr(attrs: Record<string, unknown>): Command;
634
+ export function setCellAttr(name: string, value: unknown): Command;
635
+ export function setCellAttr(nameOrAttrs: string | Record<string, unknown>, value?: unknown): Command {
636
+ return (state, dispatch) => {
637
+ if (!isInTable(state)) return false;
638
+ const $cell = selectionCell(state);
639
+
640
+ const attrs = typeof nameOrAttrs === "string" ? { [nameOrAttrs]: value } : nameOrAttrs;
689
641
 
690
642
  if (dispatch) {
691
- const tr = state.tr
643
+ const tr = state.tr;
692
644
  if (state.selection instanceof CellSelection) {
693
645
  state.selection.forEachCell((node, pos) => {
694
- const needsUpdate = Object.entries(attrs).some(
695
- ([name, val]) => node.attrs[name] !== val
696
- )
646
+ const needsUpdate = Object.entries(attrs).some(([name, val]) => node.attrs[name] !== val);
697
647
 
698
648
  if (needsUpdate) {
699
649
  tr.setNodeMarkup(pos, null, {
700
650
  ...node.attrs,
701
651
  ...attrs,
702
- })
652
+ });
703
653
  }
704
- })
654
+ });
705
655
  } else {
706
- const needsUpdate = Object.entries(attrs).some(
707
- ([name, val]) => $cell.nodeAfter!.attrs[name] !== val
708
- )
656
+ const needsUpdate = Object.entries(attrs).some(([name, val]) => $cell.nodeAfter!.attrs[name] !== val);
709
657
 
710
658
  if (needsUpdate) {
711
659
  tr.setNodeMarkup($cell.pos, null, {
712
660
  ...$cell.nodeAfter!.attrs,
713
661
  ...attrs,
714
- })
662
+ });
715
663
  }
716
664
  }
717
- dispatch(tr)
665
+ dispatch(tr);
718
666
  }
719
- return true
720
- }
667
+ return true;
668
+ };
721
669
  }
722
670
 
723
671
  /**
@@ -733,11 +681,8 @@ export function setCellAttr(
733
681
  * @param target - The table node instance to analyze (must be the same reference as in the doc)
734
682
  * @returns The number of trailing empty rows (0 if table not found)
735
683
  */
736
- export function countEmptyRowsFromEnd(
737
- editor: Editor,
738
- tablePos: number
739
- ): number {
740
- return countEmptyCellsFromEnd(editor, tablePos, "row")
684
+ export function countEmptyRowsFromEnd(editor: Editor, tablePos: number): number {
685
+ return countEmptyCellsFromEnd(editor, tablePos, "row");
741
686
  }
742
687
 
743
688
  /**
@@ -752,11 +697,8 @@ export function countEmptyRowsFromEnd(
752
697
  * @param target - The table node instance to analyze (must be the same reference as in the doc)
753
698
  * @returns The number of trailing empty columns (0 if table not found)
754
699
  */
755
- export function countEmptyColumnsFromEnd(
756
- editor: Editor,
757
- tablePos: number
758
- ): number {
759
- return countEmptyCellsFromEnd(editor, tablePos, "column")
700
+ export function countEmptyColumnsFromEnd(editor: Editor, tablePos: number): number {
701
+ return countEmptyCellsFromEnd(editor, tablePos, "column");
760
702
  }
761
703
 
762
704
  /**
@@ -773,14 +715,14 @@ export function countEmptyColumnsFromEnd(
773
715
  * @returns The rounded value using the dead-zone heuristic
774
716
  */
775
717
  export function marginRound(num: number, margin = 0.3): number {
776
- const floor = Math.floor(num)
777
- const ceil = Math.ceil(num)
778
- const lowerBound = floor + margin
779
- const upperBound = ceil - margin
780
-
781
- if (num < lowerBound) return floor
782
- if (num > upperBound) return ceil
783
- return Math.round(num)
718
+ const floor = Math.floor(num);
719
+ const ceil = Math.ceil(num);
720
+ const lowerBound = floor + margin;
721
+ const upperBound = ceil - margin;
722
+
723
+ if (num < lowerBound) return floor;
724
+ if (num > upperBound) return ceil;
725
+ return Math.round(num);
784
726
  }
785
727
 
786
728
  /**
@@ -794,14 +736,9 @@ export function marginRound(num: number, margin = 0.3): number {
794
736
  * @returns true if both rects are equal or both are undefined; false otherwise
795
737
  */
796
738
  export function rectEq(a?: DOMRect | null, b?: DOMRect | null): boolean {
797
- if (!a && !b) return true
798
- if (!a || !b) return false
799
- return (
800
- a.left === b.left &&
801
- a.top === b.top &&
802
- a.width === b.width &&
803
- a.height === b.height
804
- )
739
+ if (!a && !b) return true;
740
+ if (!a || !b) return false;
741
+ return a.left === b.left && a.top === b.top && a.width === b.width && a.height === b.height;
805
742
  }
806
743
 
807
744
  /**
@@ -810,24 +747,24 @@ export function rectEq(a?: DOMRect | null, b?: DOMRect | null): boolean {
810
747
  function applySelectionWithMode(
811
748
  state: EditorState,
812
749
  transaction: Transaction,
813
- options: BaseSelectionOptions | DispatchSelectionOptions
814
- ): EditorState | Transaction | void {
815
- const mode: SelectionReturnMode = options.mode ?? "state"
750
+ options: BaseSelectionOptions | DispatchSelectionOptions,
751
+ ): EditorState | Transaction | undefined {
752
+ const mode: SelectionReturnMode = options.mode ?? "state";
816
753
 
817
754
  switch (mode) {
818
755
  case "dispatch": {
819
- const dispatchOptions = options as DispatchSelectionOptions
756
+ const dispatchOptions = options as DispatchSelectionOptions;
820
757
  if (typeof dispatchOptions.dispatch === "function") {
821
- dispatchOptions.dispatch(transaction)
758
+ dispatchOptions.dispatch(transaction);
822
759
  }
823
- return
760
+ return;
824
761
  }
825
762
 
826
763
  case "transaction":
827
- return transaction
764
+ return transaction;
828
765
 
829
766
  default: // "state"
830
- return state.apply(transaction)
767
+ return state.apply(transaction);
831
768
  }
832
769
  }
833
770
 
@@ -860,65 +797,55 @@ export function createTableCellSelection(
860
797
  tablePosition: number,
861
798
  startCell: CellCoordinates,
862
799
  endCell?: CellCoordinates,
863
- options?: StateSelectionOptions
864
- ): EditorState
800
+ options?: StateSelectionOptions,
801
+ ): EditorState | undefined;
865
802
  export function createTableCellSelection(
866
803
  state: EditorState,
867
804
  tablePosition: number,
868
805
  startCell: CellCoordinates,
869
806
  endCell: CellCoordinates | undefined,
870
- options: TransactionSelectionOptions
871
- ): Transaction
807
+ options: TransactionSelectionOptions,
808
+ ): Transaction | undefined;
872
809
  export function createTableCellSelection(
873
810
  state: EditorState,
874
811
  tablePosition: number,
875
812
  startCell: CellCoordinates,
876
813
  endCell: CellCoordinates | undefined,
877
- options: DispatchSelectionOptions
878
- ): void
814
+ options: DispatchSelectionOptions,
815
+ ): undefined;
879
816
 
880
817
  export function createTableCellSelection(
881
818
  state: EditorState,
882
819
  tablePosition: number,
883
820
  startCell: CellCoordinates,
884
821
  endCell: CellCoordinates = startCell,
885
- options: BaseSelectionOptions | DispatchSelectionOptions = { mode: "state" }
886
- ): EditorState | Transaction | void {
887
- const startCellPosition = getCellPosition(state, tablePosition, startCell)
888
- const endCellPosition = getCellPosition(state, tablePosition, endCell)
822
+ options: BaseSelectionOptions | DispatchSelectionOptions = { mode: "state" },
823
+ ): EditorState | Transaction | undefined {
824
+ const startCellPosition = getCellPosition(state, tablePosition, startCell);
825
+ const endCellPosition = getCellPosition(state, tablePosition, endCell);
889
826
 
890
827
  if (!startCellPosition || !endCellPosition) {
891
- return
828
+ return;
892
829
  }
893
830
 
894
- const transaction = state.tr.setSelection(
895
- new CellSelection(startCellPosition, endCellPosition)
896
- )
831
+ const transaction = state.tr.setSelection(new CellSelection(startCellPosition, endCellPosition));
897
832
 
898
- return applySelectionWithMode(state, transaction, options)
833
+ return applySelectionWithMode(state, transaction, options);
899
834
  }
900
835
 
901
836
  /**
902
837
  * Get the position of a cell inside a table by relative row/col indices.
903
838
  * Returns the position *before* the cell, which is what `CellSelection` expects.
904
839
  */
905
- export function getCellPosition(
906
- state: EditorState,
907
- tablePosition: number,
908
- cellCoordinates: CellCoordinates
909
- ) {
910
- const resolvedTablePosition = state.doc.resolve(tablePosition)
911
- const resolvedRowPosition = state.doc.resolve(
912
- resolvedTablePosition.posAtIndex(cellCoordinates.row) + 1
913
- )
914
- const resolvedColPosition = state.doc.resolve(
915
- resolvedRowPosition.posAtIndex(cellCoordinates.col)
916
- )
917
-
918
- const $cell = cellAround(resolvedColPosition)
919
- if (!$cell) return null
920
-
921
- return resolvedColPosition
840
+ export function getCellPosition(state: EditorState, tablePosition: number, cellCoordinates: CellCoordinates) {
841
+ const resolvedTablePosition = state.doc.resolve(tablePosition);
842
+ const resolvedRowPosition = state.doc.resolve(resolvedTablePosition.posAtIndex(cellCoordinates.row) + 1);
843
+ const resolvedColPosition = state.doc.resolve(resolvedRowPosition.posAtIndex(cellCoordinates.col));
844
+
845
+ const $cell = cellAround(resolvedColPosition);
846
+ if (!$cell) return null;
847
+
848
+ return resolvedColPosition;
922
849
  }
923
850
 
924
851
  /**
@@ -938,108 +865,105 @@ export function selectCellsByCoords(
938
865
  editor: Editor | null,
939
866
  tablePos: number,
940
867
  coords: { row: number; col: number }[],
941
- options?: StateSelectionOptions
942
- ): EditorState
868
+ options?: StateSelectionOptions,
869
+ ): EditorState | undefined;
943
870
  export function selectCellsByCoords(
944
871
  editor: Editor | null,
945
872
  tablePos: number,
946
873
  coords: { row: number; col: number }[],
947
- options: TransactionSelectionOptions
948
- ): Transaction
874
+ options: TransactionSelectionOptions,
875
+ ): Transaction | undefined;
949
876
  export function selectCellsByCoords(
950
877
  editor: Editor | null,
951
878
  tablePos: number,
952
879
  coords: { row: number; col: number }[],
953
- options: DispatchSelectionOptions
954
- ): void
880
+ options: DispatchSelectionOptions,
881
+ ): undefined;
955
882
  export function selectCellsByCoords(
956
883
  editor: Editor | null,
957
884
  tablePos: number,
958
885
  coords: { row: number; col: number }[],
959
- options: BaseSelectionOptions | DispatchSelectionOptions = { mode: "state" }
960
- ): EditorState | Transaction | void {
961
- if (!editor) return
886
+ options: BaseSelectionOptions | DispatchSelectionOptions = { mode: "state" },
887
+ ): EditorState | Transaction | undefined {
888
+ if (!editor) return;
962
889
 
963
- const table = getTable(editor, tablePos)
964
- if (!table) return
890
+ const table = getTable(editor, tablePos);
891
+ if (!table) return;
965
892
 
966
- const { state } = editor
967
- const tableMap = table.map
893
+ const { state } = editor;
894
+ const tableMap = table.map;
968
895
 
969
896
  const cleanedCoords = coords
970
897
  .map((coord) => ({
971
898
  row: clamp(coord.row, 0, tableMap.height - 1),
972
899
  col: clamp(coord.col, 0, tableMap.width - 1),
973
900
  }))
974
- .filter((coord) => isWithinBounds(coord.row, coord.col, tableMap))
901
+ .filter((coord) => isWithinBounds(coord.row, coord.col, tableMap));
975
902
 
976
903
  if (cleanedCoords.length === 0) {
977
- return
904
+ return;
978
905
  }
979
906
 
980
907
  // --- Find the smallest rectangle that contains all our coordinates ---
981
- const allRows = cleanedCoords.map((coord) => coord.row)
982
- const topRow = Math.min(...allRows)
983
- const bottomRow = Math.max(...allRows)
908
+ const allRows = cleanedCoords.map((coord) => coord.row);
909
+ const topRow = Math.min(...allRows);
910
+ const bottomRow = Math.max(...allRows);
984
911
 
985
- const allCols = cleanedCoords.map((coord) => coord.col)
986
- const leftCol = Math.min(...allCols)
987
- const rightCol = Math.max(...allCols)
912
+ const allCols = cleanedCoords.map((coord) => coord.col);
913
+ const leftCol = Math.min(...allCols);
914
+ const rightCol = Math.max(...allCols);
988
915
 
989
916
  // --- Convert visual coordinates to document positions ---
990
917
  // Use TableMap.map array directly to handle merged cells correctly
991
918
  const getCellPositionFromMap = (row: number, col: number): number | null => {
992
919
  // TableMap.map is a flat array where each entry represents a cell
993
920
  // For merged cells, the same offset appears multiple times
994
- const cellOffset = tableMap.map[row * tableMap.width + col]
995
- if (cellOffset === undefined) return null
921
+ const cellOffset = tableMap.map[row * tableMap.width + col];
922
+ if (cellOffset === undefined) return null;
996
923
 
997
924
  // Convert the relative offset to an absolute position in the document
998
925
  // tablePos + 1 skips the table opening tag
999
- return tablePos + 1 + cellOffset
1000
- }
926
+ return tablePos + 1 + cellOffset;
927
+ };
1001
928
 
1002
929
  // Anchor = where the selection starts (top-left of bounding box)
1003
- const anchorPosition = getCellPositionFromMap(topRow, leftCol)
1004
- if (anchorPosition === null) return
930
+ const anchorPosition = getCellPositionFromMap(topRow, leftCol);
931
+ if (anchorPosition === null) return;
1005
932
 
1006
933
  // Head = where the selection ends (usually bottom-right of bounding box)
1007
- let headPosition = getCellPositionFromMap(bottomRow, rightCol)
1008
- if (headPosition === null) return
934
+ let headPosition = getCellPositionFromMap(bottomRow, rightCol);
935
+ if (headPosition === null) return;
1009
936
 
1010
- // --- Handle edge case with merged cells ---
1011
- // If anchor and head point to the same cell, we need to find a different head
1012
- // This happens when selecting a single merged cell or when all coords point to one cell
1013
937
  if (headPosition === anchorPosition) {
1014
- let foundDifferentCell = false
938
+ // --- Handle edge case with merged cells ---
939
+ // If anchor and head point to the same cell, we need to find a different head
940
+ // This happens when selecting a single merged cell or when all coords point to one cell
941
+ let foundDifferentCell = false;
1015
942
 
1016
943
  // Search backwards from bottom-right to find a cell with a different position
1017
944
  for (let row = bottomRow; row >= topRow && !foundDifferentCell; row--) {
1018
945
  for (let col = rightCol; col >= leftCol && !foundDifferentCell; col--) {
1019
- const candidatePosition = getCellPositionFromMap(row, col)
1020
-
1021
- if (
1022
- candidatePosition !== null &&
1023
- candidatePosition !== anchorPosition
1024
- ) {
1025
- headPosition = candidatePosition
1026
- foundDifferentCell = true
946
+ const candidatePosition = getCellPositionFromMap(row, col);
947
+
948
+ if (candidatePosition !== null && candidatePosition !== anchorPosition) {
949
+ headPosition = candidatePosition;
950
+ foundDifferentCell = true;
1027
951
  }
1028
952
  }
1029
953
  }
1030
954
  }
1031
955
 
1032
956
  try {
1033
- const anchorRef = state.doc.resolve(anchorPosition)
1034
- const headRef = state.doc.resolve(headPosition)
957
+ const anchorRef = state.doc.resolve(anchorPosition);
958
+ const headRef = state.doc.resolve(headPosition);
1035
959
 
1036
- const cellSelection = new CellSelection(anchorRef, headRef)
1037
- const transaction = state.tr.setSelection(cellSelection)
960
+ const cellSelection = new CellSelection(anchorRef, headRef);
961
+ const transaction = state.tr.setSelection(cellSelection);
1038
962
 
1039
- return applySelectionWithMode(state, transaction, options)
963
+ return applySelectionWithMode(state, transaction, options);
1040
964
  } catch (error) {
1041
- console.error("Failed to create cell selection:", error)
1042
- return
965
+ console.error("Failed to create cell selection:", error);
966
+ return;
1043
967
  }
1044
968
  }
1045
969
 
@@ -1059,37 +983,37 @@ export function selectCellAt({
1059
983
  tablePos,
1060
984
  dispatch,
1061
985
  }: {
1062
- editor: Editor | null
1063
- row: number
1064
- col: number
1065
- tablePos?: number
1066
- dispatch?: (tr: Transaction) => void
986
+ editor: Editor | null;
987
+ row: number;
988
+ col: number;
989
+ tablePos?: number;
990
+ dispatch?: (tr: Transaction) => void;
1067
991
  }): boolean {
1068
- if (!editor) return false
992
+ if (!editor) return false;
1069
993
 
1070
- const { state, view } = editor
1071
- const found = getTable(editor, tablePos)
1072
- if (!found) return false
994
+ const { state, view } = editor;
995
+ const found = getTable(editor, tablePos);
996
+ if (!found) return false;
1073
997
 
1074
- // Bounds check
1075
998
  if (!isWithinBounds(row, col, found.map)) {
1076
- return false
999
+ // Bounds check
1000
+ return false;
1077
1001
  }
1078
1002
 
1079
- const relCellPos = found.map.positionAt(row, col, found.node)
1080
- const absCellPos = found.start + relCellPos
1003
+ const relCellPos = found.map.positionAt(row, col, found.node);
1004
+ const absCellPos = found.start + relCellPos;
1081
1005
 
1082
- const $abs = state.doc.resolve(absCellPos)
1083
- const $cell = cellAround($abs)
1084
- const cellPos = $cell ? $cell.pos : absCellPos
1006
+ const $abs = state.doc.resolve(absCellPos);
1007
+ const $cell = cellAround($abs);
1008
+ const cellPos = $cell ? $cell.pos : absCellPos;
1085
1009
 
1086
- const sel = CellSelection.create(state.doc, cellPos)
1010
+ const sel = CellSelection.create(state.doc, cellPos);
1087
1011
 
1088
- const doDispatch = dispatch ?? view?.dispatch
1089
- if (!doDispatch) return false
1012
+ const doDispatch = dispatch ?? view?.dispatch;
1013
+ if (!doDispatch) return false;
1090
1014
 
1091
- doDispatch(state.tr.setSelection(sel))
1092
- return true
1015
+ doDispatch(state.tr.setSelection(sel));
1016
+ return true;
1093
1017
  }
1094
1018
 
1095
1019
  /**
@@ -1106,38 +1030,33 @@ export function selectCellAt({
1106
1030
  * @param orientation "row" to select bottom-left, "column" to select top-right
1107
1031
  * @returns true if the selection was successful; false otherwise
1108
1032
  */
1109
- export function selectLastCell(
1110
- editor: Editor,
1111
- tableNode: Node,
1112
- tablePos: number,
1113
- orientation: Orientation
1114
- ) {
1115
- const map = TableMap.get(tableNode)
1116
- const isRow = orientation === "row"
1033
+ export function selectLastCell(editor: Editor, tableNode: Node, tablePos: number, orientation: Orientation) {
1034
+ const map = TableMap.get(tableNode);
1035
+ const isRow = orientation === "row";
1117
1036
 
1118
1037
  // For rows, select bottom-left cell; for columns, select top-right cell
1119
- const row = isRow ? map.height - 1 : 0
1120
- const col = isRow ? 0 : map.width - 1
1038
+ const row = isRow ? map.height - 1 : 0;
1039
+ const col = isRow ? 0 : map.width - 1;
1121
1040
 
1122
1041
  // Calculate the index in the table map
1123
- const index = row * map.width + col
1042
+ const index = row * map.width + col;
1124
1043
 
1125
1044
  // Get the actual cell position from the map (handles merged cells)
1126
- const cellPos = map.map[index]
1045
+ const cellPos = map.map[index];
1127
1046
  if (!cellPos && cellPos !== 0) {
1128
1047
  console.warn("selectLastCell: cell position not found in map", {
1129
1048
  index,
1130
1049
  row,
1131
1050
  col,
1132
1051
  map,
1133
- })
1134
- return false
1052
+ });
1053
+ return false;
1135
1054
  }
1136
1055
 
1137
1056
  // Find the row and column of the actual cell
1138
- const cellIndex = map.map.indexOf(cellPos)
1139
- const actualRow = cellIndex >= 0 ? Math.floor(cellIndex / map.width) : 0
1140
- const actualCol = cellIndex >= 0 ? cellIndex % map.width : 0
1057
+ const cellIndex = map.map.indexOf(cellPos);
1058
+ const actualRow = cellIndex >= 0 ? Math.floor(cellIndex / map.width) : 0;
1059
+ const actualCol = cellIndex >= 0 ? cellIndex % map.width : 0;
1141
1060
 
1142
1061
  return selectCellAt({
1143
1062
  editor,
@@ -1145,7 +1064,7 @@ export function selectLastCell(
1145
1064
  col: actualCol,
1146
1065
  tablePos,
1147
1066
  dispatch: editor.view.dispatch.bind(editor.view),
1148
- })
1067
+ });
1149
1068
  }
1150
1069
 
1151
1070
  /**
@@ -1170,26 +1089,26 @@ export function getIndexCoordinates({
1170
1089
  orientation,
1171
1090
  tablePos,
1172
1091
  }: {
1173
- editor: Editor | null
1174
- index: number
1175
- orientation?: Orientation
1176
- tablePos?: number
1092
+ editor: Editor | null;
1093
+ index: number;
1094
+ orientation?: Orientation;
1095
+ tablePos?: number;
1177
1096
  }): { row: number; col: number }[] | null {
1178
- if (!editor) return null
1097
+ if (!editor) return null;
1179
1098
 
1180
- const table = getTable(editor, tablePos)
1181
- if (!table) return null
1099
+ const table = getTable(editor, tablePos);
1100
+ if (!table) return null;
1182
1101
 
1183
- const { map } = table
1184
- const { width, height } = map
1102
+ const { map } = table;
1103
+ const { width, height } = map;
1185
1104
 
1186
- if (index < 0) return null
1187
- if (orientation === "row" && index >= height) return null
1188
- if (orientation === "column" && index >= width) return null
1105
+ if (index < 0) return null;
1106
+ if (orientation === "row" && index >= height) return null;
1107
+ if (orientation === "column" && index >= width) return null;
1189
1108
 
1190
1109
  return orientation === "row"
1191
1110
  ? Array.from({ length: map.width }, (_, col) => ({ row: index, col }))
1192
- : Array.from({ length: map.height }, (_, row) => ({ row, col: index }))
1111
+ : Array.from({ length: map.height }, (_, row) => ({ row, col: index }));
1193
1112
  }
1194
1113
 
1195
1114
  /**
@@ -1213,33 +1132,33 @@ export function getIndexCoordinates({
1213
1132
  export function getCellIndicesFromDOM(
1214
1133
  cell: HTMLTableCellElement,
1215
1134
  tableNode: Node | null,
1216
- editor: Editor
1135
+ editor: Editor,
1217
1136
  ): { rowIndex: number; colIndex: number } | null {
1218
- if (!tableNode) return null
1137
+ if (!tableNode) return null;
1219
1138
 
1220
1139
  try {
1221
- const cellPos = editor.view.posAtDOM(cell, 0)
1222
- const $cellPos = editor.view.state.doc.resolve(cellPos)
1140
+ const cellPos = editor.view.posAtDOM(cell, 0);
1141
+ const $cellPos = editor.view.state.doc.resolve(cellPos);
1223
1142
 
1224
1143
  for (let d = $cellPos.depth; d > 0; d--) {
1225
- const node = $cellPos.node(d)
1144
+ const node = $cellPos.node(d);
1226
1145
  if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
1227
- const tableMap = TableMap.get(tableNode)
1228
- const cellNodePos = $cellPos.before(d)
1229
- const tableStart = $cellPos.start(d - 2)
1230
- const cellOffset = cellNodePos - tableStart
1231
- const cellIndex = tableMap.map.indexOf(cellOffset)
1146
+ const tableMap = TableMap.get(tableNode);
1147
+ const cellNodePos = $cellPos.before(d);
1148
+ const tableStart = $cellPos.start(d - 2);
1149
+ const cellOffset = cellNodePos - tableStart;
1150
+ const cellIndex = tableMap.map.indexOf(cellOffset);
1232
1151
 
1233
1152
  return {
1234
1153
  rowIndex: Math.floor(cellIndex / tableMap.width),
1235
1154
  colIndex: cellIndex % tableMap.width,
1236
- }
1155
+ };
1237
1156
  }
1238
1157
  }
1239
1158
  } catch (error) {
1240
- console.warn("Could not get cell position:", error)
1159
+ console.warn("Could not get cell position:", error);
1241
1160
  }
1242
- return null
1161
+ return null;
1243
1162
  }
1244
1163
 
1245
1164
  /**
@@ -1258,32 +1177,26 @@ export function getCellIndicesFromDOM(
1258
1177
  * @param editor The Tiptap editor instance
1259
1178
  * @returns An object with { node: tableNode, pos: tablePos } or null if not found
1260
1179
  */
1261
- export function getTableFromDOM(
1262
- tableElement: HTMLElement,
1263
- editor: Editor
1264
- ): { node: Node; pos: number } | null {
1180
+ export function getTableFromDOM(tableElement: HTMLElement, editor: Editor): { node: Node; pos: number } | null {
1265
1181
  try {
1266
- const pos = editor.view.posAtDOM(tableElement, 0)
1267
- const $pos = editor.view.state.doc.resolve(pos)
1182
+ const pos = editor.view.posAtDOM(tableElement, 0);
1183
+ const $pos = editor.view.state.doc.resolve(pos);
1268
1184
 
1269
1185
  for (let d = $pos.depth; d >= 0; d--) {
1270
- const node = $pos.node(d)
1186
+ const node = $pos.node(d);
1271
1187
  if (isTableNode(node)) {
1272
- return { node, pos: d === 0 ? 0 : $pos.before(d) }
1188
+ return { node, pos: d === 0 ? 0 : $pos.before(d) };
1273
1189
  }
1274
1190
  }
1275
1191
  } catch (error) {
1276
- console.warn("Could not get table from DOM:", error)
1192
+ console.warn("Could not get table from DOM:", error);
1277
1193
  }
1278
- return null
1194
+ return null;
1279
1195
  }
1280
1196
 
1281
1197
  /**
1282
1198
  * Checks if a node is a table node
1283
1199
  */
1284
1200
  export function isTableNode(node: Node | null | undefined): node is Node {
1285
- return (
1286
- !!node &&
1287
- (node.type.name === "table" || node.type.spec.tableRole === "table")
1288
- )
1201
+ return !!node && (node.type.name === "table" || node.type.spec.tableRole === "table");
1289
1202
  }