@sofereditor/react 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -38,6 +38,7 @@ __export(index_exports, {
38
38
  getBlockElement: () => getBlockElement,
39
39
  getCellElement: () => getCellElement,
40
40
  getFragmentForOffset: () => getFragmentForOffset,
41
+ htmlToSlice: () => htmlToSlice,
41
42
  isTableRectSelection: () => isTableRectSelection,
42
43
  locatePoint: () => locatePoint,
43
44
  readDomSelection: () => readDomSelection,
@@ -54,7 +55,7 @@ module.exports = __toCommonJS(index_exports);
54
55
 
55
56
  // src/Editor.tsx
56
57
  var import_react14 = require("react");
57
- var import_core6 = require("@sofereditor/core");
58
+ var import_core8 = require("@sofereditor/core");
58
59
 
59
60
  // src/dom-bridge.ts
60
61
  function findContainingBlockElement(node, root) {
@@ -329,6 +330,615 @@ function applyDomSelection(root, modelSel) {
329
330
  }
330
331
  }
331
332
 
333
+ // src/pastePlan.ts
334
+ var import_core2 = require("@sofereditor/core");
335
+
336
+ // src/htmlToSlice.ts
337
+ var import_core = require("@sofereditor/core");
338
+ function htmlToSlice(html) {
339
+ if (!html || html.trim().length === 0) return null;
340
+ let doc;
341
+ try {
342
+ doc = new DOMParser().parseFromString(html, "text/html");
343
+ } catch {
344
+ return null;
345
+ }
346
+ const body = doc.body;
347
+ if (!body) return null;
348
+ const blocks = [];
349
+ try {
350
+ walkBlockLevel(body, {}, blocks);
351
+ } catch {
352
+ return null;
353
+ }
354
+ if (blocks.length === 0) return null;
355
+ normalizeListLevels(blocks);
356
+ const temTexto = blocks.some((b) => b.text.trim().length > 0);
357
+ if (!temTexto) return null;
358
+ const blockLevel = blocks.length > 1 || blocks[0].type !== "paragraph";
359
+ return { blocks, openStart: false, openEnd: false, ...blockLevel ? { blockLevel: true } : {} };
360
+ }
361
+ var IGNORED_TAGS = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "head", "title", "xml"]);
362
+ var BLOCK_LEVEL_TAGS = /* @__PURE__ */ new Set([
363
+ "p",
364
+ "div",
365
+ "section",
366
+ "article",
367
+ "header",
368
+ "footer",
369
+ "main",
370
+ "figure",
371
+ "figcaption",
372
+ "pre",
373
+ "address"
374
+ ]);
375
+ var BLOCK_TRIGGER_TAGS = /* @__PURE__ */ new Set([
376
+ "p",
377
+ "div",
378
+ "ul",
379
+ "ol",
380
+ "li",
381
+ "h1",
382
+ "h2",
383
+ "h3",
384
+ "h4",
385
+ "h5",
386
+ "h6",
387
+ "blockquote",
388
+ "table",
389
+ "tr",
390
+ "td",
391
+ "th"
392
+ ]);
393
+ function isNamespacedTag(tag) {
394
+ return tag.includes(":");
395
+ }
396
+ function hasBlockDescendant(el) {
397
+ const all = el.querySelectorAll("*");
398
+ for (const e of Array.from(all)) {
399
+ if (BLOCK_TRIGGER_TAGS.has(e.tagName.toLowerCase())) return true;
400
+ }
401
+ return false;
402
+ }
403
+ function walkBlockLevel(container, marks, blocks) {
404
+ let loose = [];
405
+ const flushLoose = () => {
406
+ if (loose.length === 0) return;
407
+ const nodes = loose;
408
+ loose = [];
409
+ const delta = inlineDeltaFromNodes(nodes, marks);
410
+ if (delta.length > 0) blocks.push(makeBlock("paragraph", delta, {}));
411
+ };
412
+ for (const node of Array.from(container.childNodes)) {
413
+ if (node.nodeType === Node.COMMENT_NODE) continue;
414
+ if (node.nodeType === Node.TEXT_NODE) {
415
+ loose.push(node);
416
+ continue;
417
+ }
418
+ if (node.nodeType !== Node.ELEMENT_NODE) continue;
419
+ const el = node;
420
+ const tag = el.tagName.toLowerCase();
421
+ if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) continue;
422
+ switch (tag) {
423
+ case "ul":
424
+ case "ol": {
425
+ flushLoose();
426
+ handleList(el, tag === "ol" ? "ordered" : "bullet", 0, marks, blocks);
427
+ break;
428
+ }
429
+ case "h1":
430
+ case "h2":
431
+ case "h3":
432
+ case "h4":
433
+ case "h5":
434
+ case "h6": {
435
+ flushLoose();
436
+ const level = Number(tag[1]);
437
+ const attrs = { level };
438
+ const align = readAlign(el);
439
+ if (align) attrs.align = align;
440
+ const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
441
+ blocks.push(makeBlock("heading", delta, attrs));
442
+ break;
443
+ }
444
+ case "blockquote": {
445
+ flushLoose();
446
+ handleBlockquote(el, marks, blocks);
447
+ break;
448
+ }
449
+ case "table": {
450
+ flushLoose();
451
+ handleTable(el, marks, blocks);
452
+ break;
453
+ }
454
+ case "li": {
455
+ flushLoose();
456
+ const nestedLists = topLevelListDescendants(el);
457
+ const clone = el.cloneNode(true);
458
+ for (const l of topLevelListDescendants(clone)) l.remove();
459
+ const delta = inlineDeltaFromNodes(Array.from(clone.childNodes), marks);
460
+ blocks.push(makeBlock("listItem", delta, { listKind: "bullet", listLevel: 0 }));
461
+ for (const nested of nestedLists) {
462
+ const nestedKind = nested.tagName.toLowerCase() === "ol" ? "ordered" : "bullet";
463
+ handleList(nested, nestedKind, 1, marks, blocks);
464
+ }
465
+ break;
466
+ }
467
+ case "img":
468
+ break;
469
+ case "br":
470
+ loose.push(node);
471
+ break;
472
+ default: {
473
+ const styleAttr = el.getAttribute("style") ?? "";
474
+ const wordListLevel = parseWordListLevel(styleAttr);
475
+ if (wordListLevel != null && !hasBlockDescendant(el)) {
476
+ flushLoose();
477
+ const marker = findWordListMarker(el);
478
+ const listKind = marker ? detectWordListKind(marker) : "bullet";
479
+ const attrs = { listKind, listLevel: wordListLevel };
480
+ const align = readAlign(el);
481
+ if (align) attrs.align = align;
482
+ const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
483
+ blocks.push(makeBlock("listItem", delta, attrs));
484
+ break;
485
+ }
486
+ if (hasBlockDescendant(el)) {
487
+ flushLoose();
488
+ let nextMarks = applyTagMarks(tag, marks);
489
+ nextMarks = applyStyleMarks(el.getAttribute("style"), nextMarks);
490
+ walkBlockLevel(el, nextMarks, blocks);
491
+ break;
492
+ }
493
+ if (BLOCK_LEVEL_TAGS.has(tag)) {
494
+ flushLoose();
495
+ const attrs = {};
496
+ const align = readAlign(el);
497
+ if (align) attrs.align = align;
498
+ const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
499
+ blocks.push(makeBlock("paragraph", delta, attrs));
500
+ break;
501
+ }
502
+ loose.push(node);
503
+ }
504
+ }
505
+ }
506
+ flushLoose();
507
+ }
508
+ function handleList(listEl, kind, level, marks, blocks) {
509
+ for (const child of Array.from(listEl.children)) {
510
+ const tag = child.tagName.toLowerCase();
511
+ if (tag === "ul" || tag === "ol") {
512
+ const siblingKind = tag === "ol" ? "ordered" : "bullet";
513
+ handleList(child, siblingKind, level + 1, marks, blocks);
514
+ continue;
515
+ }
516
+ if (tag !== "li") continue;
517
+ const nestedLists = topLevelListDescendants(child);
518
+ const clone = child.cloneNode(true);
519
+ for (const l of topLevelListDescendants(clone)) l.remove();
520
+ const delta = inlineDeltaFromNodes(Array.from(clone.childNodes), marks);
521
+ const attrs = { listKind: kind, listLevel: clampLevel(level) };
522
+ const align = readAlign(child);
523
+ if (align) attrs.align = align;
524
+ blocks.push(makeBlock("listItem", delta, attrs));
525
+ for (const nested of nestedLists) {
526
+ const nestedKind = nested.tagName.toLowerCase() === "ol" ? "ordered" : "bullet";
527
+ handleList(nested, nestedKind, level + 1, marks, blocks);
528
+ }
529
+ }
530
+ }
531
+ function topLevelListDescendants(root) {
532
+ const all = Array.from(root.querySelectorAll("ul, ol"));
533
+ return all.filter((el) => {
534
+ let p = el.parentElement;
535
+ while (p && p !== root) {
536
+ const t = p.tagName.toLowerCase();
537
+ if (t === "ul" || t === "ol" || t === "table" || t === "td" || t === "th") return false;
538
+ p = p.parentElement;
539
+ }
540
+ return true;
541
+ });
542
+ }
543
+ function handleBlockquote(el, marks, blocks) {
544
+ if (!hasBlockDescendant(el)) {
545
+ const attrs = {};
546
+ const align = readAlign(el);
547
+ if (align) attrs.align = align;
548
+ const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
549
+ blocks.push(makeBlock("blockquote", delta, attrs));
550
+ return;
551
+ }
552
+ const nested = [];
553
+ walkBlockLevel(el, marks, nested);
554
+ for (const b of nested) {
555
+ blocks.push(b.type === "paragraph" ? { ...b, type: "blockquote" } : b);
556
+ }
557
+ }
558
+ function handleTable(table, marks, blocks) {
559
+ for (const row of directRows(table)) {
560
+ const cells = Array.from(row.children).filter(
561
+ (c) => ["td", "th"].includes(c.tagName.toLowerCase())
562
+ );
563
+ for (const cell of cells) {
564
+ const cellBlocks = [];
565
+ walkBlockLevel(cell, marks, cellBlocks);
566
+ for (const b of cellBlocks) {
567
+ const attrs = {};
568
+ if (b.attrs.align) attrs.align = b.attrs.align;
569
+ blocks.push(makeBlock("paragraph", b.delta, attrs));
570
+ }
571
+ }
572
+ }
573
+ }
574
+ function directRows(table) {
575
+ const rows = [];
576
+ for (const child of Array.from(table.children)) {
577
+ const tag = child.tagName.toLowerCase();
578
+ if (tag === "tr") {
579
+ rows.push(child);
580
+ } else if (tag === "tbody" || tag === "thead" || tag === "tfoot") {
581
+ for (const r of Array.from(child.children)) {
582
+ if (r.tagName.toLowerCase() === "tr") rows.push(r);
583
+ }
584
+ }
585
+ }
586
+ return rows;
587
+ }
588
+ function parseWordListLevel(styleAttr) {
589
+ const m = /mso-list\s*:\s*l\d+\s+level(\d+)/i.exec(styleAttr);
590
+ if (!m) return null;
591
+ const n = Number.parseInt(m[1], 10);
592
+ const fromLevelN = Number.isFinite(n) ? Math.max(0, n - 1) : 0;
593
+ const fromMargin = wordListLevelFromMarginLeft(marginLeftPt(styleAttr));
594
+ return clampLevel(Math.max(fromLevelN, fromMargin));
595
+ }
596
+ function marginLeftPt(styleAttr) {
597
+ const raw = parseStyle(styleAttr)["margin-left"];
598
+ if (!raw) return 0;
599
+ const m = /^(-?[\d.]+)\s*(pt|cm|in|px)$/i.exec(raw.trim());
600
+ if (!m) return 0;
601
+ const val = Number.parseFloat(m[1]);
602
+ if (!Number.isFinite(val) || val < 0) return 0;
603
+ switch (m[2].toLowerCase()) {
604
+ case "pt":
605
+ return val;
606
+ case "cm":
607
+ return val * 28.3465;
608
+ case "in":
609
+ return val * 72;
610
+ case "px":
611
+ return val * 0.75;
612
+ default:
613
+ return 0;
614
+ }
615
+ }
616
+ function wordListLevelFromMarginLeft(pt) {
617
+ if (pt <= 0) return 0;
618
+ return Math.max(0, Math.round(pt / 36) - 1);
619
+ }
620
+ function findWordListMarker(el) {
621
+ const all = el.querySelectorAll("*");
622
+ for (const e of Array.from(all)) {
623
+ const style = (e.getAttribute("style") ?? "").toLowerCase().replace(/\s+/g, "");
624
+ if (style.includes("mso-list:ignore")) return e;
625
+ }
626
+ return null;
627
+ }
628
+ function detectWordListKind(markerEl) {
629
+ const all = [markerEl, ...Array.from(markerEl.querySelectorAll("*"))];
630
+ for (const e of all) {
631
+ const style = (e.getAttribute("style") ?? "").toLowerCase();
632
+ if (style.includes("symbol") || style.includes("wingdings")) return "bullet";
633
+ }
634
+ const text = (markerEl.textContent ?? "").replace(/ /g, " ").trim();
635
+ if (/^[0-9]+[.)]/.test(text)) return "ordered";
636
+ if (/^[a-zA-Z][.)]/.test(text)) return "ordered";
637
+ return "bullet";
638
+ }
639
+ function inlineDeltaFromNodes(nodes, marks) {
640
+ const ops = [];
641
+ for (const n of nodes) walkInlineNode(n, marks, ops);
642
+ return trimEdgeWhitespace(collapseRepeatedSpaces(mergeAdjacentOps(ops)));
643
+ }
644
+ function collapseRepeatedSpaces(ops) {
645
+ return ops.map(
646
+ (op) => typeof op.insert === "string" ? { ...op, insert: op.insert.replace(/ {2,}/g, " ") } : op
647
+ );
648
+ }
649
+ function walkInlineNode(node, marks, ops) {
650
+ if (node.nodeType === Node.COMMENT_NODE) return;
651
+ if (node.nodeType === Node.TEXT_NODE) {
652
+ const text = normalizeWhitespace(node.textContent ?? "");
653
+ if (text.length > 0) pushOp(ops, text, marks);
654
+ return;
655
+ }
656
+ if (node.nodeType !== Node.ELEMENT_NODE) return;
657
+ const el = node;
658
+ const tag = el.tagName.toLowerCase();
659
+ if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) return;
660
+ if (tag === "img") return;
661
+ if (tag === "br") {
662
+ ops.push({ insert: " " });
663
+ return;
664
+ }
665
+ const styleAttr = (el.getAttribute("style") ?? "").toLowerCase().replace(/\s+/g, "");
666
+ if (styleAttr.includes("mso-list:ignore")) return;
667
+ let nextMarks = applyTagMarks(tag, marks);
668
+ nextMarks = applyStyleMarks(el.getAttribute("style"), nextMarks);
669
+ if (tag === "a") {
670
+ const href = el.getAttribute("href");
671
+ if (href) nextMarks = { ...nextMarks, link: { href } };
672
+ }
673
+ for (const child of Array.from(el.childNodes)) walkInlineNode(child, nextMarks, ops);
674
+ }
675
+ function applyTagMarks(tag, marks) {
676
+ switch (tag) {
677
+ case "b":
678
+ case "strong":
679
+ return { ...marks, bold: true };
680
+ case "i":
681
+ case "em":
682
+ return { ...marks, italic: true };
683
+ case "u":
684
+ return { ...marks, underline: true };
685
+ case "s":
686
+ case "strike":
687
+ case "del":
688
+ return { ...marks, strike: true };
689
+ default:
690
+ return marks;
691
+ }
692
+ }
693
+ function parseStyle(styleAttr) {
694
+ const out = {};
695
+ if (!styleAttr) return out;
696
+ for (const decl of styleAttr.split(";")) {
697
+ const idx = decl.indexOf(":");
698
+ if (idx === -1) continue;
699
+ const prop = decl.slice(0, idx).trim().toLowerCase();
700
+ const val = decl.slice(idx + 1).trim();
701
+ if (prop) out[prop] = val;
702
+ }
703
+ return out;
704
+ }
705
+ function applyStyleMarks(styleAttr, marks) {
706
+ const decls = parseStyle(styleAttr);
707
+ if (Object.keys(decls).length === 0) return marks;
708
+ const next = { ...marks };
709
+ if ("font-weight" in decls) {
710
+ const v = decls["font-weight"].toLowerCase();
711
+ if (v === "bold" || /^[5-9]00$/.test(v)) next.bold = true;
712
+ else if (v === "normal" || /^[1-4]00$/.test(v)) delete next.bold;
713
+ }
714
+ if ("font-style" in decls) {
715
+ const v = decls["font-style"].toLowerCase();
716
+ if (v === "italic" || v === "oblique") next.italic = true;
717
+ else if (v === "normal") delete next.italic;
718
+ }
719
+ const deco = decls["text-decoration-line"] ?? decls["text-decoration"];
720
+ if (deco !== void 0) {
721
+ const v = deco.toLowerCase();
722
+ delete next.underline;
723
+ delete next.strike;
724
+ if (v.includes("underline")) next.underline = true;
725
+ if (v.includes("line-through")) next.strike = true;
726
+ }
727
+ if ("color" in decls) {
728
+ const css = normalizeCssColor(decls.color);
729
+ if (css && !isBlack(css)) next.color = css;
730
+ else delete next.color;
731
+ }
732
+ const bg = decls["background-color"] ?? decls.background;
733
+ if (bg !== void 0) {
734
+ const css = normalizeCssColor(bg);
735
+ if (css && css !== "#ffffff") next.highlight = css;
736
+ else delete next.highlight;
737
+ }
738
+ if ("font-size" in decls) {
739
+ const pt = parseFontSizePt(decls["font-size"]);
740
+ if (pt != null) next.fontSize = formatFontSizePt(pt);
741
+ }
742
+ return next;
743
+ }
744
+ function parseFontSizePt(raw) {
745
+ const v = raw.trim().toLowerCase();
746
+ const m = /^(-?[\d.]+)\s*(pt|px|in)$/.exec(v);
747
+ if (!m) return void 0;
748
+ const num = Number.parseFloat(m[1]);
749
+ if (!Number.isFinite(num)) return void 0;
750
+ let pt;
751
+ switch (m[2]) {
752
+ case "pt":
753
+ pt = num;
754
+ break;
755
+ case "px":
756
+ pt = num * 0.75;
757
+ break;
758
+ case "in":
759
+ pt = num * 72;
760
+ break;
761
+ default:
762
+ return void 0;
763
+ }
764
+ const rounded = Math.round(pt * 10) / 10;
765
+ if (rounded <= 0 || rounded > 200) return void 0;
766
+ return rounded;
767
+ }
768
+ function formatFontSizePt(pt) {
769
+ const rounded = Math.round(pt * 10) / 10;
770
+ const str = Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
771
+ return `${str}pt`;
772
+ }
773
+ var NAMED_COLORS = {
774
+ black: "#000000",
775
+ white: "#ffffff",
776
+ red: "#ff0000",
777
+ green: "#008000",
778
+ blue: "#0000ff",
779
+ yellow: "#ffff00",
780
+ cyan: "#00ffff",
781
+ magenta: "#ff00ff",
782
+ gray: "#808080",
783
+ grey: "#808080",
784
+ orange: "#ffa500",
785
+ purple: "#800080"
786
+ };
787
+ function normalizeCssColor(raw) {
788
+ if (!raw) return void 0;
789
+ const v = raw.trim().toLowerCase();
790
+ if (!v || v === "transparent" || v === "none" || v === "inherit" || v === "initial") return void 0;
791
+ if (v.startsWith("#")) {
792
+ let hex = v.slice(1);
793
+ if (hex.length === 3) hex = hex.split("").map((c) => c + c).join("");
794
+ return /^[0-9a-f]{6}$/.test(hex) ? `#${hex}` : void 0;
795
+ }
796
+ const rgbMatch = /^rgba?\(([^)]+)\)$/.exec(v);
797
+ if (rgbMatch) {
798
+ const parts = rgbMatch[1].split(",").map((s) => s.trim());
799
+ if (parts.length < 3) return void 0;
800
+ const alpha = parts[3] !== void 0 ? Number.parseFloat(parts[3]) : 1;
801
+ if (alpha === 0) return void 0;
802
+ const toHex = (n) => {
803
+ const num = Math.max(0, Math.min(255, Number.parseInt(n, 10) || 0));
804
+ return num.toString(16).padStart(2, "0");
805
+ };
806
+ return `#${toHex(parts[0])}${toHex(parts[1])}${toHex(parts[2])}`;
807
+ }
808
+ return NAMED_COLORS[v];
809
+ }
810
+ function isBlack(hex) {
811
+ return hex.toLowerCase() === "#000000";
812
+ }
813
+ function readAlign(el) {
814
+ const style = parseStyle(el.getAttribute("style"));
815
+ const v = style["text-align"];
816
+ if (!v) return void 0;
817
+ switch (v.toLowerCase()) {
818
+ case "left":
819
+ case "start":
820
+ return "left";
821
+ case "center":
822
+ return "center";
823
+ case "right":
824
+ case "end":
825
+ return "right";
826
+ case "justify":
827
+ return "justify";
828
+ default:
829
+ return void 0;
830
+ }
831
+ }
832
+ function clampLevel(n) {
833
+ if (!Number.isFinite(n)) return 0;
834
+ return Math.max(0, Math.min(import_core.MAX_LIST_LEVEL, Math.trunc(n)));
835
+ }
836
+ function normalizeListLevels(blocks) {
837
+ let i = 0;
838
+ while (i < blocks.length) {
839
+ if (blocks[i].type !== "listItem") {
840
+ i++;
841
+ continue;
842
+ }
843
+ let j = i + 1;
844
+ while (j < blocks.length && blocks[j].type === "listItem") j++;
845
+ if (j - i > 1) {
846
+ let min = Number.POSITIVE_INFINITY;
847
+ for (let k = i; k < j; k++) {
848
+ const lvl = blocks[k].attrs.listLevel ?? 0;
849
+ if (lvl < min) min = lvl;
850
+ }
851
+ if (min > 0) {
852
+ for (let k = i; k < j; k++) {
853
+ const lvl = blocks[k].attrs.listLevel ?? 0;
854
+ blocks[k].attrs.listLevel = clampLevel(lvl - min);
855
+ }
856
+ }
857
+ }
858
+ i = j;
859
+ }
860
+ }
861
+ function pushOp(ops, text, marks) {
862
+ const hasMarks = Object.keys(marks).length > 0;
863
+ ops.push(hasMarks ? { insert: text, attributes: { ...marks } } : { insert: text });
864
+ }
865
+ function normalizeWhitespace(raw) {
866
+ return raw.replace(/[ \t\r\n]+/g, " ");
867
+ }
868
+ function trimEdgeWhitespace(ops) {
869
+ let out = ops;
870
+ if (out.length > 0 && typeof out[0].insert === "string") {
871
+ const trimmed = out[0].insert.replace(/^ +/, "");
872
+ if (trimmed.length === 0) out = out.slice(1);
873
+ else if (trimmed !== out[0].insert) out = [{ ...out[0], insert: trimmed }, ...out.slice(1)];
874
+ }
875
+ if (out.length > 0 && typeof out[out.length - 1].insert === "string") {
876
+ const last = out[out.length - 1];
877
+ const trimmed = last.insert.replace(/ +$/, "");
878
+ if (trimmed.length === 0) out = out.slice(0, -1);
879
+ else if (trimmed !== last.insert) out = [...out.slice(0, -1), { ...last, insert: trimmed }];
880
+ }
881
+ return out;
882
+ }
883
+ function mergeAdjacentOps(ops) {
884
+ const out = [];
885
+ for (const op of ops) {
886
+ const prev = out[out.length - 1];
887
+ if (prev && typeof prev.insert === "string" && typeof op.insert === "string" && sameMarks(prev.attributes, op.attributes)) {
888
+ prev.insert = prev.insert + op.insert;
889
+ continue;
890
+ }
891
+ out.push({ ...op });
892
+ }
893
+ return out;
894
+ }
895
+ function sameMarks(a, b) {
896
+ const ak = a ? Object.keys(a).sort() : [];
897
+ const bk = b ? Object.keys(b).sort() : [];
898
+ if (ak.length !== bk.length) return false;
899
+ for (let i = 0; i < ak.length; i++) {
900
+ if (ak[i] !== bk[i]) return false;
901
+ const av = a[ak[i]];
902
+ const bv = b[bk[i]];
903
+ if (typeof av === "object" && av !== null && typeof bv === "object" && bv !== null) {
904
+ if (JSON.stringify(av) !== JSON.stringify(bv)) return false;
905
+ } else if (av !== bv) {
906
+ return false;
907
+ }
908
+ }
909
+ return true;
910
+ }
911
+ function deltaToText(delta) {
912
+ let s = "";
913
+ for (const op of delta) if (typeof op.insert === "string") s += op.insert;
914
+ return s;
915
+ }
916
+ function makeBlock(type, delta, attrs) {
917
+ return { type, text: deltaToText(delta), delta, attrs };
918
+ }
919
+
920
+ // src/pastePlan.ts
921
+ function planPaste(cd) {
922
+ const raw = cd.getData(import_core2.SOFER_MIME);
923
+ if (raw) {
924
+ try {
925
+ const slice = JSON.parse(raw);
926
+ if (slice && Array.isArray(slice.blocks)) return { kind: "sofer", slice };
927
+ } catch {
928
+ }
929
+ }
930
+ const html = cd.getData("text/html");
931
+ if (html) {
932
+ const slice = htmlToSlice(html);
933
+ if (slice) return { kind: "html", slice };
934
+ }
935
+ const files = Array.from(cd.files ?? []).filter((f) => f.type.startsWith("image/"));
936
+ if (files.length > 0) return { kind: "images", files };
937
+ const text = cd.getData("text/plain");
938
+ if (text.length > 0) return { kind: "text", text };
939
+ return { kind: "none" };
940
+ }
941
+
332
942
  // src/BehindImageSelectAffordance.tsx
