smartrte-react 0.2.3 → 0.2.5

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,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
- import { MediaManager } from "./MediaManager";
3
+ import { MediaManager } from "./MediaManager.js";
4
4
  import * as pdfjsLib from 'pdfjs-dist';
5
5
  import mammoth from 'mammoth';
6
6
  import JSZip from 'jszip';
7
- import { ensureStyleSheet } from '../theme';
7
+ import { ensureStyleSheet } from '../theme.js';
8
8
  // Initialize PDF.js worker
9
9
  if (typeof window !== 'undefined') {
10
10
  pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString();
@@ -20,6 +20,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
20
20
  ], defaultFont, preserveFontFamily = false, preserveColors = false, preserveDocxStyles = true, theme = "light", className, }) {
21
21
  ensureStyleSheet();
22
22
  const editableRef = useRef(null);
23
+ const editorScrollRef = useRef(null);
23
24
  const lastEmittedRef = useRef("");
24
25
  const isComposingRef = useRef(false);
25
26
  const fileInputRef = useRef(null);
@@ -53,6 +54,18 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
53
54
  const savedRangeRef = useRef(null);
54
55
  const [currentFontSize, setCurrentFontSize] = useState("");
55
56
  const [currentFont, setCurrentFont] = useState("");
57
+ const [activeState, setActiveState] = useState({
58
+ bold: false,
59
+ italic: false,
60
+ underline: false,
61
+ strikeThrough: false,
62
+ subscript: false,
63
+ superscript: false,
64
+ unorderedList: false,
65
+ orderedList: false,
66
+ blockquote: false,
67
+ codeBlock: false,
68
+ });
56
69
  useEffect(() => {
57
70
  const el = editableRef.current;
58
71
  if (!el)
@@ -77,6 +90,35 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
77
90
  el.removeEventListener("contextmenu", onCtx, { capture: true });
78
91
  };
79
92
  }, [value]);
93
+ const updateActiveState = () => {
94
+ const editor = editableRef.current;
95
+ if (!editor)
96
+ return;
97
+ try {
98
+ const sel = window.getSelection();
99
+ const range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
100
+ const inEditor = range && editor.contains(range.commonAncestorContainer);
101
+ if (!inEditor)
102
+ return;
103
+ let node = range.commonAncestorContainer;
104
+ if (node.nodeType === Node.TEXT_NODE)
105
+ node = node.parentNode;
106
+ const element = node instanceof HTMLElement ? node : null;
107
+ setActiveState({
108
+ bold: document.queryCommandState("bold"),
109
+ italic: document.queryCommandState("italic"),
110
+ underline: document.queryCommandState("underline"),
111
+ strikeThrough: document.queryCommandState("strikeThrough"),
112
+ subscript: document.queryCommandState("subscript"),
113
+ superscript: document.queryCommandState("superscript"),
114
+ unorderedList: Boolean(element?.closest("ul")),
115
+ orderedList: Boolean(element?.closest("ol")),
116
+ blockquote: Boolean(element?.closest("blockquote")),
117
+ codeBlock: Boolean(element?.closest("pre")),
118
+ });
119
+ }
120
+ catch { }
121
+ };
80
122
  // Save selection whenever it changes
81
123
  useEffect(() => {
82
124
  const saveSelection = () => {
@@ -86,6 +128,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
86
128
  const editor = editableRef.current;
87
129
  if (editor && editor.contains(range.commonAncestorContainer)) {
88
130
  savedRangeRef.current = range.cloneRange();
131
+ updateActiveState();
89
132
  }
90
133
  }
91
134
  };
@@ -96,8 +139,27 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
96
139
  }, []);
97
140
  const exec = (command, valueArg) => {
98
141
  try {
99
- document.execCommand(command, false, valueArg);
100
- emitChange();
142
+ if (!restoreSavedSelection()) {
143
+ safeSelectRange(getSelectionRangeInEditor());
144
+ }
145
+ if (command === "insertUnorderedList") {
146
+ toggleList("ul");
147
+ return;
148
+ }
149
+ if (command === "insertOrderedList") {
150
+ toggleList("ol");
151
+ return;
152
+ }
153
+ const beforeHtml = editableRef.current?.innerHTML || "";
154
+ const ok = document.execCommand(command, false, valueArg);
155
+ const afterHtml = editableRef.current?.innerHTML || "";
156
+ const needsFallback = !ok ||
157
+ (command === "formatBlock" && beforeHtml === afterHtml);
158
+ if (needsFallback) {
159
+ if (command === "formatBlock" && valueArg)
160
+ applyFormatBlockFallback(valueArg);
161
+ }
162
+ handleInput();
101
163
  }
102
164
  catch { }
103
165
  };
@@ -114,6 +176,29 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
114
176
  onChange(html);
115
177
  }
116
178
  };
