@sofereditor/react 0.5.0 → 0.7.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_core9 = require("@sofereditor/core");
58
59
 
59
60
  // src/dom-bridge.ts
60
61
  function findContainingBlockElement(node, root) {
@@ -329,6 +330,746 @@ 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
+
339
+ // src/imageConstraints.ts
340
+ var MAX_INSERT_WIDTH = 600;
341
+
342
+ // src/htmlToSlice.ts
343
+ function htmlToSlice(html) {
344
+ if (!html || html.trim().length === 0) return null;
345
+ let doc;
346
+ try {
347
+ doc = new DOMParser().parseFromString(html, "text/html");
348
+ } catch {
349
+ return null;
350
+ }
351
+ const body = doc.body;
352
+ if (!body) return null;
353
+ const blocks = [];
354
+ try {
355
+ walkBlockLevel(body, {}, blocks);
356
+ } catch {
357
+ return null;
358
+ }
359
+ if (blocks.length === 0) return null;
360
+ normalizeListLevels(blocks);
361
+ const temConteudo = blocks.some(
362
+ (b) => b.text.trim().length > 0 || b.delta.some((op) => (0, import_core.isImageEmbed)(op.insert))
363
+ );
364
+ if (!temConteudo) return null;
365
+ const blockLevel = blocks.length > 1 || blocks[0].type !== "paragraph";
366
+ return { blocks, openStart: false, openEnd: false, ...blockLevel ? { blockLevel: true } : {} };
367
+ }
368
+ var IGNORED_TAGS = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "head", "title", "xml"]);
369
+ var BLOCK_LEVEL_TAGS = /* @__PURE__ */ new Set([
370
+ "p",
371
+ "div",
372
+ "section",
373
+ "article",
374
+ "header",
375
+ "footer",
376
+ "main",
377
+ "figure",
378
+ "figcaption",
379
+ "pre",
380
+ "address"
381
+ ]);
382
+ var BLOCK_TRIGGER_TAGS = /* @__PURE__ */ new Set([
383
+ "p",
384
+ "div",
385
+ "ul",
386
+ "ol",
387
+ "li",
388
+ "h1",
389
+ "h2",
390
+ "h3",
391
+ "h4",
392
+ "h5",
393
+ "h6",
394
+ "blockquote",
395
+ "table",
396
+ "tr",
397
+ "td",
398
+ "th"
399
+ ]);
400
+ function isNamespacedTag(tag) {
401
+ return tag.includes(":");
402
+ }
403
+ function hasBlockDescendant(el) {
404
+ const all = el.querySelectorAll("*");
405
+ for (const e of Array.from(all)) {
406
+ if (BLOCK_TRIGGER_TAGS.has(e.tagName.toLowerCase())) return true;
407
+ }
408
+ return false;
409
+ }
410
+ function walkBlockLevel(container, marks, blocks) {
411
+ let loose = [];
412
+ const flushLoose = () => {
413
+ if (loose.length === 0) return;
414
+ const nodes = loose;
415
+ loose = [];
416
+ const delta = inlineDeltaFromNodes(nodes, marks);
417
+ if (delta.length > 0) blocks.push(makeBlock("paragraph", delta, {}));
418
+ };
419
+ for (const node of Array.from(container.childNodes)) {
420
+ if (node.nodeType === Node.COMMENT_NODE) continue;
421
+ if (node.nodeType === Node.TEXT_NODE) {
422
+ loose.push(node);
423
+ continue;
424
+ }
425
+ if (node.nodeType !== Node.ELEMENT_NODE) continue;
426
+ const el = node;
427
+ const tag = el.tagName.toLowerCase();
428
+ if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) continue;
429
+ switch (tag) {
430
+ case "ul":
431
+ case "ol": {
432
+ flushLoose();
433
+ handleList(el, tag === "ol" ? "ordered" : "bullet", 0, marks, blocks);
434
+ break;
435
+ }
436
+ case "h1":
437
+ case "h2":
438
+ case "h3":
439
+ case "h4":
440
+ case "h5":
441
+ case "h6": {
442
+ flushLoose();
443
+ const level = Number(tag[1]);
444
+ const attrs = { level };
445
+ const align = readAlign(el);
446
+ if (align) attrs.align = align;
447
+ const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
448
+ blocks.push(makeBlock("heading", delta, attrs));
449
+ break;
450
+ }
451
+ case "blockquote": {
452
+ flushLoose();
453
+ handleBlockquote(el, marks, blocks);
454
+ break;
455
+ }
456
+ case "table": {
457
+ flushLoose();
458
+ handleTable(el, marks, blocks);
459
+ break;
460
+ }
461
+ case "li": {
462
+ flushLoose();
463
+ const nestedLists = topLevelListDescendants(el);
464
+ const clone = el.cloneNode(true);
465
+ for (const l of topLevelListDescendants(clone)) l.remove();
466
+ const delta = inlineDeltaFromNodes(Array.from(clone.childNodes), marks);
467
+ blocks.push(makeBlock("listItem", delta, { listKind: "bullet", listLevel: 0 }));
468
+ for (const nested of nestedLists) {
469
+ const nestedKind = nested.tagName.toLowerCase() === "ol" ? "ordered" : "bullet";
470
+ handleList(nested, nestedKind, 1, marks, blocks);
471
+ }
472
+ break;
473
+ }
474
+ case "img":
475
+ loose.push(node);
476
+ break;
477
+ case "br":
478
+ loose.push(node);
479
+ break;
480
+ default: {
481
+ const styleAttr = el.getAttribute("style") ?? "";
482
+ const wordListLevel = parseWordListLevel(styleAttr);
483
+ if (wordListLevel != null && !hasBlockDescendant(el)) {
484
+ flushLoose();
485
+ const marker = findWordListMarker(el);
486
+ const listKind = marker ? detectWordListKind(marker) : "bullet";
487
+ const attrs = { listKind, listLevel: wordListLevel };
488
+ const align = readAlign(el);
489
+ if (align) attrs.align = align;
490
+ const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
491
+ blocks.push(makeBlock("listItem", delta, attrs));
492
+ break;
493
+ }
494
+ if (hasBlockDescendant(el)) {
495
+ flushLoose();
496
+ let nextMarks = applyTagMarks(tag, marks);
497
+ nextMarks = applyStyleMarks(el.getAttribute("style"), nextMarks);
498
+ walkBlockLevel(el, nextMarks, blocks);
499
+ break;
500
+ }
501
+ if (BLOCK_LEVEL_TAGS.has(tag)) {
502
+ flushLoose();
503
+ const attrs = {};
504
+ const align = readAlign(el);
505
+ if (align) attrs.align = align;
506
+ const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
507
+ blocks.push(makeBlock("paragraph", delta, attrs));
508
+ break;
509
+ }
510
+ loose.push(node);
511
+ }
512
+ }
513
+ }
514
+ flushLoose();
515
+ }
516
+ function handleList(listEl, kind, level, marks, blocks) {
517
+ for (const child of Array.from(listEl.children)) {
518
+ const tag = child.tagName.toLowerCase();
519
+ if (tag === "ul" || tag === "ol") {
520
+ const siblingKind = tag === "ol" ? "ordered" : "bullet";
521
+ handleList(child, siblingKind, level + 1, marks, blocks);
522
+ continue;
523
+ }
524
+ if (tag !== "li") continue;
525
+ const nestedLists = topLevelListDescendants(child);
526
+ const clone = child.cloneNode(true);
527
+ for (const l of topLevelListDescendants(clone)) l.remove();
528
+ const delta = inlineDeltaFromNodes(Array.from(clone.childNodes), marks);
529
+ const attrs = { listKind: kind, listLevel: clampLevel(level) };
530
+ const align = readAlign(child);
531
+ if (align) attrs.align = align;
532
+ blocks.push(makeBlock("listItem", delta, attrs));
533
+ for (const nested of nestedLists) {
534
+ const nestedKind = nested.tagName.toLowerCase() === "ol" ? "ordered" : "bullet";
535
+ handleList(nested, nestedKind, level + 1, marks, blocks);
536
+ }
537
+ }
538
+ }
539
+ function topLevelListDescendants(root) {
540
+ const all = Array.from(root.querySelectorAll("ul, ol"));
541
+ return all.filter((el) => {
542
+ let p = el.parentElement;
543
+ while (p && p !== root) {
544
+ const t = p.tagName.toLowerCase();
545
+ if (t === "ul" || t === "ol" || t === "table" || t === "td" || t === "th") return false;
546
+ p = p.parentElement;
547
+ }
548
+ return true;
549
+ });
550
+ }
551
+ function handleBlockquote(el, marks, blocks) {
552
+ if (!hasBlockDescendant(el)) {
553
+ const attrs = {};
554
+ const align = readAlign(el);
555
+ if (align) attrs.align = align;
556
+ const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);
557
+ blocks.push(makeBlock("blockquote", delta, attrs));
558
+ return;
559
+ }
560
+ const nested = [];
561
+ walkBlockLevel(el, marks, nested);
562
+ for (const b of nested) {
563
+ blocks.push(b.type === "paragraph" ? { ...b, type: "blockquote" } : b);
564
+ }
565
+ }
566
+ function handleTable(table, marks, blocks) {
567
+ for (const row of directRows(table)) {
568
+ const cells = Array.from(row.children).filter(
569
+ (c) => ["td", "th"].includes(c.tagName.toLowerCase())
570
+ );
571
+ for (const cell of cells) {
572
+ const cellBlocks = [];
573
+ walkBlockLevel(cell, marks, cellBlocks);
574
+ for (const b of cellBlocks) {
575
+ const attrs = {};
576
+ if (b.attrs.align) attrs.align = b.attrs.align;
577
+ blocks.push(makeBlock("paragraph", b.delta, attrs));
578
+ }
579
+ }
580
+ }
581
+ }
582
+ function directRows(table) {
583
+ const rows = [];
584
+ for (const child of Array.from(table.children)) {
585
+ const tag = child.tagName.toLowerCase();
586
+ if (tag === "tr") {
587
+ rows.push(child);
588
+ } else if (tag === "tbody" || tag === "thead" || tag === "tfoot") {
589
+ for (const r of Array.from(child.children)) {
590
+ if (r.tagName.toLowerCase() === "tr") rows.push(r);
591
+ }
592
+ }
593
+ }
594
+ return rows;
595
+ }
596
+ function parseWordListLevel(styleAttr) {
597
+ const m = /mso-list\s*:\s*l\d+\s+level(\d+)/i.exec(styleAttr);
598
+ if (!m) return null;
599
+ const n = Number.parseInt(m[1], 10);
600
+ const fromLevelN = Number.isFinite(n) ? Math.max(0, n - 1) : 0;
601
+ const fromMargin = wordListLevelFromMarginLeft(marginLeftPt(styleAttr));
602
+ return clampLevel(Math.max(fromLevelN, fromMargin));
603
+ }
604
+ function marginLeftPt(styleAttr) {
605
+ const raw = parseStyle(styleAttr)["margin-left"];
606
+ if (!raw) return 0;
607
+ const m = /^(-?[\d.]+)\s*(pt|cm|in|px)$/i.exec(raw.trim());
608
+ if (!m) return 0;
609
+ const val = Number.parseFloat(m[1]);
610
+ if (!Number.isFinite(val) || val < 0) return 0;
611
+ switch (m[2].toLowerCase()) {
612
+ case "pt":
613
+ return val;
614
+ case "cm":
615
+ return val * 28.3465;
616
+ case "in":
617
+ return val * 72;
618
+ case "px":
619
+ return val * 0.75;
620
+ default:
621
+ return 0;
622
+ }
623
+ }
624
+ function wordListLevelFromMarginLeft(pt) {
625
+ if (pt <= 0) return 0;
626
+ return Math.max(0, Math.round(pt / 36) - 1);
627
+ }
628
+ function findWordListMarker(el) {
629
+ const all = el.querySelectorAll("*");
630
+ for (const e of Array.from(all)) {
631
+ const style = (e.getAttribute("style") ?? "").toLowerCase().replace(/\s+/g, "");
632
+ if (style.includes("mso-list:ignore")) return e;
633
+ }
634
+ return null;
635
+ }
636
+ function detectWordListKind(markerEl) {
637
+ const all = [markerEl, ...Array.from(markerEl.querySelectorAll("*"))];
638
+ for (const e of all) {
639
+ const style = (e.getAttribute("style") ?? "").toLowerCase();
640
+ if (style.includes("symbol") || style.includes("wingdings")) return "bullet";
641
+ }
642
+ const text = (markerEl.textContent ?? "").replace(/ /g, " ").trim();
643
+ if (/^[0-9]+[.)]/.test(text)) return "ordered";
644
+ if (/^[a-zA-Z][.)]/.test(text)) return "ordered";
645
+ return "bullet";
646
+ }
647
+ function inlineDeltaFromNodes(nodes, marks) {
648
+ const ops = [];
649
+ for (const n of nodes) walkInlineNode(n, marks, ops);
650
+ return trimEdgeWhitespace(collapseRepeatedSpaces(mergeAdjacentOps(ops)));
651
+ }
652
+ function collapseRepeatedSpaces(ops) {
653
+ return ops.map(
654
+ (op) => typeof op.insert === "string" ? { ...op, insert: op.insert.replace(/ {2,}/g, " ") } : op
655
+ );
656
+ }
657
+ function walkInlineNode(node, marks, ops) {
658
+ if (node.nodeType === Node.COMMENT_NODE) return;
659
+ if (node.nodeType === Node.TEXT_NODE) {
660
+ const text = normalizeWhitespace(node.textContent ?? "");
661
+ if (text.length > 0) pushOp(ops, text, marks);
662
+ return;
663
+ }
664
+ if (node.nodeType !== Node.ELEMENT_NODE) return;
665
+ const el = node;
666
+ const tag = el.tagName.toLowerCase();
667
+ if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) return;
668
+ if (tag === "img") {
669
+ const op = imageEmbedFromElement(el);
670
+ if (op) ops.push(op);
671
+ return;
672
+ }
673
+ if (tag === "br") {
674
+ ops.push({ insert: " " });
675
+ return;
676
+ }
677
+ const styleAttr = (el.getAttribute("style") ?? "").toLowerCase().replace(/\s+/g, "");
678
+ if (styleAttr.includes("mso-list:ignore")) return;
679
+ let nextMarks = applyTagMarks(tag, marks);
680
+ nextMarks = applyStyleMarks(el.getAttribute("style"), nextMarks);
681
+ if (tag === "a") {
682
+ const href = el.getAttribute("href");
683
+ if (href) nextMarks = { ...nextMarks, link: { href } };
684
+ }
685
+ for (const child of Array.from(el.childNodes)) walkInlineNode(child, nextMarks, ops);
686
+ }
687
+ var MIN_EMBED_BYTES = 1024;
688
+ function decodedDataUrlByteLength(dataUrl) {
689
+ const commaIndex = dataUrl.indexOf(",");
690
+ if (commaIndex === -1) return 0;
691
+ const meta = dataUrl.slice(0, commaIndex);
692
+ const payload = dataUrl.slice(commaIndex + 1);
693
+ if (/;base64$/i.test(meta)) {
694
+ const padding = payload.endsWith("==") ? 2 : payload.endsWith("=") ? 1 : 0;
695
+ return Math.max(0, Math.floor(payload.length * 3 / 4) - padding);
696
+ }
697
+ try {
698
+ return decodeURIComponent(payload).length;
699
+ } catch {
700
+ return payload.length;
701
+ }
702
+ }
703
+ function imageEmbedFromElement(el) {
704
+ const src = el.getAttribute("src");
705
+ if (!src || !src.startsWith("data:image/")) return null;
706
+ if (decodedDataUrlByteLength(src) < MIN_EMBED_BYTES) return null;
707
+ const style = parseStyle(el.getAttribute("style"));
708
+ const width = readImageDimension(el, "width", style);
709
+ const height = readImageDimension(el, "height", style);
710
+ if (width == null || height == null) return null;
711
+ let w = width;
712
+ let h = height;
713
+ if (w > MAX_INSERT_WIDTH) {
714
+ h = h * MAX_INSERT_WIDTH / w;
715
+ w = MAX_INSERT_WIDTH;
716
+ }
717
+ return { insert: { type: "image", src, width: Math.round(w), height: Math.round(h) } };
718
+ }
719
+ function readImageDimension(el, attr, style) {
720
+ const raw = el.getAttribute(attr);
721
+ if (raw && /^\d+(\.\d+)?$/.test(raw.trim())) {
722
+ const n = Number.parseFloat(raw);
723
+ if (Number.isFinite(n) && n > 0) return n;
724
+ }
725
+ const fromStyle = style[attr];
726
+ if (fromStyle) {
727
+ const n = cssLengthToPx(fromStyle);
728
+ if (n != null) return n;
729
+ }
730
+ return void 0;
731
+ }
732
+ function cssLengthToPx(raw) {
733
+ const v = raw.trim();
734
+ const m = /^(-?[\d.]+)\s*(px|pt|in|cm|mm)?$/i.exec(v);
735
+ if (!m) return void 0;
736
+ const num = Number.parseFloat(m[1]);
737
+ if (!Number.isFinite(num) || num <= 0) return void 0;
738
+ const unit = (m[2] ?? "px").toLowerCase();
739
+ switch (unit) {
740
+ case "px":
741
+ return num;
742
+ case "pt":
743
+ return num * (96 / 72);
744
+ case "in":
745
+ return num * 96;
746
+ case "cm":
747
+ return num * (96 / 2.54);
748
+ case "mm":
749
+ return num * (96 / 25.4);
750
+ default:
751
+ return void 0;
752
+ }
753
+ }
754
+ function applyTagMarks(tag, marks) {
755
+ switch (tag) {
756
+ case "b":
757
+ case "strong":
758
+ return { ...marks, bold: true };
759
+ case "i":
760
+ case "em":
761
+ return { ...marks, italic: true };
762
+ case "u":
763
+ return { ...marks, underline: true };
764
+ case "s":
765
+ case "strike":
766
+ case "del":
767
+ return { ...marks, strike: true };
768
+ default:
769
+ return marks;
770
+ }
771
+ }
772
+ function parseStyle(styleAttr) {
773
+ const out = {};
774
+ if (!styleAttr) return out;
775
+ for (const decl of styleAttr.split(";")) {
776
+ const idx = decl.indexOf(":");
777
+ if (idx === -1) continue;
778
+ const prop = decl.slice(0, idx).trim().toLowerCase();
779
+ const val = decl.slice(idx + 1).trim();
780
+ if (prop) out[prop] = val;
781
+ }
782
+ return out;
783
+ }
784
+ function applyStyleMarks(styleAttr, marks) {
785
+ const decls = parseStyle(styleAttr);
786
+ if (Object.keys(decls).length === 0) return marks;
787
+ const next = { ...marks };
788
+ if ("font-weight" in decls) {
789
+ const v = decls["font-weight"].toLowerCase();
790
+ if (v === "bold" || /^[5-9]00$/.test(v)) next.bold = true;
791
+ else if (v === "normal" || /^[1-4]00$/.test(v)) delete next.bold;
792
+ }
793
+ if ("font-style" in decls) {
794
+ const v = decls["font-style"].toLowerCase();
795
+ if (v === "italic" || v === "oblique") next.italic = true;
796
+ else if (v === "normal") delete next.italic;
797
+ }
798
+ const deco = decls["text-decoration-line"] ?? decls["text-decoration"];
799
+ if (deco !== void 0) {
800
+ const v = deco.toLowerCase();
801
+ delete next.underline;
802
+ delete next.strike;
803
+ if (v.includes("underline")) next.underline = true;
804
+ if (v.includes("line-through")) next.strike = true;
805
+ }
806
+ if ("color" in decls) {
807
+ const css = normalizeCssColor(decls.color);
808
+ if (css && !isBlack(css)) next.color = css;
809
+ else delete next.color;
810
+ }
811
+ const bg = decls["background-color"] ?? decls.background;
812
+ if (bg !== void 0) {
813
+ const css = normalizeCssColor(bg);
814
+ if (css && css !== "#ffffff") next.highlight = css;
815
+ else delete next.highlight;
816
+ }
817
+ if ("font-size" in decls) {
818
+ const pt = parseFontSizePt(decls["font-size"]);
819
+ if (pt != null) next.fontSize = formatFontSizePt(pt);
820
+ }
821
+ return next;
822
+ }
823
+ function parseFontSizePt(raw) {
824
+ const v = raw.trim().toLowerCase();
825
+ const m = /^(-?[\d.]+)\s*(pt|px|in)$/.exec(v);
826
+ if (!m) return void 0;
827
+ const num = Number.parseFloat(m[1]);
828
+ if (!Number.isFinite(num)) return void 0;
829
+ let pt;
830
+ switch (m[2]) {
831
+ case "pt":
832
+ pt = num;
833
+ break;
834
+ case "px":
835
+ pt = num * 0.75;
836
+ break;
837
+ case "in":
838
+ pt = num * 72;
839
+ break;
840
+ default:
841
+ return void 0;
842
+ }
843
+ const rounded = Math.round(pt * 10) / 10;
844
+ if (rounded <= 0 || rounded > 200) return void 0;
845
+ return rounded;
846
+ }
847
+ function formatFontSizePt(pt) {
848
+ const rounded = Math.round(pt * 10) / 10;
849
+ const str = Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
850
+ return `${str}pt`;
851
+ }
852
+ var NAMED_COLORS = {
853
+ black: "#000000",
854
+ white: "#ffffff",
855
+ red: "#ff0000",
856
+ green: "#008000",
857
+ blue: "#0000ff",
858
+ yellow: "#ffff00",
859
+ cyan: "#00ffff",
860
+ magenta: "#ff00ff",
861
+ gray: "#808080",
862
+ grey: "#808080",
863
+ orange: "#ffa500",
864
+ purple: "#800080"
865
+ };
866
+ function normalizeCssColor(raw) {
867
+ if (!raw) return void 0;
868
+ const v = raw.trim().toLowerCase();
869
+ if (!v || v === "transparent" || v === "none" || v === "inherit" || v === "initial") return void 0;
870
+ if (v.startsWith("#")) {
871
+ let hex = v.slice(1);
872
+ if (hex.length === 3) hex = hex.split("").map((c) => c + c).join("");
873
+ return /^[0-9a-f]{6}$/.test(hex) ? `#${hex}` : void 0;
874
+ }
875
+ const rgbMatch = /^rgba?\(([^)]+)\)$/.exec(v);
876
+ if (rgbMatch) {
877
+ const parts = rgbMatch[1].split(",").map((s) => s.trim());
878
+ if (parts.length < 3) return void 0;
879
+ const alpha = parts[3] !== void 0 ? Number.parseFloat(parts[3]) : 1;
880
+ if (alpha === 0) return void 0;
881
+ const toHex = (n) => {
882
+ const num = Math.max(0, Math.min(255, Number.parseInt(n, 10) || 0));
883
+ return num.toString(16).padStart(2, "0");
884
+ };
885
+ return `#${toHex(parts[0])}${toHex(parts[1])}${toHex(parts[2])}`;
886
+ }
887
+ return NAMED_COLORS[v];
888
+ }
889
+ function isBlack(hex) {
890
+ return hex.toLowerCase() === "#000000";
891
+ }
892
+ function readAlign(el) {
893
+ const style = parseStyle(el.getAttribute("style"));
894
+ const v = style["text-align"];
895
+ if (!v) return void 0;
896
+ switch (v.toLowerCase()) {
897
+ case "left":
898
+ case "start":
899
+ return "left";
900
+ case "center":
901
+ return "center";
902
+ case "right":
903
+ case "end":
904
+ return "right";
905
+ case "justify":
906
+ return "justify";
907
+ default:
908
+ return void 0;
909
+ }
910
+ }
911
+ function clampLevel(n) {
912
+ if (!Number.isFinite(n)) return 0;
913
+ return Math.max(0, Math.min(import_core.MAX_LIST_LEVEL, Math.trunc(n)));
914
+ }
915
+ function normalizeListLevels(blocks) {
916
+ let i = 0;
917
+ while (i < blocks.length) {
918
+ if (blocks[i].type !== "listItem") {
919
+ i++;
920
+ continue;
921
+ }
922
+ let j = i + 1;
923
+ while (j < blocks.length && blocks[j].type === "listItem") j++;
924
+ if (j - i > 1) {
925
+ let min = Number.POSITIVE_INFINITY;
926
+ for (let k = i; k < j; k++) {
927
+ const lvl = blocks[k].attrs.listLevel ?? 0;
928
+ if (lvl < min) min = lvl;
929
+ }
930
+ if (min > 0) {
931
+ for (let k = i; k < j; k++) {
932
+ const lvl = blocks[k].attrs.listLevel ?? 0;
933
+ blocks[k].attrs.listLevel = clampLevel(lvl - min);
934
+ }
935
+ }
936
+ }
937
+ i = j;
938
+ }
939
+ }
940
+ function pushOp(ops, text, marks) {
941
+ const hasMarks = Object.keys(marks).length > 0;
942
+ ops.push(hasMarks ? { insert: text, attributes: { ...marks } } : { insert: text });
943
+ }
944
+ function normalizeWhitespace(raw) {
945
+ return raw.replace(/[ \t\r\n]+/g, " ");
946
+ }
947
+ function trimEdgeWhitespace(ops) {
948
+ let out = ops;
949
+ if (out.length > 0 && typeof out[0].insert === "string") {
950
+ const trimmed = out[0].insert.replace(/^ +/, "");
951
+ if (trimmed.length === 0) out = out.slice(1);
952
+ else if (trimmed !== out[0].insert) out = [{ ...out[0], insert: trimmed }, ...out.slice(1)];
953
+ }
954
+ if (out.length > 0 && typeof out[out.length - 1].insert === "string") {
955
+ const last = out[out.length - 1];
956
+ const trimmed = last.insert.replace(/ +$/, "");
957
+ if (trimmed.length === 0) out = out.slice(0, -1);
958
+ else if (trimmed !== last.insert) out = [...out.slice(0, -1), { ...last, insert: trimmed }];
959
+ }
960
+ return out;
961
+ }
962
+ function mergeAdjacentOps(ops) {
963
+ const out = [];
964
+ for (const op of ops) {
965
+ const prev = out[out.length - 1];
966
+ if (prev && typeof prev.insert === "string" && typeof op.insert === "string" && sameMarks(prev.attributes, op.attributes)) {
967
+ prev.insert = prev.insert + op.insert;
968
+ continue;
969
+ }
970
+ out.push({ ...op });
971
+ }
972
+ return out;
973
+ }
974
+ function sameMarks(a, b) {
975
+ const ak = a ? Object.keys(a).sort() : [];
976
+ const bk = b ? Object.keys(b).sort() : [];
977
+ if (ak.length !== bk.length) return false;
978
+ for (let i = 0; i < ak.length; i++) {
979
+ if (ak[i] !== bk[i]) return false;
980
+ const av = a[ak[i]];
981
+ const bv = b[bk[i]];
982
+ if (typeof av === "object" && av !== null && typeof bv === "object" && bv !== null) {
983
+ if (JSON.stringify(av) !== JSON.stringify(bv)) return false;
984
+ } else if (av !== bv) {
985
+ return false;
986
+ }
987
+ }
988
+ return true;
989
+ }
990
+ function deltaToText(delta) {
991
+ let s = "";
992
+ for (const op of delta) if (typeof op.insert === "string") s += op.insert;
993
+ return s;
994
+ }
995
+ function makeBlock(type, delta, attrs) {
996
+ return { type, text: deltaToText(delta), delta, attrs };
997
+ }
998
+
999
+ // src/pastePlan.ts
1000
+ function planPaste(cd) {
1001
+ const raw = cd.getData(import_core2.SOFER_MIME);
1002
+ if (raw) {
1003
+ try {
1004
+ const slice = JSON.parse(raw);
1005
+ if (slice && Array.isArray(slice.blocks)) return { kind: "sofer", slice };
1006
+ } catch {
1007
+ }
1008
+ }
1009
+ const html = cd.getData("text/html");
1010
+ if (html) {
1011
+ const slice = htmlToSlice(html);
1012
+ if (slice) return { kind: "html", slice };
1013
+ }
1014
+ const files = Array.from(cd.files ?? []).filter((f) => f.type.startsWith("image/"));
1015
+ if (files.length > 0) return { kind: "images", files };
1016
+ const text = cd.getData("text/plain");
1017
+ if (text.length > 0) return { kind: "text", text };
1018
+ return { kind: "none" };
1019
+ }
1020
+
1021
+ // src/resolvePastedImages.ts
1022
+ var import_core3 = require("@sofereditor/core");
1023
+ function sliceHasDataImageEmbeds(slice) {
1024
+ return slice.blocks.some(
1025
+ (b) => b.delta.some((op) => (0, import_core3.isImageEmbed)(op.insert) && op.insert.src.startsWith("data:image/"))
1026
+ );
1027
+ }
1028
+ async function resolvePastedImageUploads(slice, upload) {
1029
+ if (!upload) return slice;
1030
+ const blocks = await Promise.all(
1031
+ slice.blocks.map(async (block) => {
1032
+ let changed = false;
1033
+ const delta = await Promise.all(
1034
+ block.delta.map(async (op) => {
1035
+ if (!(0, import_core3.isImageEmbed)(op.insert) || !op.insert.src.startsWith("data:image/")) return op;
1036
+ try {
1037
+ const file = await dataUrlToFile(op.insert.src);
1038
+ const newSrc = await upload(file);
1039
+ changed = true;
1040
+ return { ...op, insert: { ...op.insert, src: newSrc } };
1041
+ } catch (err) {
1042
+ console.error(
1043
+ "[sofereditor] falha ao subir imagem colada para o storage \u2014 mantendo a imagem embutida (data:) nesta colagem",
1044
+ err
1045
+ );
1046
+ return op;
1047
+ }
1048
+ })
1049
+ );
1050
+ return changed ? { ...block, delta } : block;
1051
+ })
1052
+ );
1053
+ return { ...slice, blocks };
1054
+ }
1055
+ async function dataUrlToFile(dataUrl) {
1056
+ const blob = await fetch(dataUrl).then((r) => r.blob());
1057
+ const mime = blob.type;
1058
+ return new File([blob], `imagem-colada-${Date.now()}.${extensionForMime(mime)}`, { type: mime });
1059
+ }
1060
+ var MIME_EXTENSIONS = {
1061
+ "image/png": "png",
1062
+ "image/jpeg": "jpg",
1063
+ "image/jpg": "jpg",
1064
+ "image/gif": "gif",
1065
+ "image/webp": "webp",
1066
+ "image/bmp": "bmp",
1067
+ "image/svg+xml": "svg"
1068
+ };
1069
+ function extensionForMime(mime) {
1070
+ return MIME_EXTENSIONS[mime.toLowerCase()] ?? "png";
1071
+ }
1072
+
332
1073
  // src/BehindImageSelectAffordance.tsx