333
943
  var import_react = require("react");
334
944
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -645,7 +1255,7 @@ function LinkDialog() {
645
1255
 
646
1256
  // src/PageConfigDialog.tsx
647
1257
  var import_react3 = require("react");
648
- var import_core = require("@sofereditor/core");
1258
+ var import_core3 = require("@sofereditor/core");
649
1259
  var import_jsx_runtime4 = require("react/jsx-runtime");
650
1260
  function PageConfigDialog() {
651
1261
  const { pageConfigRequest, resolvePageConfigRequest, setPageSettings } = useEditorContext();
@@ -679,14 +1289,14 @@ function PageConfigDialog() {
679
1289
  }, [pageConfigRequest]);
680
1290
  (0, import_react3.useEffect)(() => {
681
1291
  if (sizePreset === "custom") return;
682
- const p = import_core.PAGE_PRESETS[sizePreset];
683
- setWidthMm(roundMm((0, import_core.pxToMm)(p.width)));
684
- setHeightMm(roundMm((0, import_core.pxToMm)(p.height)));
1292
+ const p = import_core3.PAGE_PRESETS[sizePreset];
1293
+ setWidthMm(roundMm((0, import_core3.pxToMm)(p.width)));
1294
+ setHeightMm(roundMm((0, import_core3.pxToMm)(p.height)));
685
1295
  }, [sizePreset]);
686
1296
  (0, import_react3.useEffect)(() => {
687
1297
  if (marginPreset === "custom") return;
688
- const px = import_core.MARGIN_PRESETS[marginPreset];
689
- const mm = roundMm((0, import_core.pxToMm)(px));
1298
+ const px = import_core3.MARGIN_PRESETS[marginPreset];
1299
+ const mm = roundMm((0, import_core3.pxToMm)(px));
690
1300
  setMarginTopMm(mm);
691
1301
  setMarginBottomMm(mm);
692
1302
  setMarginLeftMm(mm);
@@ -695,8 +1305,8 @@ function PageConfigDialog() {
695
1305
  const customWidth = sizePreset === "custom";
696
1306
  const previewPx = (0, import_react3.useMemo)(
697
1307
  () => ({
698
- width: Math.round((0, import_core.mmToPx)(widthMm)),
699
- height: Math.round((0, import_core.mmToPx)(heightMm))
1308
+ width: Math.round((0, import_core3.mmToPx)(widthMm)),
1309
+ height: Math.round((0, import_core3.mmToPx)(heightMm))
700
1310
  }),
701
1311
  [widthMm, heightMm]
702
1312
  );
@@ -704,12 +1314,12 @@ function PageConfigDialog() {
704
1314
  const onApply = (e) => {
705
1315
  e.preventDefault();
706
1316
  const next = {
707
- width: (0, import_core.mmToPx)(widthMm),
708
- height: (0, import_core.mmToPx)(heightMm),
709
- marginTop: (0, import_core.mmToPx)(marginTopMm),
710
- marginBottom: (0, import_core.mmToPx)(marginBottomMm),
711
- marginLeft: (0, import_core.mmToPx)(marginLeftMm),
712
- marginRight: (0, import_core.mmToPx)(marginRightMm)
1317
+ width: (0, import_core3.mmToPx)(widthMm),
1318
+ height: (0, import_core3.mmToPx)(heightMm),
1319
+ marginTop: (0, import_core3.mmToPx)(marginTopMm),
1320
+ marginBottom: (0, import_core3.mmToPx)(marginBottomMm),
1321
+ marginLeft: (0, import_core3.mmToPx)(marginLeftMm),
1322
+ marginRight: (0, import_core3.mmToPx)(marginRightMm)
713
1323
  };
714
1324
  setPageSettings(next);
715
1325
  resolvePageConfigRequest(true);
@@ -968,14 +1578,14 @@ function roundMm(mm) {
968
1578
  return Math.round(mm * 10) / 10;
969
1579
  }
970
1580
  function hydrateFromSettings(s, setters) {
971
- const preset = (0, import_core.detectPreset)(s.width, s.height);
1581
+ const preset = (0, import_core3.detectPreset)(s.width, s.height);
972
1582
  setters.setSizePreset(preset);
973
- setters.setWidthMm(roundMm((0, import_core.pxToMm)(s.width)));
974
- setters.setHeightMm(roundMm((0, import_core.pxToMm)(s.height)));
975
- const top = roundMm((0, import_core.pxToMm)(s.marginTop));
976
- const bottom = roundMm((0, import_core.pxToMm)(s.marginBottom));
977
- const left = roundMm((0, import_core.pxToMm)(s.marginLeft));
978
- const right = roundMm((0, import_core.pxToMm)(s.marginRight));
1583
+ setters.setWidthMm(roundMm((0, import_core3.pxToMm)(s.width)));
1584
+ setters.setHeightMm(roundMm((0, import_core3.pxToMm)(s.height)));
1585
+ const top = roundMm((0, import_core3.pxToMm)(s.marginTop));
1586
+ const bottom = roundMm((0, import_core3.pxToMm)(s.marginBottom));
1587
+ const left = roundMm((0, import_core3.pxToMm)(s.marginLeft));
1588
+ const right = roundMm((0, import_core3.pxToMm)(s.marginRight));
979
1589
  setters.setMarginTopMm(top);
980
1590
  setters.setMarginBottomMm(bottom);
981
1591
  setters.setMarginLeftMm(left);
@@ -986,7 +1596,7 @@ function detectMarginPreset(s) {
986
1596
  const TOL = 1;
987
1597
  const allEqual = Math.abs(s.marginTop - s.marginBottom) <= TOL && Math.abs(s.marginTop - s.marginLeft) <= TOL && Math.abs(s.marginTop - s.marginRight) <= TOL;
988
1598
  if (!allEqual) return "custom";
989
- for (const [name, px] of Object.entries(import_core.MARGIN_PRESETS)) {
1599
+ for (const [name, px] of Object.entries(import_core3.MARGIN_PRESETS)) {
990
1600
  if (Math.abs(s.marginTop - px) <= TOL) return name;
991
1601
  }
992
1602
  return "custom";
@@ -1546,7 +2156,7 @@ function Handle({
1546
2156
 
1547
2157
  // src/RemoteCursorsOverlay.tsx
1548
2158
  var import_react7 = require("react");
1549
- var import_core2 = require("@sofereditor/core");
2159
+ var import_core4 = require("@sofereditor/core");
1550
2160
  var import_jsx_runtime8 = require("react/jsx-runtime");
1551
2161
  var DEFAULT_COLOR = "#3b82f6";
1552
2162
  function RemoteCursorsOverlay({
@@ -1569,7 +2179,7 @@ function RemoteCursorsOverlay({
1569
2179
  if (clientId === awareness.clientID) return;
1570
2180
  const s = state;
1571
2181
  if (!s.cursor) return;
1572
- const sel = (0, import_core2.decodeSelection)(doc, s.cursor);
2182
+ const sel = (0, import_core4.decodeSelection)(doc, s.cursor);
1573
2183
  if (!sel) return;
1574
2184
  const color = s.user?.color || DEFAULT_COLOR;
1575
2185
  const name = s.user?.name || "An\xF4nimo";
@@ -1716,11 +2326,11 @@ function RemoteCursorsOverlay({
1716
2326
 
1717
2327
  // src/NodeView.tsx
1718
2328
  var import_react10 = require("react");
1719
- var import_core4 = require("@sofereditor/core");
2329
+ var import_core6 = require("@sofereditor/core");
1720
2330
 
1721
2331
  // src/renderInline.tsx
1722
2332
  var import_react8 = require("react");
1723
- var import_core3 = require("@sofereditor/core");
2333
+ var import_core5 = require("@sofereditor/core");
1724
2334
  var import_jsx_runtime9 = require("react/jsx-runtime");
1725
2335
  function renderInline(delta, keyPrefix) {
1726
2336
  if (delta.length === 0 || delta.length === 1 && typeof delta[0].insert === "string" && delta[0].insert === "") {
@@ -1736,7 +2346,7 @@ function renderInline(delta, keyPrefix) {
1736
2346
  const key = `${keyPrefix}-${offset}`;
1737
2347
  body.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react8.Fragment, { children: wrap(text, op.attributes) }, key));
1738
2348
  offset += text.length;
1739
- } else if ((0, import_core3.isImageEmbed)(op.insert)) {
2349
+ } else if ((0, import_core5.isImageEmbed)(op.insert)) {
1740
2350
  const key = `${keyPrefix}-${offset}`;
1741
2351
  const layout = op.insert.layout ?? "inline";
1742
2352
  const commentMarkId = op.attributes?.comment?.markId;
@@ -1900,10 +2510,10 @@ function ImageEmbedViewImpl({
1900
2510
  );
1901
2511
  }
1902
2512
  function decorateBlanks(text) {
1903
- const segs = (0, import_core3.splitUnderscoreRuns)(text);
2513
+ const segs = (0, import_core5.splitUnderscoreRuns)(text);
1904
2514
  if (segs.length === 1 && !segs[0].blank) return text;
1905
2515
  return segs.map(
1906
- (s, i) => s.blank ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-blank": "true", style: import_core3.BLANK_STYLE, children: s.text }, i) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react8.Fragment, { children: s.text }, i)
2516
+ (s, i) => s.blank ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-blank": "true", style: import_core5.BLANK_STYLE, children: s.text }, i) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react8.Fragment, { children: s.text }, i)
1907
2517
  );
1908
2518
  }
1909
2519
  function wrap(text, attrs) {
@@ -2101,7 +2711,7 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
2101
2711
  const common = commonBlockProps(block.attrs, index, block.type, fragment);
2102
2712
  switch (block.type) {
2103
2713
  case "heading": {
2104
- const level = clampLevel(block.attrs.level);
2714
+ const level = clampLevel2(block.attrs.level);
2105
2715
  const Tag = `h${level}`;
2106
2716
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2107
2717
  Tag,
@@ -2162,7 +2772,7 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
2162
2772
  function commonBlockProps(attrs, index, type, fragment) {
2163
2773
  const style = {};
2164
2774
  if (attrs.align) style.textAlign = attrs.align;
2165
- Object.assign(style, (0, import_core4.answerLineStyle)(attrs) ?? {});
2775
+ Object.assign(style, (0, import_core6.answerLineStyle)(attrs) ?? {});
2166
2776
  const base = {
2167
2777
  "data-block-index": index,
2168
2778
  "data-block-type": type,
@@ -2188,7 +2798,7 @@ function cellStyle(attrs, border) {
2188
2798
  if (attrs?.bgColor) style.backgroundColor = attrs.bgColor;
2189
2799
  return Object.keys(style).length > 0 ? style : void 0;
2190
2800
  }
2191
- function clampLevel(l) {
2801
+ function clampLevel2(l) {
2192
2802
  if (l === 2 || l === 3 || l === 4 || l === 5 || l === 6) return l;
2193
2803
  return 1;
2194
2804
  }
@@ -2209,7 +2819,7 @@ function TableView({ block, index, tableFragment }) {
2209
2819
  const rect = (0, import_react10.useMemo)(() => {
2210
2820
  if (editor.selection.anchor.blockIndex !== index) return null;
2211
2821
  if (editor.selection.focus.blockIndex !== index) return null;
2212
- return (0, import_core4.tableRectSelection)(editor.doc, editor.selection);
2822
+ return (0, import_core6.tableRectSelection)(editor.doc, editor.selection);
2213
2823
  }, [editor.doc, editor.selection, index]);
2214
2824
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ed-table-wrap", children: [
2215
2825
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
@@ -2245,10 +2855,11 @@ function TableView({ block, index, tableFragment }) {
2245
2855
  cell?.attrs,
2246
2856
  // rowStart/rowEnd são os limites do FRAGMENTO: numa tabela
2247
2857
  // quebrada entre páginas, cada página fecha a própria caixa.
2248
- (0, import_core4.cellBorderStyle)(
2858
+ (0, import_core6.cellBorderStyle)(
2249
2859
  block.attrs.borderPreset,
2250
2860
  { row: r, col: c, rowspan, colspan, cols, rowStart, rowEnd },
2251
- "screen"
2861
+ "screen",
2862
+ block.attrs.borderColor
2252
2863
  )
2253
2864
  ),
2254
2865
  className: inRect ? "ed-cell ed-cell--selected" : "ed-cell",
@@ -2820,14 +3431,14 @@ function shallowEqual(a, b) {
2820
3431
  }
2821
3432
 
2822
3433
  // src/useEditor.ts
2823
- var import_core5 = require("@sofereditor/core");
3434
+ var import_core7 = require("@sofereditor/core");
2824
3435
  var MAX_INSERT_WIDTH = 600;
2825
3436
  function useEditor(opts = {}) {
2826
- const doc = (0, import_react13.useMemo)(() => opts.document ?? new import_core5.EditorDocument(), [opts.document]);
2827
- const history = (0, import_react13.useMemo)(() => new import_core5.EditorHistory(doc), [doc]);
3437
+ const doc = (0, import_react13.useMemo)(() => opts.document ?? new import_core7.EditorDocument(), [opts.document]);
3438
+ const history = (0, import_react13.useMemo)(() => new import_core7.EditorHistory(doc), [doc]);
2828
3439
  const [snapshot, setSnapshot] = (0, import_react13.useState)(() => doc.toJSON());
2829
3440
  const [selection, setSelectionState] = (0, import_react13.useState)(
2830
- () => (0, import_core5.collapsedSelection)({ blockIndex: 0, offset: 0 })
3441
+ () => (0, import_core7.collapsedSelection)({ blockIndex: 0, offset: 0 })
2831
3442
  );
2832
3443
  const selectionRef = (0, import_react13.useRef)(selection);
2833
3444
  selectionRef.current = selection;
@@ -2892,7 +3503,7 @@ function useEditor(opts = {}) {
2892
3503
  const isMarkActive = (0, import_react13.useCallback)(
2893
3504
  (name, value) => {
2894
3505
  const sel = selectionRef.current;
2895
- const { start, end } = (0, import_core5.orderedRange)(sel);
3506
+ const { start, end } = (0, import_core7.orderedRange)(sel);
2896
3507
  if (start.blockIndex === end.blockIndex && start.offset === end.offset) {
2897
3508
  const pending = pendingMarksRef.current[name];
2898
3509
  if (pending !== void 0) {
@@ -2901,7 +3512,7 @@ function useEditor(opts = {}) {
2901
3512
  }
2902
3513
  const yText2 = doc.getBlockText(start.blockIndex);
2903
3514
  if (!yText2 || start.offset === 0) return false;
2904
- return (0, import_core5.isMarkUniformInRange)(yText2, start.offset - 1, start.offset, name, value);
3515
+ return (0, import_core7.isMarkUniformInRange)(yText2, start.offset - 1, start.offset, name, value);
2905
3516
  }
2906
3517
  if (start.blockIndex !== end.blockIndex) {
2907
3518
  for (let i = start.blockIndex; i <= end.blockIndex; i++) {
@@ -2910,19 +3521,19 @@ function useEditor(opts = {}) {
2910
3521
  const s = i === start.blockIndex ? start.offset : 0;
2911
3522
  const e = i === end.blockIndex ? end.offset : yText2.length;
2912
3523
  if (s >= e) continue;
2913
- if (!(0, import_core5.isMarkUniformInRange)(yText2, s, e, name, value)) return false;
3524
+ if (!(0, import_core7.isMarkUniformInRange)(yText2, s, e, name, value)) return false;
2914
3525
  }
2915
3526
  return true;
2916
3527
  }
2917
3528
  const yText = doc.getBlockText(start.blockIndex);
2918
3529
  if (!yText) return false;
2919
- return (0, import_core5.isMarkUniformInRange)(yText, start.offset, end.offset, name, value);
3530
+ return (0, import_core7.isMarkUniformInRange)(yText, start.offset, end.offset, name, value);
2920
3531
  },
2921
3532
  [doc]
2922
3533
  );
2923
3534
  const getActiveMarks = (0, import_react13.useCallback)(() => {
2924
3535
  const sel = selectionRef.current;
2925
- const { start, end } = (0, import_core5.orderedRange)(sel);
3536
+ const { start, end } = (0, import_core7.orderedRange)(sel);
2926
3537
  if (start.blockIndex !== end.blockIndex) {
2927
3538
  return {};
2928
3539
  }
@@ -2930,14 +3541,14 @@ function useEditor(opts = {}) {
2930
3541
  if (!yText) return {};
2931
3542
  if (start.offset === end.offset) {
2932
3543
  if (start.offset === 0) return {};
2933
- return (0, import_core5.getMarksInRange)(yText, start.offset - 1, start.offset);
3544
+ return (0, import_core7.getMarksInRange)(yText, start.offset - 1, start.offset);
2934
3545
  }
2935
- return (0, import_core5.getMarksInRange)(yText, start.offset, end.offset);
3546
+ return (0, import_core7.getMarksInRange)(yText, start.offset, end.offset);
2936
3547
  }, [doc]);
2937
3548
  const toggleMark = (0, import_react13.useCallback)(
2938
3549
  (name, value) => {
2939
3550
  const sel = selectionRef.current;
2940
- const { start, end } = (0, import_core5.orderedRange)(sel);
3551
+ const { start, end } = (0, import_core7.orderedRange)(sel);
2941
3552
  const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
2942
3553
  if (isCollapsed) {
2943
3554
  const next = { ...pendingMarksRef.current };
@@ -2950,39 +3561,39 @@ function useEditor(opts = {}) {
2950
3561
  setPendingMarks(next);
2951
3562
  return;
2952
3563
  }
2953
- (0, import_core5.toggleMark)(ctxRef.current, name, value);
3564
+ (0, import_core7.toggleMark)(ctxRef.current, name, value);
2954
3565
  },
2955
3566
  [isMarkActive, setPendingMarks]
2956
3567
  );
2957
3568
  const setMark = (0, import_react13.useCallback)(
2958
3569
  (name, value) => {
2959
3570
  const sel = selectionRef.current;
2960
- const { start, end } = (0, import_core5.orderedRange)(sel);
3571
+ const { start, end } = (0, import_core7.orderedRange)(sel);
2961
3572
  const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
2962
3573
  if (isCollapsed) {
2963
3574
  setPendingMarks({ ...pendingMarksRef.current, [name]: value });
2964
3575
  return;
2965
3576
  }
2966
- (0, import_core5.setMark)(ctxRef.current, name, value);
3577
+ (0, import_core7.setMark)(ctxRef.current, name, value);
2967
3578
  },
2968
3579
  [setPendingMarks]
2969
3580
  );
2970
3581
  const removeMark = (0, import_react13.useCallback)(
2971
3582
  (name) => {
2972
3583
  const sel = selectionRef.current;
2973
- const { start, end } = (0, import_core5.orderedRange)(sel);
3584
+ const { start, end } = (0, import_core7.orderedRange)(sel);
2974
3585
  const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
2975
3586
  if (isCollapsed) {
2976
3587
  setPendingMarks({ ...pendingMarksRef.current, [name]: null });
2977
3588
  return;
2978
3589
  }
2979
- (0, import_core5.removeMark)(ctxRef.current, name);
3590
+ (0, import_core7.removeMark)(ctxRef.current, name);
2980
3591
  },
2981
3592
  [setPendingMarks]
2982
3593
  );
2983
3594
  const getBlockType = (0, import_react13.useCallback)(() => {
2984
3595
  const sel = selectionRef.current;
2985
- const { start, end } = (0, import_core5.orderedRange)(sel);
3596
+ const { start, end } = (0, import_core7.orderedRange)(sel);
2986
3597
  const first = doc.getBlockType(start.blockIndex);
2987
3598
  if (first === void 0) return "paragraph";
2988
3599
  for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {
@@ -2993,7 +3604,7 @@ function useEditor(opts = {}) {
2993
3604
  const getBlockAttr = (0, import_react13.useCallback)(
2994
3605
  (key) => {
2995
3606
  const sel = selectionRef.current;
2996
- const { start, end } = (0, import_core5.orderedRange)(sel);
3607
+ const { start, end } = (0, import_core7.orderedRange)(sel);
2997
3608
  const first = doc.getBlockAttrs(start.blockIndex)[key];
2998
3609
  for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {
2999
3610
  const next = doc.getBlockAttrs(i)[key];
@@ -3004,19 +3615,19 @@ function useEditor(opts = {}) {
3004
3615
  [doc]
3005
3616
  );
3006
3617
  const setBlockType = (0, import_react13.useCallback)((type, attrs) => {
3007
- (0, import_core5.setBlockType)(ctxRef.current, type, attrs);
3618
+ (0, import_core7.setBlockType)(ctxRef.current, type, attrs);
3008
3619
  }, []);
3009
3620
  const setBlockAttr = (0, import_react13.useCallback)(
3010
3621
  (key, value) => {
3011
- (0, import_core5.setBlockAttr)(ctxRef.current, key, value);
3622
+ (0, import_core7.setBlockAttr)(ctxRef.current, key, value);
3012
3623
  },
3013
3624
  []
3014
3625
  );
3015
3626
  const setAlign = (0, import_react13.useCallback)((value) => {
3016
3627
  const sel = selectionRef.current;
3017
3628
  const inCell = sel.focus.cellIndex != null;
3018
- if (inCell) (0, import_core5.setCellAttr)(ctxRef.current, "align", value);
3019
- else (0, import_core5.setBlockAttr)(ctxRef.current, "align", value);
3629
+ if (inCell) (0, import_core7.setCellAttr)(ctxRef.current, "align", value);
3630
+ else (0, import_core7.setBlockAttr)(ctxRef.current, "align", value);
3020
3631
  }, []);
3021
3632
  const getAlign = (0, import_react13.useCallback)(() => {
3022
3633
  const sel = selectionRef.current;
@@ -3026,7 +3637,7 @@ function useEditor(opts = {}) {
3026
3637
  return getBlockAttr("align");
3027
3638
  }, [doc, getBlockAttr]);
3028
3639
  const setCellBackground = (0, import_react13.useCallback)((color) => {
3029
- (0, import_core5.setCellAttr)(ctxRef.current, "bgColor", color);
3640
+ (0, import_core7.setCellAttr)(ctxRef.current, "bgColor", color);
3030
3641
  }, []);
3031
3642
  const getCellBackground = (0, import_react13.useCallback)(() => {
3032
3643
  const sel = selectionRef.current;
@@ -3036,17 +3647,27 @@ function useEditor(opts = {}) {
3036
3647
  const setTableBorderPreset = (0, import_react13.useCallback)((preset) => {
3037
3648
  const sel = selectionRef.current;
3038
3649
  if (!doc.isTable(sel.focus.blockIndex)) return;
3039
- (0, import_core5.setBlockAttrAtIndex)(ctxRef.current, sel.focus.blockIndex, "borderPreset", preset);
3650
+ (0, import_core7.setBlockAttrAtIndex)(ctxRef.current, sel.focus.blockIndex, "borderPreset", preset);
3040
3651
  }, [doc]);
3041
3652
  const getTableBorderPreset = (0, import_react13.useCallback)(() => {
3042
3653
  const sel = selectionRef.current;
3043
3654
  if (!doc.isTable(sel.focus.blockIndex)) return "all";
3044
3655
  return doc.getBlockAttrs(sel.focus.blockIndex).borderPreset ?? "all";
3045
3656
  }, [doc]);
3657
+ const setTableBorderColor = (0, import_react13.useCallback)((color) => {
3658
+ const sel = selectionRef.current;
3659
+ if (!doc.isTable(sel.focus.blockIndex)) return;
3660
+ (0, import_core7.setBlockAttrAtIndex)(ctxRef.current, sel.focus.blockIndex, "borderColor", color);
3661
+ }, [doc]);
3662
+ const getTableBorderColor = (0, import_react13.useCallback)(() => {
3663
+ const sel = selectionRef.current;
3664
+ if (!doc.isTable(sel.focus.blockIndex)) return void 0;
3665
+ return doc.getBlockAttrs(sel.focus.blockIndex).borderColor;
3666
+ }, [doc]);
3046
3667
  const isListActive = (0, import_react13.useCallback)(
3047
3668
  (kind) => {
3048
3669
  const sel = selectionRef.current;
3049
- const { start, end } = (0, import_core5.orderedRange)(sel);
3670
+ const { start, end } = (0, import_core7.orderedRange)(sel);
3050
3671
  for (let i = start.blockIndex; i <= end.blockIndex; i++) {
3051
3672
  if (doc.getBlockType(i) !== "listItem") return false;
3052
3673
  if (doc.getBlockAttrs(i).listKind !== kind) return false;
@@ -3056,112 +3677,112 @@ function useEditor(opts = {}) {
3056
3677
  [doc]
3057
3678
  );
3058
3679
  const toggleList = (0, import_react13.useCallback)((kind) => {
3059
- (0, import_core5.toggleList)(ctxRef.current, kind);
3680
+ (0, import_core7.toggleList)(ctxRef.current, kind);
3060
3681
  }, []);
3061
3682
  const indentList = (0, import_react13.useCallback)(() => {
3062
- (0, import_core5.indentList)(ctxRef.current);
3683
+ (0, import_core7.indentList)(ctxRef.current);
3063
3684
  }, []);
3064
3685
  const dedentList = (0, import_react13.useCallback)(() => {
3065
- (0, import_core5.dedentList)(ctxRef.current);
3686
+ (0, import_core7.dedentList)(ctxRef.current);
3066
3687
  }, []);
3067
3688
  const splitListItem = (0, import_react13.useCallback)(() => {
3068
- return (0, import_core5.splitListItem)(ctxRef.current);
3689
+ return (0, import_core7.splitListItem)(ctxRef.current);
3069
3690
  }, []);
3070
3691
  const isInTable = (0, import_react13.useCallback)(() => {
3071
3692
  return selectionRef.current.focus.cellIndex != null;
3072
3693
  }, []);
3073
3694
  const getTableLocation = (0, import_react13.useCallback)(() => {
3074
- return (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3695
+ return (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
3075
3696
  }, [doc]);
3076
3697
  const insertTable = (0, import_react13.useCallback)((rows, cols) => {
3077
- (0, import_core5.insertTable)(ctxRef.current, rows, cols);
3698
+ (0, import_core7.insertTable)(ctxRef.current, rows, cols);
3078
3699
  }, []);
3079
3700
  const insertAnswerLines = (0, import_react13.useCallback)((count, spacing) => {
3080
- (0, import_core5.insertAnswerLines)(ctxRef.current, count, spacing);
3701
+ (0, import_core7.insertAnswerLines)(ctxRef.current, count, spacing);
3081
3702
  }, []);
3082
3703
  const deleteTable = (0, import_react13.useCallback)((blockIndex) => {
3083
3704
  const idx = blockIndex ?? selectionRef.current.focus.blockIndex;
3084
- (0, import_core5.deleteTable)(ctxRef.current, idx);
3705
+ (0, import_core7.deleteTable)(ctxRef.current, idx);
3085
3706
  }, []);
3086
3707
  const insertRowAbove = (0, import_react13.useCallback)(() => {
3087
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3708
+ const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
3088
3709
  if (!loc) return;
3089
- (0, import_core5.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "before");
3710
+ (0, import_core7.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "before");
3090
3711
  }, [doc]);
3091
3712
  const insertRowBelow = (0, import_react13.useCallback)(() => {
3092
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3713
+ const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
3093
3714
  if (!loc) return;
3094
- (0, import_core5.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "after");
3715
+ (0, import_core7.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "after");
3095
3716
  }, [doc]);
3096
3717
  const deleteCurrentRow = (0, import_react13.useCallback)(() => {
3097
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3718
+ const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
3098
3719
  if (!loc) return;
3099
- (0, import_core5.deleteTableRow)(ctxRef.current, loc.blockIndex, loc.row);
3720
+ (0, import_core7.deleteTableRow)(ctxRef.current, loc.blockIndex, loc.row);
3100
3721
  }, [doc]);
3101
3722
  const insertColumnLeft = (0, import_react13.useCallback)(() => {
3102
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3723
+ const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
3103
3724
  if (!loc) return;
3104
- (0, import_core5.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "before");
3725
+ (0, import_core7.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "before");
3105
3726
  }, [doc]);
3106
3727
  const insertColumnRight = (0, import_react13.useCallback)(() => {
3107
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3728
+ const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
3108
3729
  if (!loc) return;
3109
- (0, import_core5.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "after");
3730
+ (0, import_core7.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "after");
3110
3731
  }, [doc]);
3111
3732
  const deleteCurrentColumn = (0, import_react13.useCallback)(() => {
3112
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3733
+ const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
3113
3734
  if (!loc) return;
3114
- (0, import_core5.deleteTableColumn)(ctxRef.current, loc.blockIndex, loc.col);
3735
+ (0, import_core7.deleteTableColumn)(ctxRef.current, loc.blockIndex, loc.col);
3115
3736
  }, [doc]);
3116
3737
  const moveToNextCell = (0, import_react13.useCallback)(() => {
3117
- (0, import_core5.moveToNextCell)(ctxRef.current);
3738
+ (0, import_core7.moveToNextCell)(ctxRef.current);
3118
3739
  }, []);
3119
3740
  const moveToPrevCell = (0, import_react13.useCallback)(() => {
3120
- (0, import_core5.moveToPrevCell)(ctxRef.current);
3741
+ (0, import_core7.moveToPrevCell)(ctxRef.current);
3121
3742
  }, []);
3122
3743
  const mergeRight = (0, import_react13.useCallback)(() => {
3123
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3744
+ const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
3124
3745
  if (!loc) return false;
3125
- return (0, import_core5.mergeRight)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3746
+ return (0, import_core7.mergeRight)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3126
3747
  }, [doc]);
3127
3748
  const mergeDown = (0, import_react13.useCallback)(() => {
3128
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3749
+ const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
3129
3750
  if (!loc) return false;
3130
- return (0, import_core5.mergeDown)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3751
+ return (0, import_core7.mergeDown)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3131
3752
  }, [doc]);
3132
3753
  const splitCell = (0, import_react13.useCallback)(() => {
3133
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3754
+ const loc = (0, import_core7.tableLocationOf)(doc, selectionRef.current.focus);
3134
3755
  if (!loc) return false;
3135
- return (0, import_core5.splitCell)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3756
+ return (0, import_core7.splitCell)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3136
3757
  }, [doc]);
3137
3758
  const setColumnWidth = (0, import_react13.useCallback)(
3138
3759
  (blockIndex, col, widthPx) => {
3139
- (0, import_core5.setColumnWidth)(ctxRef.current, blockIndex, col, widthPx);
3760
+ (0, import_core7.setColumnWidth)(ctxRef.current, blockIndex, col, widthPx);
3140
3761
  },
3141
3762
  []
3142
3763
  );
3143
3764
  const getTableRect = (0, import_react13.useCallback)(
3144
- () => (0, import_core5.tableRectSelection)(doc, selectionRef.current),
3765
+ () => (0, import_core7.tableRectSelection)(doc, selectionRef.current),
3145
3766
  [doc]
3146
3767
  );
3147
3768
  const hasTableRectSelection = (0, import_react13.useCallback)(() => {
3148
- return (0, import_core5.tableRectSelection)(doc, selectionRef.current) !== null;
3769
+ return (0, import_core7.tableRectSelection)(doc, selectionRef.current) !== null;
3149
3770
  }, [doc]);
3150
3771
  const mergeSelection = (0, import_react13.useCallback)(() => {
3151
- return (0, import_core5.mergeSelection)(ctxRef.current);
3772
+ return (0, import_core7.mergeSelection)(ctxRef.current);
3152
3773
  }, []);
3153
3774
  const insertImage = (0, import_react13.useCallback)((embed) => {
3154
- (0, import_core5.insertImage)(ctxRef.current, embed);
3775
+ (0, import_core7.insertImage)(ctxRef.current, embed);
3155
3776
  }, []);
3156
3777
  const setImageAttrs = (0, import_react13.useCallback)(
3157
3778
  (blockIndex, offset, partial, cellIndex, newOffset) => {
3158
- (0, import_core5.setImageAttrs)(ctxRef.current, blockIndex, offset, partial, cellIndex, newOffset);
3779
+ (0, import_core7.setImageAttrs)(ctxRef.current, blockIndex, offset, partial, cellIndex, newOffset);
3159
3780
  },
3160
3781
  []
3161
3782
  );
3162
3783
  const moveEmbedAnchor = (0, import_react13.useCallback)(
3163
3784
  (from, to, newOffsetX, newOffsetY) => {
3164
- (0, import_core5.moveEmbedAnchor)(ctxRef.current, from, to, newOffsetX, newOffsetY);
3785
+ (0, import_core7.moveEmbedAnchor)(ctxRef.current, from, to, newOffsetX, newOffsetY);
3165
3786
  },
3166
3787
  []
3167
3788
  );
@@ -3179,7 +3800,7 @@ function useEditor(opts = {}) {
3179
3800
  }
3180
3801
  const upload = uploadImageRef.current;
3181
3802
  const src = upload ? await upload(file) : dataUrl;
3182
- (0, import_core5.insertImage)(ctxRef.current, { type: "image", src, width: w, height: h });
3803
+ (0, import_core7.insertImage)(ctxRef.current, { type: "image", src, width: w, height: h });
3183
3804
  }, []);
3184
3805
  const getSelectedEmbed = (0, import_react13.useCallback)(() => {
3185
3806
  const sel = selectionRef.current;
@@ -3198,7 +3819,7 @@ function useEditor(opts = {}) {
3198
3819
  cursor += op.insert.length;
3199
3820
  continue;
3200
3821
  }
3201
- if (cursor === start && (0, import_core5.isImageEmbed)(op.insert)) {
3822
+ if (cursor === start && (0, import_core7.isImageEmbed)(op.insert)) {
3202
3823
  return {
3203
3824
  blockIndex: sel.anchor.blockIndex,
3204
3825
  cellIndex: sel.anchor.cellIndex,
@@ -3289,6 +3910,8 @@ function useEditor(opts = {}) {
3289
3910
  getCellBackground,
3290
3911
  setTableBorderPreset,
3291
3912
  getTableBorderPreset,
3913
+ setTableBorderColor,
3914
+ getTableBorderColor,
3292
3915
  isListActive,
3293
3916
  toggleList,
3294
3917
  indentList,
@@ -3391,7 +4014,7 @@ function Editor({
3391
4014
  const sel = readDomSelection(root);
3392
4015
  awareness.setLocalStateField(
3393
4016
  "cursor",
3394
- sel ? (0, import_core6.encodeSelection)(doc, sel) ?? null : null
4017
+ sel ? (0, import_core8.encodeSelection)(doc, sel) ?? null : null
3395
4018
  );
3396
4019
  };
3397
4020
  const onSelChange = () => {
@@ -3692,14 +4315,14 @@ function Editor({
3692
4315
  const data = e.data ?? "";
3693
4316
  if (data.length > 0) {
3694
4317
  e.preventDefault();
3695
- (0, import_core6.insertText)(ctx, data, ed.consumePendingMarks());
4318
+ (0, import_core8.insertText)(ctx, data, ed.consumePendingMarks());
3696
4319
  }
3697
4320
  return;
3698
4321
  }
3699
4322
  case "insertLineBreak": {
3700
4323
  e.preventDefault();
3701
4324
  if (ed.getBlockType() === "listItem") {
3702
- (0, import_core6.insertText)(ctx, "\n", ed.consumePendingMarks());
4325
+ (0, import_core8.insertText)(ctx, "\n", ed.consumePendingMarks());
3703
4326
  return;
3704
4327
  }
3705
4328
  const selEmbed = ed.getSelectedEmbed();
@@ -3719,7 +4342,7 @@ function Editor({
3719
4342
  });
3720
4343
  }
3721
4344
  if (ed.splitListItem()) return;
3722
- (0, import_core6.insertParagraph)(ctx);
4345
+ (0, import_core8.insertParagraph)(ctx);
3723
4346
  return;
3724
4347
  }
3725
4348
  case "insertParagraph": {
@@ -3741,14 +4364,14 @@ function Editor({
3741
4364
  });
3742
4365
  }
3743
4366
  if (ed.splitListItem()) return;
3744
- (0, import_core6.insertParagraph)(ctx);
4367
+ (0, import_core8.insertParagraph)(ctx);
3745
4368
  return;
3746
4369
  }
3747
4370
  case "insertReplacementText": {
3748
4371
  const text = e.data ?? e.dataTransfer?.getData("text/plain") ?? "";
3749
4372
  if (text.length > 0) {
3750
4373
  e.preventDefault();
3751
- (0, import_core6.insertText)(ctx, text, ed.consumePendingMarks());
4374
+ (0, import_core8.insertText)(ctx, text, ed.consumePendingMarks());
3752
4375
  }
3753
4376
  return;
3754
4377
  }
@@ -3761,7 +4384,7 @@ function Editor({
3761
4384
  case "deleteSoftLineBackward":
3762
4385
  case "deleteHardLineBackward": {
3763
4386
  e.preventDefault();
3764
- (0, import_core6.deleteBackward)(ctx);
4387
+ (0, import_core8.deleteBackward)(ctx);
3765
4388
  return;
3766
4389
  }
3767
4390
  case "deleteContentForward":
@@ -3769,7 +4392,7 @@ function Editor({
3769
4392
  case "deleteSoftLineForward":
3770
4393
  case "deleteHardLineForward": {
3771
4394
  e.preventDefault();
3772
- (0, import_core6.deleteForward)(ctx);
4395
+ (0, import_core8.deleteForward)(ctx);
3773
4396
  return;
3774
4397
  }
3775
4398
  case "historyUndo": {
@@ -3796,10 +4419,10 @@ function Editor({
3796
4419
  if (!root) return;
3797
4420
  const writeSlice = (e) => {
3798
4421
  const ctx = ctxRef.current;
3799
- const slice = (0, import_core6.serializeSelection)(ctx.doc, ctx.getSelection());
4422
+ const slice = (0, import_core8.serializeSelection)(ctx.doc, ctx.getSelection());
3800
4423
  if (!slice || !e.clipboardData) return false;
3801
- e.clipboardData.setData(import_core6.SOFER_MIME, JSON.stringify(slice));
3802
- e.clipboardData.setData("text/plain", (0, import_core6.sliceToText)(slice));
4424
+ e.clipboardData.setData(import_core8.SOFER_MIME, JSON.stringify(slice));
4425
+ e.clipboardData.setData("text/plain", (0, import_core8.sliceToText)(slice));
3803
4426
  return true;
3804
4427
  };
3805
4428
  const onCopy = (e) => {
@@ -3807,40 +4430,34 @@ function Editor({
3807
4430
  };
3808
4431
  const onCut = (e) => {
3809
4432
  e.preventDefault();
3810
- if (writeSlice(e)) (0, import_core6.deleteSelection)(ctxRef.current);
4433
+ if (writeSlice(e)) (0, import_core8.deleteSelection)(ctxRef.current);
3811
4434
  };
3812
4435
  const onPaste = (e) => {
3813
4436
  const ctx = ctxRef.current;
3814
4437
  const cd = e.clipboardData;
3815
4438
  if (!cd) return;
3816
- const raw = cd.getData(import_core6.SOFER_MIME);
3817
- if (raw) {
3818
- try {
3819
- const slice = JSON.parse(raw);
3820
- if (slice && Array.isArray(slice.blocks)) {
3821
- e.preventDefault();
3822
- (0, import_core6.insertSlice)(ctx, slice);
3823
- return;
3824
- }
3825
- } catch {
4439
+ const plan = planPaste(cd);
4440
+ if (plan.kind === "none") return;
4441
+ e.preventDefault();
4442
+ switch (plan.kind) {
4443
+ case "sofer":
4444
+ case "html":
4445
+ (0, import_core8.insertSlice)(ctx, plan.slice);
4446
+ return;
4447
+ case "images":
4448
+ void (async () => {
4449
+ for (const f of plan.files) await editorRef.current.insertImageFromFile(f);
4450
+ })();
4451
+ return;
4452
+ case "text": {
4453
+ const lines = plan.text.split(/\r\n|\r|\n/);
4454
+ lines.forEach((line, i) => {
4455
+ if (i > 0) (0, import_core8.insertParagraph)(ctx);
4456
+ if (line.length > 0) (0, import_core8.insertText)(ctx, line);
4457
+ });
4458
+ return;
3826
4459
  }
3827
4460
  }
3828
- const images = Array.from(cd.files ?? []).filter((f) => f.type.startsWith("image/"));
3829
- if (images.length > 0) {
3830
- e.preventDefault();
3831
- void (async () => {
3832
- for (const f of images) await editorRef.current.insertImageFromFile(f);
3833
- })();
3834
- return;
3835
- }
3836
- const text = cd.getData("text/plain");
3837
- if (text.length === 0) return;
3838
- e.preventDefault();
3839
- const lines = text.split(/\r\n|\r|\n/);
3840
- lines.forEach((line, i) => {
3841
- if (i > 0) (0, import_core6.insertParagraph)(ctx);
3842
- if (line.length > 0) (0, import_core6.insertText)(ctx, line);
3843
- });
3844
4461
  };
3845
4462
  root.addEventListener("copy", onCopy);
3846
4463
  root.addEventListener("cut", onCut);
@@ -3931,7 +4548,7 @@ function Editor({
3931
4548
  const onCompositionEnd = (0, import_react14.useCallback)((e) => {
3932
4549
  isComposingRef.current = false;
3933
4550
  if (e.data && e.data.length > 0) {
3934
- (0, import_core6.insertText)(ctxRef.current, e.data);
4551
+ (0, import_core8.insertText)(ctxRef.current, e.data);
3935
4552
  }
3936
4553
  }, []);
3937
4554
  const topLevels = (0, import_react14.useMemo)(() => groupTopLevels(snapshot.blocks), [snapshot]);
@@ -4095,11 +4712,15 @@ function groupTopLevels(snapshot) {
4095
4712
  const groupStart = i;
4096
4713
  const items = [];
4097
4714
  const kind = block.attrs.listKind === "ordered" ? "ordered" : "bullet";
4715
+ const leaderLevel = clampListLevel2(block.attrs.listLevel);
4098
4716
  const groupStyle = block.attrs.listStyle;
4099
4717
  while (i < snapshot.length) {
4100
4718
  const b = snapshot[i];
4101
4719
  if (b.type !== "listItem") break;
4102
- if ((b.attrs.listKind ?? "bullet") !== kind) break;
4720
+ const bLevel = clampListLevel2(b.attrs.listLevel);
4721
+ if (bLevel <= leaderLevel && (b.attrs.listKind ?? "bullet") !== kind) {
4722
+ break;
4723
+ }
4103
4724
  if (i > groupStart) {
4104
4725
  if (typeof b.attrs.listStart === "number") break;
4105
4726
  const itemStyle = b.attrs.listStyle;
@@ -4207,7 +4828,7 @@ function buildListTree(items) {
4207
4828
  function renderListTree(tree, top, start, key) {
4208
4829
  const className = `ed-list ed-list-${top.listKind}`;
4209
4830
  const style = top.listStyle ? { listStyleType: top.listStyle } : void 0;
4210
- const items = tree.map((node) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderNestedListTree(node.children, top.listKind, `nested-${node.entry.index}`) : null }, node.entry.index));
4831
+ const items = tree.map((node) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderNestedListTree(node.children, `nested-${node.entry.index}`) : null }, node.entry.index));
4211
4832
  if (top.listKind === "ordered") {
4212
4833
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4213
4834
  "ol",
@@ -4223,9 +4844,10 @@ function renderListTree(tree, top, start, key) {
4223
4844
  }
4224
4845
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className, "data-list-kind": "bullet", style, children: items }, key);
4225
4846
  }
4226
- function renderNestedListTree(tree, kind, key) {
4847
+ function renderNestedListTree(tree, key) {
4848
+ const kind = tree[0]?.entry.block.attrs.listKind === "ordered" ? "ordered" : "bullet";
4227
4849
  const Tag = kind === "ordered" ? "ol" : "ul";
4228
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Tag, { className: `ed-list ed-list-${kind}`, "data-list-kind": kind, children: tree.map((node) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderNestedListTree(node.children, kind, `nested-${node.entry.index}`) : null }, node.entry.index)) }, key);
4850
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Tag, { className: `ed-list ed-list-${kind}`, "data-list-kind": kind, children: tree.map((node) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderNestedListTree(node.children, `nested-${node.entry.index}`) : null }, node.entry.index)) }, key);
4229
4851
  }
4230
4852
  function clampListLevel2(l) {
4231
4853
  if (typeof l !== "number" || !Number.isFinite(l)) return 0;
@@ -4254,7 +4876,7 @@ function caretRangeFromPoint(x, y) {
4254
4876
 
4255
4877
  // src/Toolbar.tsx
4256
4878
  var import_react15 = require("react");
4257
- var import_core7 = require("@sofereditor/core");
4879
+ var import_core9 = require("@sofereditor/core");
4258
4880
  var import_jsx_runtime13 = require("react/jsx-runtime");
4259
4881
  var FONT_FAMILIES = ["Arial"];
4260
4882
  var FONT_SIZES = ["8pt", "10pt", "11pt", "12pt", "14pt", "16pt", "18pt", "24pt", "32pt", "48pt"];
@@ -4276,6 +4898,15 @@ var ALIGN_OPTIONS = [
4276
4898
  { value: "justify", label: "\u2630", title: "Justificar" }
4277
4899
  ];
4278
4900
  var IMAGE_ALIGN_VALUES = /* @__PURE__ */ new Set(["left", "center", "right"]);
4901
+ var NATIVE_CONTROL_TAGS = /* @__PURE__ */ new Set(["SELECT", "INPUT", "OPTION"]);
4902
+ function isNativeControl(t) {
4903
+ const el = t;
4904
+ return !!el && NATIVE_CONTROL_TAGS.has(el.tagName);
4905
+ }
4906
+ function keepsOwnMouseDown(t) {
4907
+ const el = t;
4908
+ return !!el && (NATIVE_CONTROL_TAGS.has(el.tagName) || el.tagName === "BUTTON");
4909
+ }
4279
4910
  var IMAGE_LAYOUT_OPTIONS = [
4280
4911
  { value: "inline", label: "\u2194", title: "Inline (no fluxo do texto)" },
4281
4912
  { value: "wrap-left", label: "\u25E7", title: "Texto contorna \xE0 direita" },
@@ -4308,10 +4939,7 @@ function Toolbar({ className }) {
4308
4939
  const blockAlign = getAlign();
4309
4940
  const blockDir = getBlockAttr("dir");
4310
4941
  const stop = (e) => {
4311
- const t = e.target;
4312
- if (t && (t.tagName === "SELECT" || t.tagName === "INPUT" || t.tagName === "OPTION")) {
4313
- return;
4314
- }
4942
+ if (isNativeControl(e.target)) return;
4315
4943
  e.preventDefault();
4316
4944
  };
4317
4945
  const onToggle = (name) => (e) => {
@@ -4720,7 +5348,7 @@ function AnswerLinesMenu() {
4720
5348
  document.addEventListener("mousedown", onDown);
4721
5349
  return () => document.removeEventListener("mousedown", onDown);
4722
5350
  }, [open]);
4723
- const clamped = Math.max(1, Math.min(import_core7.ANSWER_LINE_MAX, Math.trunc(count) || 1));
5351
+ const clamped = Math.max(1, Math.min(import_core9.ANSWER_LINE_MAX, Math.trunc(count) || 1));
4724
5352
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { ref: rootRef, className: "ed-toolbar-tablemenu", children: [
4725
5353
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4726
5354
  "button",
@@ -4743,10 +5371,7 @@ function AnswerLinesMenu() {
4743
5371
  className: "ed-table-popover ed-answerlines-popover",
4744
5372
  role: "menu",
4745
5373
  onMouseDown: (e) => {
4746
- const t = e.target;
4747
- if (!t || t.tagName !== "INPUT" && t.tagName !== "BUTTON" && t.tagName !== "SELECT") {
4748
- e.preventDefault();
4749
- }
5374
+ if (!keepsOwnMouseDown(e.target)) e.preventDefault();
4750
5375
  },
4751
5376
  children: [
4752
5377
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "ed-toolbar-label", children: [
@@ -4756,7 +5381,7 @@ function AnswerLinesMenu() {
4756
5381
  {
4757
5382
  type: "number",
4758
5383
  min: 1,
4759
- max: import_core7.ANSWER_LINE_MAX,
5384
+ max: import_core9.ANSWER_LINE_MAX,
4760
5385
  value: count,
4761
5386
  onChange: (e) => setCount(Number(e.target.value)),
4762
5387
  "aria-label": "Quantidade de linhas"
@@ -4837,10 +5462,7 @@ function TableMenu() {
4837
5462
  className: "ed-table-popover",
4838
5463
  role: "menu",
4839
5464
  onMouseDown: (e) => {
4840
- const t = e.target;
4841
- if (!t || t.tagName !== "INPUT" && t.tagName !== "BUTTON") {
4842
- e.preventDefault();
4843
- }
5465
+ if (!keepsOwnMouseDown(e.target)) e.preventDefault();
4844
5466
  },
4845
5467
  children: [
4846
5468
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
@@ -4946,6 +5568,37 @@ function TableMenu() {
4946
5568
  }
4947
5569
  )
4948
5570
  ] }),
5571
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "ed-toolbar-label ed-table-bgcolor", children: [
5572
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5573
+ "span",
5574
+ {
5575
+ className: "ed-toolbar-swatch",
5576
+ "aria-hidden": true,
5577
+ style: { background: editor.getTableBorderColor() ?? import_core9.TABLE_BORDER_COLOR }
5578
+ }
5579
+ ),
5580
+ "Cor da borda",
5581
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5582
+ "input",
5583
+ {
5584
+ type: "color",
5585
+ value: editor.getTableBorderColor() ?? import_core9.TABLE_BORDER_COLOR,
5586
+ onChange: (e) => editor.setTableBorderColor(e.target.value),
5587
+ "aria-label": "Cor da borda"
5588
+ }
5589
+ )
5590
+ ] }),
5591
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5592
+ "button",
5593
+ {
5594
+ type: "button",
5595
+ onClick: () => {
5596
+ setOpen(false);
5597
+ editor.setTableBorderColor(null);
5598
+ },
5599
+ children: "Restaurar cor padr\xE3o"
5600
+ }
5601
+ ),
4949
5602
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("hr", {}),
4950
5603
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4951
5604
  "button",
@@ -5051,6 +5704,7 @@ function MarkButton({ title, pressed, onClick, children }) {
5051
5704
  getBlockElement,
5052
5705
  getCellElement,
5053
5706
  getFragmentForOffset,
5707
+ htmlToSlice,
5054
5708
  isTableRectSelection,
5055
5709
  locatePoint,
5056
5710
  readDomSelection,