179
+ const restoreSavedSelection = () => {
180
+ const editor = editableRef.current;
181
+ if (!editor)
182
+ return false;
183
+ const saved = savedRangeRef.current;
184
+ if (saved && editor.contains(saved.commonAncestorContainer)) {
185
+ editor.focus({ preventScroll: true });
186
+ safeSelectRange(saved.cloneRange());
187
+ return true;
188
+ }
189
+ editor.focus({ preventScroll: true });
190
+ return false;
191
+ };
192
+ const preserveEditorSelection = () => {
193
+ const editor = editableRef.current;
194
+ const sel = window.getSelection();
195
+ if (!editor || !sel || sel.rangeCount === 0)
196
+ return;
197
+ const range = sel.getRangeAt(0);
198
+ if (editor.contains(range.commonAncestorContainer)) {
199
+ savedRangeRef.current = range.cloneRange();
200
+ }
201
+ };
117
202
  const insertLink = () => {
118
203
  const url = window.prompt("Enter URL", "https://");
119
204
  if (!url)
@@ -124,7 +209,6 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
124
209
  const editor = editableRef.current;
125
210
  if (!editor)
126
211
  return null;
127
- editor.focus();
128
212
  const sel = window.getSelection();
129
213
  if (sel && sel.rangeCount > 0) {
130
214
  const range = sel.getRangeAt(0);
@@ -139,6 +223,307 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
139
223
  range.collapse(false);
140
224
  return range;
141
225
  };
226
+ const getCurrentBlock = () => {
227
+ const editor = editableRef.current;
228
+ const range = getSelectionRangeInEditor();
229
+ if (!editor || !range)
230
+ return null;
231
+ let node = range.commonAncestorContainer;
232
+ if (node.nodeType === Node.TEXT_NODE)
233
+ node = node.parentNode;
234
+ const element = node instanceof HTMLElement ? node : null;
235
+ const block = element?.closest("p,h1,h2,h3,h4,h5,h6,li,blockquote,pre,div");
236
+ if (!block || block === editor || !editor.contains(block))
237
+ return null;
238
+ return block;
239
+ };
240
+ const blockSelector = "p,h1,h2,h3,h4,h5,h6,li,blockquote,pre,div";
241
+ const getSelectedBlocks = (range) => {
242
+ const editor = editableRef.current;
243
+ if (!editor)
244
+ return [];
245
+ const blocks = Array.from(editor.querySelectorAll(blockSelector))
246
+ .filter((block) => {
247
+ if (block === editor)
248
+ return false;
249
+ const parentBlock = block.parentElement?.closest(blockSelector);
250
+ if (parentBlock && parentBlock !== editor && editor.contains(parentBlock))
251
+ return false;
252
+ try {
253
+ return range.intersectsNode(block);
254
+ }
255
+ catch {
256
+ return false;
257
+ }
258
+ });
259
+ if (blocks.length > 0)
260
+ return blocks;
261
+ const current = getCurrentBlock();
262
+ return current ? [current] : [];
263
+ };
264
+ const getSelectedListItems = (blocks) => {
265
+ const seen = new Set();
266
+ const items = [];
267
+ blocks.forEach((block) => {
268
+ const li = block.tagName.toLowerCase() === "li"
269
+ ? block
270
+ : block.closest("li");
271
+ if (li && editableRef.current?.contains(li) && !seen.has(li)) {
272
+ seen.add(li);
273
+ items.push(li);
274
+ }
275
+ });
276
+ return items;
277
+ };
278
+ const copyCellOrBlockStyles = (from, to) => {
279
+ to.innerHTML = from.innerHTML || "<br>";
280
+ const style = from.getAttribute("style");
281
+ if (style)
282
+ to.setAttribute("style", style);
283
+ };
284
+ const applyHeaderCellStyle = (cell) => {
285
+ cell.style.fontWeight = "700";
286
+ cell.style.background = "#f3f4f6";
287
+ cell.style.textAlign = cell.style.textAlign || "left";
288
+ };
289
+ const clearHeaderCellStyle = (cell) => {
290
+ if (cell.style.fontWeight === "700" || cell.style.fontWeight === "bold")
291
+ cell.style.fontWeight = "";
292
+ if (cell.style.background === "var(--srte-surface-subtle)" || cell.style.background === "rgb(243, 244, 246)" || cell.style.background === "#f3f4f6")
293
+ cell.style.background = "";
294
+ };
295
+ const replaceTableCellTag = (cell, tag) => {
296
+ const replacement = document.createElement(tag);
297
+ replacement.innerHTML = cell.innerHTML || "&nbsp;";
298
+ replacement.colSpan = cell.colSpan;
299
+ replacement.rowSpan = cell.rowSpan;
300
+ const style = cell.getAttribute("style");
301
+ if (style)
302
+ replacement.setAttribute("style", style);
303
+ if (cell.__rtePrevBg != null) {
304
+ replacement.style.background = cell.__rtePrevBg || "";
305
+ delete cell.__rtePrevBg;
306
+ }
307
+ if (replacement.style.background === "var(--srte-accent-bg)" ||
308
+ replacement.style.background.includes("59, 130, 246") ||
309
+ replacement.style.background.includes("59, 158, 255")) {
310
+ replacement.style.background = "";
311
+ }
312
+ replacement.style.outline = "";
313
+ replacement.style.outlineOffset = "";
314
+ replacement.style.border = replacement.style.border || "1px solid #d1d5db";
315
+ replacement.style.padding = replacement.style.padding || "6px";
316
+ replacement.style.minWidth = replacement.style.minWidth || "60px";
317
+ if (tag === "th")
318
+ applyHeaderCellStyle(replacement);
319
+ else
320
+ clearHeaderCellStyle(replacement);
321
+ cell.parentElement?.replaceChild(replacement, cell);
322
+ return replacement;
323
+ };
324
+ const applyFormatBlockFallback = (blockName) => {
325
+ const editor = editableRef.current;
326
+ const block = getCurrentBlock();
327
+ const tag = blockName.replace(/[<>]/g, "").toLowerCase() || "p";
328
+ if (!editor || !block || block === editor || !/^(p|h1|h2|h3|h4|h5|h6|pre|blockquote)$/.test(tag))
329
+ return;
330
+ const replacement = document.createElement(tag);
331
+ copyCellOrBlockStyles(block, replacement);
332
+ block.parentElement?.replaceChild(replacement, block);
333
+ const range = document.createRange();
334
+ range.selectNodeContents(replacement);
335
+ range.collapse(false);
336
+ safeSelectRange(range);
337
+ };
338
+ const cloneListShell = (list, tagName) => {
339
+ const clone = document.createElement(tagName || list.tagName.toLowerCase());
340
+ Array.from(list.attributes).forEach((attr) => clone.setAttribute(attr.name, attr.value));
341
+ return clone;
342
+ };
343
+ const focusElementEnd = (element) => {
344
+ const range = document.createRange();
345
+ range.selectNodeContents(element);
346
+ range.collapse(false);
347
+ safeSelectRange(range);
348
+ savedRangeRef.current = range.cloneRange();
349
+ };
350
+ const insertEmptyListAtSelection = (listTag) => {
351
+ const editor = editableRef.current;
352
+ const range = getSelectionRangeInEditor();
353
+ if (!editor || !range)
354
+ return;
355
+ const list = document.createElement(listTag);
356
+ const li = document.createElement("li");
357
+ li.innerHTML = "<br>";
358
+ list.appendChild(li);
359
+ range.deleteContents();
360
+ range.insertNode(list);
361
+ focusElementEnd(li);
362
+ };
363
+ const unwrapListItem = (li, list) => {
364
+ const parent = list.parentElement;
365
+ if (!parent)
366
+ return;
367
+ const paragraph = document.createElement("p");
368
+ paragraph.innerHTML = li.innerHTML || "<br>";
369
+ const beforeList = cloneListShell(list);
370
+ const afterList = cloneListShell(list);
371
+ while (list.firstChild && list.firstChild !== li) {
372
+ beforeList.appendChild(list.firstChild);
373
+ }
374
+ while (li.nextSibling) {
375
+ afterList.appendChild(li.nextSibling);
376
+ }
377
+ if (beforeList.childNodes.length)
378
+ parent.insertBefore(beforeList, list);
379
+ parent.insertBefore(paragraph, list);
380
+ if (afterList.childNodes.length)
381
+ parent.insertBefore(afterList, list);
382
+ li.remove();
383
+ if (!list.querySelector("li"))
384
+ list.remove();
385
+ focusElementEnd(paragraph);
386
+ };
387
+ const convertListTag = (list, listTag) => {
388
+ const replacement = cloneListShell(list, listTag);
389
+ replacement.innerHTML = list.innerHTML;
390
+ list.parentElement?.replaceChild(replacement, list);
391
+ const li = replacement.querySelector("li");
392
+ focusElementEnd(li || replacement);
393
+ };
394
+ const getOrCreateNestedList = (li, listTag) => {
395
+ let nested = Array.from(li.children).find((child) => {
396
+ const tag = child.tagName.toLowerCase();
397
+ return tag === "ul" || tag === "ol";
398
+ });
399
+ if (nested && nested.tagName.toLowerCase() !== listTag) {
400
+ const replacement = cloneListShell(nested, listTag);
401
+ replacement.innerHTML = nested.innerHTML;
402
+ nested.parentElement?.replaceChild(replacement, nested);
403
+ nested = replacement;
404
+ }
405
+ if (!nested) {
406
+ nested = document.createElement(listTag);
407
+ li.appendChild(nested);
408
+ }
409
+ return nested;
410
+ };
411
+ const nestSelectedListItems = (items, listTag) => {
412
+ if (items.length === 0)
413
+ return false;
414
+ let changed = false;
415
+ let lastTargetList = null;
416
+ items.forEach((li) => {
417
+ const previous = li.previousElementSibling;
418
+ if (!previous || previous.tagName.toLowerCase() !== "li")
419
+ return;
420
+ const targetList = getOrCreateNestedList(previous, listTag);
421
+ targetList.appendChild(li);
422
+ lastTargetList = targetList;
423
+ changed = true;
424
+ });
425
+ if (changed) {
426
+ const lastItem = lastTargetList?.lastElementChild;
427
+ if (lastItem)
428
+ focusElementEnd(lastItem);
429
+ }
430
+ return changed;
431
+ };
432
+ const convertSelectedBlocksToList = (blocks, listTag) => {
433
+ const editor = editableRef.current;
434
+ if (!editor || blocks.length === 0)
435
+ return false;
436
+ const selected = blocks.filter((block) => {
437
+ if (!editor.contains(block))
438
+ return false;
439
+ if (block.tagName.toLowerCase() === "li")
440
+ return false;
441
+ if (block.closest("ul,ol"))
442
+ return false;
443
+ return block.parentElement;
444
+ });
445
+ if (selected.length === 0)
446
+ return false;
447
+ const groups = new Map();
448
+ selected.forEach((block) => {
449
+ const parent = block.parentElement;
450
+ if (!parent)
451
+ return;
452
+ const group = groups.get(parent) || [];
453
+ group.push(block);
454
+ groups.set(parent, group);
455
+ });
456
+ let lastLi = null;
457
+ groups.forEach((group, parent) => {
458
+ group.sort((a, b) => {
459
+ const position = a.compareDocumentPosition(b);
460
+ return position & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
461
+ });
462
+ const list = document.createElement(listTag);
463
+ parent.insertBefore(list, group[0]);
464
+ group.forEach((block) => {
465
+ const li = document.createElement("li");
466
+ li.innerHTML = block.innerHTML || "<br>";
467
+ list.appendChild(li);
468
+ block.remove();
469
+ lastLi = li;
470
+ });
471
+ });
472
+ if (lastLi)
473
+ focusElementEnd(lastLi);
474
+ return true;
475
+ };
476
+ const toggleList = (listTag) => {
477
+ const editor = editableRef.current;
478
+ if (!editor)
479
+ return;
480
+ if (!restoreSavedSelection())
481
+ safeSelectRange(getSelectionRangeInEditor());
482
+ const range = getSelectionRangeInEditor();
483
+ if (range && !range.collapsed) {
484
+ const blocks = getSelectedBlocks(range);
485
+ const selectedListItems = getSelectedListItems(blocks);
486
+ const convertedBlocks = convertSelectedBlocksToList(blocks, listTag);
487
+ const nestedItems = nestSelectedListItems(selectedListItems, listTag);
488
+ if (convertedBlocks || nestedItems) {
489
+ handleInput();
490
+ requestAnimationFrame(updateActiveState);
491
+ return;
492
+ }
493
+ }
494
+ const block = getCurrentBlock();
495
+ if (!block) {
496
+ insertEmptyListAtSelection(listTag);
497
+ handleInput();
498
+ requestAnimationFrame(updateActiveState);
499
+ return;
500
+ }
501
+ const currentList = block.closest("ul,ol");
502
+ if (currentList && editor.contains(currentList)) {
503
+ if (currentList.tagName.toLowerCase() === listTag) {
504
+ const li = block.closest("li");
505
+ if (li)
506
+ unwrapListItem(li, currentList);
507
+ }
508
+ else {
509
+ convertListTag(currentList, listTag);
510
+ }
511
+ handleInput();
512
+ requestAnimationFrame(updateActiveState);
513
+ return;
514
+ }
515
+ const list = document.createElement(listTag);
516
+ const li = document.createElement("li");
517
+ li.innerHTML = block.innerHTML || "<br>";
518
+ list.appendChild(li);
519
+ block.parentElement?.replaceChild(list, block);
520
+ focusElementEnd(li);
521
+ handleInput();
522
+ requestAnimationFrame(updateActiveState);
523
+ };
524
+ const toggleListFallback = (listTag) => {
525
+ toggleList(listTag);
526
+ };
142
527
  const insertTextAtSelection = (text) => {
143
528
  if (!text)
144
529
  return;
@@ -310,15 +695,17 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
310
695
  };
311
696
  const scheduleImageOverlay = () => {
312
697
  const img = selectedImage;
698
+ const scroller = editorScrollRef.current;
313
699
  if (!img) {
314
700
  setImageOverlay(null);
315
701
  return;
316
702
  }
317
703
  try {
318
704
  const rect = img.getBoundingClientRect();
705
+ const hostRect = scroller?.getBoundingClientRect();
319
706
  setImageOverlay({
320
- left: rect.left,
321
- top: rect.top,
707
+ left: hostRect && scroller ? rect.left - hostRect.left + scroller.scrollLeft : rect.left,
708
+ top: hostRect && scroller ? rect.top - hostRect.top + scroller.scrollTop : rect.top,
322
709
  width: rect.width,
323
710
  height: rect.height,
324
711
  });
@@ -617,6 +1004,16 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
617
1004
  if (!media)
618
1005
  return;
619
1006
  const list = Array.from(files).filter((f) => f.type.startsWith("image/"));
1007
+ if (mediaManager) {
1008
+ try {
1009
+ const uploaded = await mediaManager.upload(list);
1010
+ uploaded.forEach((item) => insertImageAtSelection(item));
1011
+ return;
1012
+ }
1013
+ catch (error) {
1014
+ console.error("Image upload failed, inserting local image data instead:", error);
1015
+ }
1016
+ }
620
1017
  for (const f of list) {
621
1018
  await new Promise((resolve) => {
622
1019
  const reader = new FileReader();
@@ -866,7 +1263,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
866
1263
  cellContents[colIdx] += styledTxt;
867
1264
  }
868
1265
  cellContents.forEach(content => {
869
- rowHtml += `<td style="border:1px solid var(--srte-border);padding:8px;vertical-align:top;">${content || '&nbsp;'}</td>`;
1266
+ rowHtml += `<td style="border:1px solid #d1d5db;padding:8px;vertical-align:top;">${content || '&nbsp;'}</td>`;
870
1267
  });
871
1268
  rowHtml += '</tr>';
872
1269
  tableHtml += rowHtml;
@@ -1525,13 +1922,12 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1525
1922
  };
1526
1923
  const exportPdf = () => {
1527
1924
  const html = editableRef.current?.innerHTML || "";
1528
- const printWindow = window.open("", "_blank", "noopener,noreferrer,width=900,height=700");
1925
+ const printWindow = window.open("", "_blank", "width=900,height=700");
1529
1926
  if (!printWindow)
1530
1927
  return;
1531
- printWindow.document.write(`<!doctype html><html><head><title>Export PDF</title><style>body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;line-height:1.6;padding:32px;color:#111}table{border-collapse:collapse;width:100%;margin:12px 0}td,th{border:1px solid #d1d5db;padding:8px;vertical-align:top}img{max-width:100%;height:auto}blockquote{border-left:4px solid #d1d5db;padding-left:12px;color:#374151}pre,code{background:#f3f4f6}pre{padding:12px;white-space:pre-wrap}@media print{body{padding:0}}</style></head><body>${html}</body></html>`);
1928
+ printWindow.document.open();
1929
+ printWindow.document.write(`<!doctype html><html><head><title>Export PDF</title><style>@page{margin:18mm}html,body{background:#fff}body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;line-height:1.6;padding:32px;color:#111}table{border-collapse:collapse;width:100%;margin:12px 0;break-inside:auto}tr,img,blockquote,pre{break-inside:avoid}td,th{border:1px solid #d1d5db;padding:8px;vertical-align:top}img{max-width:100%;height:auto}blockquote{border-left:4px solid #d1d5db;padding-left:12px;color:#374151}pre,code{background:#f3f4f6}pre{padding:12px;white-space:pre-wrap}@media print{body{padding:0}}</style></head><body>${html || "<p></p>"}<script>window.addEventListener("load",function(){setTimeout(function(){window.focus();window.print();},150);});</script></body></html>`);
1532
1930
  printWindow.document.close();
1533
- printWindow.focus();
1534
- setTimeout(() => printWindow.print(), 250);
1535
1931
  };
1536
1932
  const fixNegativeMargins = (root) => {
1537
1933
  try {
@@ -1753,7 +2149,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1753
2149
  html += "<tr>";
1754
2150
  for (let c = 0; c < safeCols; c++) {
1755
2151
  html +=
1756
- '<td style="border:1px solid var(--srte-border);padding:6px;min-width:60px;">&nbsp;</td>';
2152
+ '<td style="border:1px solid #d1d5db;padding:6px;min-width:60px;">&nbsp;</td>';
1757
2153
  }
1758
2154
  html += "</tr>";
1759
2155
  }
@@ -1836,16 +2232,56 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1836
2232
  }
1837
2233
  catch { }
1838
2234
  };
2235
+ const getTableGrid = (tbody) => {
2236
+ const rows = Array.from(tbody.querySelectorAll("tr"));
2237
+ const grid = [];
2238
+ rows.forEach((row, rIdx) => {
2239
+ grid[rIdx] = grid[rIdx] || [];
2240
+ let cIdx = 0;
2241
+ const cells = cellsOfRow(row);
2242
+ cells.forEach((cell) => {
2243
+ while (grid[rIdx][cIdx])
2244
+ cIdx += 1;
2245
+ const rowSpan = Math.max(1, cell.rowSpan || 1);
2246
+ const colSpan = Math.max(1, cell.colSpan || 1);
2247
+ for (let r = rIdx; r < rIdx + rowSpan; r += 1) {
2248
+ grid[r] = grid[r] || [];
2249
+ for (let c = cIdx; c < cIdx + colSpan; c += 1) {
2250
+ grid[r][c] = cell;
2251
+ }
2252
+ }
2253
+ cIdx += colSpan;
2254
+ });
2255
+ });
2256
+ return { rows, grid };
2257
+ };
2258
+ const getCellsInGridRect = (tbody, sr, sc, er, ec) => {
2259
+ const { grid } = getTableGrid(tbody);
2260
+ const seen = new Set();
2261
+ const cells = [];
2262
+ for (let r = sr; r <= er; r += 1) {
2263
+ for (let c = sc; c <= ec; c += 1) {
2264
+ const cell = grid[r]?.[c];
2265
+ if (cell && !seen.has(cell)) {
2266
+ seen.add(cell);
2267
+ cells.push(cell);
2268
+ }
2269
+ }
2270
+ }
2271
+ return cells;
2272
+ };
1839
2273
  const getCellPosition = (cell) => {
1840
2274
  const row = cell.parentElement;
1841
2275
  const tbody = row?.parentElement;
1842
2276
  const table = tbody?.parentElement;
1843
2277
  if (!row || !tbody || !table)
1844
2278
  return null;
1845
- const rows = Array.from(tbody.querySelectorAll("tr"));
2279
+ const { rows, grid } = getTableGrid(tbody);
1846
2280
  const rIdx = rows.indexOf(row);
1847
- const cells = Array.from(row.children).filter((c) => ["TD", "TH"].includes(c.tagName));
1848
- const cIdx = cells.indexOf(cell);
2281
+ let cIdx = -1;
2282
+ if (rIdx >= 0) {
2283
+ cIdx = (grid[rIdx] || []).findIndex((candidate) => candidate === cell);
2284
+ }
1849
2285
  return { row, tbody, table, rIdx, cIdx };
1850
2286
  };
1851
2287
  const cellsOfRow = (row) => Array.from(row.children).filter((c) => ["TD", "TH"].includes(c.tagName));
@@ -1854,42 +2290,27 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1854
2290
  if (!sel)
1855
2291
  return;
1856
2292
  const { tbody, sr, sc, er, ec } = sel;
1857
- const rows = Array.from(tbody.querySelectorAll("tr"));
1858
- for (let r = sr; r <= er; r++) {
1859
- const row = rows[r];
1860
- const cells = cellsOfRow(row);
1861
- for (let c = sc; c <= ec; c++) {
1862
- const cell = cells[c];
1863
- if (!cell)
1864
- continue;
1865
- if (cell.__rtePrevBg != null) {
1866
- cell.style.background = cell.__rtePrevBg;
1867
- delete cell.__rtePrevBg;
1868
- }
1869
- cell.style.outline = "";
1870
- cell.style.outlineOffset = "";
2293
+ const cells = getCellsInGridRect(tbody, sr, sc, er, ec);
2294
+ cells.forEach((cell) => {
2295
+ if (cell.__rtePrevBg != null) {
2296
+ cell.style.background = cell.__rtePrevBg;
2297
+ delete cell.__rtePrevBg;
1871
2298
  }
1872
- }
2299
+ cell.style.outline = "";
2300
+ cell.style.outlineOffset = "";
2301
+ });
1873
2302
  selectionRef.current = null;
1874
2303
  };
1875
2304
  const updateSelectionDecor = (tbody, sr, sc, er, ec) => {
1876
2305
  clearSelectionDecor();
1877
2306
  selectionRef.current = { tbody, sr, sc, er, ec };
1878
- const rows = Array.from(tbody.querySelectorAll("tr"));
1879
- for (let r = sr; r <= er; r++) {
1880
- const row = rows[r];
1881
- const cells = cellsOfRow(row);
1882
- for (let c = sc; c <= ec; c++) {
1883
- const cell = cells[c];
1884
- if (!cell)
1885
- continue;
1886
- cell.__rtePrevBg =
1887
- cell.style.background || "";
1888
- cell.style.background = "var(--srte-accent-bg)";
1889
- cell.style.outline = "2px solid var(--srte-accent)";
1890
- cell.style.outlineOffset = "-2px";
1891
- }
1892
- }
2307
+ const cells = getCellsInGridRect(tbody, sr, sc, er, ec);
2308
+ cells.forEach((cell) => {
2309
+ cell.__rtePrevBg = cell.style.background || "";
2310
+ cell.style.background = "var(--srte-accent-bg)";
2311
+ cell.style.outline = "2px solid var(--srte-accent)";
2312
+ cell.style.outlineOffset = "-2px";
2313
+ });
1893
2314
  };
1894
2315
  const canMergeSelection = () => {
1895
2316
  const sel = selectionRef.current;
@@ -1897,32 +2318,37 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1897
2318
  return false;
1898
2319
  return sel.sr !== sel.er || sel.sc !== sel.ec;
1899
2320
  };
2321
+ const canMergeFromCell = (cell) => Boolean(cell && canMergeSelection() && isCellInsideSelection(cell));
2322
+ const canSplitCell = (cell) => Boolean(cell && (Math.max(1, cell.rowSpan || 1) > 1 || Math.max(1, cell.colSpan || 1) > 1));
2323
+ const isCellInsideSelection = (cell) => {
2324
+ const sel = selectionRef.current;
2325
+ if (!sel)
2326
+ return false;
2327
+ const pos = getCellPosition(cell);
2328
+ if (!pos || pos.tbody !== sel.tbody)
2329
+ return false;
2330
+ return pos.rIdx >= sel.sr && pos.rIdx <= sel.er && pos.cIdx >= sel.sc && pos.cIdx <= sel.ec;
2331
+ };
2332
+ const shouldUseTableSelection = (fallbackCell) => Boolean(selectionRef.current && fallbackCell && isCellInsideSelection(fallbackCell));
1900
2333
  const mergeSelection = () => {
1901
2334
  const sel = selectionRef.current;
1902
2335
  if (!sel)
1903
2336
  return;
1904
2337
  const { tbody, sr, sc, er, ec } = sel;
1905
- const rows = Array.from(tbody.querySelectorAll("tr"));
1906
- const anchorRow = rows[sr];
1907
- const anchor = cellsOfRow(anchorRow)[sc];
2338
+ const { grid } = getTableGrid(tbody);
2339
+ const anchor = grid[sr]?.[sc];
1908
2340
  if (!anchor)
1909
2341
  return;
1910
2342
  // Collect content and remove other cells
1911
2343
  const contents = [];
1912
- for (let r = sr; r <= er; r++) {
1913
- const row = rows[r];
1914
- const cells = cellsOfRow(row);
1915
- for (let c = sc; c <= ec; c++) {
1916
- const cell = cells[c];
1917
- if (!cell)
1918
- continue;
1919
- if (r === sr && c === sc)
1920
- continue;
1921
- const html = cell.innerHTML.trim();
1922
- if (html)
1923
- contents.push(html);
1924
- }
1925
- }
2344
+ const cellsToMerge = getCellsInGridRect(tbody, sr, sc, er, ec);
2345
+ cellsToMerge.forEach((cell) => {
2346
+ if (cell === anchor)
2347
+ return;
2348
+ const html = cell.innerHTML.trim();
2349
+ if (html)
2350
+ contents.push(html);
2351
+ });
1926
2352
  if (contents.length) {
1927
2353
  anchor.innerHTML = (anchor.innerHTML || "") + " " + contents.join(" ");
1928
2354
  }
@@ -1930,18 +2356,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1930
2356
  anchor.colSpan = ec - sc + 1;
1931
2357
  anchor.rowSpan = er - sr + 1;
1932
2358
  // Remove other cells
1933
- for (let r = sr; r <= er; r++) {
1934
- const row = rows[r];
1935
- const cells = cellsOfRow(row);
1936
- for (let c = ec; c >= sc; c--) {
1937
- const cell = cells[c];
1938
- if (!cell)
1939
- continue;
1940
- if (r === sr && c === sc)
1941
- continue;
2359
+ cellsToMerge.forEach((cell) => {
2360
+ if (cell !== anchor)
1942
2361
  cell.remove();
1943
- }
1944
- }
2362
+ });
1945
2363
  moveCaretToCell(anchor, false);
1946
2364
  clearSelectionDecor();
1947
2365
  handleInput();
@@ -1955,7 +2373,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1955
2373
  const numCols = Array.from(row.children).filter((c) => ["TD", "TH"].includes(c.tagName)).length;
1956
2374
  for (let i = 0; i < numCols; i++) {
1957
2375
  const td = document.createElement("td");
1958
- td.style.border = "1px solid var(--srte-border)";
2376
+ td.style.border = "1px solid #d1d5db";
1959
2377
  td.style.padding = "6px";
1960
2378
  td.style.minWidth = "60px";
1961
2379
  td.innerHTML = "&nbsp;";
@@ -1985,7 +2403,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
1985
2403
  for (const r of rows) {
1986
2404
  const cells = Array.from(r.children).filter((c) => ["TD", "TH"].includes(c.tagName));
1987
2405
  const td = document.createElement("td");
1988
- td.style.border = "1px solid var(--srte-border)";
2406
+ td.style.border = "1px solid #d1d5db";
1989
2407
  td.style.padding = "6px";
1990
2408
  td.style.minWidth = "60px";
1991
2409
  td.innerHTML = "&nbsp;";
@@ -2014,14 +2432,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2014
2432
  table.parentElement?.removeChild(table);
2015
2433
  };
2016
2434
  const toggleHeaderCell = (cell) => {
2435
+ clearSelectionDecor();
2017
2436
  const isTh = cell.tagName === "TH";
2018
- const replacement = document.createElement(isTh ? "td" : "th");
2019
- replacement.innerHTML = cell.innerHTML || "&nbsp;";
2020
- replacement.style.border =
2021
- cell.style.border || "1px solid var(--srte-border)";
2022
- replacement.style.padding = cell.style.padding || "6px";
2023
- replacement.style.minWidth = cell.style.minWidth || "60px";
2024
- cell.parentElement?.replaceChild(replacement, cell);
2437
+ replaceTableCellTag(cell, isTh ? "td" : "th");
2438
+ handleInput();
2025
2439
  };
2026
2440
  const deleteTable = (cell) => {
2027
2441
  const pos = getCellPosition(cell);
@@ -2046,7 +2460,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2046
2460
  const currentRow = Array.from(tbody.querySelectorAll("tr"))[rIdx];
2047
2461
  for (let j = 1; j < cs; j++) {
2048
2462
  const td = document.createElement("td");
2049
- td.style.border = "1px solid var(--srte-border)";
2463
+ td.style.border = "1px solid #d1d5db";
2050
2464
  td.style.padding = "6px";
2051
2465
  td.style.minWidth = "60px";
2052
2466
  td.innerHTML = "&nbsp;";
@@ -2059,7 +2473,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2059
2473
  const row = Array.from(tbody.querySelectorAll("tr"))[rIdx + i];
2060
2474
  for (let j = 0; j < cs; j++) {
2061
2475
  const td = document.createElement("td");
2062
- td.style.border = "1px solid var(--srte-border)";
2476
+ td.style.border = "1px solid #d1d5db";
2063
2477
  td.style.padding = "6px";
2064
2478
  td.style.minWidth = "60px";
2065
2479
  td.innerHTML = "&nbsp;";
@@ -2074,29 +2488,17 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2074
2488
  const pos = getCellPosition(cell);
2075
2489
  if (!pos)
2076
2490
  return;
2077
- const { tbody } = pos;
2078
- const firstRow = tbody.querySelector("tr");
2079
- if (!firstRow)
2080
- return;
2081
- const cells = cellsOfRow(firstRow);
2491
+ clearSelectionDecor();
2492
+ const { row } = pos;
2493
+ const cells = cellsOfRow(row);
2082
2494
  const shouldMakeHeader = cells.some((c) => c.tagName !== "TH");
2083
2495
  for (const c of cells) {
2084
2496
  const isTh = c.tagName === "TH";
2085
2497
  if (shouldMakeHeader && !isTh) {
2086
- const th = document.createElement("th");
2087
- th.innerHTML = c.innerHTML || "&nbsp;";
2088
- th.style.border = c.style.border || "1px solid var(--srte-border)";
2089
- th.style.padding = c.style.padding || "6px";
2090
- th.style.minWidth = c.style.minWidth || "60px";
2091
- firstRow.replaceChild(th, c);
2498
+ replaceTableCellTag(c, "th");
2092
2499
  }
2093
2500
  else if (!shouldMakeHeader && isTh) {
2094
- const td = document.createElement("td");
2095
- td.innerHTML = c.innerHTML || "&nbsp;";
2096
- td.style.border = c.style.border || "1px solid var(--srte-border)";
2097
- td.style.padding = c.style.padding || "6px";
2098
- td.style.minWidth = c.style.minWidth || "60px";
2099
- firstRow.replaceChild(td, c);
2501
+ replaceTableCellTag(c, "td");
2100
2502
  }
2101
2503
  }
2102
2504
  handleInput();
@@ -2105,37 +2507,35 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2105
2507
  const pos = getCellPosition(cell);
2106
2508
  if (!pos)
2107
2509
  return;
2510
+ clearSelectionDecor();
2108
2511
  const { tbody, cIdx } = pos;
2109
- const rows = Array.from(tbody.querySelectorAll("tr"));
2110
- const columnCells = rows
2111
- .map((row) => cellsOfRow(row)[cIdx])
2112
- .filter(Boolean);
2512
+ const { grid } = getTableGrid(tbody);
2513
+ const seen = new Set();
2514
+ const columnCells = grid
2515
+ .map((row) => row?.[cIdx])
2516
+ .filter((candidate) => {
2517
+ if (!candidate || seen.has(candidate))
2518
+ return false;
2519
+ seen.add(candidate);
2520
+ return true;
2521
+ });
2113
2522
  const shouldMakeHeader = columnCells.some((c) => c.tagName !== "TH");
2114
2523
  for (const c of columnCells) {
2115
- const replacement = document.createElement(shouldMakeHeader ? "th" : "td");
2116
- replacement.innerHTML = c.innerHTML || "&nbsp;";
2117
- replacement.style.border = c.style.border || "1px solid var(--srte-border)";
2118
- replacement.style.padding = c.style.padding || "6px";
2119
- replacement.style.minWidth = c.style.minWidth || "60px";
2120
- c.parentElement?.replaceChild(replacement, c);
2524
+ replaceTableCellTag(c, shouldMakeHeader ? "th" : "td");
2121
2525
  }
2122
2526
  handleInput();
2123
2527
  };
2124
2528
  const applyBgToSelection = (hex, fallbackCell) => {
2125
- const sel = selectionRef.current;
2529
+ const sel = shouldUseTableSelection(fallbackCell) ? selectionRef.current : null;
2126
2530
  if (sel) {
2127
- const rows = Array.from(sel.tbody.querySelectorAll("tr"));
2128
- for (let r = sel.sr; r <= sel.er; r++) {
2129
- const row = rows[r];
2130
- const cells = cellsOfRow(row);
2131
- for (let c = sel.sc; c <= sel.ec; c++) {
2132
- const cell = cells[c];
2133
- if (cell)
2134
- cell.style.background = hex;
2135
- }
2136
- }
2531
+ const cells = getCellsInGridRect(sel.tbody, sel.sr, sel.sc, sel.er, sel.ec);
2532
+ clearSelectionDecor();
2533
+ cells.forEach((cell) => {
2534
+ cell.style.background = hex;
2535
+ });
2137
2536
  }
2138
2537
  else if (fallbackCell) {
2538
+ clearSelectionDecor();
2139
2539
  fallbackCell.style.background = hex;
2140
2540
  }
2141
2541
  };
@@ -2143,25 +2543,21 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2143
2543
  const applyToggle = (cell) => {
2144
2544
  const cur = cell.style.border;
2145
2545
  cell.style.border =
2146
- cur && cur !== "none" ? "none" : "1px solid #000";
2546
+ cur && cur !== "none" ? "none" : "1px solid #d1d5db";
2147
2547
  };
2148
- const sel = selectionRef.current;
2548
+ const sel = shouldUseTableSelection(fallbackCell) ? selectionRef.current : null;
2149
2549
  if (sel) {
2150
- const rows = Array.from(sel.tbody.querySelectorAll("tr"));
2151
- for (let r = sel.sr; r <= sel.er; r++) {
2152
- const row = rows[r];
2153
- const cells = cellsOfRow(row);
2154
- for (let c = sel.sc; c <= sel.ec; c++) {
2155
- const cell = cells[c];
2156
- if (cell)
2157
- applyToggle(cell);
2158
- }
2159
- }
2550
+ getCellsInGridRect(sel.tbody, sel.sr, sel.sc, sel.er, sel.ec).forEach(applyToggle);
2160
2551
  }
2161
2552
  else if (fallbackCell) {
2162
2553
  applyToggle(fallbackCell);
2163
2554
  }
2164
2555
  };
2556
+ const runTableCellAction = (cell, action) => {
2557
+ action(cell);
2558
+ handleInput();
2559
+ setTableMenu(null);
2560
+ };
2165
2561
  // Table column and row resizing functions
2166
2562
  const getColumnCells = (table, colIndex) => {
2167
2563
  const tbody = table.querySelector('tbody');
@@ -2282,6 +2678,27 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2282
2678
  });
2283
2679
  });
2284
2680
  };
2681
+ const activeButtonStyle = (active = false, extra = {}) => ({
2682
+ height: 32,
2683
+ minWidth: 32,
2684
+ padding: "0 8px",
2685
+ border: active
2686
+ ? "2px solid var(--srte-accent)"
2687
+ : "1px solid var(--srte-input-border)",
2688
+ borderRadius: 6,
2689
+ background: active ? "var(--srte-accent-bg)" : "var(--srte-input-bg)",
2690
+ color: "var(--srte-input-text)",
2691
+ boxShadow: active ? "inset 0 0 0 1px var(--srte-accent)" : "none",
2692
+ ...extra,
2693
+ });
2694
+ const preserveToolbarMouseDown = (event) => {
2695
+ const target = event.target;
2696
+ const button = target?.closest("button");
2697
+ preserveEditorSelection();
2698
+ if (button && !button.hasAttribute("disabled")) {
2699
+ event.preventDefault();
2700
+ }
2701
+ };
2285
2702
  const editorClass = `srte-editor${theme === 'dark' ? ' srte-dark' : ''}${className ? ' ' + className : ''}`;
2286
2703
  return (_jsxs("div", { className: editorClass, style: {
2287
2704
  border: "1px solid var(--srte-border)",
@@ -2294,7 +2711,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2294
2711
  background: "var(--srte-bg)",
2295
2712
  color: "var(--srte-text)",
2296
2713
  boxSizing: "border-box"
2297
- }, children: [_jsxs("div", { style: {
2714
+ }, children: [_jsxs("div", { onMouseDown: preserveToolbarMouseDown, style: {
2298
2715
  display: "flex",
2299
2716
  flexWrap: "wrap",
2300
2717
  maxWidth: "100%",
@@ -2343,7 +2760,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2343
2760
  } }), _jsx("input", { ref: mdInputRef, type: "file", accept: ".md,.markdown,text/markdown,text/plain", style: { display: "none" }, onChange: (e) => {
2344
2761
  importTextFile(e.currentTarget.files, "md");
2345
2762
  e.currentTarget.value = "";
2346
- } }), _jsxs("select", { defaultValue: "p", onChange: (e) => {
2763
+ } }), _jsxs("select", { defaultValue: "p", onMouseDown: preserveEditorSelection, onChange: (e) => {
2347
2764
  const val = e.target.value;
2348
2765
  if (val === "p")
2349
2766
  applyFormatBlock("<p>");
@@ -2360,42 +2777,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2360
2777
  borderRadius: 6,
2361
2778
  background: "var(--srte-input-bg)",
2362
2779
  color: "var(--srte-input-text)",
2363
- }, children: [_jsx("option", { value: "p", children: "Paragraph" }), _jsx("option", { value: "h1", children: "Heading 1" }), _jsx("option", { value: "h2", children: "Heading 2" }), _jsx("option", { value: "h3", children: "Heading 3" })] }), _jsx("button", { title: "Bold", onClick: () => exec("bold"), style: {
2364
- height: 32,
2365
- minWidth: 32,
2366
- padding: "0 8px",
2367
- border: "1px solid var(--srte-input-border)",
2368
- borderRadius: 6,
2369
- background: "var(--srte-input-bg)",
2370
- color: "var(--srte-input-text)",
2371
- }, children: _jsx("span", { style: { fontWeight: 700 }, children: "B" }) }), _jsx("button", { title: "Italic", onClick: () => exec("italic"), style: {
2372
- height: 32,
2373
- minWidth: 32,
2374
- padding: "0 8px",
2375
- border: "1px solid var(--srte-input-border)",
2376
- borderRadius: 6,
2377
- background: "var(--srte-input-bg)",
2378
- fontStyle: "italic",
2379
- color: "var(--srte-input-text)",
2380
- }, children: "I" }), _jsx("button", { title: "Underline", onClick: () => exec("underline"), style: {
2381
- height: 32,
2382
- minWidth: 32,
2383
- padding: "0 8px",
2384
- border: "1px solid var(--srte-input-border)",
2385
- borderRadius: 6,
2386
- background: "var(--srte-input-bg)",
2387
- textDecoration: "underline",
2388
- color: "var(--srte-input-text)",
2389
- }, children: "U" }), _jsx("button", { title: "Strikethrough", onClick: () => exec("strikeThrough"), style: {
2390
- height: 32,
2391
- minWidth: 32,
2392
- padding: "0 8px",
2393
- border: "1px solid var(--srte-input-border)",
2394
- borderRadius: 6,
2395
- background: "var(--srte-input-bg)",
2396
- textDecoration: "line-through",
2397
- color: "var(--srte-input-text)",
2398
- }, children: "S" }), _jsxs("select", { value: currentFontSize, onMouseDown: () => {
2780
+ }, children: [_jsx("option", { value: "p", children: "Paragraph" }), _jsx("option", { value: "h1", children: "Heading 1" }), _jsx("option", { value: "h2", children: "Heading 2" }), _jsx("option", { value: "h3", children: "Heading 3" })] }), _jsx("button", { title: "Bold", onClick: () => exec("bold"), "aria-pressed": activeState.bold, style: activeButtonStyle(activeState.bold), children: _jsx("span", { style: { fontWeight: 700 }, children: "B" }) }), _jsx("button", { title: "Italic", onClick: () => exec("italic"), "aria-pressed": activeState.italic, style: activeButtonStyle(activeState.italic, { fontStyle: "italic" }), children: "I" }), _jsx("button", { title: "Underline", onClick: () => exec("underline"), "aria-pressed": activeState.underline, style: activeButtonStyle(activeState.underline, { textDecoration: "underline" }), children: "U" }), _jsx("button", { title: "Strikethrough", onClick: () => exec("strikeThrough"), "aria-pressed": activeState.strikeThrough, style: activeButtonStyle(activeState.strikeThrough, { textDecoration: "line-through" }), children: "S" }), _jsxs("select", { value: currentFontSize, onMouseDown: () => {
2399
2781
  // Save selection before dropdown interaction
2400
2782
  const sel = window.getSelection();
2401
2783
  if (sel && sel.rangeCount > 0) {
@@ -2452,45 +2834,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2452
2834
  borderRadius: 6,
2453
2835
  background: "var(--srte-input-bg)",
2454
2836
  color: "var(--srte-input-text)",
2455
- }, children: _jsx("span", { style: { fontWeight: 700, padding: "1px 4px", background: "var(--srte-accent-bg)", borderRadius: 3 }, children: "A" }) }), _jsxs("button", { title: "Subscript", onClick: () => exec("subscript"), style: {
2456
- height: 32,
2457
- minWidth: 32,
2458
- padding: "0 8px",
2459
- border: "1px solid var(--srte-input-border)",
2460
- borderRadius: 6,
2461
- background: "var(--srte-input-bg)",
2462
- color: "var(--srte-input-text)",
2463
- }, children: ["X", _jsx("sub", { children: "2" })] }), _jsxs("button", { title: "Superscript", onClick: () => exec("superscript"), style: {
2464
- height: 32,
2465
- minWidth: 32,
2466
- padding: "0 8px",
2467
- border: "1px solid var(--srte-input-border)",
2468
- borderRadius: 6,
2469
- background: "var(--srte-input-bg)",
2470
- color: "var(--srte-input-text)",
2471
- }, children: ["X", _jsx("sup", { children: "2" })] }), _jsx("button", { title: "Bulleted list", onClick: () => exec("insertUnorderedList"), style: {
2472
- height: 32,
2473
- padding: "0 10px",
2474
- border: "1px solid var(--srte-input-border)",
2475
- borderRadius: 6,
2476
- background: "var(--srte-input-bg)",
2477
- color: "var(--srte-input-text)",
2478
- }, children: "\u2022 List" }), _jsx("button", { title: "Numbered list", onClick: () => exec("insertOrderedList"), style: {
2479
- height: 32,
2480
- padding: "0 10px",
2481
- border: "1px solid var(--srte-input-border)",
2482
- borderRadius: 6,
2483
- background: "var(--srte-input-bg)",
2484
- color: "var(--srte-input-text)",
2485
- }, children: "1. List" }), _jsx("button", { title: "Blockquote", onClick: toggleBlockquote, style: {
2486
- height: 32,
2487
- minWidth: 32,
2488
- padding: "0 8px",
2489
- border: "1px solid var(--srte-input-border)",
2490
- borderRadius: 6,
2491
- background: "var(--srte-input-bg)",
2492
- color: "var(--srte-input-text)",
2493
- }, children: "\u275D" }), _jsx("button", { title: "Special characters", onClick: () => setShowSpecialChars(true), style: {
2837
+ }, children: _jsx("span", { style: { fontWeight: 700, padding: "1px 4px", background: "var(--srte-accent-bg)", borderRadius: 3 }, children: "A" }) }), _jsxs("button", { title: "Subscript", onClick: () => exec("subscript"), "aria-pressed": activeState.subscript, style: activeButtonStyle(activeState.subscript), children: ["X", _jsx("sub", { children: "2" })] }), _jsxs("button", { title: "Superscript", onClick: () => exec("superscript"), "aria-pressed": activeState.superscript, style: activeButtonStyle(activeState.superscript), children: ["X", _jsx("sup", { children: "2" })] }), _jsx("button", { title: "Bulleted list", onClick: () => exec("insertUnorderedList"), "aria-pressed": activeState.unorderedList, style: activeButtonStyle(activeState.unorderedList, { padding: "0 10px" }), children: "\u2022 List" }), _jsx("button", { title: "Numbered list", onClick: () => exec("insertOrderedList"), "aria-pressed": activeState.orderedList, style: activeButtonStyle(activeState.orderedList, { padding: "0 10px" }), children: "1. List" }), _jsx("button", { title: "Blockquote", onClick: toggleBlockquote, "aria-pressed": activeState.blockquote, style: activeButtonStyle(activeState.blockquote), children: "\u275D" }), _jsx("button", { title: "Special characters", onClick: () => setShowSpecialChars(true), style: {
2494
2838
  height: 32,
2495
2839
  minWidth: 32,
2496
2840
  padding: "0 8px",
@@ -2498,16 +2842,10 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2498
2842
  borderRadius: 6,
2499
2843
  background: "var(--srte-input-bg)",
2500
2844
  color: "var(--srte-input-text)",
2501
- }, children: "\u03A9" }), _jsx("button", { title: "Code block", onClick: () => exec("formatBlock", "<pre>"), style: {
2502
- height: 32,
2845
+ }, children: "\u03A9" }), _jsx("button", { title: "Code block", onClick: () => exec("formatBlock", "<pre>"), "aria-pressed": activeState.codeBlock, style: activeButtonStyle(activeState.codeBlock, {
2503
2846
  minWidth: 36,
2504
- padding: "0 8px",
2505
- border: "1px solid var(--srte-input-border)",
2506
- borderRadius: 6,
2507
- background: "var(--srte-input-bg)",
2508
2847
  fontFamily: "ui-monospace, SFMono-Regular, Menlo",
2509
- color: "var(--srte-input-text)",
2510
- }, children: "< />" }), formula && (_jsx("button", { title: "Insert formula", onClick: () => setShowFormulaDialog(true), style: {
2848
+ }), children: "< />" }), formula && (_jsx("button", { title: "Insert formula", onClick: () => setShowFormulaDialog(true), style: {
2511
2849
  height: 32,
2512
2850
  minWidth: 32,
2513
2851
  padding: "0 8px",
@@ -2685,17 +3023,19 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2685
3023
  position: "fixed",
2686
3024
  inset: 0,
2687
3025
  background: "var(--srte-modal-backdrop)",
3026
+ backdropFilter: "var(--srte-modal-backdrop-filter)",
3027
+ WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
2688
3028
  display: "flex",
2689
3029
  alignItems: "center",
2690
3030
  justifyContent: "center",
2691
- zIndex: 50,
3031
+ zIndex: 90,
2692
3032
  }, onClick: () => setShowTableDialog(false), children: _jsxs("div", { style: {
2693
3033
  background: "var(--srte-modal-bg)",
2694
3034
  color: "var(--srte-modal-text)",
2695
3035
  padding: 16,
2696
3036
  borderRadius: 8,
2697
3037
  minWidth: 280,
2698
- }, onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Insert table" }), _jsxs("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [_jsx("div", { style: {
3038
+ }, onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Insert table" }), _jsxs("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [_jsx("div", { style: {
2699
3039
  display: "grid",
2700
3040
  gridTemplateColumns: "repeat(10, 18px)",
2701
3041
  gap: 2,
@@ -2729,6 +3069,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2729
3069
  position: "fixed",
2730
3070
  inset: 0,
2731
3071
  background: "var(--srte-modal-backdrop)",
3072
+ backdropFilter: "var(--srte-modal-backdrop-filter)",
3073
+ WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
2732
3074
  display: "flex",
2733
3075
  alignItems: "center",
2734
3076
  justifyContent: "center",
@@ -2783,6 +3125,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2783
3125
  position: "fixed",
2784
3126
  inset: 0,
2785
3127
  background: "var(--srte-modal-backdrop)",
3128
+ backdropFilter: "var(--srte-modal-backdrop-filter)",
3129
+ WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
2786
3130
  display: "flex",
2787
3131
  alignItems: "center",
2788
3132
  justifyContent: "center",
@@ -2849,6 +3193,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2849
3193
  position: "fixed",
2850
3194
  inset: 0,
2851
3195
  background: "var(--srte-modal-backdrop)",
3196
+ backdropFilter: "var(--srte-modal-backdrop-filter)",
3197
+ WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
2852
3198
  display: "flex",
2853
3199
  alignItems: "center",
2854
3200
  justifyContent: "center",
@@ -2893,6 +3239,8 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2893
3239
  position: "fixed",
2894
3240
  inset: 0,
2895
3241
  background: "var(--srte-modal-backdrop)",
3242
+ backdropFilter: "var(--srte-modal-backdrop-filter)",
3243
+ WebkitBackdropFilter: "var(--srte-modal-backdrop-filter)",
2896
3244
  display: "flex",
2897
3245
  alignItems: "center",
2898
3246
  justifyContent: "center",
@@ -2990,7 +3338,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
2990
3338
  borderRadius: 4,
2991
3339
  background: "var(--srte-input-bg)",
2992
3340
  color: "var(--srte-modal-text)",
2993
- }, title: sym, children: sym }, i)))] })] }) })), _jsx("div", { style: {
3341
+ }, title: sym, children: sym }, i)))] })] }) })), _jsxs("div", { ref: editorScrollRef, style: {
2994
3342
  width: "100%",
2995
3343
  maxWidth: "100%",
2996
3344
  flex: "1 1 auto",
@@ -3003,374 +3351,376 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
3003
3351
  boxSizing: "border-box",
3004
3352
  position: "relative",
3005
3353
  scrollPaddingBottom: 24,
3006
- }, children: _jsx("div", { ref: editableRef, contentEditable: !readOnly, suppressContentEditableWarning: true, onInput: handleInput, onCompositionStart: () => (isComposingRef.current = true), onCompositionEnd: () => {
3007
- isComposingRef.current = false;
3008
- handleInput();
3009
- }, onPaste: (e) => {
3010
- const items = e.clipboardData?.files;
3011
- if (media && items && items.length) {
3012
- const hasImage = Array.from(items).some((f) => f.type.startsWith("image/"));
3013
- if (hasImage) {
3354
+ }, children: [_jsx("div", { ref: editableRef, contentEditable: !readOnly, suppressContentEditableWarning: true, onInput: handleInput, onKeyUp: updateActiveState, onMouseUp: updateActiveState, onCompositionStart: () => (isComposingRef.current = true), onCompositionEnd: () => {
3355
+ isComposingRef.current = false;
3356
+ handleInput();
3357
+ }, onPaste: (e) => {
3358
+ const items = e.clipboardData?.files;
3359
+ if (media && items && items.length) {
3360
+ const hasImage = Array.from(items).some((f) => f.type.startsWith("image/"));
3361
+ if (hasImage) {
3362
+ e.preventDefault();
3363
+ handleLocalImageFiles(items);
3364
+ return;
3365
+ }
3366
+ }
3367
+ const html = e.clipboardData?.getData("text/html");
3368
+ if (html) {
3014
3369
  e.preventDefault();
3015
- handleLocalImageFiles(items);
3016
- return;
3370
+ insertCleanHtml(cleanPastedHtml(html));
3017
3371
  }
3018
- }
3019
- const html = e.clipboardData?.getData("text/html");
3020
- if (html) {
3021
- e.preventDefault();
3022
- insertCleanHtml(cleanPastedHtml(html));
3023
- }
3024
- }, onDragOver: (e) => {
3025
- // Allow dragging images within editor and file drops
3026
- if (draggedImageRef.current ||
3027
- e.dataTransfer?.types?.includes("Files")) {
3028
- e.preventDefault();
3029
- }
3030
- }, onDrop: (e) => {
3031
- // Move existing dragged image inside editor
3032
- if (draggedImageRef.current) {
3033
- e.preventDefault();
3034
- const x = e.clientX;
3035
- const y = e.clientY;
3036
- let range = null;
3037
- // @ts-ignore
3038
- if (document.caretRangeFromPoint) {
3039
- // @ts-ignore
3040
- range = document.caretRangeFromPoint(x, y);
3372
+ }, onDragOver: (e) => {
3373
+ // Allow dragging images within editor and file drops
3374
+ if (draggedImageRef.current ||
3375
+ e.dataTransfer?.types?.includes("Files")) {
3376
+ e.preventDefault();
3041
3377
  }
3042
- else if (document.caretPositionFromPoint) {
3043
- const pos = document.caretPositionFromPoint(x, y);
3044
- if (pos) {
3045
- range = document.createRange();
3046
- range.setStart(pos.offsetNode, pos.offset);
3378
+ }, onDrop: (e) => {
3379
+ // Move existing dragged image inside editor
3380
+ if (draggedImageRef.current) {
3381
+ e.preventDefault();
3382
+ const x = e.clientX;
3383
+ const y = e.clientY;
3384
+ let range = null;
3385
+ // @ts-ignore
3386
+ if (document.caretRangeFromPoint) {
3387
+ // @ts-ignore
3388
+ range = document.caretRangeFromPoint(x, y);
3047
3389
  }
3048
- }
3049
- const img = draggedImageRef.current;
3050
- draggedImageRef.current = null;
3051
- if (range &&
3052
- img &&
3053
- editableRef.current?.contains(range.commonAncestorContainer)) {
3054
- // Avoid inserting inside the image itself
3055
- if (range.startContainer === img || range.endContainer === img)
3056
- return;
3057
- // If dropping inside a link, insert right after the link element
3058
- let container = range.commonAncestorContainer;
3059
- let linkAncestor = null;
3060
- let el = container;
3061
- while (el && el !== editableRef.current) {
3062
- if (el.tagName === "A") {
3063
- linkAncestor = el;
3064
- break;
3390
+ else if (document.caretPositionFromPoint) {
3391
+ const pos = document.caretPositionFromPoint(x, y);
3392
+ if (pos) {
3393
+ range = document.createRange();
3394
+ range.setStart(pos.offsetNode, pos.offset);
3065
3395
  }
3066
- el = el.parentElement;
3067
- }
3068
- if (linkAncestor) {
3069
- linkAncestor.parentElement?.insertBefore(img, linkAncestor.nextSibling);
3070
3396
  }
3071
- else {
3072
- range.insertNode(img);
3397
+ const img = draggedImageRef.current;
3398
+ draggedImageRef.current = null;
3399
+ if (range &&
3400
+ img &&
3401
+ editableRef.current?.contains(range.commonAncestorContainer)) {
3402
+ // Avoid inserting inside the image itself
3403
+ if (range.startContainer === img || range.endContainer === img)
3404
+ return;
3405
+ // If dropping inside a link, insert right after the link element
3406
+ let container = range.commonAncestorContainer;
3407
+ let linkAncestor = null;
3408
+ let el = container;
3409
+ while (el && el !== editableRef.current) {
3410
+ if (el.tagName === "A") {
3411
+ linkAncestor = el;
3412
+ break;
3413
+ }
3414
+ el = el.parentElement;
3415
+ }
3416
+ if (linkAncestor) {
3417
+ linkAncestor.parentElement?.insertBefore(img, linkAncestor.nextSibling);
3418
+ }
3419
+ else {
3420
+ range.insertNode(img);
3421
+ }
3422
+ const r = document.createRange();
3423
+ r.setStartAfter(img);
3424
+ r.collapse(true);
3425
+ safeSelectRange(r);
3426
+ setSelectedImage(img);
3427
+ scheduleImageOverlay();
3428
+ handleInput();
3073
3429
  }
3074
- const r = document.createRange();
3075
- r.setStartAfter(img);
3076
- r.collapse(true);
3077
- safeSelectRange(r);
3078
- setSelectedImage(img);
3079
- scheduleImageOverlay();
3080
- handleInput();
3430
+ return;
3081
3431
  }
3082
- return;
3083
- }
3084
- if (media && e.dataTransfer?.files?.length) {
3085
- e.preventDefault();
3086
- // Try to move caret to drop point
3087
- const x = e.clientX;
3088
- const y = e.clientY;
3089
- let range = null;
3090
- // @ts-ignore
3091
- if (document.caretRangeFromPoint) {
3432
+ if (media && e.dataTransfer?.files?.length) {
3433
+ e.preventDefault();
3434
+ // Try to move caret to drop point
3435
+ const x = e.clientX;
3436
+ const y = e.clientY;
3437
+ let range = null;
3092
3438
  // @ts-ignore
3093
- range = document.caretRangeFromPoint(x, y);
3094
- }
3095
- else if (document.caretPositionFromPoint) {
3096
- const pos = document.caretPositionFromPoint(x, y);
3097
- if (pos) {
3098
- range = document.createRange();
3099
- range.setStart(pos.offsetNode, pos.offset);
3439
+ if (document.caretRangeFromPoint) {
3440
+ // @ts-ignore
3441
+ range = document.caretRangeFromPoint(x, y);
3100
3442
  }
3443
+ else if (document.caretPositionFromPoint) {
3444
+ const pos = document.caretPositionFromPoint(x, y);
3445
+ if (pos) {
3446
+ range = document.createRange();
3447
+ range.setStart(pos.offsetNode, pos.offset);
3448
+ }
3449
+ }
3450
+ if (range) {
3451
+ const sel = window.getSelection();
3452
+ sel?.removeAllRanges();
3453
+ sel?.addRange(range);
3454
+ }
3455
+ handleLocalImageFiles(e.dataTransfer.files);
3101
3456
  }
3102
- if (range) {
3103
- const sel = window.getSelection();
3104
- sel?.removeAllRanges();
3105
- sel?.addRange(range);
3457
+ }, onClick: (e) => {
3458
+ const t = e.target;
3459
+ if (t && t.tagName === "IMG") {
3460
+ setSelectedImage(t);
3461
+ scheduleImageOverlay();
3106
3462
  }
3107
- handleLocalImageFiles(e.dataTransfer.files);
3108
- }
3109
- }, onClick: (e) => {
3110
- const t = e.target;
3111
- if (t && t.tagName === "IMG") {
3112
- setSelectedImage(t);
3113
- scheduleImageOverlay();
3114
- }
3115
- else {
3116
- setSelectedImage(null);
3117
- setImageOverlay(null);
3118
- }
3119
- }, onDragStart: (e) => {
3120
- const t = e.target;
3121
- if (t && t.tagName === "IMG") {
3122
- draggedImageRef.current = t;
3123
- try {
3124
- e.dataTransfer?.setData("text/plain", "moving-image");
3125
- e.dataTransfer.effectAllowed = "move";
3126
- // Provide a subtle drag image
3127
- const dt = e.dataTransfer;
3128
- if (dt && typeof dt.setDragImage === "function") {
3129
- const ghost = new Image();
3130
- ghost.src = t.src;
3131
- ghost.width = Math.min(120, t.width);
3132
- ghost.height = Math.min(120, t.height);
3133
- dt.setDragImage(ghost, 10, 10);
3463
+ else {
3464
+ setSelectedImage(null);
3465
+ setImageOverlay(null);
3466
+ }
3467
+ }, onDragStart: (e) => {
3468
+ const t = e.target;
3469
+ if (t && t.tagName === "IMG") {
3470
+ draggedImageRef.current = t;
3471
+ try {
3472
+ e.dataTransfer?.setData("text/plain", "moving-image");
3473
+ e.dataTransfer.effectAllowed = "move";
3474
+ // Provide a subtle drag image
3475
+ const dt = e.dataTransfer;
3476
+ if (dt && typeof dt.setDragImage === "function") {
3477
+ const ghost = new Image();
3478
+ ghost.src = t.src;
3479
+ ghost.width = Math.min(120, t.width);
3480
+ ghost.height = Math.min(120, t.height);
3481
+ dt.setDragImage(ghost, 10, 10);
3482
+ }
3134
3483
  }
3484
+ catch { }
3135
3485
  }
3136
- catch { }
3137
- }
3138
- else {
3486
+ else {
3487
+ draggedImageRef.current = null;
3488
+ }
3489
+ }, onDragEnd: () => {
3139
3490
  draggedImageRef.current = null;
3140
- }
3141
- }, onDragEnd: () => {
3142
- draggedImageRef.current = null;
3143
- }, style: {
3144
- minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight,
3145
- maxWidth: "100%",
3146
- overflowX: "visible",
3147
- padding: "16px",
3148
- paddingBottom: "32px",
3149
- outline: "none",
3150
- lineHeight: 1.6,
3151
- boxSizing: "border-box",
3152
- fontFamily: defaultFont || "inherit",
3153
- }, "data-placeholder": placeholder, onFocus: (e) => {
3154
- // Ensure the editor has at least one paragraph to type into
3155
- const el = e.currentTarget;
3156
- if (!el.innerHTML || el.innerHTML === "<br>") {
3157
- el.innerHTML = "<p><br></p>";
3158
- }
3159
- }, onKeyDown: (e) => {
3160
- if (formula &&
3161
- (e.metaKey || e.ctrlKey) &&
3162
- String(e.key).toLowerCase() === "m") {
3163
- e.preventDefault();
3164
- setShowFormulaDialog(true);
3165
- return;
3166
- }
3167
- // Keep Tab for indentation in lists; otherwise insert 2 spaces
3168
- if (e.key === "Tab") {
3169
- e.preventDefault();
3170
- if (document.queryCommandState("insertUnorderedList") ||
3171
- document.queryCommandState("insertOrderedList")) {
3172
- exec(e.shiftKey ? "outdent" : "indent");
3491
+ }, style: {
3492
+ minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight,
3493
+ maxWidth: "100%",
3494
+ overflowX: "visible",
3495
+ padding: "16px",
3496
+ paddingBottom: "32px",
3497
+ outline: "none",
3498
+ lineHeight: 1.6,
3499
+ boxSizing: "border-box",
3500
+ fontFamily: defaultFont || "inherit",
3501
+ }, "data-placeholder": placeholder, onFocus: (e) => {
3502
+ // Ensure the editor has at least one paragraph to type into
3503
+ const el = e.currentTarget;
3504
+ if (!el.innerHTML || el.innerHTML === "<br>") {
3505
+ el.innerHTML = "<p><br></p>";
3173
3506
  }
3174
- else {
3175
- document.execCommand("insertText", false, " ");
3507
+ updateActiveState();
3508
+ }, onKeyDown: (e) => {
3509
+ if (formula &&
3510
+ (e.metaKey || e.ctrlKey) &&
3511
+ String(e.key).toLowerCase() === "m") {
3512
+ e.preventDefault();
3513
+ setShowFormulaDialog(true);
3514
+ return;
3176
3515
  }
3177
- }
3178
- // Table navigation with arrows inside cells
3179
- if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
3180
- const sel = window.getSelection();
3181
- const cell = getClosestCell(sel?.anchorNode || null);
3182
- if (table &&
3183
- cell &&
3184
- cell.parentElement &&
3185
- cell.parentElement.parentElement) {
3186
- const row = cell.parentElement;
3187
- const tbody = row.parentElement;
3188
- const cells = Array.from(row.children).filter((c) => c.tagName === "TD" ||
3189
- c.tagName === "TH");
3190
- const rows = Array.from(tbody.children);
3191
- const rIdx = rows.indexOf(row);
3192
- const cIdx = cells.indexOf(cell);
3193
- const atStart = (sel?.anchorOffset || 0) === 0;
3194
- const cellTextLen = (cell.textContent || "").length;
3195
- const atEnd = (sel?.anchorOffset || 0) >= cellTextLen;
3196
- let target = null;
3197
- if (e.key === "ArrowLeft" && atStart && cIdx > 0) {
3198
- target = row.children[cIdx - 1];
3199
- }
3200
- else if (e.key === "ArrowRight" &&
3201
- atEnd &&
3202
- cIdx < row.children.length - 1) {
3203
- target = row.children[cIdx + 1];
3204
- }
3205
- else if (e.key === "ArrowUp" && rIdx > 0 && atStart) {
3206
- target = rows[rIdx - 1].children[cIdx];
3516
+ // Keep Tab for indentation in lists; otherwise insert 2 spaces
3517
+ if (e.key === "Tab") {
3518
+ e.preventDefault();
3519
+ if (document.queryCommandState("insertUnorderedList") ||
3520
+ document.queryCommandState("insertOrderedList")) {
3521
+ exec(e.shiftKey ? "outdent" : "indent");
3207
3522
  }
3208
- else if (e.key === "ArrowDown" &&
3209
- rIdx < rows.length - 1 &&
3210
- atEnd) {
3211
- target = rows[rIdx + 1].children[cIdx];
3523
+ else {
3524
+ document.execCommand("insertText", false, " ");
3212
3525
  }
3213
- if (target) {
3214
- e.preventDefault();
3215
- moveCaretToCell(target, e.key === "ArrowRight" || e.key === "ArrowDown");
3526
+ }
3527
+ // Table navigation with arrows inside cells
3528
+ if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
3529
+ const sel = window.getSelection();
3530
+ const cell = getClosestCell(sel?.anchorNode || null);
3531
+ if (table &&
3532
+ cell &&
3533
+ cell.parentElement &&
3534
+ cell.parentElement.parentElement) {
3535
+ const row = cell.parentElement;
3536
+ const tbody = row.parentElement;
3537
+ const cells = Array.from(row.children).filter((c) => c.tagName === "TD" ||
3538
+ c.tagName === "TH");
3539
+ const rows = Array.from(tbody.children);
3540
+ const rIdx = rows.indexOf(row);
3541
+ const cIdx = cells.indexOf(cell);
3542
+ const atStart = (sel?.anchorOffset || 0) === 0;
3543
+ const cellTextLen = (cell.textContent || "").length;
3544
+ const atEnd = (sel?.anchorOffset || 0) >= cellTextLen;
3545
+ let target = null;
3546
+ if (e.key === "ArrowLeft" && atStart && cIdx > 0) {
3547
+ target = row.children[cIdx - 1];
3548
+ }
3549
+ else if (e.key === "ArrowRight" &&
3550
+ atEnd &&
3551
+ cIdx < row.children.length - 1) {
3552
+ target = row.children[cIdx + 1];
3553
+ }
3554
+ else if (e.key === "ArrowUp" && rIdx > 0 && atStart) {
3555
+ target = rows[rIdx - 1].children[cIdx];
3556
+ }
3557
+ else if (e.key === "ArrowDown" &&
3558
+ rIdx < rows.length - 1 &&
3559
+ atEnd) {
3560
+ target = rows[rIdx + 1].children[cIdx];
3561
+ }
3562
+ if (target) {
3563
+ e.preventDefault();
3564
+ moveCaretToCell(target, e.key === "ArrowRight" || e.key === "ArrowDown");
3565
+ }
3216
3566
  }
3217
3567
  }
3218
- }
3219
- }, onMouseDown: (e) => {
3220
- const cell = getClosestCell(e.target);
3221
- if (!cell) {
3222
- clearSelectionDecor();
3223
- return;
3224
- }
3225
- const pos = getCellPosition(cell);
3226
- if (!pos)
3227
- return;
3228
- selectingRef.current = { tbody: pos.tbody, start: cell };
3229
- const onMove = (ev) => {
3230
- const under = document.elementFromPoint(ev.clientX, ev.clientY);
3231
- const overCell = getClosestCell(under);
3232
- const startInfo = selectingRef.current;
3233
- if (!overCell || !startInfo)
3568
+ }, onMouseDown: (e) => {
3569
+ const cell = getClosestCell(e.target);
3570
+ if (!cell) {
3571
+ clearSelectionDecor();
3234
3572
  return;
3235
- const a = getCellPosition(startInfo.start);
3236
- const b = getCellPosition(overCell);
3237
- if (!a || !b || a.tbody !== b.tbody)
3238
- return;
3239
- const sr = Math.min(a.rIdx, b.rIdx);
3240
- const sc = Math.min(a.cIdx, b.cIdx);
3241
- const er = Math.max(a.rIdx, b.rIdx);
3242
- const ec = Math.max(a.cIdx, b.cIdx);
3243
- updateSelectionDecor(a.tbody, sr, sc, er, ec);
3244
- };
3245
- const onUp = () => {
3246
- window.removeEventListener("mousemove", onMove);
3247
- window.removeEventListener("mouseup", onUp);
3248
- selectingRef.current = null;
3249
- };
3250
- window.addEventListener("mousemove", onMove);
3251
- window.addEventListener("mouseup", onUp);
3252
- }, onContextMenu: (e) => {
3253
- const target = e.target;
3254
- if (target && target.tagName === "IMG") {
3255
- e.preventDefault();
3256
- const vw = window.innerWidth;
3257
- const vh = window.innerHeight;
3258
- const menuW = 220;
3259
- const menuH = 200;
3260
- const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
3261
- const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
3262
- setImageMenu({ x, y, img: target });
3263
- setTableMenu(null);
3264
- return;
3265
- }
3266
- const cell = getClosestCell(e.target);
3267
- if (cell) {
3268
- e.preventDefault();
3269
- const vw = window.innerWidth;
3270
- const vh = window.innerHeight;
3271
- const menuW = 220;
3272
- const menuH = 300;
3273
- const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
3274
- const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
3275
- setTableMenu({ x, y, cell });
3276
- }
3277
- else {
3278
- setTableMenu(null);
3279
- setImageMenu(null);
3280
- }
3281
- } }) }), selectedImage && imageOverlay && (_jsxs("div", { style: {
3282
- position: "fixed",
3283
- left: imageOverlay.left,
3284
- top: imageOverlay.top,
3285
- width: imageOverlay.width,
3286
- height: imageOverlay.height,
3287
- pointerEvents: "none",
3288
- zIndex: 49,
3289
- }, children: [_jsx("div", { style: {
3290
- position: "absolute",
3291
- inset: 0,
3292
- outline: "2px solid var(--srte-accent)",
3293
- outlineOffset: -2,
3294
- } }), _jsx("div", { title: "Resize", onMouseDown: (e) => {
3295
- e.preventDefault();
3296
- if (!selectedImage)
3573
+ }
3574
+ const pos = getCellPosition(cell);
3575
+ if (!pos)
3297
3576
  return;
3298
- resizingRef.current = {
3299
- side: "left",
3300
- startX: e.clientX,
3301
- startWidth: selectedImage.getBoundingClientRect().width,
3302
- };
3577
+ selectingRef.current = { tbody: pos.tbody, start: cell };
3303
3578
  const onMove = (ev) => {
3304
- const info = resizingRef.current;
3305
- if (!info || !selectedImage)
3579
+ const under = document.elementFromPoint(ev.clientX, ev.clientY);
3580
+ const overCell = getClosestCell(under);
3581
+ const startInfo = selectingRef.current;
3582
+ if (!overCell || !startInfo)
3306
3583
  return;
3307
- const delta = info.startX - ev.clientX;
3308
- const next = Math.max(80, Math.round(info.startWidth + delta));
3309
- selectedImage.style.width = next + "px";
3310
- selectedImage.style.height = "auto";
3311
- scheduleImageOverlay();
3312
- };
3313
- const onUp = () => {
3314
- window.removeEventListener("mousemove", onMove);
3315
- window.removeEventListener("mouseup", onUp);
3316
- resizingRef.current = null;
3317
- handleInput();
3318
- };
3319
- window.addEventListener("mousemove", onMove);
3320
- window.addEventListener("mouseup", onUp);
3321
- }, style: {
3322
- position: "absolute",
3323
- left: -6,
3324
- top: "50%",
3325
- transform: "translateY(-50%)",
3326
- width: 8,
3327
- height: 24,
3328
- background: "var(--srte-accent)",
3329
- borderRadius: 2,
3330
- cursor: "ew-resize",
3331
- pointerEvents: "auto",
3332
- } }), _jsx("div", { title: "Resize", onMouseDown: (e) => {
3333
- e.preventDefault();
3334
- if (!selectedImage)
3335
- return;
3336
- resizingRef.current = {
3337
- side: "right",
3338
- startX: e.clientX,
3339
- startWidth: selectedImage.getBoundingClientRect().width,
3340
- };
3341
- const onMove = (ev) => {
3342
- const info = resizingRef.current;
3343
- if (!info || !selectedImage)
3584
+ const a = getCellPosition(startInfo.start);
3585
+ const b = getCellPosition(overCell);
3586
+ if (!a || !b || a.tbody !== b.tbody)
3344
3587
  return;
3345
- const delta = ev.clientX - info.startX;
3346
- const next = Math.max(80, Math.round(info.startWidth + delta));
3347
- selectedImage.style.width = next + "px";
3348
- selectedImage.style.height = "auto";
3349
- scheduleImageOverlay();
3588
+ const sr = Math.min(a.rIdx, b.rIdx);
3589
+ const sc = Math.min(a.cIdx, b.cIdx);
3590
+ const er = Math.max(a.rIdx, b.rIdx);
3591
+ const ec = Math.max(a.cIdx, b.cIdx);
3592
+ updateSelectionDecor(a.tbody, sr, sc, er, ec);
3350
3593
  };
3351
3594
  const onUp = () => {
3352
3595
  window.removeEventListener("mousemove", onMove);
3353
3596
  window.removeEventListener("mouseup", onUp);
3354
- resizingRef.current = null;
3355
- handleInput();
3597
+ selectingRef.current = null;
3356
3598
  };
3357
3599
  window.addEventListener("mousemove", onMove);
3358
3600
  window.addEventListener("mouseup", onUp);
3359
- }, style: {
3601
+ }, onContextMenu: (e) => {
3602
+ const target = e.target;
3603
+ if (target && target.tagName === "IMG") {
3604
+ e.preventDefault();
3605
+ const vw = window.innerWidth;
3606
+ const vh = window.innerHeight;
3607
+ const menuW = 220;
3608
+ const menuH = 200;
3609
+ const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
3610
+ const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
3611
+ setImageMenu({ x, y, img: target });
3612
+ setTableMenu(null);
3613
+ return;
3614
+ }
3615
+ const cell = getClosestCell(e.target);
3616
+ if (cell) {
3617
+ e.preventDefault();
3618
+ const vw = window.innerWidth;
3619
+ const vh = window.innerHeight;
3620
+ const menuW = 220;
3621
+ const menuH = 300;
3622
+ const x = Math.max(8, Math.min(e.clientX, vw - menuW - 8));
3623
+ const y = Math.max(8, Math.min(e.clientY, vh - menuH - 8));
3624
+ setTableMenu({ x, y, cell });
3625
+ }
3626
+ else {
3627
+ setTableMenu(null);
3628
+ setImageMenu(null);
3629
+ }
3630
+ } }), selectedImage && imageOverlay && (_jsxs("div", { style: {
3360
3631
  position: "absolute",
3361
- right: -6,
3362
- top: "50%",
3363
- transform: "translateY(-50%)",
3364
- width: 8,
3365
- height: 24,
3366
- background: "var(--srte-accent)",
3367
- borderRadius: 2,
3368
- cursor: "ew-resize",
3369
- pointerEvents: "auto",
3370
- } })] })), tableMenu && (_jsx("div", { style: {
3632
+ left: imageOverlay.left,
3633
+ top: imageOverlay.top,
3634
+ width: imageOverlay.width,
3635
+ height: imageOverlay.height,
3636
+ pointerEvents: "none",
3637
+ zIndex: 5,
3638
+ }, children: [_jsx("div", { style: {
3639
+ position: "absolute",
3640
+ inset: 0,
3641
+ outline: "2px solid var(--srte-accent)",
3642
+ outlineOffset: -2,
3643
+ } }), _jsx("div", { title: "Resize", onMouseDown: (e) => {
3644
+ e.preventDefault();
3645
+ if (!selectedImage)
3646
+ return;
3647
+ resizingRef.current = {
3648
+ side: "left",
3649
+ startX: e.clientX,
3650
+ startWidth: selectedImage.getBoundingClientRect().width,
3651
+ };
3652
+ const onMove = (ev) => {
3653
+ const info = resizingRef.current;
3654
+ if (!info || !selectedImage)
3655
+ return;
3656
+ const delta = info.startX - ev.clientX;
3657
+ const next = Math.max(80, Math.round(info.startWidth + delta));
3658
+ selectedImage.style.width = next + "px";
3659
+ selectedImage.style.height = "auto";
3660
+ scheduleImageOverlay();
3661
+ };
3662
+ const onUp = () => {
3663
+ window.removeEventListener("mousemove", onMove);
3664
+ window.removeEventListener("mouseup", onUp);
3665
+ resizingRef.current = null;
3666
+ handleInput();
3667
+ };
3668
+ window.addEventListener("mousemove", onMove);
3669
+ window.addEventListener("mouseup", onUp);
3670
+ }, style: {
3671
+ position: "absolute",
3672
+ left: -4,
3673
+ top: "50%",
3674
+ transform: "translateY(-50%)",
3675
+ width: 8,
3676
+ height: 24,
3677
+ background: "var(--srte-accent)",
3678
+ borderRadius: 2,
3679
+ cursor: "ew-resize",
3680
+ pointerEvents: "auto",
3681
+ } }), _jsx("div", { title: "Resize", onMouseDown: (e) => {
3682
+ e.preventDefault();
3683
+ if (!selectedImage)
3684
+ return;
3685
+ resizingRef.current = {
3686
+ side: "right",
3687
+ startX: e.clientX,
3688
+ startWidth: selectedImage.getBoundingClientRect().width,
3689
+ };
3690
+ const onMove = (ev) => {
3691
+ const info = resizingRef.current;
3692
+ if (!info || !selectedImage)
3693
+ return;
3694
+ const delta = ev.clientX - info.startX;
3695
+ const next = Math.max(80, Math.round(info.startWidth + delta));
3696
+ selectedImage.style.width = next + "px";
3697
+ selectedImage.style.height = "auto";
3698
+ scheduleImageOverlay();
3699
+ };
3700
+ const onUp = () => {
3701
+ window.removeEventListener("mousemove", onMove);
3702
+ window.removeEventListener("mouseup", onUp);
3703
+ resizingRef.current = null;
3704
+ handleInput();
3705
+ };
3706
+ window.addEventListener("mousemove", onMove);
3707
+ window.addEventListener("mouseup", onUp);
3708
+ }, style: {
3709
+ position: "absolute",
3710
+ right: -4,
3711
+ top: "50%",
3712
+ transform: "translateY(-50%)",
3713
+ width: 8,
3714
+ height: 24,
3715
+ background: "var(--srte-accent)",
3716
+ borderRadius: 2,
3717
+ cursor: "ew-resize",
3718
+ pointerEvents: "auto",
3719
+ } })] }))] }), tableMenu && (_jsx("div", { style: {
3371
3720
  position: "fixed",
3372
3721
  inset: 0,
3373
3722
  zIndex: 60,
3723
+ background: "transparent",
3374
3724
  }, onClick: () => setTableMenu(null), onContextMenu: (e) => {
3375
3725
  // Prevent native menu while overlay is shown and reposition our menu
3376
3726
  e.preventDefault();
@@ -3402,56 +3752,53 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
3402
3752
  maxHeight: 260,
3403
3753
  overflowY: "auto",
3404
3754
  color: "var(--srte-menu-text)",
3405
- }, onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, fontSize: 11, margin: "2px 6px 6px" }, children: "Table" }), _jsxs("div", { style: { display: "grid", gap: 4 }, children: [_jsxs("button", { style: {
3406
- display: "flex",
3407
- alignItems: "center",
3408
- gap: 8,
3409
- padding: "6px 8px",
3410
- fontSize: 12,
3411
- }, onClick: () => setShowTableDialog(true), children: [_jsx("span", { children: "\u2795" }), _jsx("span", { children: "Insert table\u2026" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("div", { style: {
3755
+ }, onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, fontSize: 11, margin: "2px 6px 6px" }, children: "Table" }), _jsxs("div", { style: { display: "grid", gap: 4 }, children: [_jsxs("div", { style: {
3412
3756
  display: "flex",
3413
3757
  gap: 8,
3414
3758
  alignItems: "center",
3415
3759
  padding: "4px 6px",
3416
3760
  fontSize: 12,
3417
3761
  }, children: [_jsx("span", { children: "Fill:" }), _jsx("input", { type: "color", defaultValue: "#ffffff", onChange: (e) => {
3418
- applyBgToSelection(e.target.value, tableMenu.cell);
3419
- setTableMenu(null);
3762
+ runTableCellAction(tableMenu.cell, (cell) => applyBgToSelection(e.target.value, cell));
3420
3763
  }, style: {
3421
3764
  width: 28,
3422
3765
  height: 18,
3423
3766
  padding: 0,
3424
3767
  border: "none",
3425
3768
  background: "transparent",
3426
- } })] }), _jsxs("button", { style: {
3769
+ } })] }), _jsxs("button", { title: "Show or hide border for this cell, or for the selected cell range.", style: {
3427
3770
  display: "flex",
3428
3771
  alignItems: "center",
3429
3772
  gap: 8,
3430
3773
  padding: "6px 8px",
3431
3774
  fontSize: 12,
3432
3775
  }, onClick: () => {
3433
- toggleBorderSelection(tableMenu.cell);
3434
- setTableMenu(null);
3435
- }, children: [_jsx("span", { children: "\u25A6" }), _jsx("span", { children: "Toggle border" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { disabled: !canMergeSelection(), style: {
3776
+ runTableCellAction(tableMenu.cell, toggleBorderSelection);
3777
+ }, children: [_jsx("span", { children: "\u25A6" }), _jsx("span", { children: "Show/hide cell border" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { disabled: !canMergeFromCell(tableMenu.cell), style: {
3436
3778
  display: "flex",
3437
3779
  alignItems: "center",
3438
3780
  gap: 8,
3439
3781
  padding: "6px 8px",
3440
3782
  fontSize: 12,
3441
- opacity: canMergeSelection() ? 1 : 0.5,
3442
- cursor: canMergeSelection() ? "pointer" : "default",
3783
+ opacity: canMergeFromCell(tableMenu.cell) ? 1 : 0.45,
3784
+ cursor: canMergeFromCell(tableMenu.cell) ? "pointer" : "not-allowed",
3443
3785
  }, onClick: () => {
3786
+ if (!canMergeFromCell(tableMenu.cell))
3787
+ return;
3444
3788
  mergeSelection();
3445
3789
  setTableMenu(null);
3446
- }, children: [_jsx("span", { children: "\u21C4" }), _jsx("span", { children: "Merge cells" })] }), _jsxs("button", { style: {
3790
+ }, children: [_jsx("span", { children: "\u21C4" }), _jsx("span", { children: "Merge cells" })] }), _jsxs("button", { disabled: !canSplitCell(tableMenu.cell), title: canSplitCell(tableMenu.cell) ? "Split this merged cell back into individual cells." : "Only merged cells can be split.", style: {
3447
3791
  display: "flex",
3448
3792
  alignItems: "center",
3449
3793
  gap: 8,
3450
3794
  padding: "6px 8px",
3451
3795
  fontSize: 12,
3796
+ opacity: canSplitCell(tableMenu.cell) ? 1 : 0.45,
3797
+ cursor: canSplitCell(tableMenu.cell) ? "pointer" : "not-allowed",
3452
3798
  }, onClick: () => {
3453
- splitCell(tableMenu.cell);
3454
- setTableMenu(null);
3799
+ if (!canSplitCell(tableMenu.cell))
3800
+ return;
3801
+ runTableCellAction(tableMenu.cell, splitCell);
3455
3802
  }, children: [_jsx("span", { children: "\u2922" }), _jsx("span", { children: "Split cell" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { style: {
3456
3803
  display: "flex",
3457
3804
  alignItems: "center",
@@ -3459,8 +3806,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
3459
3806
  padding: "6px 8px",
3460
3807
  fontSize: 12,
3461
3808
  }, onClick: () => {
3462
- addRow(tableMenu.cell, "above");
3463
- setTableMenu(null);
3809
+ runTableCellAction(tableMenu.cell, (cell) => addRow(cell, "above"));
3464
3810
  }, children: [_jsx("span", { children: "\u21A5" }), _jsx("span", { children: "Row above" })] }), _jsxs("button", { style: {
3465
3811
  display: "flex",
3466
3812
  alignItems: "center",
@@ -3468,8 +3814,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
3468
3814
  padding: "6px 8px",
3469
3815
  fontSize: 12,
3470
3816
  }, onClick: () => {
3471
- addRow(tableMenu.cell, "below");
3472
- setTableMenu(null);
3817
+ runTableCellAction(tableMenu.cell, (cell) => addRow(cell, "below"));
3473
3818
  }, children: [_jsx("span", { children: "\u21A7" }), _jsx("span", { children: "Row below" })] }), _jsxs("button", { style: {
3474
3819
  display: "flex",
3475
3820
  alignItems: "center",
@@ -3477,8 +3822,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
3477
3822
  padding: "6px 8px",
3478
3823
  fontSize: 12,
3479
3824
  }, onClick: () => {
3480
- addCol(tableMenu.cell, "left");
3481
- setTableMenu(null);
3825
+ runTableCellAction(tableMenu.cell, (cell) => addCol(cell, "left"));
3482
3826
  }, children: [_jsx("span", { children: "\u2190" }), _jsx("span", { children: "Column left" })] }), _jsxs("button", { style: {
3483
3827
  display: "flex",
3484
3828
  alignItems: "center",
@@ -3486,8 +3830,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
3486
3830
  padding: "6px 8px",
3487
3831
  fontSize: 12,
3488
3832
  }, onClick: () => {
3489
- addCol(tableMenu.cell, "right");
3490
- setTableMenu(null);
3833
+ runTableCellAction(tableMenu.cell, (cell) => addCol(cell, "right"));
3491
3834
  }, children: [_jsx("span", { children: "\u2192" }), _jsx("span", { children: "Column right" })] }), _jsxs("button", { style: {
3492
3835
  display: "flex",
3493
3836
  alignItems: "center",
@@ -3495,8 +3838,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
3495
3838
  padding: "6px 8px",
3496
3839
  fontSize: 12,
3497
3840
  }, onClick: () => {
3498
- deleteRow(tableMenu.cell);
3499
- setTableMenu(null);
3841
+ runTableCellAction(tableMenu.cell, deleteRow);
3500
3842
  }, children: [_jsx("span", { children: "\u2716" }), _jsx("span", { children: "Delete row" })] }), _jsxs("button", { style: {
3501
3843
  display: "flex",
3502
3844
  alignItems: "center",
@@ -3504,8 +3846,7 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
3504
3846
  padding: "6px 8px",
3505
3847
  fontSize: 12,
3506
3848
  }, onClick: () => {
3507
- deleteCol(tableMenu.cell);
3508
- setTableMenu(null);
3849
+ runTableCellAction(tableMenu.cell, deleteCol);
3509
3850
  }, children: [_jsx("span", { children: "\u2716" }), _jsx("span", { children: "Delete column" })] }), _jsxs("button", { style: {
3510
3851
  display: "flex",
3511
3852
  alignItems: "center",
@@ -3513,36 +3854,37 @@ export function ClassicEditor({ value, onChange, placeholder = "Type here…", m
3513
3854
  padding: "6px 8px",
3514
3855
  fontSize: 12,
3515
3856
  }, onClick: () => {
3516
- toggleHeaderCell(tableMenu.cell);
3517
- setTableMenu(null);
3518
- }, children: [_jsx("span", { children: "H" }), _jsx("span", { children: "Toggle header" })] }), _jsxs("button", { style: {
3857
+ runTableCellAction(tableMenu.cell, toggleHeaderCell);
3858
+ }, children: [_jsx("span", { children: "H" }), _jsx("span", { children: tableMenu.cell.tagName === "TH" ? "Remove cell header" : "Make cell header" })] }), _jsxs("button", { style: {
3519
3859
  display: "flex",
3520
3860
  alignItems: "center",
3521
3861
  gap: 8,
3522
3862
  padding: "6px 8px",
3523
3863
  fontSize: 12,
3524
3864
  }, onClick: () => {
3525
- toggleHeaderRow(tableMenu.cell);
3526
- setTableMenu(null);
3527
- }, children: [_jsx("span", { children: "H\u2081" }), _jsx("span", { children: "Toggle header row" })] }), _jsxs("button", { style: {
3865
+ runTableCellAction(tableMenu.cell, toggleHeaderRow);
3866
+ }, children: [_jsx("span", { children: "H\u2081" }), _jsx("span", { children: "Make this row header" })] }), _jsxs("button", { style: {
3528
3867
  display: "flex",
3529
3868
  alignItems: "center",
3530
3869
  gap: 8,
3531
3870
  padding: "6px 8px",
3532
3871
  fontSize: 12,
3533
3872
  }, onClick: () => {
3534
- toggleHeaderColumn(tableMenu.cell);
3535
- setTableMenu(null);
3536
- }, children: [_jsx("span", { children: "H\u2195" }), _jsx("span", { children: "Toggle header column" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { style: {
3873
+ runTableCellAction(tableMenu.cell, toggleHeaderColumn);
3874
+ }, children: [_jsx("span", { children: "H\u2195" }), _jsx("span", { children: "Make this column header" })] }), _jsx("hr", { style: { margin: "4px 0" } }), _jsxs("button", { style: {
3537
3875
  display: "flex",
3538
3876
  alignItems: "center",
3539
3877
  gap: 8,
3540
3878
  padding: "6px 8px",
3541
3879
  fontSize: 12,
3542
3880
  }, onClick: () => {
3543
- deleteTable(tableMenu.cell);
3544
- setTableMenu(null);
3545
- }, children: [_jsx("span", { children: "\uD83D\uDDD1" }), _jsx("span", { children: "Delete table" })] })] })] }) })), imageMenu && (_jsx("div", { style: { position: "fixed", inset: 0, zIndex: 60 }, onClick: () => setImageMenu(null), onContextMenu: (e) => {
3881
+ runTableCellAction(tableMenu.cell, deleteTable);
3882
+ }, children: [_jsx("span", { children: "\uD83D\uDDD1" }), _jsx("span", { children: "Delete table" })] })] })] }) })), imageMenu && (_jsx("div", { style: {
3883
+ position: "fixed",
3884
+ inset: 0,
3885
+ zIndex: 60,
3886
+ background: "transparent",
3887
+ }, onClick: () => setImageMenu(null), onContextMenu: (e) => {
3546
3888
  e.preventDefault();
3547
3889
  const vw = window.innerWidth;
3548
3890
  const vh = window.innerHeight;