333
1074
  var import_react = require("react");
334
1075
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -645,7 +1386,7 @@ function LinkDialog() {
645
1386
 
646
1387
  // src/PageConfigDialog.tsx
647
1388
  var import_react3 = require("react");
648
- var import_core = require("@sofereditor/core");
1389
+ var import_core4 = require("@sofereditor/core");
649
1390
  var import_jsx_runtime4 = require("react/jsx-runtime");
650
1391
  function PageConfigDialog() {
651
1392
  const { pageConfigRequest, resolvePageConfigRequest, setPageSettings } = useEditorContext();
@@ -679,14 +1420,14 @@ function PageConfigDialog() {
679
1420
  }, [pageConfigRequest]);
680
1421
  (0, import_react3.useEffect)(() => {
681
1422
  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)));
1423
+ const p = import_core4.PAGE_PRESETS[sizePreset];
1424
+ setWidthMm(roundMm((0, import_core4.pxToMm)(p.width)));
1425
+ setHeightMm(roundMm((0, import_core4.pxToMm)(p.height)));
685
1426
  }, [sizePreset]);
686
1427
  (0, import_react3.useEffect)(() => {
687
1428
  if (marginPreset === "custom") return;
688
- const px = import_core.MARGIN_PRESETS[marginPreset];
689
- const mm = roundMm((0, import_core.pxToMm)(px));
1429
+ const px = import_core4.MARGIN_PRESETS[marginPreset];
1430
+ const mm = roundMm((0, import_core4.pxToMm)(px));
690
1431
  setMarginTopMm(mm);
691
1432
  setMarginBottomMm(mm);
692
1433
  setMarginLeftMm(mm);
@@ -695,8 +1436,8 @@ function PageConfigDialog() {
695
1436
  const customWidth = sizePreset === "custom";
696
1437
  const previewPx = (0, import_react3.useMemo)(
697
1438
  () => ({
698
- width: Math.round((0, import_core.mmToPx)(widthMm)),
699
- height: Math.round((0, import_core.mmToPx)(heightMm))
1439
+ width: Math.round((0, import_core4.mmToPx)(widthMm)),
1440
+ height: Math.round((0, import_core4.mmToPx)(heightMm))
700
1441
  }),
701
1442
  [widthMm, heightMm]
702
1443
  );
@@ -704,12 +1445,12 @@ function PageConfigDialog() {
704
1445
  const onApply = (e) => {
705
1446
  e.preventDefault();
706
1447
  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)
1448
+ width: (0, import_core4.mmToPx)(widthMm),
1449
+ height: (0, import_core4.mmToPx)(heightMm),
1450
+ marginTop: (0, import_core4.mmToPx)(marginTopMm),
1451
+ marginBottom: (0, import_core4.mmToPx)(marginBottomMm),
1452
+ marginLeft: (0, import_core4.mmToPx)(marginLeftMm),
1453
+ marginRight: (0, import_core4.mmToPx)(marginRightMm)
713
1454
  };
714
1455
  setPageSettings(next);
715
1456
  resolvePageConfigRequest(true);
@@ -968,14 +1709,14 @@ function roundMm(mm) {
968
1709
  return Math.round(mm * 10) / 10;
969
1710
  }
970
1711
  function hydrateFromSettings(s, setters) {
971
- const preset = (0, import_core.detectPreset)(s.width, s.height);
1712
+ const preset = (0, import_core4.detectPreset)(s.width, s.height);
972
1713
  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));
1714
+ setters.setWidthMm(roundMm((0, import_core4.pxToMm)(s.width)));
1715
+ setters.setHeightMm(roundMm((0, import_core4.pxToMm)(s.height)));
1716
+ const top = roundMm((0, import_core4.pxToMm)(s.marginTop));
1717
+ const bottom = roundMm((0, import_core4.pxToMm)(s.marginBottom));
1718
+ const left = roundMm((0, import_core4.pxToMm)(s.marginLeft));
1719
+ const right = roundMm((0, import_core4.pxToMm)(s.marginRight));
979
1720
  setters.setMarginTopMm(top);
980
1721
  setters.setMarginBottomMm(bottom);
981
1722
  setters.setMarginLeftMm(left);
@@ -986,7 +1727,7 @@ function detectMarginPreset(s) {
986
1727
  const TOL = 1;
987
1728
  const allEqual = Math.abs(s.marginTop - s.marginBottom) <= TOL && Math.abs(s.marginTop - s.marginLeft) <= TOL && Math.abs(s.marginTop - s.marginRight) <= TOL;
988
1729
  if (!allEqual) return "custom";
989
- for (const [name, px] of Object.entries(import_core.MARGIN_PRESETS)) {
1730
+ for (const [name, px] of Object.entries(import_core4.MARGIN_PRESETS)) {
990
1731
  if (Math.abs(s.marginTop - px) <= TOL) return name;
991
1732
  }
992
1733
  return "custom";
@@ -1546,7 +2287,7 @@ function Handle({
1546
2287
 
1547
2288
  // src/RemoteCursorsOverlay.tsx
1548
2289
  var import_react7 = require("react");
1549
- var import_core2 = require("@sofereditor/core");
2290
+ var import_core5 = require("@sofereditor/core");
1550
2291
  var import_jsx_runtime8 = require("react/jsx-runtime");
1551
2292
  var DEFAULT_COLOR = "#3b82f6";
1552
2293
  function RemoteCursorsOverlay({
@@ -1569,7 +2310,7 @@ function RemoteCursorsOverlay({
1569
2310
  if (clientId === awareness.clientID) return;
1570
2311
  const s = state;
1571
2312
  if (!s.cursor) return;
1572
- const sel = (0, import_core2.decodeSelection)(doc, s.cursor);
2313
+ const sel = (0, import_core5.decodeSelection)(doc, s.cursor);
1573
2314
  if (!sel) return;
1574
2315
  const color = s.user?.color || DEFAULT_COLOR;
1575
2316
  const name = s.user?.name || "An\xF4nimo";
@@ -1716,11 +2457,11 @@ function RemoteCursorsOverlay({
1716
2457
 
1717
2458
  // src/NodeView.tsx
1718
2459
  var import_react10 = require("react");
1719
- var import_core4 = require("@sofereditor/core");
2460
+ var import_core7 = require("@sofereditor/core");
1720
2461
 
1721
2462
  // src/renderInline.tsx
1722
2463
  var import_react8 = require("react");
1723
- var import_core3 = require("@sofereditor/core");
2464
+ var import_core6 = require("@sofereditor/core");
1724
2465
  var import_jsx_runtime9 = require("react/jsx-runtime");
1725
2466
  function renderInline(delta, keyPrefix) {
1726
2467
  if (delta.length === 0 || delta.length === 1 && typeof delta[0].insert === "string" && delta[0].insert === "") {
@@ -1736,7 +2477,7 @@ function renderInline(delta, keyPrefix) {
1736
2477
  const key = `${keyPrefix}-${offset}`;
1737
2478
  body.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react8.Fragment, { children: wrap(text, op.attributes) }, key));
1738
2479
  offset += text.length;
1739
- } else if ((0, import_core3.isImageEmbed)(op.insert)) {
2480
+ } else if ((0, import_core6.isImageEmbed)(op.insert)) {
1740
2481
  const key = `${keyPrefix}-${offset}`;
1741
2482
  const layout = op.insert.layout ?? "inline";
1742
2483
  const commentMarkId = op.attributes?.comment?.markId;
@@ -1900,10 +2641,10 @@ function ImageEmbedViewImpl({
1900
2641
  );
1901
2642
  }
1902
2643
  function decorateBlanks(text) {
1903
- const segs = (0, import_core3.splitUnderscoreRuns)(text);
2644
+ const segs = (0, import_core6.splitUnderscoreRuns)(text);
1904
2645
  if (segs.length === 1 && !segs[0].blank) return text;
1905
2646
  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)
2647
+ (s, i) => s.blank ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-blank": "true", style: import_core6.BLANK_STYLE, children: s.text }, i) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react8.Fragment, { children: s.text }, i)
1907
2648
  );
1908
2649
  }
1909
2650
  function wrap(text, attrs) {
@@ -2101,7 +2842,7 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
2101
2842
  const common = commonBlockProps(block.attrs, index, block.type, fragment);
2102
2843
  switch (block.type) {
2103
2844
  case "heading": {
2104
- const level = clampLevel(block.attrs.level);
2845
+ const level = clampLevel2(block.attrs.level);
2105
2846
  const Tag = `h${level}`;
2106
2847
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2107
2848
  Tag,
@@ -2162,7 +2903,7 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
2162
2903
  function commonBlockProps(attrs, index, type, fragment) {
2163
2904
  const style = {};
2164
2905
  if (attrs.align) style.textAlign = attrs.align;
2165
- Object.assign(style, (0, import_core4.answerLineStyle)(attrs) ?? {});
2906
+ Object.assign(style, (0, import_core7.answerLineStyle)(attrs) ?? {});
2166
2907
  const base = {
2167
2908
  "data-block-index": index,
2168
2909
  "data-block-type": type,
@@ -2188,7 +2929,7 @@ function cellStyle(attrs, border) {
2188
2929
  if (attrs?.bgColor) style.backgroundColor = attrs.bgColor;
2189
2930
  return Object.keys(style).length > 0 ? style : void 0;
2190
2931
  }
2191
- function clampLevel(l) {
2932
+ function clampLevel2(l) {
2192
2933
  if (l === 2 || l === 3 || l === 4 || l === 5 || l === 6) return l;
2193
2934
  return 1;
2194
2935
  }
@@ -2209,7 +2950,7 @@ function TableView({ block, index, tableFragment }) {
2209
2950
  const rect = (0, import_react10.useMemo)(() => {
2210
2951
  if (editor.selection.anchor.blockIndex !== index) return null;
2211
2952
  if (editor.selection.focus.blockIndex !== index) return null;
2212
- return (0, import_core4.tableRectSelection)(editor.doc, editor.selection);
2953
+ return (0, import_core7.tableRectSelection)(editor.doc, editor.selection);
2213
2954
  }, [editor.doc, editor.selection, index]);
2214
2955
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ed-table-wrap", children: [
2215
2956
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
@@ -2245,7 +2986,7 @@ function TableView({ block, index, tableFragment }) {
2245
2986
  cell?.attrs,
2246
2987
  // rowStart/rowEnd são os limites do FRAGMENTO: numa tabela
2247
2988
  // quebrada entre páginas, cada página fecha a própria caixa.
2248
- (0, import_core4.cellBorderStyle)(
2989
+ (0, import_core7.cellBorderStyle)(
2249
2990
  block.attrs.borderPreset,
2250
2991
  { row: r, col: c, rowspan, colspan, cols, rowStart, rowEnd },
2251
2992
  "screen",
@@ -2821,14 +3562,13 @@ function shallowEqual(a, b) {
2821
3562
  }
2822
3563
 
2823
3564
  // src/useEditor.ts
2824
- var import_core5 = require("@sofereditor/core");
2825
- var MAX_INSERT_WIDTH = 600;
3565
+ var import_core8 = require("@sofereditor/core");
2826
3566
  function useEditor(opts = {}) {
2827
- const doc = (0, import_react13.useMemo)(() => opts.document ?? new import_core5.EditorDocument(), [opts.document]);
2828
- const history = (0, import_react13.useMemo)(() => new import_core5.EditorHistory(doc), [doc]);
3567
+ const doc = (0, import_react13.useMemo)(() => opts.document ?? new import_core8.EditorDocument(), [opts.document]);
3568
+ const history = (0, import_react13.useMemo)(() => new import_core8.EditorHistory(doc), [doc]);
2829
3569
  const [snapshot, setSnapshot] = (0, import_react13.useState)(() => doc.toJSON());
2830
3570
  const [selection, setSelectionState] = (0, import_react13.useState)(
2831
- () => (0, import_core5.collapsedSelection)({ blockIndex: 0, offset: 0 })
3571
+ () => (0, import_core8.collapsedSelection)({ blockIndex: 0, offset: 0 })
2832
3572
  );
2833
3573
  const selectionRef = (0, import_react13.useRef)(selection);
2834
3574
  selectionRef.current = selection;
@@ -2893,7 +3633,7 @@ function useEditor(opts = {}) {
2893
3633
  const isMarkActive = (0, import_react13.useCallback)(
2894
3634
  (name, value) => {
2895
3635
  const sel = selectionRef.current;
2896
- const { start, end } = (0, import_core5.orderedRange)(sel);
3636
+ const { start, end } = (0, import_core8.orderedRange)(sel);
2897
3637
  if (start.blockIndex === end.blockIndex && start.offset === end.offset) {
2898
3638
  const pending = pendingMarksRef.current[name];
2899
3639
  if (pending !== void 0) {
@@ -2902,7 +3642,7 @@ function useEditor(opts = {}) {
2902
3642
  }
2903
3643
  const yText2 = doc.getBlockText(start.blockIndex);
2904
3644
  if (!yText2 || start.offset === 0) return false;
2905
- return (0, import_core5.isMarkUniformInRange)(yText2, start.offset - 1, start.offset, name, value);
3645
+ return (0, import_core8.isMarkUniformInRange)(yText2, start.offset - 1, start.offset, name, value);
2906
3646
  }
2907
3647
  if (start.blockIndex !== end.blockIndex) {
2908
3648
  for (let i = start.blockIndex; i <= end.blockIndex; i++) {
@@ -2911,19 +3651,19 @@ function useEditor(opts = {}) {
2911
3651
  const s = i === start.blockIndex ? start.offset : 0;
2912
3652
  const e = i === end.blockIndex ? end.offset : yText2.length;
2913
3653
  if (s >= e) continue;
2914
- if (!(0, import_core5.isMarkUniformInRange)(yText2, s, e, name, value)) return false;
3654
+ if (!(0, import_core8.isMarkUniformInRange)(yText2, s, e, name, value)) return false;
2915
3655
  }
2916
3656
  return true;
2917
3657
  }
2918
3658
  const yText = doc.getBlockText(start.blockIndex);
2919
3659
  if (!yText) return false;
2920
- return (0, import_core5.isMarkUniformInRange)(yText, start.offset, end.offset, name, value);
3660
+ return (0, import_core8.isMarkUniformInRange)(yText, start.offset, end.offset, name, value);
2921
3661
  },
2922
3662
  [doc]
2923
3663
  );
2924
3664
  const getActiveMarks = (0, import_react13.useCallback)(() => {
2925
3665
  const sel = selectionRef.current;
2926
- const { start, end } = (0, import_core5.orderedRange)(sel);
3666
+ const { start, end } = (0, import_core8.orderedRange)(sel);
2927
3667
  if (start.blockIndex !== end.blockIndex) {
2928
3668
  return {};
2929
3669
  }
@@ -2931,14 +3671,14 @@ function useEditor(opts = {}) {
2931
3671
  if (!yText) return {};
2932
3672
  if (start.offset === end.offset) {
2933
3673
  if (start.offset === 0) return {};
2934
- return (0, import_core5.getMarksInRange)(yText, start.offset - 1, start.offset);
3674
+ return (0, import_core8.getMarksInRange)(yText, start.offset - 1, start.offset);
2935
3675
  }
2936
- return (0, import_core5.getMarksInRange)(yText, start.offset, end.offset);
3676
+ return (0, import_core8.getMarksInRange)(yText, start.offset, end.offset);
2937
3677
  }, [doc]);
2938
3678
  const toggleMark = (0, import_react13.useCallback)(
2939
3679
  (name, value) => {
2940
3680
  const sel = selectionRef.current;
2941
- const { start, end } = (0, import_core5.orderedRange)(sel);
3681
+ const { start, end } = (0, import_core8.orderedRange)(sel);
2942
3682
  const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
2943
3683
  if (isCollapsed) {
2944
3684
  const next = { ...pendingMarksRef.current };
@@ -2951,39 +3691,39 @@ function useEditor(opts = {}) {
2951
3691
  setPendingMarks(next);
2952
3692
  return;
2953
3693
  }
2954
- (0, import_core5.toggleMark)(ctxRef.current, name, value);
3694
+ (0, import_core8.toggleMark)(ctxRef.current, name, value);
2955
3695
  },
2956
3696
  [isMarkActive, setPendingMarks]
2957
3697
  );
2958
3698
  const setMark = (0, import_react13.useCallback)(
2959
3699
  (name, value) => {
2960
3700
  const sel = selectionRef.current;
2961
- const { start, end } = (0, import_core5.orderedRange)(sel);
3701
+ const { start, end } = (0, import_core8.orderedRange)(sel);
2962
3702
  const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
2963
3703
  if (isCollapsed) {
2964
3704
  setPendingMarks({ ...pendingMarksRef.current, [name]: value });
2965
3705
  return;
2966
3706
  }
2967
- (0, import_core5.setMark)(ctxRef.current, name, value);
3707
+ (0, import_core8.setMark)(ctxRef.current, name, value);
2968
3708
  },
2969
3709
  [setPendingMarks]
2970
3710
  );
2971
3711
  const removeMark = (0, import_react13.useCallback)(
2972
3712
  (name) => {
2973
3713
  const sel = selectionRef.current;
2974
- const { start, end } = (0, import_core5.orderedRange)(sel);
3714
+ const { start, end } = (0, import_core8.orderedRange)(sel);
2975
3715
  const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
2976
3716
  if (isCollapsed) {
2977
3717
  setPendingMarks({ ...pendingMarksRef.current, [name]: null });
2978
3718
  return;
2979
3719
  }
2980
- (0, import_core5.removeMark)(ctxRef.current, name);
3720
+ (0, import_core8.removeMark)(ctxRef.current, name);
2981
3721
  },
2982
3722
  [setPendingMarks]
2983
3723
  );
2984
3724
  const getBlockType = (0, import_react13.useCallback)(() => {
2985
3725
  const sel = selectionRef.current;
2986
- const { start, end } = (0, import_core5.orderedRange)(sel);
3726
+ const { start, end } = (0, import_core8.orderedRange)(sel);
2987
3727
  const first = doc.getBlockType(start.blockIndex);
2988
3728
  if (first === void 0) return "paragraph";
2989
3729
  for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {
@@ -2994,7 +3734,7 @@ function useEditor(opts = {}) {
2994
3734
  const getBlockAttr = (0, import_react13.useCallback)(
2995
3735
  (key) => {
2996
3736
  const sel = selectionRef.current;
2997
- const { start, end } = (0, import_core5.orderedRange)(sel);
3737
+ const { start, end } = (0, import_core8.orderedRange)(sel);
2998
3738
  const first = doc.getBlockAttrs(start.blockIndex)[key];
2999
3739
  for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {
3000
3740
  const next = doc.getBlockAttrs(i)[key];
@@ -3005,19 +3745,19 @@ function useEditor(opts = {}) {
3005
3745
  [doc]
3006
3746
  );
3007
3747
  const setBlockType = (0, import_react13.useCallback)((type, attrs) => {
3008
- (0, import_core5.setBlockType)(ctxRef.current, type, attrs);
3748
+ (0, import_core8.setBlockType)(ctxRef.current, type, attrs);
3009
3749
  }, []);
3010
3750
  const setBlockAttr = (0, import_react13.useCallback)(
3011
3751
  (key, value) => {
3012
- (0, import_core5.setBlockAttr)(ctxRef.current, key, value);
3752
+ (0, import_core8.setBlockAttr)(ctxRef.current, key, value);
3013
3753
  },
3014
3754
  []
3015
3755
  );
3016
3756
  const setAlign = (0, import_react13.useCallback)((value) => {
3017
3757
  const sel = selectionRef.current;
3018
3758
  const inCell = sel.focus.cellIndex != null;
3019
- if (inCell) (0, import_core5.setCellAttr)(ctxRef.current, "align", value);
3020
- else (0, import_core5.setBlockAttr)(ctxRef.current, "align", value);
3759
+ if (inCell) (0, import_core8.setCellAttr)(ctxRef.current, "align", value);
3760
+ else (0, import_core8.setBlockAttr)(ctxRef.current, "align", value);
3021
3761
  }, []);
3022
3762
  const getAlign = (0, import_react13.useCallback)(() => {
3023
3763
  const sel = selectionRef.current;
@@ -3027,7 +3767,7 @@ function useEditor(opts = {}) {
3027
3767
  return getBlockAttr("align");
3028
3768
  }, [doc, getBlockAttr]);
3029
3769
  const setCellBackground = (0, import_react13.useCallback)((color) => {
3030
- (0, import_core5.setCellAttr)(ctxRef.current, "bgColor", color);
3770
+ (0, import_core8.setCellAttr)(ctxRef.current, "bgColor", color);
3031
3771
  }, []);
3032
3772
  const getCellBackground = (0, import_react13.useCallback)(() => {
3033
3773
  const sel = selectionRef.current;
@@ -3037,7 +3777,7 @@ function useEditor(opts = {}) {
3037
3777
  const setTableBorderPreset = (0, import_react13.useCallback)((preset) => {
3038
3778
  const sel = selectionRef.current;
3039
3779
  if (!doc.isTable(sel.focus.blockIndex)) return;
3040
- (0, import_core5.setBlockAttrAtIndex)(ctxRef.current, sel.focus.blockIndex, "borderPreset", preset);
3780
+ (0, import_core8.setBlockAttrAtIndex)(ctxRef.current, sel.focus.blockIndex, "borderPreset", preset);
3041
3781
  }, [doc]);
3042
3782
  const getTableBorderPreset = (0, import_react13.useCallback)(() => {
3043
3783
  const sel = selectionRef.current;
@@ -3047,7 +3787,7 @@ function useEditor(opts = {}) {
3047
3787
  const setTableBorderColor = (0, import_react13.useCallback)((color) => {
3048
3788
  const sel = selectionRef.current;
3049
3789
  if (!doc.isTable(sel.focus.blockIndex)) return;
3050
- (0, import_core5.setBlockAttrAtIndex)(ctxRef.current, sel.focus.blockIndex, "borderColor", color);
3790
+ (0, import_core8.setBlockAttrAtIndex)(ctxRef.current, sel.focus.blockIndex, "borderColor", color);
3051
3791
  }, [doc]);
3052
3792
  const getTableBorderColor = (0, import_react13.useCallback)(() => {
3053
3793
  const sel = selectionRef.current;
@@ -3057,7 +3797,7 @@ function useEditor(opts = {}) {
3057
3797
  const isListActive = (0, import_react13.useCallback)(
3058
3798
  (kind) => {
3059
3799
  const sel = selectionRef.current;
3060
- const { start, end } = (0, import_core5.orderedRange)(sel);
3800
+ const { start, end } = (0, import_core8.orderedRange)(sel);
3061
3801
  for (let i = start.blockIndex; i <= end.blockIndex; i++) {
3062
3802
  if (doc.getBlockType(i) !== "listItem") return false;
3063
3803
  if (doc.getBlockAttrs(i).listKind !== kind) return false;
@@ -3067,112 +3807,112 @@ function useEditor(opts = {}) {
3067
3807
  [doc]
3068
3808
  );
3069
3809
  const toggleList = (0, import_react13.useCallback)((kind) => {
3070
- (0, import_core5.toggleList)(ctxRef.current, kind);
3810
+ (0, import_core8.toggleList)(ctxRef.current, kind);
3071
3811
  }, []);
3072
3812
  const indentList = (0, import_react13.useCallback)(() => {
3073
- (0, import_core5.indentList)(ctxRef.current);
3813
+ (0, import_core8.indentList)(ctxRef.current);
3074
3814
  }, []);
3075
3815
  const dedentList = (0, import_react13.useCallback)(() => {
3076
- (0, import_core5.dedentList)(ctxRef.current);
3816
+ (0, import_core8.dedentList)(ctxRef.current);
3077
3817
  }, []);
3078
3818
  const splitListItem = (0, import_react13.useCallback)(() => {
3079
- return (0, import_core5.splitListItem)(ctxRef.current);
3819
+ return (0, import_core8.splitListItem)(ctxRef.current);
3080
3820
  }, []);
3081
3821
  const isInTable = (0, import_react13.useCallback)(() => {
3082
3822
  return selectionRef.current.focus.cellIndex != null;
3083
3823
  }, []);
3084
3824
  const getTableLocation = (0, import_react13.useCallback)(() => {
3085
- return (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3825
+ return (0, import_core8.tableLocationOf)(doc, selectionRef.current.focus);
3086
3826
  }, [doc]);
3087
3827
  const insertTable = (0, import_react13.useCallback)((rows, cols) => {
3088
- (0, import_core5.insertTable)(ctxRef.current, rows, cols);
3828
+ (0, import_core8.insertTable)(ctxRef.current, rows, cols);
3089
3829
  }, []);
3090
3830
  const insertAnswerLines = (0, import_react13.useCallback)((count, spacing) => {
3091
- (0, import_core5.insertAnswerLines)(ctxRef.current, count, spacing);
3831
+ (0, import_core8.insertAnswerLines)(ctxRef.current, count, spacing);
3092
3832
  }, []);
3093
3833
  const deleteTable = (0, import_react13.useCallback)((blockIndex) => {
3094
3834
  const idx = blockIndex ?? selectionRef.current.focus.blockIndex;
3095
- (0, import_core5.deleteTable)(ctxRef.current, idx);
3835
+ (0, import_core8.deleteTable)(ctxRef.current, idx);
3096
3836
  }, []);
3097
3837
  const insertRowAbove = (0, import_react13.useCallback)(() => {
3098
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3838
+ const loc = (0, import_core8.tableLocationOf)(doc, selectionRef.current.focus);
3099
3839
  if (!loc) return;
3100
- (0, import_core5.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "before");
3840
+ (0, import_core8.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "before");
3101
3841
  }, [doc]);
3102
3842
  const insertRowBelow = (0, import_react13.useCallback)(() => {
3103
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3843
+ const loc = (0, import_core8.tableLocationOf)(doc, selectionRef.current.focus);
3104
3844
  if (!loc) return;
3105
- (0, import_core5.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "after");
3845
+ (0, import_core8.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "after");
3106
3846
  }, [doc]);
3107
3847
  const deleteCurrentRow = (0, import_react13.useCallback)(() => {
3108
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3848
+ const loc = (0, import_core8.tableLocationOf)(doc, selectionRef.current.focus);
3109
3849
  if (!loc) return;
3110
- (0, import_core5.deleteTableRow)(ctxRef.current, loc.blockIndex, loc.row);
3850
+ (0, import_core8.deleteTableRow)(ctxRef.current, loc.blockIndex, loc.row);
3111
3851
  }, [doc]);
3112
3852
  const insertColumnLeft = (0, import_react13.useCallback)(() => {
3113
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3853
+ const loc = (0, import_core8.tableLocationOf)(doc, selectionRef.current.focus);
3114
3854
  if (!loc) return;
3115
- (0, import_core5.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "before");
3855
+ (0, import_core8.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "before");
3116
3856
  }, [doc]);
3117
3857
  const insertColumnRight = (0, import_react13.useCallback)(() => {
3118
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3858
+ const loc = (0, import_core8.tableLocationOf)(doc, selectionRef.current.focus);
3119
3859
  if (!loc) return;
3120
- (0, import_core5.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "after");
3860
+ (0, import_core8.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "after");
3121
3861
  }, [doc]);
3122
3862
  const deleteCurrentColumn = (0, import_react13.useCallback)(() => {
3123
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3863
+ const loc = (0, import_core8.tableLocationOf)(doc, selectionRef.current.focus);
3124
3864
  if (!loc) return;
3125
- (0, import_core5.deleteTableColumn)(ctxRef.current, loc.blockIndex, loc.col);
3865
+ (0, import_core8.deleteTableColumn)(ctxRef.current, loc.blockIndex, loc.col);
3126
3866
  }, [doc]);
3127
3867
  const moveToNextCell = (0, import_react13.useCallback)(() => {
3128
- (0, import_core5.moveToNextCell)(ctxRef.current);
3868
+ (0, import_core8.moveToNextCell)(ctxRef.current);
3129
3869
  }, []);
3130
3870
  const moveToPrevCell = (0, import_react13.useCallback)(() => {
3131
- (0, import_core5.moveToPrevCell)(ctxRef.current);
3871
+ (0, import_core8.moveToPrevCell)(ctxRef.current);
3132
3872
  }, []);
3133
3873
  const mergeRight = (0, import_react13.useCallback)(() => {
3134
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3874
+ const loc = (0, import_core8.tableLocationOf)(doc, selectionRef.current.focus);
3135
3875
  if (!loc) return false;
3136
- return (0, import_core5.mergeRight)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3876
+ return (0, import_core8.mergeRight)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3137
3877
  }, [doc]);
3138
3878
  const mergeDown = (0, import_react13.useCallback)(() => {
3139
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3879
+ const loc = (0, import_core8.tableLocationOf)(doc, selectionRef.current.focus);
3140
3880
  if (!loc) return false;
3141
- return (0, import_core5.mergeDown)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3881
+ return (0, import_core8.mergeDown)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3142
3882
  }, [doc]);
3143
3883
  const splitCell = (0, import_react13.useCallback)(() => {
3144
- const loc = (0, import_core5.tableLocationOf)(doc, selectionRef.current.focus);
3884
+ const loc = (0, import_core8.tableLocationOf)(doc, selectionRef.current.focus);
3145
3885
  if (!loc) return false;
3146
- return (0, import_core5.splitCell)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3886
+ return (0, import_core8.splitCell)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3147
3887
  }, [doc]);
3148
3888
  const setColumnWidth = (0, import_react13.useCallback)(
3149
3889
  (blockIndex, col, widthPx) => {
3150
- (0, import_core5.setColumnWidth)(ctxRef.current, blockIndex, col, widthPx);
3890
+ (0, import_core8.setColumnWidth)(ctxRef.current, blockIndex, col, widthPx);
3151
3891
  },
3152
3892
  []
3153
3893
  );
3154
3894
  const getTableRect = (0, import_react13.useCallback)(
3155
- () => (0, import_core5.tableRectSelection)(doc, selectionRef.current),
3895
+ () => (0, import_core8.tableRectSelection)(doc, selectionRef.current),
3156
3896
  [doc]
3157
3897
  );
3158
3898
  const hasTableRectSelection = (0, import_react13.useCallback)(() => {
3159
- return (0, import_core5.tableRectSelection)(doc, selectionRef.current) !== null;
3899
+ return (0, import_core8.tableRectSelection)(doc, selectionRef.current) !== null;
3160
3900
  }, [doc]);
3161
3901
  const mergeSelection = (0, import_react13.useCallback)(() => {
3162
- return (0, import_core5.mergeSelection)(ctxRef.current);
3902
+ return (0, import_core8.mergeSelection)(ctxRef.current);
3163
3903
  }, []);
3164
3904
  const insertImage = (0, import_react13.useCallback)((embed) => {
3165
- (0, import_core5.insertImage)(ctxRef.current, embed);
3905
+ (0, import_core8.insertImage)(ctxRef.current, embed);
3166
3906
  }, []);
3167
3907
  const setImageAttrs = (0, import_react13.useCallback)(
3168
3908
  (blockIndex, offset, partial, cellIndex, newOffset) => {
3169
- (0, import_core5.setImageAttrs)(ctxRef.current, blockIndex, offset, partial, cellIndex, newOffset);
3909
+ (0, import_core8.setImageAttrs)(ctxRef.current, blockIndex, offset, partial, cellIndex, newOffset);
3170
3910
  },
3171
3911
  []
3172
3912
  );
3173
3913
  const moveEmbedAnchor = (0, import_react13.useCallback)(
3174
3914
  (from, to, newOffsetX, newOffsetY) => {
3175
- (0, import_core5.moveEmbedAnchor)(ctxRef.current, from, to, newOffsetX, newOffsetY);
3915
+ (0, import_core8.moveEmbedAnchor)(ctxRef.current, from, to, newOffsetX, newOffsetY);
3176
3916
  },
3177
3917
  []
3178
3918
  );
@@ -3190,7 +3930,7 @@ function useEditor(opts = {}) {
3190
3930
  }
3191
3931
  const upload = uploadImageRef.current;
3192
3932
  const src = upload ? await upload(file) : dataUrl;
3193
- (0, import_core5.insertImage)(ctxRef.current, { type: "image", src, width: w, height: h });
3933
+ (0, import_core8.insertImage)(ctxRef.current, { type: "image", src, width: w, height: h });
3194
3934
  }, []);
3195
3935
  const getSelectedEmbed = (0, import_react13.useCallback)(() => {
3196
3936
  const sel = selectionRef.current;
@@ -3209,7 +3949,7 @@ function useEditor(opts = {}) {
3209
3949
  cursor += op.insert.length;
3210
3950
  continue;
3211
3951
  }
3212
- if (cursor === start && (0, import_core5.isImageEmbed)(op.insert)) {
3952
+ if (cursor === start && (0, import_core8.isImageEmbed)(op.insert)) {
3213
3953
  return {
3214
3954
  blockIndex: sel.anchor.blockIndex,
3215
3955
  cellIndex: sel.anchor.cellIndex,
@@ -3342,7 +4082,8 @@ function useEditor(opts = {}) {
3342
4082
  resolvePageConfigRequest,
3343
4083
  imageCaptionRequest,
3344
4084
  requestImageCaption,
3345
- resolveImageCaptionRequest
4085
+ resolveImageCaptionRequest,
4086
+ uploadImage: opts.uploadImage
3346
4087
  };
3347
4088
  }
3348
4089
  function readAsDataURL(file) {
@@ -3389,6 +4130,7 @@ function Editor({
3389
4130
  const { doc, history, snapshot, getSelection, setSelection } = editor;
3390
4131
  const rootRef = (0, import_react14.useRef)(null);
3391
4132
  const isComposingRef = (0, import_react14.useRef)(false);
4133
+ const pasteInFlightRef = (0, import_react14.useRef)(false);
3392
4134
  const [hoveredBehind, setHoveredBehind] = (0, import_react14.useState)(null);
3393
4135
  const hoveredBehindRef = (0, import_react14.useRef)(null);
3394
4136
  const [hoveredLink, setHoveredLink] = (0, import_react14.useState)(null);
@@ -3404,7 +4146,7 @@ function Editor({
3404
4146
  const sel = readDomSelection(root);
3405
4147
  awareness.setLocalStateField(
3406
4148
  "cursor",
3407
- sel ? (0, import_core6.encodeSelection)(doc, sel) ?? null : null
4149
+ sel ? (0, import_core9.encodeSelection)(doc, sel) ?? null : null
3408
4150
  );
3409
4151
  };
3410
4152
  const onSelChange = () => {
@@ -3705,14 +4447,14 @@ function Editor({
3705
4447
  const data = e.data ?? "";
3706
4448
  if (data.length > 0) {
3707
4449
  e.preventDefault();
3708
- (0, import_core6.insertText)(ctx, data, ed.consumePendingMarks());
4450
+ (0, import_core9.insertText)(ctx, data, ed.consumePendingMarks());
3709
4451
  }
3710
4452
  return;
3711
4453
  }
3712
4454
  case "insertLineBreak": {
3713
4455
  e.preventDefault();
3714
4456
  if (ed.getBlockType() === "listItem") {
3715
- (0, import_core6.insertText)(ctx, "\n", ed.consumePendingMarks());
4457
+ (0, import_core9.insertText)(ctx, "\n", ed.consumePendingMarks());
3716
4458
  return;
3717
4459
  }
3718
4460
  const selEmbed = ed.getSelectedEmbed();
@@ -3732,7 +4474,7 @@ function Editor({
3732
4474
  });
3733
4475
  }
3734
4476
  if (ed.splitListItem()) return;
3735
- (0, import_core6.insertParagraph)(ctx);
4477
+ (0, import_core9.insertParagraph)(ctx);
3736
4478
  return;
3737
4479
  }
3738
4480
  case "insertParagraph": {
@@ -3754,14 +4496,14 @@ function Editor({
3754
4496
  });
3755
4497
  }
3756
4498
  if (ed.splitListItem()) return;
3757
- (0, import_core6.insertParagraph)(ctx);
4499
+ (0, import_core9.insertParagraph)(ctx);
3758
4500
  return;
3759
4501
  }
3760
4502
  case "insertReplacementText": {
3761
4503
  const text = e.data ?? e.dataTransfer?.getData("text/plain") ?? "";
3762
4504
  if (text.length > 0) {
3763
4505
  e.preventDefault();
3764
- (0, import_core6.insertText)(ctx, text, ed.consumePendingMarks());
4506
+ (0, import_core9.insertText)(ctx, text, ed.consumePendingMarks());
3765
4507
  }
3766
4508
  return;
3767
4509
  }
@@ -3774,7 +4516,7 @@ function Editor({
3774
4516
  case "deleteSoftLineBackward":
3775
4517
  case "deleteHardLineBackward": {
3776
4518
  e.preventDefault();
3777
- (0, import_core6.deleteBackward)(ctx);
4519
+ (0, import_core9.deleteBackward)(ctx);
3778
4520
  return;
3779
4521
  }
3780
4522
  case "deleteContentForward":
@@ -3782,7 +4524,7 @@ function Editor({
3782
4524
  case "deleteSoftLineForward":
3783
4525
  case "deleteHardLineForward": {
3784
4526
  e.preventDefault();
3785
- (0, import_core6.deleteForward)(ctx);
4527
+ (0, import_core9.deleteForward)(ctx);
3786
4528
  return;
3787
4529
  }
3788
4530
  case "historyUndo": {
@@ -3809,10 +4551,10 @@ function Editor({
3809
4551
  if (!root) return;
3810
4552
  const writeSlice = (e) => {
3811
4553
  const ctx = ctxRef.current;
3812
- const slice = (0, import_core6.serializeSelection)(ctx.doc, ctx.getSelection());
4554
+ const slice = (0, import_core9.serializeSelection)(ctx.doc, ctx.getSelection());
3813
4555
  if (!slice || !e.clipboardData) return false;
3814
- e.clipboardData.setData(import_core6.SOFER_MIME, JSON.stringify(slice));
3815
- e.clipboardData.setData("text/plain", (0, import_core6.sliceToText)(slice));
4556
+ e.clipboardData.setData(import_core9.SOFER_MIME, JSON.stringify(slice));
4557
+ e.clipboardData.setData("text/plain", (0, import_core9.sliceToText)(slice));
3816
4558
  return true;
3817
4559
  };
3818
4560
  const onCopy = (e) => {
@@ -3820,40 +4562,73 @@ function Editor({
3820
4562
  };
3821
4563
  const onCut = (e) => {
3822
4564
  e.preventDefault();
3823
- if (writeSlice(e)) (0, import_core6.deleteSelection)(ctxRef.current);
4565
+ if (writeSlice(e)) (0, import_core9.deleteSelection)(ctxRef.current);
3824
4566
  };
3825
4567
  const onPaste = (e) => {
3826
4568
  const ctx = ctxRef.current;
3827
4569
  const cd = e.clipboardData;
3828
4570
  if (!cd) return;
3829
- const raw = cd.getData(import_core6.SOFER_MIME);
3830
- if (raw) {
3831
- try {
3832
- const slice = JSON.parse(raw);
3833
- if (slice && Array.isArray(slice.blocks)) {
3834
- e.preventDefault();
3835
- (0, import_core6.insertSlice)(ctx, slice);
4571
+ const plan = planPaste(cd);
4572
+ if (plan.kind === "none") return;
4573
+ e.preventDefault();
4574
+ switch (plan.kind) {
4575
+ case "sofer":
4576
+ (0, import_core9.insertSlice)(ctx, plan.slice);
4577
+ return;
4578
+ case "html": {
4579
+ if (!sliceHasDataImageEmbeds(plan.slice)) {
4580
+ (0, import_core9.insertSlice)(ctx, plan.slice);
3836
4581
  return;
3837
4582
  }
3838
- } catch {
4583
+ if (pasteInFlightRef.current) {
4584
+ console.warn(
4585
+ "[sofereditor] colagem com imagem ainda subindo \u2014 descartando esta colagem concorrente para n\xE3o corromper a posi\xE7\xE3o de inser\xE7\xE3o"
4586
+ );
4587
+ return;
4588
+ }
4589
+ pasteInFlightRef.current = true;
4590
+ const capturedSelection = ctx.getSelection();
4591
+ void (async () => {
4592
+ try {
4593
+ const resolved = await resolvePastedImageUploads(plan.slice, editorRef.current.uploadImage);
4594
+ const liveSelection = ctxRef.current.getSelection();
4595
+ const userMovedAway = !selectionsEqual(liveSelection, capturedSelection);
4596
+ const { blockIndex, cellIndex } = capturedSelection.focus;
4597
+ const targetText = ctxRef.current.doc.textAt(blockIndex, cellIndex);
4598
+ let insertAt = capturedSelection;
4599
+ if (!targetText) {
4600
+ console.error(
4601
+ "[sofereditor] bloco de destino da colagem sumiu durante o upload da imagem \u2014 inserindo no fim do documento em vez de descartar a colagem"
4602
+ );
4603
+ const lastIndex = Math.max(0, ctxRef.current.doc.blockCount() - 1);
4604
+ const lastText = ctxRef.current.doc.getBlockText(lastIndex);
4605
+ insertAt = (0, import_core9.collapsedSelection)({ blockIndex: lastIndex, offset: lastText ? lastText.length : 0 });
4606
+ }
4607
+ ctxRef.current.setSelection(insertAt);
4608
+ (0, import_core9.insertSlice)(ctxRef.current, resolved);
4609
+ if (userMovedAway) {
4610
+ ctxRef.current.setSelection(liveSelection);
4611
+ }
4612
+ } finally {
4613
+ pasteInFlightRef.current = false;
4614
+ }
4615
+ })();
4616
+ return;
4617
+ }
4618
+ case "images":
4619
+ void (async () => {
4620
+ for (const f of plan.files) await editorRef.current.insertImageFromFile(f);
4621
+ })();
4622
+ return;
4623
+ case "text": {
4624
+ const lines = plan.text.split(/\r\n|\r|\n/);
4625
+ lines.forEach((line, i) => {
4626
+ if (i > 0) (0, import_core9.insertParagraph)(ctx);
4627
+ if (line.length > 0) (0, import_core9.insertText)(ctx, line);
4628
+ });
4629
+ return;
3839
4630
  }
3840
4631
  }
3841
- const images = Array.from(cd.files ?? []).filter((f) => f.type.startsWith("image/"));
3842
- if (images.length > 0) {
3843
- e.preventDefault();
3844
- void (async () => {
3845
- for (const f of images) await editorRef.current.insertImageFromFile(f);
3846
- })();
3847
- return;
3848
- }
3849
- const text = cd.getData("text/plain");
3850
- if (text.length === 0) return;
3851
- e.preventDefault();
3852
- const lines = text.split(/\r\n|\r|\n/);
3853
- lines.forEach((line, i) => {
3854
- if (i > 0) (0, import_core6.insertParagraph)(ctx);
3855
- if (line.length > 0) (0, import_core6.insertText)(ctx, line);
3856
- });
3857
4632
  };
3858
4633
  root.addEventListener("copy", onCopy);
3859
4634
  root.addEventListener("cut", onCut);
@@ -3892,6 +4667,17 @@ function Editor({
3892
4667
  }
3893
4668
  return;
3894
4669
  }
4670
+ if (!mod && (e.key === "Backspace" || e.key === "Delete")) {
4671
+ if (isComposingRef.current) return;
4672
+ const direction = e.key === "Backspace" ? "backward" : "forward";
4673
+ const shouldIntercept = ed.getSelectedEmbed() != null || (0, import_core9.isEmbedAdjacentToCaret)(ctxRef.current, direction);
4674
+ if (shouldIntercept) {
4675
+ e.preventDefault();
4676
+ if (direction === "backward") (0, import_core9.deleteBackward)(ctxRef.current);
4677
+ else (0, import_core9.deleteForward)(ctxRef.current);
4678
+ }
4679
+ return;
4680
+ }
3895
4681
  if (!mod) return;
3896
4682
  const key = e.key.toLowerCase();
3897
4683
  if (!e.shiftKey && !e.altKey && key === "z") {
@@ -3944,7 +4730,7 @@ function Editor({
3944
4730
  const onCompositionEnd = (0, import_react14.useCallback)((e) => {
3945
4731
  isComposingRef.current = false;
3946
4732
  if (e.data && e.data.length > 0) {
3947
- (0, import_core6.insertText)(ctxRef.current, e.data);
4733
+ (0, import_core9.insertText)(ctxRef.current, e.data);
3948
4734
  }
3949
4735
  }, []);
3950
4736
  const topLevels = (0, import_react14.useMemo)(() => groupTopLevels(snapshot.blocks), [snapshot]);
@@ -4108,11 +4894,15 @@ function groupTopLevels(snapshot) {
4108
4894
  const groupStart = i;
4109
4895
  const items = [];
4110
4896
  const kind = block.attrs.listKind === "ordered" ? "ordered" : "bullet";
4897
+ const leaderLevel = clampListLevel2(block.attrs.listLevel);
4111
4898
  const groupStyle = block.attrs.listStyle;
4112
4899
  while (i < snapshot.length) {
4113
4900
  const b = snapshot[i];
4114
4901
  if (b.type !== "listItem") break;
4115
- if ((b.attrs.listKind ?? "bullet") !== kind) break;
4902
+ const bLevel = clampListLevel2(b.attrs.listLevel);
4903
+ if (bLevel <= leaderLevel && (b.attrs.listKind ?? "bullet") !== kind) {
4904
+ break;
4905
+ }
4116
4906
  if (i > groupStart) {
4117
4907
  if (typeof b.attrs.listStart === "number") break;
4118
4908
  const itemStyle = b.attrs.listStyle;
@@ -4220,7 +5010,7 @@ function buildListTree(items) {
4220
5010
  function renderListTree(tree, top, start, key) {
4221
5011
  const className = `ed-list ed-list-${top.listKind}`;
4222
5012
  const style = top.listStyle ? { listStyleType: top.listStyle } : void 0;
4223
- 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));
5013
+ 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));
4224
5014
  if (top.listKind === "ordered") {
4225
5015
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4226
5016
  "ol",
@@ -4236,9 +5026,10 @@ function renderListTree(tree, top, start, key) {
4236
5026
  }
4237
5027
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className, "data-list-kind": "bullet", style, children: items }, key);
4238
5028
  }
4239
- function renderNestedListTree(tree, kind, key) {
5029
+ function renderNestedListTree(tree, key) {
5030
+ const kind = tree[0]?.entry.block.attrs.listKind === "ordered" ? "ordered" : "bullet";
4240
5031
  const Tag = kind === "ordered" ? "ol" : "ul";
4241
- 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);
5032
+ 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);
4242
5033
  }
4243
5034
  function clampListLevel2(l) {
4244
5035
  if (typeof l !== "number" || !Number.isFinite(l)) return 0;
@@ -4267,7 +5058,7 @@ function caretRangeFromPoint(x, y) {
4267
5058
 
4268
5059
  // src/Toolbar.tsx
4269
5060
  var import_react15 = require("react");
4270
- var import_core7 = require("@sofereditor/core");
5061
+ var import_core10 = require("@sofereditor/core");
4271
5062
  var import_jsx_runtime13 = require("react/jsx-runtime");
4272
5063
  var FONT_FAMILIES = ["Arial"];
4273
5064
  var FONT_SIZES = ["8pt", "10pt", "11pt", "12pt", "14pt", "16pt", "18pt", "24pt", "32pt", "48pt"];
@@ -4739,7 +5530,7 @@ function AnswerLinesMenu() {
4739
5530
  document.addEventListener("mousedown", onDown);
4740
5531
  return () => document.removeEventListener("mousedown", onDown);
4741
5532
  }, [open]);
4742
- const clamped = Math.max(1, Math.min(import_core7.ANSWER_LINE_MAX, Math.trunc(count) || 1));
5533
+ const clamped = Math.max(1, Math.min(import_core10.ANSWER_LINE_MAX, Math.trunc(count) || 1));
4743
5534
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { ref: rootRef, className: "ed-toolbar-tablemenu", children: [
4744
5535
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4745
5536
  "button",
@@ -4772,7 +5563,7 @@ function AnswerLinesMenu() {
4772
5563
  {
4773
5564
  type: "number",
4774
5565
  min: 1,
4775
- max: import_core7.ANSWER_LINE_MAX,
5566
+ max: import_core10.ANSWER_LINE_MAX,
4776
5567
  value: count,
4777
5568
  onChange: (e) => setCount(Number(e.target.value)),
4778
5569
  "aria-label": "Quantidade de linhas"
@@ -4965,7 +5756,7 @@ function TableMenu() {
4965
5756
  {
4966
5757
  className: "ed-toolbar-swatch",
4967
5758
  "aria-hidden": true,
4968
- style: { background: editor.getTableBorderColor() ?? import_core7.TABLE_BORDER_COLOR }
5759
+ style: { background: editor.getTableBorderColor() ?? import_core10.TABLE_BORDER_COLOR }
4969
5760
  }
4970
5761
  ),
4971
5762
  "Cor da borda",
@@ -4973,7 +5764,7 @@ function TableMenu() {
4973
5764
  "input",
4974
5765
  {
4975
5766
  type: "color",
4976
- value: editor.getTableBorderColor() ?? import_core7.TABLE_BORDER_COLOR,
5767
+ value: editor.getTableBorderColor() ?? import_core10.TABLE_BORDER_COLOR,
4977
5768
  onChange: (e) => editor.setTableBorderColor(e.target.value),
4978
5769
  "aria-label": "Cor da borda"
4979
5770
  }
@@ -5095,6 +5886,7 @@ function MarkButton({ title, pressed, onClick, children }) {
5095
5886
  getBlockElement,
5096
5887
  getCellElement,
5097
5888
  getFragmentForOffset,
5889
+ htmlToSlice,
5098
5890
  isTableRectSelection,
5099
5891
  locatePoint,
5100
5892
  readDomSelection,