@sofereditor/react 0.6.0 → 0.9.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.js CHANGED
@@ -1,13 +1,14 @@
1
1
  // src/Editor.tsx
2
2
  import {
3
- useCallback as useCallback7,
4
- useEffect as useEffect9,
3
+ useCallback as useCallback8,
4
+ useEffect as useEffect10,
5
5
  useLayoutEffect as useLayoutEffect5,
6
6
  useMemo as useMemo4,
7
- useRef as useRef11,
8
- useState as useState10
7
+ useRef as useRef12,
8
+ useState as useState11
9
9
  } from "react";
10
10
  import {
11
+ collapsedSelection as collapsedSelection2,
11
12
  deleteBackward,
12
13
  deleteForward,
13
14
  deleteSelection,
@@ -15,6 +16,8 @@ import {
15
16
  insertParagraph,
16
17
  insertSlice,
17
18
  insertText,
19
+ isEmbedAdjacentToCaret,
20
+ isFormulaEmbed as isFormulaEmbed2,
18
21
  serializeSelection,
19
22
  sliceToText,
20
23
  SOFER_MIME as SOFER_MIME2
@@ -297,7 +300,12 @@ function applyDomSelection(root, modelSel) {
297
300
  import { SOFER_MIME } from "@sofereditor/core";
298
301
 
299
302
  // src/htmlToSlice.ts
300
- import { MAX_LIST_LEVEL } from "@sofereditor/core";
303
+ import { isImageEmbed, MAX_LIST_LEVEL } from "@sofereditor/core";
304
+
305
+ // src/imageConstraints.ts
306
+ var MAX_INSERT_WIDTH = 600;
307
+
308
+ // src/htmlToSlice.ts
301
309
  function htmlToSlice(html) {
302
310
  if (!html || html.trim().length === 0) return null;
303
311
  let doc;
@@ -316,8 +324,10 @@ function htmlToSlice(html) {
316
324
  }
317
325
  if (blocks.length === 0) return null;
318
326
  normalizeListLevels(blocks);
319
- const temTexto = blocks.some((b) => b.text.trim().length > 0);
320
- if (!temTexto) return null;
327
+ const temConteudo = blocks.some(
328
+ (b) => b.text.trim().length > 0 || b.delta.some((op) => isImageEmbed(op.insert))
329
+ );
330
+ if (!temConteudo) return null;
321
331
  const blockLevel = blocks.length > 1 || blocks[0].type !== "paragraph";
322
332
  return { blocks, openStart: false, openEnd: false, ...blockLevel ? { blockLevel: true } : {} };
323
333
  }
@@ -428,6 +438,7 @@ function walkBlockLevel(container, marks, blocks) {
428
438
  break;
429
439
  }
430
440
  case "img":
441
+ loose.push(node);
431
442
  break;
432
443
  case "br":
433
444
  loose.push(node);
@@ -620,7 +631,11 @@ function walkInlineNode(node, marks, ops) {
620
631
  const el = node;
621
632
  const tag = el.tagName.toLowerCase();
622
633
  if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) return;
623
- if (tag === "img") return;
634
+ if (tag === "img") {
635
+ const op = imageEmbedFromElement(el);
636
+ if (op) ops.push(op);
637
+ return;
638
+ }
624
639
  if (tag === "br") {
625
640
  ops.push({ insert: " " });
626
641
  return;
@@ -635,6 +650,73 @@ function walkInlineNode(node, marks, ops) {
635
650
  }
636
651
  for (const child of Array.from(el.childNodes)) walkInlineNode(child, nextMarks, ops);
637
652
  }
653
+ var MIN_EMBED_BYTES = 1024;
654
+ function decodedDataUrlByteLength(dataUrl) {
655
+ const commaIndex = dataUrl.indexOf(",");
656
+ if (commaIndex === -1) return 0;
657
+ const meta = dataUrl.slice(0, commaIndex);
658
+ const payload = dataUrl.slice(commaIndex + 1);
659
+ if (/;base64$/i.test(meta)) {
660
+ const padding = payload.endsWith("==") ? 2 : payload.endsWith("=") ? 1 : 0;
661
+ return Math.max(0, Math.floor(payload.length * 3 / 4) - padding);
662
+ }
663
+ try {
664
+ return decodeURIComponent(payload).length;
665
+ } catch {
666
+ return payload.length;
667
+ }
668
+ }
669
+ function imageEmbedFromElement(el) {
670
+ const src = el.getAttribute("src");
671
+ if (!src || !src.startsWith("data:image/")) return null;
672
+ if (decodedDataUrlByteLength(src) < MIN_EMBED_BYTES) return null;
673
+ const style = parseStyle(el.getAttribute("style"));
674
+ const width = readImageDimension(el, "width", style);
675
+ const height = readImageDimension(el, "height", style);
676
+ if (width == null || height == null) return null;
677
+ let w = width;
678
+ let h = height;
679
+ if (w > MAX_INSERT_WIDTH) {
680
+ h = h * MAX_INSERT_WIDTH / w;
681
+ w = MAX_INSERT_WIDTH;
682
+ }
683
+ return { insert: { type: "image", src, width: Math.round(w), height: Math.round(h) } };
684
+ }
685
+ function readImageDimension(el, attr, style) {
686
+ const raw = el.getAttribute(attr);
687
+ if (raw && /^\d+(\.\d+)?$/.test(raw.trim())) {
688
+ const n = Number.parseFloat(raw);
689
+ if (Number.isFinite(n) && n > 0) return n;
690
+ }
691
+ const fromStyle = style[attr];
692
+ if (fromStyle) {
693
+ const n = cssLengthToPx(fromStyle);
694
+ if (n != null) return n;
695
+ }
696
+ return void 0;
697
+ }
698
+ function cssLengthToPx(raw) {
699
+ const v = raw.trim();
700
+ const m = /^(-?[\d.]+)\s*(px|pt|in|cm|mm)?$/i.exec(v);
701
+ if (!m) return void 0;
702
+ const num = Number.parseFloat(m[1]);
703
+ if (!Number.isFinite(num) || num <= 0) return void 0;
704
+ const unit = (m[2] ?? "px").toLowerCase();
705
+ switch (unit) {
706
+ case "px":
707
+ return num;
708
+ case "pt":
709
+ return num * (96 / 72);
710
+ case "in":
711
+ return num * 96;
712
+ case "cm":
713
+ return num * (96 / 2.54);
714
+ case "mm":
715
+ return num * (96 / 25.4);
716
+ default:
717
+ return void 0;
718
+ }
719
+ }
638
720
  function applyTagMarks(tag, marks) {
639
721
  switch (tag) {
640
722
  case "b":
@@ -902,6 +984,58 @@ function planPaste(cd) {
902
984
  return { kind: "none" };
903
985
  }
904
986
 
987
+ // src/resolvePastedImages.ts
988
+ import { isImageEmbed as isImageEmbed2 } from "@sofereditor/core";
989
+ function sliceHasDataImageEmbeds(slice) {
990
+ return slice.blocks.some(
991
+ (b) => b.delta.some((op) => isImageEmbed2(op.insert) && op.insert.src.startsWith("data:image/"))
992
+ );
993
+ }
994
+ async function resolvePastedImageUploads(slice, upload) {
995
+ if (!upload) return slice;
996
+ const blocks = await Promise.all(
997
+ slice.blocks.map(async (block) => {
998
+ let changed = false;
999
+ const delta = await Promise.all(
1000
+ block.delta.map(async (op) => {
1001
+ if (!isImageEmbed2(op.insert) || !op.insert.src.startsWith("data:image/")) return op;
1002
+ try {
1003
+ const file = await dataUrlToFile(op.insert.src);
1004
+ const newSrc = await upload(file);
1005
+ changed = true;
1006
+ return { ...op, insert: { ...op.insert, src: newSrc } };
1007
+ } catch (err) {
1008
+ console.error(
1009
+ "[sofereditor] falha ao subir imagem colada para o storage \u2014 mantendo a imagem embutida (data:) nesta colagem",
1010
+ err
1011
+ );
1012
+ return op;
1013
+ }
1014
+ })
1015
+ );
1016
+ return changed ? { ...block, delta } : block;
1017
+ })
1018
+ );
1019
+ return { ...slice, blocks };
1020
+ }
1021
+ async function dataUrlToFile(dataUrl) {
1022
+ const blob = await fetch(dataUrl).then((r) => r.blob());
1023
+ const mime = blob.type;
1024
+ return new File([blob], `imagem-colada-${Date.now()}.${extensionForMime(mime)}`, { type: mime });
1025
+ }
1026
+ var MIME_EXTENSIONS = {
1027
+ "image/png": "png",
1028
+ "image/jpeg": "jpg",
1029
+ "image/jpg": "jpg",
1030
+ "image/gif": "gif",
1031
+ "image/webp": "webp",
1032
+ "image/bmp": "bmp",
1033
+ "image/svg+xml": "svg"
1034
+ };
1035
+ function extensionForMime(mime) {
1036
+ return MIME_EXTENSIONS[mime.toLowerCase()] ?? "png";
1037
+ }
1038
+
905
1039
  // src/BehindImageSelectAffordance.tsx
906
1040
  import {
907
1041
  useCallback,
@@ -1669,15 +1803,376 @@ function ImageCaptionDialog() {
1669
1803
  );
1670
1804
  }
1671
1805
 
1672
- // src/EditorContext.tsx
1806
+ // src/FormulaDialog.tsx
1807
+ import { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef4, useState as useState5 } from "react";
1808
+
1809
+ // src/formulaSnippet.ts
1810
+ var PALETA = [
1811
+ {
1812
+ nome: "Estruturas",
1813
+ colunas: 4,
1814
+ itens: [
1815
+ { label: "Fra\xE7\xE3o", snippet: "\\frac{}{}" },
1816
+ { label: "Expoente", snippet: "^{}" },
1817
+ { label: "\xCDndice", snippet: "_{}" },
1818
+ { label: "Raiz", snippet: "\\sqrt{}" },
1819
+ { label: "Raiz n-\xE9sima", snippet: "\\sqrt[{}]{}" },
1820
+ { label: "Somat\xF3rio", snippet: "\\sum_{}^{}" },
1821
+ { label: "Produt\xF3rio", snippet: "\\prod_{}^{}" },
1822
+ { label: "Integral", snippet: "\\int_{}^{}" },
1823
+ { label: "Limite", snippet: "\\lim_{{} \\to {}}" },
1824
+ { label: "Derivada", snippet: "\\frac{d}{d{}}" },
1825
+ {
1826
+ label: "Matriz 2\xD72",
1827
+ snippet: "\\begin{pmatrix} {} & {} \\\\ {} & {} \\end{pmatrix}"
1828
+ },
1829
+ { label: "Sistema", snippet: "\\begin{cases} {} \\\\ \\end{cases}" },
1830
+ { label: "Par\xEAnteses", snippet: "\\left({}\\right)" },
1831
+ { label: "Binomial", snippet: "\\binom{}{}" },
1832
+ { label: "M\xF3dulo", snippet: "\\left|{}\\right|" }
1833
+ ]
1834
+ },
1835
+ {
1836
+ nome: "S\xEDmbolos",
1837
+ colunas: 6,
1838
+ itens: [
1839
+ { label: "\xB1", snippet: "\\pm", titulo: "mais ou menos" },
1840
+ { label: "\xD7", snippet: "\\times", titulo: "multiplica\xE7\xE3o" },
1841
+ { label: "\xF7", snippet: "\\div", titulo: "divis\xE3o" },
1842
+ { label: "\xB7", snippet: "\\cdot", titulo: "multiplica\xE7\xE3o (ponto)" },
1843
+ { label: "\u221E", snippet: "\\infty", titulo: "infinito" },
1844
+ { label: "\xB0", snippet: "^\\circ", titulo: "grau" },
1845
+ { label: "%", snippet: "\\%", titulo: "por cento" },
1846
+ { label: ",", snippet: "{,}", titulo: "v\xEDrgula decimal" }
1847
+ ]
1848
+ },
1849
+ {
1850
+ nome: "Rela\xE7\xF5es",
1851
+ colunas: 6,
1852
+ itens: [
1853
+ { label: "\u2260", snippet: "\\neq", titulo: "diferente de" },
1854
+ { label: "\u2248", snippet: "\\approx", titulo: "aproximadamente" },
1855
+ { label: "\u2261", snippet: "\\equiv", titulo: "id\xEAntico a" },
1856
+ { label: "\u2264", snippet: "\\leq", titulo: "menor ou igual a" },
1857
+ { label: "\u2265", snippet: "\\geq", titulo: "maior ou igual a" },
1858
+ { label: "\u221D", snippet: "\\propto", titulo: "proporcional a" },
1859
+ { label: "\u22A5", snippet: "\\perp", titulo: "perpendicular a" },
1860
+ { label: "\u2225", snippet: "\\parallel", titulo: "paralelo a" },
1861
+ { label: "\u2245", snippet: "\\cong", titulo: "congruente a" },
1862
+ { label: "\u223C", snippet: "\\sim", titulo: "semelhante a" }
1863
+ ]
1864
+ },
1865
+ {
1866
+ nome: "Gregas",
1867
+ colunas: 6,
1868
+ itens: [
1869
+ { label: "\u03B1", snippet: "\\alpha", titulo: "alfa" },
1870
+ { label: "\u03B2", snippet: "\\beta", titulo: "beta" },
1871
+ { label: "\u03B3", snippet: "\\gamma", titulo: "gama" },
1872
+ { label: "\u03B4", snippet: "\\delta", titulo: "delta" },
1873
+ { label: "\u03B5", snippet: "\\varepsilon", titulo: "\xE9psilon" },
1874
+ { label: "\u03B8", snippet: "\\theta", titulo: "teta" },
1875
+ { label: "\u03BB", snippet: "\\lambda", titulo: "lambda" },
1876
+ { label: "\u03BC", snippet: "\\mu", titulo: "mi" },
1877
+ { label: "\u03C0", snippet: "\\pi", titulo: "pi" },
1878
+ { label: "\u03C1", snippet: "\\rho", titulo: "r\xF4" },
1879
+ { label: "\u03C3", snippet: "\\sigma", titulo: "sigma" },
1880
+ { label: "\u03C6", snippet: "\\varphi", titulo: "fi" },
1881
+ { label: "\u03C9", snippet: "\\omega", titulo: "\xF4mega" },
1882
+ { label: "\u0394", snippet: "\\Delta", titulo: "delta mai\xFAsculo" },
1883
+ {
1884
+ label: "\u03A3",
1885
+ snippet: "\\Sigma",
1886
+ titulo: "sigma mai\xFAsculo \u2014 letra grega; para somat\xF3rio use Estruturas"
1887
+ },
1888
+ {
1889
+ label: "\u03A0",
1890
+ snippet: "\\Pi",
1891
+ titulo: "pi mai\xFAsculo \u2014 letra grega; para produt\xF3rio use Estruturas"
1892
+ },
1893
+ { label: "\u03A9", snippet: "\\Omega", titulo: "\xF4mega mai\xFAsculo" },
1894
+ { label: "\u03A6", snippet: "\\Phi", titulo: "fi mai\xFAsculo" }
1895
+ ]
1896
+ },
1897
+ {
1898
+ nome: "Conjuntos",
1899
+ colunas: 6,
1900
+ itens: [
1901
+ { label: "\u2208", snippet: "\\in", titulo: "pertence a" },
1902
+ { label: "\u2209", snippet: "\\notin", titulo: "n\xE3o pertence a" },
1903
+ { label: "\u2282", snippet: "\\subset", titulo: "contido em" },
1904
+ { label: "\u2286", snippet: "\\subseteq", titulo: "contido ou igual a" },
1905
+ { label: "\u222A", snippet: "\\cup", titulo: "uni\xE3o" },
1906
+ { label: "\u2229", snippet: "\\cap", titulo: "interse\xE7\xE3o" },
1907
+ { label: "\u2205", snippet: "\\varnothing", titulo: "conjunto vazio" },
1908
+ { label: "\u2115", snippet: "\\mathbb{N}", titulo: "conjunto dos naturais" },
1909
+ { label: "\u2124", snippet: "\\mathbb{Z}", titulo: "conjunto dos inteiros" },
1910
+ { label: "\u211A", snippet: "\\mathbb{Q}", titulo: "conjunto dos racionais" },
1911
+ { label: "\u211D", snippet: "\\mathbb{R}", titulo: "conjunto dos reais" },
1912
+ { label: "\u2102", snippet: "\\mathbb{C}", titulo: "conjunto dos complexos" },
1913
+ { label: "\u2200", snippet: "\\forall", titulo: "para todo" },
1914
+ { label: "\u2203", snippet: "\\exists", titulo: "existe" },
1915
+ { label: "{ }", snippet: "\\{{}\\}", titulo: "chaves de conjunto" }
1916
+ ]
1917
+ },
1918
+ {
1919
+ nome: "Fun\xE7\xF5es",
1920
+ colunas: 6,
1921
+ itens: [
1922
+ { label: "sen", snippet: "\\operatorname{sen}", titulo: "seno" },
1923
+ { label: "cos", snippet: "\\cos", titulo: "cosseno" },
1924
+ { label: "tg", snippet: "\\operatorname{tg}", titulo: "tangente" },
1925
+ { label: "cotg", snippet: "\\operatorname{cotg}", titulo: "cotangente" },
1926
+ { label: "log", snippet: "\\log_{}", titulo: "logaritmo" },
1927
+ { label: "ln", snippet: "\\ln", titulo: "logaritmo natural" },
1928
+ { label: "exp", snippet: "\\exp", titulo: "exponencial" }
1929
+ ]
1930
+ },
1931
+ {
1932
+ nome: "Setas",
1933
+ colunas: 6,
1934
+ itens: [
1935
+ { label: "\u2192", snippet: "\\to", titulo: "tende a / vai para" },
1936
+ { label: "\u2190", snippet: "\\leftarrow", titulo: "vem de / sentido inverso" },
1937
+ { label: "\u21D2", snippet: "\\Rightarrow", titulo: "implica em" },
1938
+ { label: "\u21D4", snippet: "\\Leftrightarrow", titulo: "se e somente se" },
1939
+ { label: "\u21CC", snippet: "\\rightleftharpoons", titulo: "equil\xEDbrio qu\xEDmico" },
1940
+ { label: "vetor AB", snippet: "\\overrightarrow{}", titulo: "vetor entre dois pontos (AB)" },
1941
+ { label: "vetor", snippet: "\\vec{}", titulo: "vetor (f\xEDsica)" },
1942
+ { label: "\u2220", snippet: "\\angle", titulo: "\xE2ngulo" },
1943
+ { label: "\u25B3", snippet: "\\triangle", titulo: "tri\xE2ngulo" }
1944
+ ]
1945
+ }
1946
+ ];
1947
+ function paraMathlive(snippet) {
1948
+ return snippet.replaceAll("{}", "{#?}");
1949
+ }
1950
+ function paraMarkup(snippet) {
1951
+ return snippet.replaceAll("{}", "{\u25A1}");
1952
+ }
1953
+ function conteudoDoBotao(markup, snippet, label) {
1954
+ if (!markup) return { children: label };
1955
+ return { dangerouslySetInnerHTML: { __html: markup(paraMarkup(snippet)) } };
1956
+ }
1957
+
1958
+ // src/formulaGuarda.ts
1959
+ var PLACEHOLDER = "\\placeholder{}";
1960
+ var GRUPO_VAZIO = /\{\s*\}/;
1961
+ var CELULA_VAZIA = /(?:\\begin\{[a-zA-Z]+\*?\}|&|\\\\)\s*(?:&|\\\\|\\end\{)/;
1962
+ function podeInserir(latex, preview) {
1963
+ if (motivoBloqueio(latex, preview) !== null) return false;
1964
+ return latex.trim() !== "" && preview?.ok === true;
1965
+ }
1966
+ function motivoBloqueio(latex, preview) {
1967
+ if (latex.includes(PLACEHOLDER) || GRUPO_VAZIO.test(latex) || CELULA_VAZIA.test(latex)) {
1968
+ return "Preencha os campos em branco da f\xF3rmula.";
1969
+ }
1970
+ if (preview && !preview.ok) return preview.error ?? null;
1971
+ return null;
1972
+ }
1973
+
1974
+ // src/FormulaDialog.tsx
1673
1975
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1976
+ var CIRCUNFLEXO_DE_TECLA_MORTA = /* @__PURE__ */ new Set(["\u02C6", "\u0302"]);
1977
+ function FormulaDialog({
1978
+ fontsDirectory
1979
+ }) {
1980
+ const { formulaRequest, resolveFormulaRequest } = useEditorContext();
1981
+ const dialogRef = useRef4(null);
1982
+ const hostRef = useRef4(null);
1983
+ const fieldRef = useRef4(null);
1984
+ const [latex, setLatex] = useState5("");
1985
+ const [display, setDisplay] = useState5(false);
1986
+ const [abaAtiva, setAbaAtiva] = useState5(0);
1987
+ useEffect5(() => {
1988
+ const dialog = dialogRef.current;
1989
+ if (!dialog) return;
1990
+ if (formulaRequest) {
1991
+ setLatex(formulaRequest.initialLatex);
1992
+ setDisplay(formulaRequest.initialDisplay);
1993
+ setAbaAtiva(0);
1994
+ if (!dialog.open) dialog.showModal();
1995
+ } else if (dialog.open) {
1996
+ dialog.close();
1997
+ }
1998
+ }, [formulaRequest]);
1999
+ const rendererRef = useRef4(null);
2000
+ const mathfieldCtorRef = useRef4(null);
2001
+ const markupRef = useRef4(null);
2002
+ const [preview, setPreview] = useState5(null);
2003
+ const [carregando, setCarregando] = useState5(false);
2004
+ const [erroCarregando, setErroCarregando] = useState5(null);
2005
+ useEffect5(() => {
2006
+ if (!formulaRequest || rendererRef.current) return;
2007
+ setCarregando(true);
2008
+ setErroCarregando(null);
2009
+ void Promise.all([import("@sofereditor/math"), import("mathlive")]).then(([math, mathlive]) => {
2010
+ rendererRef.current = math.renderLatexToSvg;
2011
+ mathfieldCtorRef.current = mathlive.MathfieldElement;
2012
+ markupRef.current = mathlive.convertLatexToMarkup;
2013
+ mathlive.MathfieldElement.soundsDirectory = null;
2014
+ if (fontsDirectory) {
2015
+ mathlive.MathfieldElement.fontsDirectory = fontsDirectory;
2016
+ }
2017
+ setCarregando(false);
2018
+ }).catch(() => {
2019
+ setCarregando(false);
2020
+ setErroCarregando(
2021
+ "N\xE3o foi poss\xEDvel carregar o editor de f\xF3rmulas. Recarregue a p\xE1gina e tente de novo."
2022
+ );
2023
+ });
2024
+ }, [formulaRequest]);
2025
+ useEffect5(() => {
2026
+ const render = rendererRef.current;
2027
+ if (!render || !latex.trim()) {
2028
+ setPreview(null);
2029
+ return;
2030
+ }
2031
+ setPreview(render(latex, display));
2032
+ }, [latex, display, carregando]);
2033
+ const onCircunflexoMorto = useCallback2((e) => {
2034
+ const ev = e;
2035
+ if (!ev.data || !CIRCUNFLEXO_DE_TECLA_MORTA.has(ev.data)) return;
2036
+ ev.preventDefault();
2037
+ ev.stopPropagation();
2038
+ fieldRef.current?.insert("^{#?}", { focus: true });
2039
+ }, []);
2040
+ useEffect5(() => {
2041
+ const Ctor = mathfieldCtorRef.current;
2042
+ const host = hostRef.current;
2043
+ if (!Ctor || !host || !formulaRequest) return;
2044
+ const mf = new Ctor();
2045
+ mf.className = "ed-formula-field";
2046
+ const onInput = () => setLatex(mf.getValue("latex"));
2047
+ mf.addEventListener("input", onInput);
2048
+ mf.addEventListener("beforeinput", onCircunflexoMorto, true);
2049
+ host.appendChild(mf);
2050
+ mf.mathVirtualKeyboardPolicy = "manual";
2051
+ mf.inlineShortcuts = {
2052
+ ...mf.inlineShortcuts,
2053
+ sen: "\\operatorname{sen}",
2054
+ tg: "\\operatorname{tg}",
2055
+ cotg: "\\operatorname{cotg}",
2056
+ // Vírgula decimal. Em modo matemático a vírgula crua é átomo de
2057
+ // pontuação e ganha espaço DEPOIS de si: digitar "3,14" sai impresso
2058
+ // como "3, 14" (medido: 4.4ex contra 4.023ex do agrupado). A paleta
2059
+ // tem um botão `{,}`, mas ninguém clica num botão para uma tecla que
2060
+ // está no teclado — e decimal aparece em toda prova de matemática,
2061
+ // física e química.
2062
+ //
2063
+ // O `after: "digit"` é o que separa os dois usos da vírgula, e foi
2064
+ // verificado no navegador: "3,14" vira `3{,}14` e "f(x,y)" continua
2065
+ // `f\left(x,y\right)`, com a vírgula de lista intacta — que é o
2066
+ // comportamento certo, ali ela É pontuação.
2067
+ ",": { after: "digit", value: "{,}" }
2068
+ };
2069
+ mf.setValue(formulaRequest.initialLatex);
2070
+ setLatex(mf.getValue("latex"));
2071
+ fieldRef.current = mf;
2072
+ queueMicrotask(() => mf.focus());
2073
+ return () => {
2074
+ mf.removeEventListener("input", onInput);
2075
+ mf.removeEventListener("beforeinput", onCircunflexoMorto, true);
2076
+ mf.remove();
2077
+ fieldRef.current = null;
2078
+ };
2079
+ }, [formulaRequest, carregando]);
2080
+ if (!formulaRequest) return null;
2081
+ const markup = markupRef.current;
2082
+ const motivo = motivoBloqueio(latex, preview);
2083
+ const podeSubmeter = podeInserir(latex, preview);
2084
+ const onSubmit = (e) => {
2085
+ e.preventDefault();
2086
+ if (!podeSubmeter) return;
2087
+ resolveFormulaRequest({ latex, display });
2088
+ };
2089
+ const onCancel = () => resolveFormulaRequest(null);
2090
+ const onPaleta = (snippet) => {
2091
+ const mf = fieldRef.current;
2092
+ if (!mf) return;
2093
+ mf.insert(paraMathlive(snippet), { focus: true });
2094
+ setLatex(mf.getValue("latex"));
2095
+ };
2096
+ return /* @__PURE__ */ jsx6(
2097
+ "dialog",
2098
+ {
2099
+ ref: dialogRef,
2100
+ className: "ed-formula-dialog ed-link-dialog",
2101
+ style: DIALOG_CENTER_STYLE,
2102
+ onClose: onCancel,
2103
+ onCancel: (e) => {
2104
+ e.preventDefault();
2105
+ onCancel();
2106
+ },
2107
+ children: /* @__PURE__ */ jsxs6("form", { onSubmit, className: "ed-formula-form", children: [
2108
+ /* @__PURE__ */ jsx6("div", { className: "ed-formula-abas", role: "tablist", children: PALETA.map((cat, i) => /* @__PURE__ */ jsx6(
2109
+ "button",
2110
+ {
2111
+ type: "button",
2112
+ role: "tab",
2113
+ "aria-selected": i === abaAtiva,
2114
+ className: "ed-formula-aba",
2115
+ onClick: () => setAbaAtiva(i),
2116
+ children: cat.nome
2117
+ },
2118
+ cat.nome
2119
+ )) }),
2120
+ /* @__PURE__ */ jsx6(
2121
+ "div",
2122
+ {
2123
+ className: "ed-formula-paleta",
2124
+ role: "tabpanel",
2125
+ style: { gridTemplateColumns: `repeat(${PALETA[abaAtiva].colunas}, 1fr)` },
2126
+ children: PALETA[abaAtiva].itens.map((p) => /* @__PURE__ */ jsx6(
2127
+ "button",
2128
+ {
2129
+ type: "button",
2130
+ className: "ed-formula-paleta-btn",
2131
+ title: p.titulo ?? p.label,
2132
+ "aria-label": p.titulo ?? p.label,
2133
+ onClick: () => onPaleta(p.snippet),
2134
+ ...conteudoDoBotao(markup, p.snippet, p.label)
2135
+ },
2136
+ p.label
2137
+ ))
2138
+ }
2139
+ ),
2140
+ /* @__PURE__ */ jsx6("div", { ref: hostRef, className: "ed-formula-host" }),
2141
+ /* @__PURE__ */ jsxs6("label", { className: "ed-formula-display", children: [
2142
+ /* @__PURE__ */ jsx6(
2143
+ "input",
2144
+ {
2145
+ type: "checkbox",
2146
+ checked: display,
2147
+ onChange: (e) => setDisplay(e.target.checked)
2148
+ }
2149
+ ),
2150
+ "F\xF3rmula em bloco (centralizada, limites acima e abaixo)"
2151
+ ] }),
2152
+ /* @__PURE__ */ jsx6("div", { className: "ed-formula-preview", "aria-live": "polite", children: carregando ? /* @__PURE__ */ jsx6("span", { className: "ed-formula-vazio", children: "Carregando o editor\u2026" }) : erroCarregando ? /* @__PURE__ */ jsx6("span", { className: "ed-formula-erro", role: "alert", children: erroCarregando }) : motivo ? /* @__PURE__ */ jsx6("span", { className: "ed-formula-erro", role: "alert", children: motivo }) : null }),
2153
+ /* @__PURE__ */ jsxs6("div", { className: "ed-formula-acoes", children: [
2154
+ /* @__PURE__ */ jsx6("button", { type: "button", onClick: onCancel, children: "Cancelar" }),
2155
+ /* @__PURE__ */ jsx6("button", { type: "submit", disabled: !podeSubmeter, children: "Inserir" })
2156
+ ] })
2157
+ ] })
2158
+ }
2159
+ );
2160
+ }
2161
+
2162
+ // src/EditorContext.tsx
2163
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1674
2164
  var EditorContext = createContext(null);
1675
- function EditorProvider({ editor, children }) {
1676
- return /* @__PURE__ */ jsxs6(EditorContext.Provider, { value: editor, children: [
2165
+ function EditorProvider({
2166
+ editor,
2167
+ children,
2168
+ mathliveFontsDirectory
2169
+ }) {
2170
+ return /* @__PURE__ */ jsxs7(EditorContext.Provider, { value: editor, children: [
1677
2171
  children,
1678
- /* @__PURE__ */ jsx6(LinkDialog, {}),
1679
- /* @__PURE__ */ jsx6(PageConfigDialog, {}),
1680
- /* @__PURE__ */ jsx6(ImageCaptionDialog, {})
2172
+ /* @__PURE__ */ jsx7(LinkDialog, {}),
2173
+ /* @__PURE__ */ jsx7(PageConfigDialog, {}),
2174
+ /* @__PURE__ */ jsx7(ImageCaptionDialog, {}),
2175
+ /* @__PURE__ */ jsx7(FormulaDialog, { fontsDirectory: mathliveFontsDirectory })
1681
2176
  ] });
1682
2177
  }
1683
2178
  function useEditorContext() {
@@ -1690,13 +2185,13 @@ function useEditorContext() {
1690
2185
 
1691
2186
  // src/ImageResizeOverlay.tsx
1692
2187
  import {
1693
- useCallback as useCallback2,
1694
- useEffect as useEffect5,
2188
+ useCallback as useCallback3,
2189
+ useEffect as useEffect6,
1695
2190
  useLayoutEffect as useLayoutEffect2,
1696
- useRef as useRef4,
1697
- useState as useState5
2191
+ useRef as useRef5,
2192
+ useState as useState6
1698
2193
  } from "react";
1699
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2194
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1700
2195
  function ImageResizeOverlay({
1701
2196
  rootRef,
1702
2197
  blockIndex,
@@ -1707,10 +2202,10 @@ function ImageResizeOverlay({
1707
2202
  onCommitMove,
1708
2203
  onReanchor
1709
2204
  }) {
1710
- const [rect, setRect] = useState5(null);
1711
- const [isMoving, setIsMoving] = useState5(false);
1712
- const imgRef = useRef4(null);
1713
- const locateImage = useCallback2(() => {
2205
+ const [rect, setRect] = useState6(null);
2206
+ const [isMoving, setIsMoving] = useState6(false);
2207
+ const imgRef = useRef5(null);
2208
+ const locateImage = useCallback3(() => {
1714
2209
  const root = rootRef.current;
1715
2210
  if (!root) return null;
1716
2211
  let container = null;
@@ -1728,7 +2223,7 @@ function ImageResizeOverlay({
1728
2223
  `img[data-embed="image"][data-embed-offset="${localOffset}"]`
1729
2224
  );
1730
2225
  }, [rootRef, blockIndex, offset, cellIndex]);
1731
- const measure = useCallback2(() => {
2226
+ const measure = useCallback3(() => {
1732
2227
  const img = locateImage();
1733
2228
  if (!img) {
1734
2229
  setRect((prev) => prev === null ? prev : null);
@@ -1779,7 +2274,7 @@ function ImageResizeOverlay({
1779
2274
  embed.offsetX,
1780
2275
  embed.offsetY
1781
2276
  ]);
1782
- useEffect5(() => {
2277
+ useEffect6(() => {
1783
2278
  const root = rootRef.current;
1784
2279
  if (!root) return;
1785
2280
  const onScroll = () => measure();
@@ -1790,7 +2285,7 @@ function ImageResizeOverlay({
1790
2285
  root.removeEventListener("scroll", onScroll);
1791
2286
  };
1792
2287
  }, [measure, rootRef]);
1793
- useEffect5(() => {
2288
+ useEffect6(() => {
1794
2289
  if (typeof ResizeObserver === "undefined") return;
1795
2290
  const img = locateImage();
1796
2291
  const root = rootRef.current;
@@ -1803,7 +2298,7 @@ function ImageResizeOverlay({
1803
2298
  if (!rect) return null;
1804
2299
  const layout = embed.layout ?? "inline";
1805
2300
  const movable = layout !== "inline" && onCommitMove !== void 0;
1806
- return /* @__PURE__ */ jsxs7(
2301
+ return /* @__PURE__ */ jsxs8(
1807
2302
  "div",
1808
2303
  {
1809
2304
  className: "ed-image-overlay",
@@ -1814,7 +2309,7 @@ function ImageResizeOverlay({
1814
2309
  // follows the cursor, so the image stays visible while it crosses the
1815
2310
  // page boundary (the real, clipped figure is hidden during the drag).
1816
2311
  // Inline styles only — the package does not ship CSS.
1817
- /* @__PURE__ */ jsx7(
2312
+ /* @__PURE__ */ jsx8(
1818
2313
  "img",
1819
2314
  {
1820
2315
  src: embed.src,
@@ -1832,7 +2327,7 @@ function ImageResizeOverlay({
1832
2327
  }
1833
2328
  }
1834
2329
  ),
1835
- movable && /* @__PURE__ */ jsx7(
2330
+ movable && /* @__PURE__ */ jsx8(
1836
2331
  MoveHandle,
1837
2332
  {
1838
2333
  layout,
@@ -1850,7 +2345,7 @@ function ImageResizeOverlay({
1850
2345
  onMoveEnd: () => setIsMoving(false)
1851
2346
  }
1852
2347
  ),
1853
- /* @__PURE__ */ jsx7(
2348
+ /* @__PURE__ */ jsx8(
1854
2349
  Handle,
1855
2350
  {
1856
2351
  corner: "nw",
@@ -1861,7 +2356,7 @@ function ImageResizeOverlay({
1861
2356
  onLiveChange: measure
1862
2357
  }
1863
2358
  ),
1864
- /* @__PURE__ */ jsx7(
2359
+ /* @__PURE__ */ jsx8(
1865
2360
  Handle,
1866
2361
  {
1867
2362
  corner: "ne",
@@ -1872,7 +2367,7 @@ function ImageResizeOverlay({
1872
2367
  onLiveChange: measure
1873
2368
  }
1874
2369
  ),
1875
- /* @__PURE__ */ jsx7(
2370
+ /* @__PURE__ */ jsx8(
1876
2371
  Handle,
1877
2372
  {
1878
2373
  corner: "sw",
@@ -1883,7 +2378,7 @@ function ImageResizeOverlay({
1883
2378
  onLiveChange: measure
1884
2379
  }
1885
2380
  ),
1886
- /* @__PURE__ */ jsx7(
2381
+ /* @__PURE__ */ jsx8(
1887
2382
  Handle,
1888
2383
  {
1889
2384
  corner: "se",
@@ -1913,7 +2408,7 @@ function MoveHandle({
1913
2408
  onMoveStart,
1914
2409
  onMoveEnd
1915
2410
  }) {
1916
- const dragRef = useRef4(null);
2411
+ const dragRef = useRef5(null);
1917
2412
  const onPointerDown = (e) => {
1918
2413
  if (e.button !== 0) return;
1919
2414
  if (!imgRef.current) return;
@@ -2063,7 +2558,7 @@ function MoveHandle({
2063
2558
  newOffsetY
2064
2559
  );
2065
2560
  };
2066
- return /* @__PURE__ */ jsx7(
2561
+ return /* @__PURE__ */ jsx8(
2067
2562
  "div",
2068
2563
  {
2069
2564
  className: "ed-image-move",
@@ -2083,7 +2578,7 @@ function Handle({
2083
2578
  onCommit,
2084
2579
  onLiveChange
2085
2580
  }) {
2086
- const dragRef = useRef4(null);
2581
+ const dragRef = useRef5(null);
2087
2582
  const onPointerDown = (e) => {
2088
2583
  if (e.button !== 0) return;
2089
2584
  if (!imgRef.current) return;
@@ -2126,7 +2621,7 @@ function Handle({
2126
2621
  }
2127
2622
  onCommit(Math.round(d.liveW), Math.round(d.liveH));
2128
2623
  };
2129
- return /* @__PURE__ */ jsx7(
2624
+ return /* @__PURE__ */ jsx8(
2130
2625
  "div",
2131
2626
  {
2132
2627
  className: `ed-image-handle ${corner}`,
@@ -2140,15 +2635,15 @@ function Handle({
2140
2635
 
2141
2636
  // src/RemoteCursorsOverlay.tsx
2142
2637
  import {
2143
- useCallback as useCallback3,
2144
- useEffect as useEffect6,
2145
- useRef as useRef5,
2146
- useState as useState6
2638
+ useCallback as useCallback4,
2639
+ useEffect as useEffect7,
2640
+ useRef as useRef6,
2641
+ useState as useState7
2147
2642
  } from "react";
2148
2643
  import {
2149
2644
  decodeSelection
2150
2645
  } from "@sofereditor/core";
2151
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2646
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2152
2647
  var DEFAULT_COLOR = "#3b82f6";
2153
2648
  function RemoteCursorsOverlay({
2154
2649
  rootRef,
@@ -2156,9 +2651,9 @@ function RemoteCursorsOverlay({
2156
2651
  awareness,
2157
2652
  revision
2158
2653
  }) {
2159
- const [views, setViews] = useState6([]);
2160
- const rafRef = useRef5(null);
2161
- const measure = useCallback3(() => {
2654
+ const [views, setViews] = useState7([]);
2655
+ const rafRef = useRef6(null);
2656
+ const measure = useCallback4(() => {
2162
2657
  const root = rootRef.current;
2163
2658
  if (!root) {
2164
2659
  setViews((prev) => prev.length === 0 ? prev : []);
@@ -2223,7 +2718,7 @@ function RemoteCursorsOverlay({
2223
2718
  });
2224
2719
  setViews(out);
2225
2720
  }, [rootRef, doc, awareness]);
2226
- const scheduleMeasure = useCallback3(() => {
2721
+ const scheduleMeasure = useCallback4(() => {
2227
2722
  if (rafRef.current != null) return;
2228
2723
  const raf = typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(() => cb(0), 16);
2229
2724
  rafRef.current = raf(() => {
@@ -2231,16 +2726,16 @@ function RemoteCursorsOverlay({
2231
2726
  measure();
2232
2727
  });
2233
2728
  }, [measure]);
2234
- useEffect6(() => {
2729
+ useEffect7(() => {
2235
2730
  const onChange = () => scheduleMeasure();
2236
2731
  awareness.on("change", onChange);
2237
2732
  scheduleMeasure();
2238
2733
  return () => awareness.off("change", onChange);
2239
2734
  }, [awareness, scheduleMeasure]);
2240
- useEffect6(() => {
2735
+ useEffect7(() => {
2241
2736
  scheduleMeasure();
2242
2737
  }, [revision, scheduleMeasure]);
2243
- useEffect6(() => {
2738
+ useEffect7(() => {
2244
2739
  const root = rootRef.current;
2245
2740
  if (!root) return;
2246
2741
  const onScroll = () => scheduleMeasure();
@@ -2262,8 +2757,8 @@ function RemoteCursorsOverlay({
2262
2757
  pointerEvents: "none",
2263
2758
  zIndex: 6
2264
2759
  };
2265
- return /* @__PURE__ */ jsx8("div", { className: "ed-remote-cursors", "aria-hidden": true, style: layer, children: views.map((v) => /* @__PURE__ */ jsxs8("div", { children: [
2266
- v.selection.map((r, i) => /* @__PURE__ */ jsx8(
2760
+ return /* @__PURE__ */ jsx9("div", { className: "ed-remote-cursors", "aria-hidden": true, style: layer, children: views.map((v) => /* @__PURE__ */ jsxs9("div", { children: [
2761
+ v.selection.map((r, i) => /* @__PURE__ */ jsx9(
2267
2762
  "div",
2268
2763
  {
2269
2764
  style: {
@@ -2279,7 +2774,7 @@ function RemoteCursorsOverlay({
2279
2774
  },
2280
2775
  i
2281
2776
  )),
2282
- v.caret && /* @__PURE__ */ jsx8(
2777
+ v.caret && /* @__PURE__ */ jsx9(
2283
2778
  "div",
2284
2779
  {
2285
2780
  style: {
@@ -2290,7 +2785,7 @@ function RemoteCursorsOverlay({
2290
2785
  width: 2,
2291
2786
  background: v.color
2292
2787
  },
2293
- children: /* @__PURE__ */ jsx8(
2788
+ children: /* @__PURE__ */ jsx9(
2294
2789
  "span",
2295
2790
  {
2296
2791
  style: {
@@ -2316,10 +2811,11 @@ function RemoteCursorsOverlay({
2316
2811
  }
2317
2812
 
2318
2813
  // src/NodeView.tsx
2319
- import { useMemo as useMemo2, useRef as useRef7 } from "react";
2814
+ import { useMemo as useMemo2, useRef as useRef8 } from "react";
2320
2815
  import {
2321
2816
  answerLineStyle,
2322
2817
  cellBorderStyle,
2818
+ normalizarLarguras as normalizarLarguras2,
2323
2819
  tableRectSelection
2324
2820
  } from "@sofereditor/core";
2325
2821
 
@@ -2327,13 +2823,13 @@ import {
2327
2823
  import { Fragment as Fragment3, memo } from "react";
2328
2824
  import {
2329
2825
  BLANK_STYLE,
2330
- isImageEmbed,
2826
+ isImageEmbed as isImageEmbed3,
2331
2827
  splitUnderscoreRuns
2332
2828
  } from "@sofereditor/core";
2333
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2829
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
2334
2830
  function renderInline(delta, keyPrefix) {
2335
2831
  if (delta.length === 0 || delta.length === 1 && typeof delta[0].insert === "string" && delta[0].insert === "") {
2336
- return /* @__PURE__ */ jsx9("br", { "data-empty": "true" });
2832
+ return /* @__PURE__ */ jsx10("br", { "data-empty": "true" });
2337
2833
  }
2338
2834
  let offset = 0;
2339
2835
  const wrapAnchors = [];
@@ -2343,18 +2839,18 @@ function renderInline(delta, keyPrefix) {
2343
2839
  const text = op.insert;
2344
2840
  if (text.length === 0) continue;
2345
2841
  const key = `${keyPrefix}-${offset}`;
2346
- body.push(/* @__PURE__ */ jsx9(Fragment3, { children: wrap(text, op.attributes) }, key));
2842
+ body.push(/* @__PURE__ */ jsx10(Fragment3, { children: wrap(text, op.attributes) }, key));
2347
2843
  offset += text.length;
2348
- } else if (isImageEmbed(op.insert)) {
2844
+ } else if (isImageEmbed3(op.insert)) {
2349
2845
  const key = `${keyPrefix}-${offset}`;
2350
2846
  const layout = op.insert.layout ?? "inline";
2351
2847
  const commentMarkId = op.attributes?.comment?.markId;
2352
2848
  if (layout === "wrap-left" || layout === "wrap-right") {
2353
2849
  wrapAnchors.push(
2354
- /* @__PURE__ */ jsx9(ImageEmbedView, { embed: op.insert, offset, wrapAnchor: true }, `${key}-anchor`)
2850
+ /* @__PURE__ */ jsx10(ImageEmbedView, { embed: op.insert, offset, wrapAnchor: true }, `${key}-anchor`)
2355
2851
  );
2356
2852
  body.push(
2357
- /* @__PURE__ */ jsx9(
2853
+ /* @__PURE__ */ jsx10(
2358
2854
  "span",
2359
2855
  {
2360
2856
  "data-embed-phantom": "true",
@@ -2366,10 +2862,10 @@ function renderInline(delta, keyPrefix) {
2366
2862
  )
2367
2863
  );
2368
2864
  } else {
2369
- const imgNode = /* @__PURE__ */ jsx9(ImageEmbedView, { embed: op.insert, offset }, key);
2865
+ const imgNode = /* @__PURE__ */ jsx10(ImageEmbedView, { embed: op.insert, offset }, key);
2370
2866
  if (commentMarkId) {
2371
2867
  body.push(
2372
- /* @__PURE__ */ jsx9("span", { "data-comment-id": commentMarkId, className: "ed-comment", children: imgNode }, `${key}-cmt`)
2868
+ /* @__PURE__ */ jsx10("span", { "data-comment-id": commentMarkId, className: "ed-comment", children: imgNode }, `${key}-cmt`)
2373
2869
  );
2374
2870
  } else {
2375
2871
  body.push(imgNode);
@@ -2380,7 +2876,7 @@ function renderInline(delta, keyPrefix) {
2380
2876
  offset += 1;
2381
2877
  }
2382
2878
  }
2383
- if (wrapAnchors.length === 0 && body.length === 0) return /* @__PURE__ */ jsx9("br", { "data-empty": "true" });
2879
+ if (wrapAnchors.length === 0 && body.length === 0) return /* @__PURE__ */ jsx10("br", { "data-empty": "true" });
2384
2880
  return [...wrapAnchors, ...body];
2385
2881
  }
2386
2882
  var PHANTOM_STYLE = {
@@ -2455,7 +2951,9 @@ function ImageEmbedViewImpl({
2455
2951
  marginRight: align === "right" ? 0 : "auto"
2456
2952
  } : {
2457
2953
  display: "inline-block",
2458
- verticalAlign: "text-bottom",
2954
+ // Fórmula inline traz o deslocamento de base que o MathJax mediu;
2955
+ // sem ele a fórmula flutua no topo da linha em vez de sentar nela.
2956
+ verticalAlign: embed.formula?.vAlign ?? "text-bottom",
2459
2957
  width: embed.width
2460
2958
  };
2461
2959
  break;
@@ -2465,7 +2963,7 @@ function ImageEmbedViewImpl({
2465
2963
  height: embed.height,
2466
2964
  display: "block"
2467
2965
  };
2468
- const img = /* @__PURE__ */ jsx9(
2966
+ const img = /* @__PURE__ */ jsx10(
2469
2967
  "img",
2470
2968
  {
2471
2969
  src: embed.src,
@@ -2485,7 +2983,7 @@ function ImageEmbedViewImpl({
2485
2983
  }
2486
2984
  );
2487
2985
  const captionAlign = embed.captionAlign ?? "center";
2488
- return /* @__PURE__ */ jsxs9(
2986
+ return /* @__PURE__ */ jsxs10(
2489
2987
  "figure",
2490
2988
  {
2491
2989
  className: "ed-figure",
@@ -2495,7 +2993,7 @@ function ImageEmbedViewImpl({
2495
2993
  style: outerStyle,
2496
2994
  children: [
2497
2995
  img,
2498
- hasCaption && /* @__PURE__ */ jsx9(
2996
+ hasCaption && /* @__PURE__ */ jsx10(
2499
2997
  "figcaption",
2500
2998
  {
2501
2999
  className: "ed-figcaption",
@@ -2512,26 +3010,26 @@ function decorateBlanks(text) {
2512
3010
  const segs = splitUnderscoreRuns(text);
2513
3011
  if (segs.length === 1 && !segs[0].blank) return text;
2514
3012
  return segs.map(
2515
- (s, i) => s.blank ? /* @__PURE__ */ jsx9("span", { "data-blank": "true", style: BLANK_STYLE, children: s.text }, i) : /* @__PURE__ */ jsx9(Fragment3, { children: s.text }, i)
3013
+ (s, i) => s.blank ? /* @__PURE__ */ jsx10("span", { "data-blank": "true", style: BLANK_STYLE, children: s.text }, i) : /* @__PURE__ */ jsx10(Fragment3, { children: s.text }, i)
2516
3014
  );
2517
3015
  }
2518
3016
  function wrap(text, attrs) {
2519
3017
  let node = decorateBlanks(text);
2520
3018
  if (!attrs) return node;
2521
- if (attrs.bold) node = /* @__PURE__ */ jsx9("strong", { children: node });
2522
- if (attrs.italic) node = /* @__PURE__ */ jsx9("em", { children: node });
2523
- if (attrs.underline) node = /* @__PURE__ */ jsx9("u", { children: node });
2524
- if (attrs.strike) node = /* @__PURE__ */ jsx9("s", { children: node });
3019
+ if (attrs.bold) node = /* @__PURE__ */ jsx10("strong", { children: node });
3020
+ if (attrs.italic) node = /* @__PURE__ */ jsx10("em", { children: node });
3021
+ if (attrs.underline) node = /* @__PURE__ */ jsx10("u", { children: node });
3022
+ if (attrs.strike) node = /* @__PURE__ */ jsx10("s", { children: node });
2525
3023
  const styleParts = {};
2526
3024
  if (attrs.color) styleParts.color = attrs.color;
2527
3025
  if (attrs.highlight) styleParts.backgroundColor = attrs.highlight;
2528
3026
  if (attrs.fontFamily) styleParts.fontFamily = attrs.fontFamily;
2529
3027
  if (attrs.fontSize) styleParts.fontSize = attrs.fontSize;
2530
3028
  if (Object.keys(styleParts).length > 0) {
2531
- node = /* @__PURE__ */ jsx9("span", { style: styleParts, children: node });
3029
+ node = /* @__PURE__ */ jsx10("span", { style: styleParts, children: node });
2532
3030
  }
2533
3031
  if (attrs.link?.href) {
2534
- node = /* @__PURE__ */ jsx9(
3032
+ node = /* @__PURE__ */ jsx10(
2535
3033
  "a",
2536
3034
  {
2537
3035
  href: attrs.link.href,
@@ -2550,7 +3048,7 @@ function wrap(text, attrs) {
2550
3048
  );
2551
3049
  }
2552
3050
  if (attrs.comment?.markId) {
2553
- node = /* @__PURE__ */ jsx9("span", { "data-comment-id": attrs.comment.markId, className: "ed-comment", children: node });
3051
+ node = /* @__PURE__ */ jsx10("span", { "data-comment-id": attrs.comment.markId, className: "ed-comment", children: node });
2554
3052
  }
2555
3053
  return node;
2556
3054
  }
@@ -2595,21 +3093,30 @@ function sliceDelta(delta, start, end) {
2595
3093
 
2596
3094
  // src/TableResizeOverlay.tsx
2597
3095
  import {
2598
- useCallback as useCallback4,
2599
- useEffect as useEffect7,
3096
+ useCallback as useCallback5,
3097
+ useEffect as useEffect8,
2600
3098
  useLayoutEffect as useLayoutEffect3,
2601
- useRef as useRef6,
2602
- useState as useState7
3099
+ useRef as useRef7,
3100
+ useState as useState8
2603
3101
  } from "react";
2604
- import { Fragment as Fragment4, jsx as jsx10 } from "react/jsx-runtime";
2605
- function TableResizeOverlay({ tableRef, blockIndex, cols }) {
3102
+ import { MIN_LINHA_PX, normalizarLarguras } from "@sofereditor/core";
3103
+ import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
3104
+ function deltaPctDoArrasto(dxPx, larguraTabelaPx) {
3105
+ if (!(larguraTabelaPx > 0)) return 0;
3106
+ return dxPx / larguraTabelaPx * 100;
3107
+ }
3108
+ function distribuirAltura(base, deltaPx) {
3109
+ if (base.length === 0) return base;
3110
+ const fatia = deltaPx / base.length;
3111
+ return base.map((h) => Math.max(MIN_LINHA_PX, Math.round(h + fatia)));
3112
+ }
3113
+ function TableResizeOverlay({ tableRef, blockIndex, cols, rows }) {
2606
3114
  const editor = useEditorContext();
2607
- const [positions, setPositions] = useState7(null);
2608
- const [layout, setLayout] = useState7(null);
2609
- const draggingRef = useRef6(null);
2610
- const measure = useCallback4(() => {
3115
+ const [layout, setLayout] = useState8(null);
3116
+ const arrastoRef = useRef7(null);
3117
+ const measureNow = useCallback5(() => {
2611
3118
  const table = tableRef.current;
2612
- if (!table) return;
3119
+ if (!table) return null;
2613
3120
  const tableRect = table.getBoundingClientRect();
2614
3121
  const cgCols = table.querySelectorAll("colgroup > col");
2615
3122
  let rights = [];
@@ -2632,55 +3139,160 @@ function TableResizeOverlay({ tableRef, blockIndex, cols }) {
2632
3139
  });
2633
3140
  }
2634
3141
  if (rights.length !== cols) {
2635
- setLayout(null);
2636
- return;
3142
+ return null;
2637
3143
  }
2638
- setLayout({ rights, height: tableRect.height });
2639
- }, [cols, tableRef]);
3144
+ const trs = table.querySelectorAll("tbody > tr");
3145
+ const bottoms = [];
3146
+ let accY = 0;
3147
+ trs.forEach((tr) => {
3148
+ accY += tr.getBoundingClientRect().height;
3149
+ bottoms.push(accY);
3150
+ });
3151
+ return {
3152
+ rights,
3153
+ bottoms: trs.length === rows ? bottoms : [],
3154
+ width: tableRect.width,
3155
+ height: tableRect.height
3156
+ };
3157
+ }, [cols, rows, tableRef]);
3158
+ const measure = useCallback5(() => {
3159
+ setLayout(measureNow());
3160
+ }, [measureNow]);
2640
3161
  useLayoutEffect3(() => {
2641
3162
  measure();
2642
3163
  }, [measure]);
2643
- useEffect7(() => {
3164
+ useEffect8(() => {
2644
3165
  const table = tableRef.current;
2645
3166
  if (!table) return;
2646
3167
  const ro = new ResizeObserver(() => measure());
2647
3168
  ro.observe(table);
2648
3169
  return () => ro.disconnect();
2649
3170
  }, [measure, tableRef]);
2650
- const onPointerDown = useCallback4(
2651
- (col) => (e) => {
3171
+ const onPointerUp = useCallback5(
3172
+ (e) => {
3173
+ arrastoRef.current = null;
3174
+ measure();
3175
+ try {
3176
+ e.currentTarget.releasePointerCapture(e.pointerId);
3177
+ } catch {
3178
+ }
3179
+ },
3180
+ [measure]
3181
+ );
3182
+ const onPointerMove = useCallback5(
3183
+ (e) => {
3184
+ const d = arrastoRef.current;
3185
+ if (!d) return;
3186
+ switch (d.tipo) {
3187
+ case "coluna": {
3188
+ const dPct = deltaPctDoArrasto(e.clientX - d.startX, d.tableWidthPx);
3189
+ editor.setColumnBoundary(blockIndex, d.boundary, dPct, d.baseWidths);
3190
+ break;
3191
+ }
3192
+ case "larguraTotal": {
3193
+ const dPct = deltaPctDoArrasto(e.clientX - d.startX, d.utilPx);
3194
+ editor.setTableWidth(blockIndex, d.baseWidthPct + dPct);
3195
+ break;
3196
+ }
3197
+ case "linha": {
3198
+ editor.setRowHeight(blockIndex, d.row, d.baseHeightPx + (e.clientY - d.startY));
3199
+ break;
3200
+ }
3201
+ case "alturaTotal": {
3202
+ editor.setRowHeights(blockIndex, distribuirAltura(d.baseHeights, e.clientY - d.startY));
3203
+ break;
3204
+ }
3205
+ case "canto": {
3206
+ const dPct = deltaPctDoArrasto(e.clientX - d.startX, d.utilPx);
3207
+ editor.setTableWidth(blockIndex, d.baseWidthPct + dPct);
3208
+ editor.setRowHeights(blockIndex, distribuirAltura(d.baseHeights, e.clientY - d.startY));
3209
+ break;
3210
+ }
3211
+ }
3212
+ },
3213
+ [blockIndex, editor]
3214
+ );
3215
+ const containerWidthPxOf = useCallback5((table, tableWidthPx) => {
3216
+ return table.parentElement?.getBoundingClientRect().width || tableWidthPx;
3217
+ }, []);
3218
+ const onColumnPointerDown = useCallback5(
3219
+ (boundary) => (e) => {
2652
3220
  e.preventDefault();
2653
3221
  e.stopPropagation();
2654
3222
  const table = tableRef.current;
2655
3223
  if (!table) return;
2656
- const cgCols = table.querySelectorAll("colgroup > col");
2657
- const startWidth = cgCols[col]?.getBoundingClientRect().width ?? 100;
2658
- draggingRef.current = { col, startX: e.clientX, startWidth };
2659
- setPositions({ x: e.clientX, height: layout?.height ?? 0 });
3224
+ const tableWidthPx = table.getBoundingClientRect().width || 1;
3225
+ const attrs = editor.doc.getBlockAttrs(blockIndex);
3226
+ const baseWidths = normalizarLarguras(attrs.colWidths, cols);
3227
+ arrastoRef.current = { tipo: "coluna", boundary, startX: e.clientX, baseWidths, tableWidthPx };
2660
3228
  e.currentTarget.setPointerCapture(e.pointerId);
2661
3229
  },
2662
- [layout, tableRef]
3230
+ [tableRef, editor, blockIndex, cols]
2663
3231
  );
2664
- const onPointerMove = useCallback4(
3232
+ const onTableWidthPointerDown = useCallback5(
2665
3233
  (e) => {
2666
- const d = draggingRef.current;
2667
- if (!d) return;
2668
- const dx = e.clientX - d.startX;
2669
- const next = Math.max(40, d.startWidth + dx);
2670
- editor.setColumnWidth(blockIndex, d.col, next);
3234
+ e.preventDefault();
3235
+ e.stopPropagation();
3236
+ const table = tableRef.current;
3237
+ if (!table) return;
3238
+ const tableWidthPx = table.getBoundingClientRect().width || 1;
3239
+ const utilPx = containerWidthPxOf(table, tableWidthPx);
3240
+ const attrs = editor.doc.getBlockAttrs(blockIndex);
3241
+ const baseWidthPct = typeof attrs.tableWidth === "number" ? attrs.tableWidth : 100;
3242
+ arrastoRef.current = { tipo: "larguraTotal", startX: e.clientX, baseWidthPct, utilPx };
3243
+ e.currentTarget.setPointerCapture(e.pointerId);
2671
3244
  },
2672
- [blockIndex, editor]
3245
+ [tableRef, editor, blockIndex, containerWidthPxOf]
3246
+ );
3247
+ const heightsFromBottoms = (bottoms) => bottoms.map((b, i) => b - (i === 0 ? 0 : bottoms[i - 1]));
3248
+ const onRowPointerDown = useCallback5(
3249
+ (row) => (e) => {
3250
+ e.preventDefault();
3251
+ e.stopPropagation();
3252
+ const m = measureNow();
3253
+ if (!m || m.bottoms.length === 0) return;
3254
+ const baseHeightPx = m.bottoms[row] - (row === 0 ? 0 : m.bottoms[row - 1]);
3255
+ arrastoRef.current = { tipo: "linha", row, startY: e.clientY, baseHeightPx };
3256
+ e.currentTarget.setPointerCapture(e.pointerId);
3257
+ },
3258
+ [measureNow]
3259
+ );
3260
+ const onTableHeightPointerDown = useCallback5(
3261
+ (e) => {
3262
+ e.preventDefault();
3263
+ e.stopPropagation();
3264
+ const m = measureNow();
3265
+ if (!m || m.bottoms.length === 0) return;
3266
+ arrastoRef.current = { tipo: "alturaTotal", startY: e.clientY, baseHeights: heightsFromBottoms(m.bottoms) };
3267
+ e.currentTarget.setPointerCapture(e.pointerId);
3268
+ },
3269
+ [measureNow]
3270
+ );
3271
+ const onCornerPointerDown = useCallback5(
3272
+ (e) => {
3273
+ e.preventDefault();
3274
+ e.stopPropagation();
3275
+ const table = tableRef.current;
3276
+ const m = measureNow();
3277
+ if (!table || !m) return;
3278
+ const tableWidthPx = table.getBoundingClientRect().width || 1;
3279
+ const utilPx = containerWidthPxOf(table, tableWidthPx);
3280
+ const attrs = editor.doc.getBlockAttrs(blockIndex);
3281
+ const baseWidthPct = typeof attrs.tableWidth === "number" ? attrs.tableWidth : 100;
3282
+ arrastoRef.current = {
3283
+ tipo: "canto",
3284
+ startX: e.clientX,
3285
+ startY: e.clientY,
3286
+ baseWidthPct,
3287
+ utilPx,
3288
+ baseHeights: heightsFromBottoms(m.bottoms)
3289
+ };
3290
+ e.currentTarget.setPointerCapture(e.pointerId);
3291
+ },
3292
+ [tableRef, editor, blockIndex, measureNow, containerWidthPxOf]
2673
3293
  );
2674
- const onPointerUp = useCallback4((e) => {
2675
- draggingRef.current = null;
2676
- setPositions(null);
2677
- try {
2678
- e.currentTarget.releasePointerCapture(e.pointerId);
2679
- } catch {
2680
- }
2681
- }, []);
2682
3294
  if (!layout) return null;
2683
- const handleStyle = (x) => ({
3295
+ const colHandleStyle = (x) => ({
2684
3296
  position: "absolute",
2685
3297
  top: 0,
2686
3298
  left: `${x - 3}px`,
@@ -2689,28 +3301,104 @@ function TableResizeOverlay({ tableRef, blockIndex, cols }) {
2689
3301
  cursor: "col-resize",
2690
3302
  zIndex: 5
2691
3303
  });
2692
- void positions;
2693
- return /* @__PURE__ */ jsx10(Fragment4, { children: layout.rights.map((x, c) => /* @__PURE__ */ jsx10(
2694
- "div",
2695
- {
2696
- contentEditable: false,
2697
- className: "ed-col-resize-handle",
2698
- style: handleStyle(x),
2699
- onPointerDown: onPointerDown(c),
2700
- onPointerMove,
2701
- onPointerUp,
2702
- "aria-label": `Redimensionar coluna ${c + 1}`,
2703
- role: "separator"
2704
- },
2705
- c
2706
- )) });
3304
+ const rowHandleStyle = (y) => ({
3305
+ position: "absolute",
3306
+ left: 0,
3307
+ top: `${y - 3}px`,
3308
+ width: `${layout.width}px`,
3309
+ height: "6px",
3310
+ cursor: "row-resize",
3311
+ zIndex: 5
3312
+ });
3313
+ const cornerHandleStyle = {
3314
+ position: "absolute",
3315
+ left: `${layout.width - 5}px`,
3316
+ top: `${layout.height - 5}px`,
3317
+ width: "10px",
3318
+ height: "10px",
3319
+ cursor: "nwse-resize",
3320
+ zIndex: 6
3321
+ };
3322
+ return /* @__PURE__ */ jsxs11(Fragment4, { children: [
3323
+ layout.rights.map(
3324
+ (x, c) => c < cols - 1 ? /* @__PURE__ */ jsx11(
3325
+ "div",
3326
+ {
3327
+ contentEditable: false,
3328
+ className: "ed-col-resize-handle",
3329
+ style: colHandleStyle(x),
3330
+ onPointerDown: onColumnPointerDown(c),
3331
+ onPointerMove,
3332
+ onPointerUp,
3333
+ "aria-label": `Redimensionar coluna ${c + 1}`,
3334
+ role: "separator"
3335
+ },
3336
+ `col-${c}`
3337
+ ) : /* @__PURE__ */ jsx11(
3338
+ "div",
3339
+ {
3340
+ contentEditable: false,
3341
+ className: "ed-col-resize-handle",
3342
+ style: colHandleStyle(x),
3343
+ onPointerDown: onTableWidthPointerDown,
3344
+ onPointerMove,
3345
+ onPointerUp,
3346
+ "aria-label": "Largura da tabela",
3347
+ role: "separator"
3348
+ },
3349
+ "col-total"
3350
+ )
3351
+ ),
3352
+ layout.bottoms.map(
3353
+ (y, r) => r < rows - 1 ? /* @__PURE__ */ jsx11(
3354
+ "div",
3355
+ {
3356
+ contentEditable: false,
3357
+ className: "ed-row-resize-handle",
3358
+ style: rowHandleStyle(y),
3359
+ onPointerDown: onRowPointerDown(r),
3360
+ onPointerMove,
3361
+ onPointerUp,
3362
+ "aria-label": `Redimensionar linha ${r + 1}`,
3363
+ role: "separator"
3364
+ },
3365
+ `row-${r}`
3366
+ ) : /* @__PURE__ */ jsx11(
3367
+ "div",
3368
+ {
3369
+ contentEditable: false,
3370
+ className: "ed-row-resize-handle",
3371
+ style: rowHandleStyle(y),
3372
+ onPointerDown: onTableHeightPointerDown,
3373
+ onPointerMove,
3374
+ onPointerUp,
3375
+ "aria-label": "Altura da tabela",
3376
+ role: "separator"
3377
+ },
3378
+ "row-total"
3379
+ )
3380
+ ),
3381
+ /* @__PURE__ */ jsx11(
3382
+ "div",
3383
+ {
3384
+ contentEditable: false,
3385
+ className: "ed-table-corner-handle",
3386
+ style: cornerHandleStyle,
3387
+ onPointerDown: onCornerPointerDown,
3388
+ onPointerMove,
3389
+ onPointerUp,
3390
+ "aria-label": "Redimensionar tabela",
3391
+ role: "separator"
3392
+ }
3393
+ )
3394
+ ] });
2707
3395
  }
2708
3396
 
2709
3397
  // src/NodeView.tsx
2710
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
3398
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2711
3399
  function NodeView({ block, index, children, fragment, tableFragment }) {
2712
3400
  if (block.type === "table") {
2713
- return /* @__PURE__ */ jsx11(TableView, { block, index, tableFragment });
3401
+ return /* @__PURE__ */ jsx12(TableView, { block, index, tableFragment });
2714
3402
  }
2715
3403
  const inline = fragment ? renderInline(sliceDelta(block.delta, fragment.start, fragment.end), index) : renderInline(block.delta, index);
2716
3404
  const common = commonBlockProps(block.attrs, index, block.type, fragment);
@@ -2718,7 +3406,7 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
2718
3406
  case "heading": {
2719
3407
  const level = clampLevel2(block.attrs.level);
2720
3408
  const Tag = `h${level}`;
2721
- return /* @__PURE__ */ jsx11(
3409
+ return /* @__PURE__ */ jsx12(
2722
3410
  Tag,
2723
3411
  {
2724
3412
  ...common,
@@ -2728,7 +3416,7 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
2728
3416
  );
2729
3417
  }
2730
3418
  case "blockquote":
2731
- return /* @__PURE__ */ jsx11(
3419
+ return /* @__PURE__ */ jsx12(
2732
3420
  "blockquote",
2733
3421
  {
2734
3422
  ...common,
@@ -2737,18 +3425,18 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
2737
3425
  }
2738
3426
  );
2739
3427
  case "codeBlock":
2740
- return /* @__PURE__ */ jsx11(
3428
+ return /* @__PURE__ */ jsx12(
2741
3429
  "pre",
2742
3430
  {
2743
3431
  ...common,
2744
3432
  "data-language": block.attrs.language ?? void 0,
2745
3433
  className: `ed-block ed-codeblock${alignClass(block.attrs.align)}`,
2746
- children: /* @__PURE__ */ jsx11("code", { children: inline })
3434
+ children: /* @__PURE__ */ jsx12("code", { children: inline })
2747
3435
  }
2748
3436
  );
2749
3437
  case "listItem": {
2750
3438
  const level = clampListLevel(block.attrs.listLevel);
2751
- return /* @__PURE__ */ jsxs10(
3439
+ return /* @__PURE__ */ jsxs12(
2752
3440
  "li",
2753
3441
  {
2754
3442
  ...common,
@@ -2764,7 +3452,7 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
2764
3452
  }
2765
3453
  case "paragraph":
2766
3454
  default:
2767
- return /* @__PURE__ */ jsx11(
3455
+ return /* @__PURE__ */ jsx12(
2768
3456
  "div",
2769
3457
  {
2770
3458
  ...common,
@@ -2815,8 +3503,10 @@ function TableView({ block, index, tableFragment }) {
2815
3503
  const rows = clampPositive(block.attrs.rows);
2816
3504
  const cols = clampPositive(block.attrs.cols);
2817
3505
  const cells = block.cells ?? [];
2818
- const widths = readColWidths(block.attrs.colWidths, cols);
2819
- const tableRef = useRef7(null);
3506
+ const widths = normalizarLarguras2(block.attrs.colWidths, cols);
3507
+ const tableWidth = block.attrs.tableWidth;
3508
+ const rowHeights = block.attrs.rowHeights;
3509
+ const tableRef = useRef8(null);
2820
3510
  const rowStart = tableFragment?.rowStart ?? 0;
2821
3511
  const rowEnd = tableFragment?.rowEnd ?? rows;
2822
3512
  const isFirstFragment = !tableFragment || tableFragment.index === 0;
@@ -2826,69 +3516,69 @@ function TableView({ block, index, tableFragment }) {
2826
3516
  if (editor.selection.focus.blockIndex !== index) return null;
2827
3517
  return tableRectSelection(editor.doc, editor.selection);
2828
3518
  }, [editor.doc, editor.selection, index]);
2829
- return /* @__PURE__ */ jsxs10("div", { className: "ed-table-wrap", children: [
2830
- /* @__PURE__ */ jsxs10(
3519
+ return /* @__PURE__ */ jsxs12("div", { className: "ed-table-wrap", children: [
3520
+ /* @__PURE__ */ jsxs12(
2831
3521
  "table",
2832
3522
  {
2833
3523
  ref: tableRef,
2834
3524
  "data-block-index": index,
2835
3525
  "data-block-type": "table",
2836
3526
  className: "ed-block ed-table",
3527
+ style: typeof tableWidth === "number" ? { width: `${tableWidth}%` } : void 0,
2837
3528
  children: [
2838
- widths && /* @__PURE__ */ jsx11("colgroup", { children: widths.map((w, c) => /* @__PURE__ */ jsx11("col", { style: w != null ? { width: `${w}px` } : void 0 }, c)) }),
2839
- /* @__PURE__ */ jsx11("tbody", { children: Array.from({ length: rowEnd - rowStart }, (_, i) => {
3529
+ /* @__PURE__ */ jsx12("colgroup", { children: widths.map((w, c) => /* @__PURE__ */ jsx12("col", { style: { width: `${w}%` } }, c)) }),
3530
+ /* @__PURE__ */ jsx12("tbody", { children: Array.from({ length: rowEnd - rowStart }, (_, i) => {
2840
3531
  const r = rowStart + i;
2841
- return /* @__PURE__ */ jsx11("tr", { "data-cell-row": r, children: Array.from({ length: cols }, (_2, c) => {
2842
- const flat = r * cols + c;
2843
- const cell = cells[flat];
2844
- if (cell?.attrs.covered === true) return null;
2845
- const rowspan = spanAttr(cell?.attrs.rowspan);
2846
- const colspan = spanAttr(cell?.attrs.colspan);
2847
- const delta = cell?.delta ?? [];
2848
- const inRect = rect !== null && r >= rect.top && r <= rect.bottom && c >= rect.left && c <= rect.right;
2849
- return /* @__PURE__ */ jsx11(
2850
- "td",
2851
- {
2852
- "data-cell-index": flat,
2853
- "data-cell-row": r,
2854
- "data-cell-col": c,
2855
- "data-cell-rowspan": rowspan,
2856
- "data-cell-colspan": colspan,
2857
- rowSpan: rowspan > 1 ? rowspan : void 0,
2858
- colSpan: colspan > 1 ? colspan : void 0,
2859
- style: cellStyle(
2860
- cell?.attrs,
2861
- // rowStart/rowEnd são os limites do FRAGMENTO: numa tabela
2862
- // quebrada entre páginas, cada página fecha a própria caixa.
2863
- cellBorderStyle(
2864
- block.attrs.borderPreset,
2865
- { row: r, col: c, rowspan, colspan, cols, rowStart, rowEnd },
2866
- "screen",
2867
- block.attrs.borderColor
2868
- )
2869
- ),
2870
- className: inRect ? "ed-cell ed-cell--selected" : "ed-cell",
2871
- children: renderInline(delta, `t${index}-c${flat}`)
2872
- },
2873
- c
2874
- );
2875
- }) }, r);
3532
+ return /* @__PURE__ */ jsx12(
3533
+ "tr",
3534
+ {
3535
+ "data-cell-row": r,
3536
+ style: typeof rowHeights?.[r] === "number" ? { height: `${rowHeights[r]}px` } : void 0,
3537
+ children: Array.from({ length: cols }, (_2, c) => {
3538
+ const flat = r * cols + c;
3539
+ const cell = cells[flat];
3540
+ if (cell?.attrs.covered === true) return null;
3541
+ const rowspan = spanAttr(cell?.attrs.rowspan);
3542
+ const colspan = spanAttr(cell?.attrs.colspan);
3543
+ const delta = cell?.delta ?? [];
3544
+ const inRect = rect !== null && r >= rect.top && r <= rect.bottom && c >= rect.left && c <= rect.right;
3545
+ return /* @__PURE__ */ jsx12(
3546
+ "td",
3547
+ {
3548
+ "data-cell-index": flat,
3549
+ "data-cell-row": r,
3550
+ "data-cell-col": c,
3551
+ "data-cell-rowspan": rowspan,
3552
+ "data-cell-colspan": colspan,
3553
+ rowSpan: rowspan > 1 ? rowspan : void 0,
3554
+ colSpan: colspan > 1 ? colspan : void 0,
3555
+ style: cellStyle(
3556
+ cell?.attrs,
3557
+ // rowStart/rowEnd são os limites do FRAGMENTO: numa tabela
3558
+ // quebrada entre páginas, cada página fecha a própria caixa.
3559
+ cellBorderStyle(
3560
+ block.attrs.borderPreset,
3561
+ { row: r, col: c, rowspan, colspan, cols, rowStart, rowEnd },
3562
+ "screen",
3563
+ block.attrs.borderColor
3564
+ )
3565
+ ),
3566
+ className: inRect ? "ed-cell ed-cell--selected" : "ed-cell",
3567
+ children: renderInline(delta, `t${index}-c${flat}`)
3568
+ },
3569
+ c
3570
+ );
3571
+ })
3572
+ },
3573
+ r
3574
+ );
2876
3575
  }) })
2877
3576
  ]
2878
3577
  }
2879
3578
  ),
2880
- isFirstFragment && /* @__PURE__ */ jsx11(TableResizeOverlay, { tableRef, blockIndex: index, cols })
3579
+ isFirstFragment && /* @__PURE__ */ jsx12(TableResizeOverlay, { tableRef, blockIndex: index, cols, rows })
2881
3580
  ] });
2882
3581
  }
2883
- function readColWidths(raw, cols) {
2884
- if (!Array.isArray(raw)) return null;
2885
- const out = [];
2886
- for (let i = 0; i < cols; i++) {
2887
- const v = raw[i];
2888
- out.push(typeof v === "number" && Number.isFinite(v) && v > 0 ? v : null);
2889
- }
2890
- return out;
2891
- }
2892
3582
  function spanAttr(v) {
2893
3583
  if (typeof v !== "number" || !Number.isFinite(v) || v < 1) return 1;
2894
3584
  return Math.trunc(v);
@@ -2899,7 +3589,7 @@ function clampPositive(n) {
2899
3589
  }
2900
3590
 
2901
3591
  // src/usePagination.ts
2902
- import { useLayoutEffect as useLayoutEffect4, useRef as useRef8, useState as useState8 } from "react";
3592
+ import { useLayoutEffect as useLayoutEffect4, useRef as useRef9, useState as useState9 } from "react";
2903
3593
  var A4_PAGE = {
2904
3594
  width: 794,
2905
3595
  height: 1123,
@@ -2925,9 +3615,9 @@ function defaultPageLayout(topLevelCount) {
2925
3615
  };
2926
3616
  }
2927
3617
  function usePageLayout(rootRef, geometry, topLevelCount, deps) {
2928
- const [phase, setPhase] = useState8("measure");
2929
- const [layout, setLayout] = useState8(() => defaultPageLayout(topLevelCount));
2930
- const lastDepsRef = useRef8(null);
3618
+ const [phase, setPhase] = useState9("measure");
3619
+ const [layout, setLayout] = useState9(() => defaultPageLayout(topLevelCount));
3620
+ const lastDepsRef = useRef9(null);
2931
3621
  useLayoutEffect4(() => {
2932
3622
  const currentDeps = [...deps, topLevelCount, geometry];
2933
3623
  if (lastDepsRef.current !== null && shallowArrayEqual(lastDepsRef.current, currentDeps)) {
@@ -3412,17 +4102,17 @@ function layoutsDeepEqual(a, b) {
3412
4102
  }
3413
4103
 
3414
4104
  // src/useEditor.ts
3415
- import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo3, useRef as useRef10, useState as useState9 } from "react";
4105
+ import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo3, useRef as useRef11, useState as useState10 } from "react";
3416
4106
 
3417
4107
  // src/usePageSettings.ts
3418
- import { useCallback as useCallback5, useRef as useRef9, useSyncExternalStore } from "react";
4108
+ import { useCallback as useCallback6, useRef as useRef10, useSyncExternalStore } from "react";
3419
4109
  function usePageSettings(doc) {
3420
- const cacheRef = useRef9(null);
3421
- const subscribe = useCallback5(
4110
+ const cacheRef = useRef10(null);
4111
+ const subscribe = useCallback6(
3422
4112
  (notify) => doc.observePageSettings(notify),
3423
4113
  [doc]
3424
4114
  );
3425
- const getSnapshot = useCallback5(() => {
4115
+ const getSnapshot = useCallback6(() => {
3426
4116
  const next = doc.getPageSettings();
3427
4117
  const prev = cacheRef.current;
3428
4118
  if (prev && shallowEqual(prev, next)) return prev;
@@ -3450,8 +4140,10 @@ import {
3450
4140
  mergeSelection as cmdMergeSelection,
3451
4141
  moveEmbedAnchor as cmdMoveEmbedAnchor,
3452
4142
  setBlockAttrAtIndex as cmdSetBlockAttrAtIndex,
3453
- setColumnWidth as cmdSetColumnWidth,
4143
+ setColumnBoundary as cmdSetColumnBoundary,
3454
4144
  setImageAttrs as cmdSetImageAttrs,
4145
+ setRowHeight as cmdSetRowHeight,
4146
+ setRowHeights as cmdSetRowHeights,
3455
4147
  splitCell as cmdSplitCell,
3456
4148
  tableRectSelection as tableRectSelection2,
3457
4149
  getMarksInRange,
@@ -3460,7 +4152,8 @@ import {
3460
4152
  insertTable as cmdInsertTable,
3461
4153
  insertTableColumn as cmdInsertColumn,
3462
4154
  insertTableRow as cmdInsertRow,
3463
- isImageEmbed as isImageEmbed2,
4155
+ isFormulaEmbed,
4156
+ isImageEmbed as isImageEmbed4,
3464
4157
  isMarkUniformInRange,
3465
4158
  moveToNextCell as cmdMoveNextCell,
3466
4159
  moveToPrevCell as cmdMovePrevCell,
@@ -3470,28 +4163,28 @@ import {
3470
4163
  setCellAttr as cmdSetCellAttr,
3471
4164
  setBlockType as cmdSetBlockType,
3472
4165
  setMark as cmdSetMark,
4166
+ setTableWidth as cmdSetTableWidth,
3473
4167
  splitListItem as cmdSplitListItem,
3474
4168
  tableLocationOf,
3475
4169
  toggleList as cmdToggleList,
3476
4170
  toggleMark as cmdToggleMark
3477
4171
  } from "@sofereditor/core";
3478
- var MAX_INSERT_WIDTH = 600;
3479
4172
  function useEditor(opts = {}) {
3480
4173
  const doc = useMemo3(() => opts.document ?? new EditorDocument(), [opts.document]);
3481
4174
  const history = useMemo3(() => new EditorHistory(doc), [doc]);
3482
- const [snapshot, setSnapshot] = useState9(() => doc.toJSON());
3483
- const [selection, setSelectionState] = useState9(
4175
+ const [snapshot, setSnapshot] = useState10(() => doc.toJSON());
4176
+ const [selection, setSelectionState] = useState10(
3484
4177
  () => collapsedSelection({ blockIndex: 0, offset: 0 })
3485
4178
  );
3486
- const selectionRef = useRef10(selection);
4179
+ const selectionRef = useRef11(selection);
3487
4180
  selectionRef.current = selection;
3488
- const pendingMarksRef = useRef10({});
3489
- const [pendingMarks, setPendingMarksState] = useState9({});
3490
- const setPendingMarks = useCallback6((next) => {
4181
+ const pendingMarksRef = useRef11({});
4182
+ const [pendingMarks, setPendingMarksState] = useState10({});
4183
+ const setPendingMarks = useCallback7((next) => {
3491
4184
  pendingMarksRef.current = next;
3492
4185
  setPendingMarksState(next);
3493
4186
  }, []);
3494
- useEffect8(() => {
4187
+ useEffect9(() => {
3495
4188
  let scheduled = false;
3496
4189
  let cancelled = false;
3497
4190
  let frame = 0;
@@ -3518,8 +4211,8 @@ function useEditor(opts = {}) {
3518
4211
  doc.ydoc.off("afterTransaction", schedule);
3519
4212
  };
3520
4213
  }, [doc]);
3521
- const getSelection = useCallback6(() => selectionRef.current, []);
3522
- const setSelection = useCallback6(
4214
+ const getSelection = useCallback7(() => selectionRef.current, []);
4215
+ const setSelection = useCallback7(
3523
4216
  (sel) => {
3524
4217
  const prev = selectionRef.current;
3525
4218
  selectionRef.current = sel;
@@ -3534,16 +4227,16 @@ function useEditor(opts = {}) {
3534
4227
  },
3535
4228
  []
3536
4229
  );
3537
- const ctxRef = useRef10({ doc, getSelection, setSelection });
4230
+ const ctxRef = useRef11({ doc, getSelection, setSelection });
3538
4231
  ctxRef.current = { doc, getSelection, setSelection };
3539
- const consumePendingMarks = useCallback6(() => {
4232
+ const consumePendingMarks = useCallback7(() => {
3540
4233
  const current = pendingMarksRef.current;
3541
4234
  if (Object.keys(current).length === 0) return void 0;
3542
4235
  pendingMarksRef.current = {};
3543
4236
  setPendingMarksState({});
3544
4237
  return current;
3545
4238
  }, []);
3546
- const isMarkActive = useCallback6(
4239
+ const isMarkActive = useCallback7(
3547
4240
  (name, value) => {
3548
4241
  const sel = selectionRef.current;
3549
4242
  const { start, end } = orderedRange(sel);
@@ -3574,7 +4267,7 @@ function useEditor(opts = {}) {
3574
4267
  },
3575
4268
  [doc]
3576
4269
  );
3577
- const getActiveMarks = useCallback6(() => {
4270
+ const getActiveMarks = useCallback7(() => {
3578
4271
  const sel = selectionRef.current;
3579
4272
  const { start, end } = orderedRange(sel);
3580
4273
  if (start.blockIndex !== end.blockIndex) {
@@ -3588,7 +4281,7 @@ function useEditor(opts = {}) {
3588
4281
  }
3589
4282
  return getMarksInRange(yText, start.offset, end.offset);
3590
4283
  }, [doc]);
3591
- const toggleMark = useCallback6(
4284
+ const toggleMark = useCallback7(
3592
4285
  (name, value) => {
3593
4286
  const sel = selectionRef.current;
3594
4287
  const { start, end } = orderedRange(sel);
@@ -3608,7 +4301,7 @@ function useEditor(opts = {}) {
3608
4301
  },
3609
4302
  [isMarkActive, setPendingMarks]
3610
4303
  );
3611
- const setMark = useCallback6(
4304
+ const setMark = useCallback7(
3612
4305
  (name, value) => {
3613
4306
  const sel = selectionRef.current;
3614
4307
  const { start, end } = orderedRange(sel);
@@ -3621,7 +4314,7 @@ function useEditor(opts = {}) {
3621
4314
  },
3622
4315
  [setPendingMarks]
3623
4316
  );
3624
- const removeMark = useCallback6(
4317
+ const removeMark = useCallback7(
3625
4318
  (name) => {
3626
4319
  const sel = selectionRef.current;
3627
4320
  const { start, end } = orderedRange(sel);
@@ -3634,7 +4327,7 @@ function useEditor(opts = {}) {
3634
4327
  },
3635
4328
  [setPendingMarks]
3636
4329
  );
3637
- const getBlockType = useCallback6(() => {
4330
+ const getBlockType = useCallback7(() => {
3638
4331
  const sel = selectionRef.current;
3639
4332
  const { start, end } = orderedRange(sel);
3640
4333
  const first = doc.getBlockType(start.blockIndex);
@@ -3644,7 +4337,7 @@ function useEditor(opts = {}) {
3644
4337
  }
3645
4338
  return first;
3646
4339
  }, [doc]);
3647
- const getBlockAttr = useCallback6(
4340
+ const getBlockAttr = useCallback7(
3648
4341
  (key) => {
3649
4342
  const sel = selectionRef.current;
3650
4343
  const { start, end } = orderedRange(sel);
@@ -3657,57 +4350,57 @@ function useEditor(opts = {}) {
3657
4350
  },
3658
4351
  [doc]
3659
4352
  );
3660
- const setBlockType = useCallback6((type, attrs) => {
4353
+ const setBlockType = useCallback7((type, attrs) => {
3661
4354
  cmdSetBlockType(ctxRef.current, type, attrs);
3662
4355
  }, []);
3663
- const setBlockAttr = useCallback6(
4356
+ const setBlockAttr = useCallback7(
3664
4357
  (key, value) => {
3665
4358
  cmdSetBlockAttr(ctxRef.current, key, value);
3666
4359
  },
3667
4360
  []
3668
4361
  );
3669
- const setAlign = useCallback6((value) => {
4362
+ const setAlign = useCallback7((value) => {
3670
4363
  const sel = selectionRef.current;
3671
4364
  const inCell = sel.focus.cellIndex != null;
3672
4365
  if (inCell) cmdSetCellAttr(ctxRef.current, "align", value);
3673
4366
  else cmdSetBlockAttr(ctxRef.current, "align", value);
3674
4367
  }, []);
3675
- const getAlign = useCallback6(() => {
4368
+ const getAlign = useCallback7(() => {
3676
4369
  const sel = selectionRef.current;
3677
4370
  if (sel.focus.cellIndex != null && doc.isTable(sel.focus.blockIndex)) {
3678
4371
  return doc.getCellAttrs(sel.focus.blockIndex, sel.focus.cellIndex).align;
3679
4372
  }
3680
4373
  return getBlockAttr("align");
3681
4374
  }, [doc, getBlockAttr]);
3682
- const setCellBackground = useCallback6((color) => {
4375
+ const setCellBackground = useCallback7((color) => {
3683
4376
  cmdSetCellAttr(ctxRef.current, "bgColor", color);
3684
4377
  }, []);
3685
- const getCellBackground = useCallback6(() => {
4378
+ const getCellBackground = useCallback7(() => {
3686
4379
  const sel = selectionRef.current;
3687
4380
  if (sel.focus.cellIndex == null || !doc.isTable(sel.focus.blockIndex)) return void 0;
3688
4381
  return doc.getCellAttrs(sel.focus.blockIndex, sel.focus.cellIndex).bgColor;
3689
4382
  }, [doc]);
3690
- const setTableBorderPreset = useCallback6((preset) => {
4383
+ const setTableBorderPreset = useCallback7((preset) => {
3691
4384
  const sel = selectionRef.current;
3692
4385
  if (!doc.isTable(sel.focus.blockIndex)) return;
3693
4386
  cmdSetBlockAttrAtIndex(ctxRef.current, sel.focus.blockIndex, "borderPreset", preset);
3694
4387
  }, [doc]);
3695
- const getTableBorderPreset = useCallback6(() => {
4388
+ const getTableBorderPreset = useCallback7(() => {
3696
4389
  const sel = selectionRef.current;
3697
4390
  if (!doc.isTable(sel.focus.blockIndex)) return "all";
3698
4391
  return doc.getBlockAttrs(sel.focus.blockIndex).borderPreset ?? "all";
3699
4392
  }, [doc]);
3700
- const setTableBorderColor = useCallback6((color) => {
4393
+ const setTableBorderColor = useCallback7((color) => {
3701
4394
  const sel = selectionRef.current;
3702
4395
  if (!doc.isTable(sel.focus.blockIndex)) return;
3703
4396
  cmdSetBlockAttrAtIndex(ctxRef.current, sel.focus.blockIndex, "borderColor", color);
3704
4397
  }, [doc]);
3705
- const getTableBorderColor = useCallback6(() => {
4398
+ const getTableBorderColor = useCallback7(() => {
3706
4399
  const sel = selectionRef.current;
3707
4400
  if (!doc.isTable(sel.focus.blockIndex)) return void 0;
3708
4401
  return doc.getBlockAttrs(sel.focus.blockIndex).borderColor;
3709
4402
  }, [doc]);
3710
- const isListActive = useCallback6(
4403
+ const isListActive = useCallback7(
3711
4404
  (kind) => {
3712
4405
  const sel = selectionRef.current;
3713
4406
  const { start, end } = orderedRange(sel);
@@ -3719,119 +4412,128 @@ function useEditor(opts = {}) {
3719
4412
  },
3720
4413
  [doc]
3721
4414
  );
3722
- const toggleList = useCallback6((kind) => {
4415
+ const toggleList = useCallback7((kind) => {
3723
4416
  cmdToggleList(ctxRef.current, kind);
3724
4417
  }, []);
3725
- const indentList = useCallback6(() => {
4418
+ const indentList = useCallback7(() => {
3726
4419
  cmdIndentList(ctxRef.current);
3727
4420
  }, []);
3728
- const dedentList = useCallback6(() => {
4421
+ const dedentList = useCallback7(() => {
3729
4422
  cmdDedentList(ctxRef.current);
3730
4423
  }, []);
3731
- const splitListItem = useCallback6(() => {
4424
+ const splitListItem = useCallback7(() => {
3732
4425
  return cmdSplitListItem(ctxRef.current);
3733
4426
  }, []);
3734
- const isInTable = useCallback6(() => {
4427
+ const isInTable = useCallback7(() => {
3735
4428
  return selectionRef.current.focus.cellIndex != null;
3736
4429
  }, []);
3737
- const getTableLocation = useCallback6(() => {
4430
+ const getTableLocation = useCallback7(() => {
3738
4431
  return tableLocationOf(doc, selectionRef.current.focus);
3739
4432
  }, [doc]);
3740
- const insertTable = useCallback6((rows, cols) => {
4433
+ const insertTable = useCallback7((rows, cols) => {
3741
4434
  cmdInsertTable(ctxRef.current, rows, cols);
3742
4435
  }, []);
3743
- const insertAnswerLines = useCallback6((count, spacing) => {
4436
+ const insertAnswerLines = useCallback7((count, spacing) => {
3744
4437
  cmdInsertAnswerLines(ctxRef.current, count, spacing);
3745
4438
  }, []);
3746
- const deleteTable = useCallback6((blockIndex) => {
4439
+ const deleteTable = useCallback7((blockIndex) => {
3747
4440
  const idx = blockIndex ?? selectionRef.current.focus.blockIndex;
3748
4441
  cmdDeleteTable(ctxRef.current, idx);
3749
4442
  }, []);
3750
- const insertRowAbove = useCallback6(() => {
4443
+ const insertRowAbove = useCallback7(() => {
3751
4444
  const loc = tableLocationOf(doc, selectionRef.current.focus);
3752
4445
  if (!loc) return;
3753
4446
  cmdInsertRow(ctxRef.current, loc.blockIndex, loc.row, "before");
3754
4447
  }, [doc]);
3755
- const insertRowBelow = useCallback6(() => {
4448
+ const insertRowBelow = useCallback7(() => {
3756
4449
  const loc = tableLocationOf(doc, selectionRef.current.focus);
3757
4450
  if (!loc) return;
3758
4451
  cmdInsertRow(ctxRef.current, loc.blockIndex, loc.row, "after");
3759
4452
  }, [doc]);
3760
- const deleteCurrentRow = useCallback6(() => {
4453
+ const deleteCurrentRow = useCallback7(() => {
3761
4454
  const loc = tableLocationOf(doc, selectionRef.current.focus);
3762
4455
  if (!loc) return;
3763
4456
  cmdDeleteRow(ctxRef.current, loc.blockIndex, loc.row);
3764
4457
  }, [doc]);
3765
- const insertColumnLeft = useCallback6(() => {
4458
+ const insertColumnLeft = useCallback7(() => {
3766
4459
  const loc = tableLocationOf(doc, selectionRef.current.focus);
3767
4460
  if (!loc) return;
3768
4461
  cmdInsertColumn(ctxRef.current, loc.blockIndex, loc.col, "before");
3769
4462
  }, [doc]);
3770
- const insertColumnRight = useCallback6(() => {
4463
+ const insertColumnRight = useCallback7(() => {
3771
4464
  const loc = tableLocationOf(doc, selectionRef.current.focus);
3772
4465
  if (!loc) return;
3773
4466
  cmdInsertColumn(ctxRef.current, loc.blockIndex, loc.col, "after");
3774
4467
  }, [doc]);
3775
- const deleteCurrentColumn = useCallback6(() => {
4468
+ const deleteCurrentColumn = useCallback7(() => {
3776
4469
  const loc = tableLocationOf(doc, selectionRef.current.focus);
3777
4470
  if (!loc) return;
3778
4471
  cmdDeleteColumn(ctxRef.current, loc.blockIndex, loc.col);
3779
4472
  }, [doc]);
3780
- const moveToNextCell = useCallback6(() => {
4473
+ const moveToNextCell = useCallback7(() => {
3781
4474
  cmdMoveNextCell(ctxRef.current);
3782
4475
  }, []);
3783
- const moveToPrevCell = useCallback6(() => {
4476
+ const moveToPrevCell = useCallback7(() => {
3784
4477
  cmdMovePrevCell(ctxRef.current);
3785
4478
  }, []);
3786
- const mergeRight = useCallback6(() => {
4479
+ const mergeRight = useCallback7(() => {
3787
4480
  const loc = tableLocationOf(doc, selectionRef.current.focus);
3788
4481
  if (!loc) return false;
3789
4482
  return cmdMergeRight(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3790
4483
  }, [doc]);
3791
- const mergeDown = useCallback6(() => {
4484
+ const mergeDown = useCallback7(() => {
3792
4485
  const loc = tableLocationOf(doc, selectionRef.current.focus);
3793
4486
  if (!loc) return false;
3794
4487
  return cmdMergeDown(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3795
4488
  }, [doc]);
3796
- const splitCell = useCallback6(() => {
4489
+ const splitCell = useCallback7(() => {
3797
4490
  const loc = tableLocationOf(doc, selectionRef.current.focus);
3798
4491
  if (!loc) return false;
3799
4492
  return cmdSplitCell(ctxRef.current, loc.blockIndex, loc.row, loc.col);
3800
4493
  }, [doc]);
3801
- const setColumnWidth = useCallback6(
3802
- (blockIndex, col, widthPx) => {
3803
- cmdSetColumnWidth(ctxRef.current, blockIndex, col, widthPx);
4494
+ const setColumnBoundary = useCallback7(
4495
+ (blockIndex, boundary, deltaPct, base) => {
4496
+ cmdSetColumnBoundary(ctxRef.current, blockIndex, boundary, deltaPct, base);
3804
4497
  },
3805
4498
  []
3806
4499
  );
3807
- const getTableRect = useCallback6(
4500
+ const setTableWidth = useCallback7((blockIndex, pct) => {
4501
+ cmdSetTableWidth(ctxRef.current, blockIndex, pct);
4502
+ }, []);
4503
+ const setRowHeight = useCallback7((blockIndex, row, px) => {
4504
+ cmdSetRowHeight(ctxRef.current, blockIndex, row, px);
4505
+ }, []);
4506
+ const setRowHeights = useCallback7((blockIndex, heights) => {
4507
+ cmdSetRowHeights(ctxRef.current, blockIndex, heights);
4508
+ }, []);
4509
+ const getTableRect = useCallback7(
3808
4510
  () => tableRectSelection2(doc, selectionRef.current),
3809
4511
  [doc]
3810
4512
  );
3811
- const hasTableRectSelection = useCallback6(() => {
4513
+ const hasTableRectSelection = useCallback7(() => {
3812
4514
  return tableRectSelection2(doc, selectionRef.current) !== null;
3813
4515
  }, [doc]);
3814
- const mergeSelection = useCallback6(() => {
4516
+ const mergeSelection = useCallback7(() => {
3815
4517
  return cmdMergeSelection(ctxRef.current);
3816
4518
  }, []);
3817
- const insertImage = useCallback6((embed) => {
4519
+ const insertImage = useCallback7((embed) => {
3818
4520
  cmdInsertImage(ctxRef.current, embed);
3819
4521
  }, []);
3820
- const setImageAttrs = useCallback6(
4522
+ const setImageAttrs = useCallback7(
3821
4523
  (blockIndex, offset, partial, cellIndex, newOffset) => {
3822
4524
  cmdSetImageAttrs(ctxRef.current, blockIndex, offset, partial, cellIndex, newOffset);
3823
4525
  },
3824
4526
  []
3825
4527
  );
3826
- const moveEmbedAnchor = useCallback6(
4528
+ const moveEmbedAnchor = useCallback7(
3827
4529
  (from, to, newOffsetX, newOffsetY) => {
3828
4530
  cmdMoveEmbedAnchor(ctxRef.current, from, to, newOffsetX, newOffsetY);
3829
4531
  },
3830
4532
  []
3831
4533
  );
3832
- const uploadImageRef = useRef10(opts.uploadImage);
4534
+ const uploadImageRef = useRef11(opts.uploadImage);
3833
4535
  uploadImageRef.current = opts.uploadImage;
3834
- const insertImageFromFile = useCallback6(async (file) => {
4536
+ const insertImageFromFile = useCallback7(async (file) => {
3835
4537
  if (!file.type.startsWith("image/")) return;
3836
4538
  const dataUrl = await readAsDataURL(file);
3837
4539
  const dims = await readImageDimensions(dataUrl);
@@ -3845,7 +4547,7 @@ function useEditor(opts = {}) {
3845
4547
  const src = upload ? await upload(file) : dataUrl;
3846
4548
  cmdInsertImage(ctxRef.current, { type: "image", src, width: w, height: h });
3847
4549
  }, []);
3848
- const getSelectedEmbed = useCallback6(() => {
4550
+ const getSelectedEmbed = useCallback7(() => {
3849
4551
  const sel = selectionRef.current;
3850
4552
  if (sel.anchor.blockIndex !== sel.focus.blockIndex) return null;
3851
4553
  if ((sel.anchor.cellIndex ?? -1) !== (sel.focus.cellIndex ?? -1)) return null;
@@ -3862,7 +4564,7 @@ function useEditor(opts = {}) {
3862
4564
  cursor += op.insert.length;
3863
4565
  continue;
3864
4566
  }
3865
- if (cursor === start && isImageEmbed2(op.insert)) {
4567
+ if (cursor === start && isImageEmbed4(op.insert)) {
3866
4568
  return {
3867
4569
  blockIndex: sel.anchor.blockIndex,
3868
4570
  cellIndex: sel.anchor.cellIndex,
@@ -3874,16 +4576,16 @@ function useEditor(opts = {}) {
3874
4576
  }
3875
4577
  return null;
3876
4578
  }, [doc]);
3877
- const [linkRequest, setLinkRequest] = useState9(null);
3878
- const linkRequestRef = useRef10(null);
3879
- const requestLink = useCallback6((initialHref = "") => {
4579
+ const [linkRequest, setLinkRequest] = useState10(null);
4580
+ const linkRequestRef = useRef11(null);
4581
+ const requestLink = useCallback7((initialHref = "") => {
3880
4582
  return new Promise((resolve) => {
3881
4583
  const req = { initialHref, resolve, selection: selectionRef.current };
3882
4584
  linkRequestRef.current = req;
3883
4585
  setLinkRequest(req);
3884
4586
  });
3885
4587
  }, []);
3886
- const resolveLinkRequest = useCallback6(
4588
+ const resolveLinkRequest = useCallback7(
3887
4589
  (href) => {
3888
4590
  const req = linkRequestRef.current;
3889
4591
  linkRequestRef.current = null;
@@ -3896,26 +4598,26 @@ function useEditor(opts = {}) {
3896
4598
  [setSelection]
3897
4599
  );
3898
4600
  const pageSettings = usePageSettings(doc);
3899
- const setPageSettings = useCallback6(
4601
+ const setPageSettings = useCallback7(
3900
4602
  (partial) => {
3901
4603
  doc.setPageSettings(partial);
3902
4604
  },
3903
4605
  [doc]
3904
4606
  );
3905
- const [pageConfigRequest, setPageConfigRequest] = useState9(null);
3906
- const requestPageConfig = useCallback6(() => {
4607
+ const [pageConfigRequest, setPageConfigRequest] = useState10(null);
4608
+ const requestPageConfig = useCallback7(() => {
3907
4609
  return new Promise((resolve) => {
3908
4610
  setPageConfigRequest({ initial: doc.getPageSettings(), resolve });
3909
4611
  });
3910
4612
  }, [doc]);
3911
- const resolvePageConfigRequest = useCallback6((applied) => {
4613
+ const resolvePageConfigRequest = useCallback7((applied) => {
3912
4614
  setPageConfigRequest((prev) => {
3913
4615
  if (prev) prev.resolve(applied);
3914
4616
  return null;
3915
4617
  });
3916
4618
  }, []);
3917
- const [imageCaptionRequest, setImageCaptionRequest] = useState9(null);
3918
- const requestImageCaption = useCallback6(
4619
+ const [imageCaptionRequest, setImageCaptionRequest] = useState10(null);
4620
+ const requestImageCaption = useCallback7(
3919
4621
  (initialCaption = "", initialAlign = "center") => {
3920
4622
  return new Promise((resolve) => {
3921
4623
  setImageCaptionRequest({ initialCaption, initialAlign, resolve });
@@ -3923,12 +4625,82 @@ function useEditor(opts = {}) {
3923
4625
  },
3924
4626
  []
3925
4627
  );
3926
- const resolveImageCaptionRequest = useCallback6((result) => {
4628
+ const resolveImageCaptionRequest = useCallback7((result) => {
3927
4629
  setImageCaptionRequest((prev) => {
3928
4630
  if (prev) prev.resolve(result);
3929
4631
  return null;
3930
4632
  });
3931
4633
  }, []);
4634
+ const [formulaRequest, setFormulaRequest] = useState10(null);
4635
+ const requestFormula = useCallback7(
4636
+ (initialLatex = "", initialDisplay = false) => {
4637
+ return new Promise((resolve) => {
4638
+ setFormulaRequest({ initialLatex, initialDisplay, resolve });
4639
+ });
4640
+ },
4641
+ []
4642
+ );
4643
+ const resolveFormulaRequest = useCallback7((result) => {
4644
+ setFormulaRequest((prev) => {
4645
+ if (prev) prev.resolve(result);
4646
+ return null;
4647
+ });
4648
+ }, []);
4649
+ const insertFormula = useCallback7(
4650
+ async (latex, display) => {
4651
+ const selectionAtCall = ctxRef.current.getSelection();
4652
+ const embedAtCall = getSelectedEmbed();
4653
+ let mathModule;
4654
+ try {
4655
+ mathModule = await import("@sofereditor/math");
4656
+ } catch {
4657
+ return;
4658
+ }
4659
+ const { renderLatexToSvg, measureExInPx, svgToDataUrl, svgToPngDataUrl } = mathModule;
4660
+ const r = renderLatexToSvg(latex, display);
4661
+ if (!r.ok) return;
4662
+ const root = document.querySelector(".ed-root");
4663
+ const exPx = root ? measureExInPx(root) : 8;
4664
+ let w = Math.round(r.widthEx * exPx);
4665
+ let h = Math.round(r.heightEx * exPx);
4666
+ let vAlignScale = 1;
4667
+ if (w > MAX_INSERT_WIDTH) {
4668
+ vAlignScale = MAX_INSERT_WIDTH / w;
4669
+ h = Math.round(h * MAX_INSERT_WIDTH / w);
4670
+ w = MAX_INSERT_WIDTH;
4671
+ }
4672
+ const src = svgToDataUrl(r.svg);
4673
+ let svgFallback;
4674
+ try {
4675
+ svgFallback = await svgToPngDataUrl(r.svg, w, h);
4676
+ } catch {
4677
+ svgFallback = void 0;
4678
+ }
4679
+ ctxRef.current.setSelection(selectionAtCall);
4680
+ const inheritedLayout = {};
4681
+ if (isFormulaEmbed(embedAtCall?.embed)) {
4682
+ const prev = embedAtCall.embed;
4683
+ if (prev.layout !== void 0) inheritedLayout.layout = prev.layout;
4684
+ if (prev.offsetX !== void 0) inheritedLayout.offsetX = prev.offsetX;
4685
+ if (prev.offsetY !== void 0) inheritedLayout.offsetY = prev.offsetY;
4686
+ }
4687
+ cmdInsertImage(ctxRef.current, {
4688
+ type: "image",
4689
+ src,
4690
+ width: w,
4691
+ height: h,
4692
+ ...inheritedLayout,
4693
+ ...display ? { align: "center" } : {},
4694
+ formula: {
4695
+ latex,
4696
+ display,
4697
+ ...display ? {} : { vAlign: `${r.vAlignEx * vAlignScale}ex` }
4698
+ },
4699
+ ...svgFallback ? { svgFallback } : {}
4700
+ });
4701
+ },
4702
+ [getSelectedEmbed]
4703
+ );
3932
4704
  return {
3933
4705
  doc,
3934
4706
  history,
@@ -3979,7 +4751,10 @@ function useEditor(opts = {}) {
3979
4751
  mergeRight,
3980
4752
  mergeDown,
3981
4753
  splitCell,
3982
- setColumnWidth,
4754
+ setColumnBoundary,
4755
+ setTableWidth,
4756
+ setRowHeight,
4757
+ setRowHeights,
3983
4758
  getTableRect,
3984
4759
  hasTableRectSelection,
3985
4760
  mergeSelection,
@@ -3995,7 +4770,12 @@ function useEditor(opts = {}) {
3995
4770
  resolvePageConfigRequest,
3996
4771
  imageCaptionRequest,
3997
4772
  requestImageCaption,
3998
- resolveImageCaptionRequest
4773
+ resolveImageCaptionRequest,
4774
+ formulaRequest,
4775
+ requestFormula,
4776
+ resolveFormulaRequest,
4777
+ insertFormula,
4778
+ uploadImage: opts.uploadImage
3999
4779
  };
4000
4780
  }
4001
4781
  function readAsDataURL(file) {
@@ -4027,7 +4807,7 @@ function sameValue(a, b) {
4027
4807
  }
4028
4808
 
4029
4809
  // src/Editor.tsx
4030
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
4810
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
4031
4811
  var defaultFooter = ({ pageNumber, pageCount }) => `${pageNumber} / ${pageCount}`;
4032
4812
  function Editor({
4033
4813
  editor: providedEditor,
@@ -4040,14 +4820,15 @@ function Editor({
4040
4820
  const internal = useEditor();
4041
4821
  const editor = providedEditor ?? internal;
4042
4822
  const { doc, history, snapshot, getSelection, setSelection } = editor;
4043
- const rootRef = useRef11(null);
4044
- const isComposingRef = useRef11(false);
4045
- const [hoveredBehind, setHoveredBehind] = useState10(null);
4046
- const hoveredBehindRef = useRef11(null);
4047
- const [hoveredLink, setHoveredLink] = useState10(null);
4048
- const hoveredLinkElRef = useRef11(null);
4049
- const suppressSelectionSyncRef = useRef11(false);
4050
- useEffect9(() => {
4823
+ const rootRef = useRef12(null);
4824
+ const isComposingRef = useRef12(false);
4825
+ const pasteInFlightRef = useRef12(false);
4826
+ const [hoveredBehind, setHoveredBehind] = useState11(null);
4827
+ const hoveredBehindRef = useRef12(null);
4828
+ const [hoveredLink, setHoveredLink] = useState11(null);
4829
+ const hoveredLinkElRef = useRef12(null);
4830
+ const suppressSelectionSyncRef = useRef12(false);
4831
+ useEffect10(() => {
4051
4832
  if (!awareness) return;
4052
4833
  const root = rootRef.current;
4053
4834
  if (!root) return;
@@ -4077,11 +4858,11 @@ function Editor({
4077
4858
  awareness.setLocalStateField("cursor", null);
4078
4859
  };
4079
4860
  }, [awareness, doc]);
4080
- const ctxRef = useRef11({ doc, getSelection, setSelection });
4861
+ const ctxRef = useRef12({ doc, getSelection, setSelection });
4081
4862
  ctxRef.current = { doc, getSelection, setSelection };
4082
- const historyRef = useRef11(history);
4863
+ const historyRef = useRef12(history);
4083
4864
  historyRef.current = history;
4084
- const editorRef = useRef11(editor);
4865
+ const editorRef = useRef12(editor);
4085
4866
  editorRef.current = editor;
4086
4867
  useLayoutEffect5(() => {
4087
4868
  const root = rootRef.current;
@@ -4098,7 +4879,7 @@ function Editor({
4098
4879
  suppressSelectionSyncRef.current = true;
4099
4880
  applyDomSelection(root, editor.selection);
4100
4881
  });
4101
- useEffect9(() => {
4882
+ useEffect10(() => {
4102
4883
  const root = rootRef.current;
4103
4884
  if (!root) return;
4104
4885
  const handler = () => {
@@ -4119,7 +4900,7 @@ function Editor({
4119
4900
  document.addEventListener("selectionchange", handler);
4120
4901
  return () => document.removeEventListener("selectionchange", handler);
4121
4902
  }, [setSelection]);
4122
- useEffect9(() => {
4903
+ useEffect10(() => {
4123
4904
  const root = rootRef.current;
4124
4905
  if (!root) return;
4125
4906
  let drag = null;
@@ -4190,7 +4971,7 @@ function Editor({
4190
4971
  document.removeEventListener("pointerup", onPointerUp);
4191
4972
  };
4192
4973
  }, []);
4193
- useEffect9(() => {
4974
+ useEffect10(() => {
4194
4975
  const root = rootRef.current;
4195
4976
  if (!root) return;
4196
4977
  const onDragOver = (e) => {
@@ -4228,7 +5009,7 @@ function Editor({
4228
5009
  root.removeEventListener("drop", onDrop);
4229
5010
  };
4230
5011
  }, []);
4231
- useEffect9(() => {
5012
+ useEffect10(() => {
4232
5013
  const root = rootRef.current;
4233
5014
  if (!root) return;
4234
5015
  const onPointerDown = (e) => {
@@ -4256,7 +5037,27 @@ function Editor({
4256
5037
  root.addEventListener("pointerdown", onPointerDown);
4257
5038
  return () => root.removeEventListener("pointerdown", onPointerDown);
4258
5039
  }, []);
4259
- useEffect9(() => {
5040
+ useEffect10(() => {
5041
+ const root = rootRef.current;
5042
+ if (!root) return;
5043
+ const onDoubleClick = (e) => {
5044
+ const target = e.target;
5045
+ if (!(target instanceof Element)) return;
5046
+ if (!target.closest('img[data-embed="image"]')) return;
5047
+ const ed = editorRef.current;
5048
+ const sel = ed.getSelectedEmbed();
5049
+ if (!sel || !isFormulaEmbed2(sel.embed)) return;
5050
+ e.preventDefault();
5051
+ const { latex, display } = sel.embed.formula;
5052
+ void ed.requestFormula(latex, display).then((r) => {
5053
+ if (!r) return;
5054
+ void ed.insertFormula(r.latex, r.display);
5055
+ });
5056
+ };
5057
+ root.addEventListener("dblclick", onDoubleClick);
5058
+ return () => root.removeEventListener("dblclick", onDoubleClick);
5059
+ }, []);
5060
+ useEffect10(() => {
4260
5061
  const root = rootRef.current;
4261
5062
  if (!root) return;
4262
5063
  const same = (a, b) => a === b || !!a && !!b && a.blockIndex === b.blockIndex && a.offset === b.offset && (a.cellIndex ?? -1) === (b.cellIndex ?? -1);
@@ -4309,7 +5110,7 @@ function Editor({
4309
5110
  if (raf) cancelAnimationFrame(raf);
4310
5111
  };
4311
5112
  }, []);
4312
- useEffect9(() => {
5113
+ useEffect10(() => {
4313
5114
  const root = rootRef.current;
4314
5115
  if (!root) return;
4315
5116
  const clear = () => {
@@ -4344,7 +5145,7 @@ function Editor({
4344
5145
  window.removeEventListener("scroll", clear, true);
4345
5146
  };
4346
5147
  }, []);
4347
- useEffect9(() => {
5148
+ useEffect10(() => {
4348
5149
  const root = rootRef.current;
4349
5150
  if (!root) return;
4350
5151
  const handler = (e) => {
@@ -4457,7 +5258,7 @@ function Editor({
4457
5258
  root.addEventListener("beforeinput", handler);
4458
5259
  return () => root.removeEventListener("beforeinput", handler);
4459
5260
  }, []);
4460
- useEffect9(() => {
5261
+ useEffect10(() => {
4461
5262
  const root = rootRef.current;
4462
5263
  if (!root) return;
4463
5264
  const writeSlice = (e) => {
@@ -4484,9 +5285,48 @@ function Editor({
4484
5285
  e.preventDefault();
4485
5286
  switch (plan.kind) {
4486
5287
  case "sofer":
4487
- case "html":
4488
5288
  insertSlice(ctx, plan.slice);
4489
5289
  return;
5290
+ case "html": {
5291
+ if (!sliceHasDataImageEmbeds(plan.slice)) {
5292
+ insertSlice(ctx, plan.slice);
5293
+ return;
5294
+ }
5295
+ if (pasteInFlightRef.current) {
5296
+ console.warn(
5297
+ "[sofereditor] colagem com imagem ainda subindo \u2014 descartando esta colagem concorrente para n\xE3o corromper a posi\xE7\xE3o de inser\xE7\xE3o"
5298
+ );
5299
+ return;
5300
+ }
5301
+ pasteInFlightRef.current = true;
5302
+ const capturedSelection = ctx.getSelection();
5303
+ void (async () => {
5304
+ try {
5305
+ const resolved = await resolvePastedImageUploads(plan.slice, editorRef.current.uploadImage);
5306
+ const liveSelection = ctxRef.current.getSelection();
5307
+ const userMovedAway = !selectionsEqual(liveSelection, capturedSelection);
5308
+ const { blockIndex, cellIndex } = capturedSelection.focus;
5309
+ const targetText = ctxRef.current.doc.textAt(blockIndex, cellIndex);
5310
+ let insertAt = capturedSelection;
5311
+ if (!targetText) {
5312
+ console.error(
5313
+ "[sofereditor] bloco de destino da colagem sumiu durante o upload da imagem \u2014 inserindo no fim do documento em vez de descartar a colagem"
5314
+ );
5315
+ const lastIndex = Math.max(0, ctxRef.current.doc.blockCount() - 1);
5316
+ const lastText = ctxRef.current.doc.getBlockText(lastIndex);
5317
+ insertAt = collapsedSelection2({ blockIndex: lastIndex, offset: lastText ? lastText.length : 0 });
5318
+ }
5319
+ ctxRef.current.setSelection(insertAt);
5320
+ insertSlice(ctxRef.current, resolved);
5321
+ if (userMovedAway) {
5322
+ ctxRef.current.setSelection(liveSelection);
5323
+ }
5324
+ } finally {
5325
+ pasteInFlightRef.current = false;
5326
+ }
5327
+ })();
5328
+ return;
5329
+ }
4490
5330
  case "images":
4491
5331
  void (async () => {
4492
5332
  for (const f of plan.files) await editorRef.current.insertImageFromFile(f);
@@ -4511,7 +5351,7 @@ function Editor({
4511
5351
  root.removeEventListener("paste", onPaste);
4512
5352
  };
4513
5353
  }, []);
4514
- const onKeyDown = useCallback7((e) => {
5354
+ const onKeyDown = useCallback8((e) => {
4515
5355
  const ed = editorRef.current;
4516
5356
  const mod = e.metaKey || e.ctrlKey;
4517
5357
  if (e.key === "Tab") {
@@ -4539,6 +5379,17 @@ function Editor({
4539
5379
  }
4540
5380
  return;
4541
5381
  }
5382
+ if (!mod && (e.key === "Backspace" || e.key === "Delete")) {
5383
+ if (isComposingRef.current) return;
5384
+ const direction = e.key === "Backspace" ? "backward" : "forward";
5385
+ const shouldIntercept = ed.getSelectedEmbed() != null || isEmbedAdjacentToCaret(ctxRef.current, direction);
5386
+ if (shouldIntercept) {
5387
+ e.preventDefault();
5388
+ if (direction === "backward") deleteBackward(ctxRef.current);
5389
+ else deleteForward(ctxRef.current);
5390
+ }
5391
+ return;
5392
+ }
4542
5393
  if (!mod) return;
4543
5394
  const key = e.key.toLowerCase();
4544
5395
  if (!e.shiftKey && !e.altKey && key === "z") {
@@ -4585,10 +5436,10 @@ function Editor({
4585
5436
  });
4586
5437
  }
4587
5438
  }, []);
4588
- const onCompositionStart = useCallback7(() => {
5439
+ const onCompositionStart = useCallback8(() => {
4589
5440
  isComposingRef.current = true;
4590
5441
  }, []);
4591
- const onCompositionEnd = useCallback7((e) => {
5442
+ const onCompositionEnd = useCallback8((e) => {
4592
5443
  isComposingRef.current = false;
4593
5444
  if (e.data && e.data.length > 0) {
4594
5445
  insertText(ctxRef.current, e.data);
@@ -4636,7 +5487,7 @@ function Editor({
4636
5487
  geom.marginRight
4637
5488
  ]
4638
5489
  );
4639
- const body = /* @__PURE__ */ jsxs11(
5490
+ const body = /* @__PURE__ */ jsxs13(
4640
5491
  "div",
4641
5492
  {
4642
5493
  ref: rootRef,
@@ -4662,7 +5513,7 @@ function Editor({
4662
5513
  const pageInlineStyle = pi === 0 && geom.firstPageExtraTop ? {
4663
5514
  "--ed-page-margin-top": `${geom.marginTop + geom.firstPageExtraTop}px`
4664
5515
  } : void 0;
4665
- return /* @__PURE__ */ jsxs11(
5516
+ return /* @__PURE__ */ jsxs13(
4666
5517
  "div",
4667
5518
  {
4668
5519
  className: "ed-page",
@@ -4670,15 +5521,15 @@ function Editor({
4670
5521
  "data-page-total": ctx.pageCount,
4671
5522
  style: pageInlineStyle,
4672
5523
  children: [
4673
- renderPageHeader && /* @__PURE__ */ jsx12("div", { className: "ed-page-header", contentEditable: false, children: renderPageHeader(ctx) }),
4674
- /* @__PURE__ */ jsx12("div", { className: "ed-page-content", children: page.slots.map((slot) => renderSlot(slot, topLevels)) }),
4675
- renderPageFooter && /* @__PURE__ */ jsx12("div", { className: "ed-page-footer", contentEditable: false, children: renderPageFooter(ctx) })
5524
+ renderPageHeader && /* @__PURE__ */ jsx13("div", { className: "ed-page-header", contentEditable: false, children: renderPageHeader(ctx) }),
5525
+ /* @__PURE__ */ jsx13("div", { className: "ed-page-content", children: page.slots.map((slot) => renderSlot(slot, topLevels)) }),
5526
+ renderPageFooter && /* @__PURE__ */ jsx13("div", { className: "ed-page-footer", contentEditable: false, children: renderPageFooter(ctx) })
4676
5527
  ]
4677
5528
  },
4678
5529
  `page-${pi}`
4679
5530
  );
4680
5531
  }) : topLevels.map((t) => renderTopLevel(t)),
4681
- selectedEmbed && /* @__PURE__ */ jsx12(
5532
+ selectedEmbed && /* @__PURE__ */ jsx13(
4682
5533
  ImageResizeOverlay,
4683
5534
  {
4684
5535
  rootRef,
@@ -4710,7 +5561,7 @@ function Editor({
4710
5561
  )
4711
5562
  }
4712
5563
  ),
4713
- hoveredBehind && !(selectedEmbed && selectedEmbed.blockIndex === hoveredBehind.blockIndex && selectedEmbed.offset === hoveredBehind.offset && (selectedEmbed.cellIndex ?? -1) === (hoveredBehind.cellIndex ?? -1)) && /* @__PURE__ */ jsx12(
5564
+ hoveredBehind && !(selectedEmbed && selectedEmbed.blockIndex === hoveredBehind.blockIndex && selectedEmbed.offset === hoveredBehind.offset && (selectedEmbed.cellIndex ?? -1) === (hoveredBehind.cellIndex ?? -1)) && /* @__PURE__ */ jsx13(
4714
5565
  BehindImageSelectAffordance,
4715
5566
  {
4716
5567
  rootRef,
@@ -4731,8 +5582,8 @@ function Editor({
4731
5582
  })
4732
5583
  }
4733
5584
  ),
4734
- hoveredLink && hoveredLink.href && /* @__PURE__ */ jsx12(LinkHoverTooltip, { href: hoveredLink.href, rect: hoveredLink.rect }),
4735
- awareness && /* @__PURE__ */ jsx12(
5585
+ hoveredLink && hoveredLink.href && /* @__PURE__ */ jsx13(LinkHoverTooltip, { href: hoveredLink.href, rect: hoveredLink.rect }),
5586
+ awareness && /* @__PURE__ */ jsx13(
4736
5587
  RemoteCursorsOverlay,
4737
5588
  {
4738
5589
  rootRef,
@@ -4744,7 +5595,7 @@ function Editor({
4744
5595
  ]
4745
5596
  }
4746
5597
  );
4747
- return /* @__PURE__ */ jsx12(EditorProvider, { editor, children: body });
5598
+ return /* @__PURE__ */ jsx13(EditorProvider, { editor, children: body });
4748
5599
  }
4749
5600
  function groupTopLevels(snapshot) {
4750
5601
  const out = [];
@@ -4798,7 +5649,7 @@ function renderSlot(slot, topLevels) {
4798
5649
  start: slot.fragment.start,
4799
5650
  end: slot.fragment.end
4800
5651
  };
4801
- return /* @__PURE__ */ jsx12(
5652
+ return /* @__PURE__ */ jsx13(
4802
5653
  NodeView,
4803
5654
  {
4804
5655
  block: top.block,
@@ -4809,7 +5660,7 @@ function renderSlot(slot, topLevels) {
4809
5660
  );
4810
5661
  }
4811
5662
  if (slot.tableFragment && top.kind === "block" && top.block.type === "table") {
4812
- return /* @__PURE__ */ jsx12(
5663
+ return /* @__PURE__ */ jsx13(
4813
5664
  NodeView,
4814
5665
  {
4815
5666
  block: top.block,
@@ -4829,7 +5680,7 @@ function renderTopLevel(top) {
4829
5680
  const tree = buildListTree(top.items);
4830
5681
  return renderListTree(tree, top, top.ordinalStart, `list-${top.key}`);
4831
5682
  }
4832
- return /* @__PURE__ */ jsx12(NodeView, { block: top.block, index: top.blockIndex }, top.key);
5683
+ return /* @__PURE__ */ jsx13(NodeView, { block: top.block, index: top.blockIndex }, top.key);
4833
5684
  }
4834
5685
  function renderListFragment(top, frag) {
4835
5686
  const ranges = topLevelItemRanges(top.items);
@@ -4871,9 +5722,9 @@ function buildListTree(items) {
4871
5722
  function renderListTree(tree, top, start, key) {
4872
5723
  const className = `ed-list ed-list-${top.listKind}`;
4873
5724
  const style = top.listStyle ? { listStyleType: top.listStyle } : void 0;
4874
- const items = tree.map((node) => /* @__PURE__ */ jsx12(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));
5725
+ const items = tree.map((node) => /* @__PURE__ */ jsx13(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));
4875
5726
  if (top.listKind === "ordered") {
4876
- return /* @__PURE__ */ jsx12(
5727
+ return /* @__PURE__ */ jsx13(
4877
5728
  "ol",
4878
5729
  {
4879
5730
  className,
@@ -4885,12 +5736,12 @@ function renderListTree(tree, top, start, key) {
4885
5736
  key
4886
5737
  );
4887
5738
  }
4888
- return /* @__PURE__ */ jsx12("ul", { className, "data-list-kind": "bullet", style, children: items }, key);
5739
+ return /* @__PURE__ */ jsx13("ul", { className, "data-list-kind": "bullet", style, children: items }, key);
4889
5740
  }
4890
5741
  function renderNestedListTree(tree, key) {
4891
5742
  const kind = tree[0]?.entry.block.attrs.listKind === "ordered" ? "ordered" : "bullet";
4892
5743
  const Tag = kind === "ordered" ? "ol" : "ul";
4893
- return /* @__PURE__ */ jsx12(Tag, { className: `ed-list ed-list-${kind}`, "data-list-kind": kind, children: tree.map((node) => /* @__PURE__ */ jsx12(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);
5744
+ return /* @__PURE__ */ jsx13(Tag, { className: `ed-list ed-list-${kind}`, "data-list-kind": kind, children: tree.map((node) => /* @__PURE__ */ jsx13(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);
4894
5745
  }
4895
5746
  function clampListLevel2(l) {
4896
5747
  if (typeof l !== "number" || !Number.isFinite(l)) return 0;
@@ -4919,13 +5770,80 @@ function caretRangeFromPoint(x, y) {
4919
5770
 
4920
5771
  // src/Toolbar.tsx
4921
5772
  import {
4922
- useCallback as useCallback8,
4923
- useEffect as useEffect10,
4924
- useRef as useRef12,
4925
- useState as useState11
5773
+ useCallback as useCallback9,
5774
+ useEffect as useEffect11,
5775
+ useRef as useRef13,
5776
+ useState as useState12
4926
5777
  } from "react";
4927
- import { ANSWER_LINE_MAX, TABLE_BORDER_COLOR } from "@sofereditor/core";
4928
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
5778
+ import { ANSWER_LINE_MAX, isFormulaEmbed as isFormulaEmbed3, TABLE_BORDER_COLOR } from "@sofereditor/core";
5779
+
5780
+ // src/TableBorderPanel.tsx
5781
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
5782
+ var BORDER_PRESETS = [
5783
+ { key: "all", label: "Todas", glyph: "\u229E" },
5784
+ { key: "outer", label: "S\xF3 externas", glyph: "\u25A1" },
5785
+ { key: "horizontal", label: "S\xF3 horizontais", glyph: "\u25A4" },
5786
+ { key: "vertical", label: "S\xF3 verticais", glyph: "\u25A5" },
5787
+ { key: "none", label: "Nenhuma", glyph: "\u2B1A" }
5788
+ ];
5789
+ var BORDER_MENU_GLYPH = "\u229E";
5790
+ function TableBorderPanel({
5791
+ preset,
5792
+ color,
5793
+ onPreset,
5794
+ onColor,
5795
+ onResetColor
5796
+ }) {
5797
+ return /* @__PURE__ */ jsxs14("div", { className: "ed-border-panel", children: [
5798
+ /* @__PURE__ */ jsx14("div", { className: "ed-border-presets", children: BORDER_PRESETS.map((p) => /* @__PURE__ */ jsxs14(
5799
+ "button",
5800
+ {
5801
+ type: "button",
5802
+ className: "ed-border-preset",
5803
+ "aria-pressed": preset === p.key,
5804
+ title: p.label,
5805
+ onClick: (e) => {
5806
+ e.preventDefault();
5807
+ onPreset(p.key);
5808
+ },
5809
+ children: [
5810
+ /* @__PURE__ */ jsx14("span", { className: "ed-border-glyph", "aria-hidden": true, children: p.glyph }),
5811
+ p.label
5812
+ ]
5813
+ },
5814
+ p.key
5815
+ )) }),
5816
+ /* @__PURE__ */ jsx14("hr", {}),
5817
+ /* @__PURE__ */ jsxs14("label", { className: "ed-toolbar-label ed-border-color", children: [
5818
+ /* @__PURE__ */ jsx14("span", { className: "ed-toolbar-swatch", "aria-hidden": true, style: { background: color } }),
5819
+ "Cor da linha",
5820
+ /* @__PURE__ */ jsx14(
5821
+ "input",
5822
+ {
5823
+ type: "color",
5824
+ value: color,
5825
+ onChange: (e) => onColor(e.target.value),
5826
+ "aria-label": "Cor da linha da borda"
5827
+ }
5828
+ )
5829
+ ] }),
5830
+ /* @__PURE__ */ jsx14(
5831
+ "button",
5832
+ {
5833
+ type: "button",
5834
+ className: "ed-border-reset",
5835
+ onClick: (e) => {
5836
+ e.preventDefault();
5837
+ onResetColor();
5838
+ },
5839
+ children: "Padr\xE3o"
5840
+ }
5841
+ )
5842
+ ] });
5843
+ }
5844
+
5845
+ // src/Toolbar.tsx
5846
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
4929
5847
  var FONT_FAMILIES = ["Arial"];
4930
5848
  var FONT_SIZES = ["8pt", "10pt", "11pt", "12pt", "14pt", "16pt", "18pt", "24pt", "32pt", "48pt"];
4931
5849
  var BLOCK_OPTIONS = [
@@ -5029,14 +5947,14 @@ function Toolbar({ className }) {
5029
5947
  }
5030
5948
  setAlign(value);
5031
5949
  };
5032
- const onInsertImageClick = useCallback8(
5950
+ const onInsertImageClick = useCallback9(
5033
5951
  (e) => {
5034
5952
  e.preventDefault();
5035
5953
  imageInputRef.current?.click();
5036
5954
  },
5037
5955
  []
5038
5956
  );
5039
- const onImageInputChange = useCallback8(
5957
+ const onImageInputChange = useCallback9(
5040
5958
  (e) => {
5041
5959
  const file = e.target.files?.[0];
5042
5960
  if (file) void editor.insertImageFromFile(file);
@@ -5044,14 +5962,14 @@ function Toolbar({ className }) {
5044
5962
  },
5045
5963
  [editor]
5046
5964
  );
5047
- const imageInputRef = useRef12(null);
5965
+ const imageInputRef = useRef13(null);
5048
5966
  const onToggleDir = (e) => {
5049
5967
  e.preventDefault();
5050
5968
  const next = blockDir === "rtl" ? "ltr" : "rtl";
5051
5969
  setBlockAttr("dir", next);
5052
5970
  };
5053
5971
  const linkActive = isMarkActive("link");
5054
- const onLinkClick = useCallback8(
5972
+ const onLinkClick = useCallback9(
5055
5973
  (e) => {
5056
5974
  e.preventDefault();
5057
5975
  const currentHref = active.link && typeof active.link === "object" && "href" in active.link ? active.link.href : "";
@@ -5082,7 +6000,7 @@ function Toolbar({ className }) {
5082
6000
  sel.cellIndex
5083
6001
  );
5084
6002
  };
5085
- return /* @__PURE__ */ jsxs12(
6003
+ return /* @__PURE__ */ jsxs15(
5086
6004
  "div",
5087
6005
  {
5088
6006
  className: className ?? "ed-toolbar",
@@ -5090,7 +6008,7 @@ function Toolbar({ className }) {
5090
6008
  "aria-label": "Editor toolbar",
5091
6009
  onMouseDown: stop,
5092
6010
  children: [
5093
- /* @__PURE__ */ jsx13(Group, { children: /* @__PURE__ */ jsxs12(
6011
+ /* @__PURE__ */ jsx15(Group, { children: /* @__PURE__ */ jsxs15(
5094
6012
  "select",
5095
6013
  {
5096
6014
  className: "ed-toolbar-select",
@@ -5099,23 +6017,23 @@ function Toolbar({ className }) {
5099
6017
  onMouseDown: stop,
5100
6018
  "aria-label": "Tipo de bloco",
5101
6019
  children: [
5102
- blockValue === "" && /* @__PURE__ */ jsx13("option", { value: "", children: "\u2014 (misto)" }),
5103
- BLOCK_OPTIONS.map((o) => /* @__PURE__ */ jsx13("option", { value: o.value, children: o.label }, o.value))
6020
+ blockValue === "" && /* @__PURE__ */ jsx15("option", { value: "", children: "\u2014 (misto)" }),
6021
+ BLOCK_OPTIONS.map((o) => /* @__PURE__ */ jsx15("option", { value: o.value, children: o.label }, o.value))
5104
6022
  ]
5105
6023
  }
5106
6024
  ) }),
5107
- /* @__PURE__ */ jsxs12(Group, { children: [
5108
- /* @__PURE__ */ jsx13(MarkButton, { title: "Negrito (\u2318B)", pressed: isMarkActive("bold"), onClick: onToggle("bold"), children: /* @__PURE__ */ jsx13("strong", { children: "B" }) }),
5109
- /* @__PURE__ */ jsx13(MarkButton, { title: "It\xE1lico (\u2318I)", pressed: isMarkActive("italic"), onClick: onToggle("italic"), children: /* @__PURE__ */ jsx13("em", { children: "I" }) }),
5110
- /* @__PURE__ */ jsx13(MarkButton, { title: "Sublinhado (\u2318U)", pressed: isMarkActive("underline"), onClick: onToggle("underline"), children: /* @__PURE__ */ jsx13("u", { children: "U" }) }),
5111
- /* @__PURE__ */ jsx13(MarkButton, { title: "Tachado (\u2318\u21E7X)", pressed: isMarkActive("strike"), onClick: onToggle("strike"), children: /* @__PURE__ */ jsx13("s", { children: "S" }) })
6025
+ /* @__PURE__ */ jsxs15(Group, { children: [
6026
+ /* @__PURE__ */ jsx15(MarkButton, { title: "Negrito (\u2318B)", pressed: isMarkActive("bold"), onClick: onToggle("bold"), children: /* @__PURE__ */ jsx15("strong", { children: "B" }) }),
6027
+ /* @__PURE__ */ jsx15(MarkButton, { title: "It\xE1lico (\u2318I)", pressed: isMarkActive("italic"), onClick: onToggle("italic"), children: /* @__PURE__ */ jsx15("em", { children: "I" }) }),
6028
+ /* @__PURE__ */ jsx15(MarkButton, { title: "Sublinhado (\u2318U)", pressed: isMarkActive("underline"), onClick: onToggle("underline"), children: /* @__PURE__ */ jsx15("u", { children: "U" }) }),
6029
+ /* @__PURE__ */ jsx15(MarkButton, { title: "Tachado (\u2318\u21E7X)", pressed: isMarkActive("strike"), onClick: onToggle("strike"), children: /* @__PURE__ */ jsx15("s", { children: "S" }) })
5112
6030
  ] }),
5113
- /* @__PURE__ */ jsxs12(Group, { children: [
5114
- /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label", children: [
5115
- /* @__PURE__ */ jsx13("span", { className: "ed-toolbar-swatch", "aria-hidden": true, style: { background: colorValue } }),
5116
- /* @__PURE__ */ jsx13("input", { type: "color", value: colorValue, onChange: onColorChange, onMouseDown: stop, "aria-label": "Cor do texto" })
6031
+ /* @__PURE__ */ jsxs15(Group, { children: [
6032
+ /* @__PURE__ */ jsxs15("label", { className: "ed-toolbar-label", children: [
6033
+ /* @__PURE__ */ jsx15("span", { className: "ed-toolbar-swatch", "aria-hidden": true, style: { background: colorValue } }),
6034
+ /* @__PURE__ */ jsx15("input", { type: "color", value: colorValue, onChange: onColorChange, onMouseDown: stop, "aria-label": "Cor do texto" })
5117
6035
  ] }),
5118
- /* @__PURE__ */ jsx13(
6036
+ /* @__PURE__ */ jsx15(
5119
6037
  "button",
5120
6038
  {
5121
6039
  type: "button",
@@ -5129,8 +6047,8 @@ function Toolbar({ className }) {
5129
6047
  children: "\u232B"
5130
6048
  }
5131
6049
  ),
5132
- /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label", title: "Marca-texto", children: [
5133
- /* @__PURE__ */ jsx13(
6050
+ /* @__PURE__ */ jsxs15("label", { className: "ed-toolbar-label", title: "Marca-texto", children: [
6051
+ /* @__PURE__ */ jsx15(
5134
6052
  "span",
5135
6053
  {
5136
6054
  className: "ed-toolbar-swatch ed-toolbar-swatch--highlight",
@@ -5138,7 +6056,7 @@ function Toolbar({ className }) {
5138
6056
  style: { background: highlightValue }
5139
6057
  }
5140
6058
  ),
5141
- /* @__PURE__ */ jsx13(
6059
+ /* @__PURE__ */ jsx15(
5142
6060
  "input",
5143
6061
  {
5144
6062
  type: "color",
@@ -5149,7 +6067,7 @@ function Toolbar({ className }) {
5149
6067
  }
5150
6068
  )
5151
6069
  ] }),
5152
- /* @__PURE__ */ jsx13(
6070
+ /* @__PURE__ */ jsx15(
5153
6071
  "button",
5154
6072
  {
5155
6073
  type: "button",
@@ -5164,8 +6082,8 @@ function Toolbar({ className }) {
5164
6082
  }
5165
6083
  )
5166
6084
  ] }),
5167
- /* @__PURE__ */ jsxs12(Group, { children: [
5168
- /* @__PURE__ */ jsxs12(
6085
+ /* @__PURE__ */ jsxs15(Group, { children: [
6086
+ /* @__PURE__ */ jsxs15(
5169
6087
  "select",
5170
6088
  {
5171
6089
  className: "ed-toolbar-select",
@@ -5174,12 +6092,12 @@ function Toolbar({ className }) {
5174
6092
  onMouseDown: stop,
5175
6093
  "aria-label": "Fam\xEDlia da fonte",
5176
6094
  children: [
5177
- /* @__PURE__ */ jsx13("option", { value: "", children: "Fonte\u2026" }),
5178
- FONT_FAMILIES.map((f) => /* @__PURE__ */ jsx13("option", { value: f, children: f }, f))
6095
+ /* @__PURE__ */ jsx15("option", { value: "", children: "Fonte\u2026" }),
6096
+ FONT_FAMILIES.map((f) => /* @__PURE__ */ jsx15("option", { value: f, children: f }, f))
5179
6097
  ]
5180
6098
  }
5181
6099
  ),
5182
- /* @__PURE__ */ jsxs12(
6100
+ /* @__PURE__ */ jsxs15(
5183
6101
  "select",
5184
6102
  {
5185
6103
  className: "ed-toolbar-select",
@@ -5188,13 +6106,13 @@ function Toolbar({ className }) {
5188
6106
  onMouseDown: stop,
5189
6107
  "aria-label": "Tamanho da fonte",
5190
6108
  children: [
5191
- /* @__PURE__ */ jsx13("option", { value: "", children: "Tamanho\u2026" }),
5192
- FONT_SIZES.map((s) => /* @__PURE__ */ jsx13("option", { value: s, children: s }, s))
6109
+ /* @__PURE__ */ jsx15("option", { value: "", children: "Tamanho\u2026" }),
6110
+ FONT_SIZES.map((s) => /* @__PURE__ */ jsx15("option", { value: s, children: s }, s))
5193
6111
  ]
5194
6112
  }
5195
6113
  )
5196
6114
  ] }),
5197
- /* @__PURE__ */ jsx13(Group, { children: ALIGN_OPTIONS.map((a) => /* @__PURE__ */ jsx13(
6115
+ /* @__PURE__ */ jsx15(Group, { children: ALIGN_OPTIONS.map((a) => /* @__PURE__ */ jsx15(
5198
6116
  "button",
5199
6117
  {
5200
6118
  type: "button",
@@ -5207,7 +6125,7 @@ function Toolbar({ className }) {
5207
6125
  },
5208
6126
  a.value
5209
6127
  )) }),
5210
- /* @__PURE__ */ jsx13(Group, { children: /* @__PURE__ */ jsx13(
6128
+ /* @__PURE__ */ jsx15(Group, { children: /* @__PURE__ */ jsx15(
5211
6129
  "button",
5212
6130
  {
5213
6131
  type: "button",
@@ -5219,8 +6137,8 @@ function Toolbar({ className }) {
5219
6137
  children: blockDir === "rtl" ? "RTL" : "LTR"
5220
6138
  }
5221
6139
  ) }),
5222
- /* @__PURE__ */ jsxs12(Group, { children: [
5223
- /* @__PURE__ */ jsx13(
6140
+ /* @__PURE__ */ jsxs15(Group, { children: [
6141
+ /* @__PURE__ */ jsx15(
5224
6142
  "button",
5225
6143
  {
5226
6144
  type: "button",
@@ -5235,7 +6153,7 @@ function Toolbar({ className }) {
5235
6153
  children: "\u2022"
5236
6154
  }
5237
6155
  ),
5238
- /* @__PURE__ */ jsx13(
6156
+ /* @__PURE__ */ jsx15(
5239
6157
  "button",
5240
6158
  {
5241
6159
  type: "button",
@@ -5250,7 +6168,7 @@ function Toolbar({ className }) {
5250
6168
  children: "1."
5251
6169
  }
5252
6170
  ),
5253
- /* @__PURE__ */ jsx13(
6171
+ /* @__PURE__ */ jsx15(
5254
6172
  "button",
5255
6173
  {
5256
6174
  type: "button",
@@ -5264,7 +6182,7 @@ function Toolbar({ className }) {
5264
6182
  children: "\u21E4"
5265
6183
  }
5266
6184
  ),
5267
- /* @__PURE__ */ jsx13(
6185
+ /* @__PURE__ */ jsx15(
5268
6186
  "button",
5269
6187
  {
5270
6188
  type: "button",
@@ -5279,7 +6197,7 @@ function Toolbar({ className }) {
5279
6197
  }
5280
6198
  )
5281
6199
  ] }),
5282
- /* @__PURE__ */ jsx13(Group, { children: /* @__PURE__ */ jsx13(
6200
+ /* @__PURE__ */ jsx15(Group, { children: /* @__PURE__ */ jsx15(
5283
6201
  "button",
5284
6202
  {
5285
6203
  type: "button",
@@ -5291,12 +6209,12 @@ function Toolbar({ className }) {
5291
6209
  children: "\u{1F517}"
5292
6210
  }
5293
6211
  ) }),
5294
- /* @__PURE__ */ jsxs12(Group, { children: [
5295
- /* @__PURE__ */ jsx13(TableMenu, {}),
5296
- /* @__PURE__ */ jsx13(AnswerLinesMenu, {})
6212
+ /* @__PURE__ */ jsxs15(Group, { children: [
6213
+ /* @__PURE__ */ jsx15(TableMenu, {}),
6214
+ /* @__PURE__ */ jsx15(AnswerLinesMenu, {})
5297
6215
  ] }),
5298
- /* @__PURE__ */ jsxs12(Group, { children: [
5299
- /* @__PURE__ */ jsx13(
6216
+ /* @__PURE__ */ jsxs15(Group, { children: [
6217
+ /* @__PURE__ */ jsx15(
5300
6218
  "button",
5301
6219
  {
5302
6220
  type: "button",
@@ -5307,7 +6225,7 @@ function Toolbar({ className }) {
5307
6225
  children: "\u{1F5BC}"
5308
6226
  }
5309
6227
  ),
5310
- /* @__PURE__ */ jsx13(
6228
+ /* @__PURE__ */ jsx15(
5311
6229
  "input",
5312
6230
  {
5313
6231
  ref: imageInputRef,
@@ -5316,10 +6234,26 @@ function Toolbar({ className }) {
5316
6234
  hidden: true,
5317
6235
  onChange: onImageInputChange
5318
6236
  }
6237
+ ),
6238
+ /* @__PURE__ */ jsx15(
6239
+ "button",
6240
+ {
6241
+ type: "button",
6242
+ className: "ed-toolbar-btn",
6243
+ title: "Inserir f\xF3rmula",
6244
+ onMouseDown: stop,
6245
+ onClick: (e) => {
6246
+ e.preventDefault();
6247
+ void editor.requestFormula().then((r) => r ? editor.insertFormula(r.latex, r.display) : void 0).catch((err) => {
6248
+ console.error("Falha ao inserir f\xF3rmula:", err);
6249
+ });
6250
+ },
6251
+ children: "\u221Ax"
6252
+ }
5319
6253
  )
5320
6254
  ] }),
5321
- selectedEmbed && /* @__PURE__ */ jsxs12(Group, { children: [
5322
- IMAGE_LAYOUT_OPTIONS.map((opt) => /* @__PURE__ */ jsx13(
6255
+ selectedEmbed && /* @__PURE__ */ jsxs15(Group, { children: [
6256
+ IMAGE_LAYOUT_OPTIONS.map((opt) => /* @__PURE__ */ jsx15(
5323
6257
  "button",
5324
6258
  {
5325
6259
  type: "button",
@@ -5332,7 +6266,25 @@ function Toolbar({ className }) {
5332
6266
  },
5333
6267
  opt.value
5334
6268
  )),
5335
- /* @__PURE__ */ jsx13(
6269
+ isFormulaEmbed3(selectedEmbed.embed) ? /* @__PURE__ */ jsx15(
6270
+ "button",
6271
+ {
6272
+ type: "button",
6273
+ className: "ed-toolbar-btn",
6274
+ title: "Editar f\xF3rmula",
6275
+ onMouseDown: stop,
6276
+ onClick: (e) => {
6277
+ e.preventDefault();
6278
+ if (!isFormulaEmbed3(selectedEmbed.embed)) return;
6279
+ const f = selectedEmbed.embed.formula;
6280
+ void editor.requestFormula(f.latex, f.display).then((r) => {
6281
+ if (!r) return;
6282
+ void editor.insertFormula(r.latex, r.display);
6283
+ });
6284
+ },
6285
+ children: "Editar f\xF3rmula"
6286
+ }
6287
+ ) : /* @__PURE__ */ jsx15(
5336
6288
  "button",
5337
6289
  {
5338
6290
  type: "button",
@@ -5362,7 +6314,7 @@ function Toolbar({ className }) {
5362
6314
  }
5363
6315
  )
5364
6316
  ] }),
5365
- /* @__PURE__ */ jsx13(Group, { children: /* @__PURE__ */ jsx13(
6317
+ /* @__PURE__ */ jsx15(Group, { children: /* @__PURE__ */ jsx15(
5366
6318
  "button",
5367
6319
  {
5368
6320
  type: "button",
@@ -5382,11 +6334,11 @@ function Toolbar({ className }) {
5382
6334
  }
5383
6335
  function AnswerLinesMenu() {
5384
6336
  const editor = useEditorContext();
5385
- const [open, setOpen] = useState11(false);
5386
- const [count, setCount] = useState11(5);
5387
- const [spacing, setSpacing] = useState11(1.5);
5388
- const rootRef = useRef12(null);
5389
- useEffect10(() => {
6337
+ const [open, setOpen] = useState12(false);
6338
+ const [count, setCount] = useState12(5);
6339
+ const [spacing, setSpacing] = useState12(1.5);
6340
+ const rootRef = useRef13(null);
6341
+ useEffect11(() => {
5390
6342
  if (!open) return;
5391
6343
  const onDown = (e) => {
5392
6344
  if (!rootRef.current) return;
@@ -5397,8 +6349,8 @@ function AnswerLinesMenu() {
5397
6349
  return () => document.removeEventListener("mousedown", onDown);
5398
6350
  }, [open]);
5399
6351
  const clamped = Math.max(1, Math.min(ANSWER_LINE_MAX, Math.trunc(count) || 1));
5400
- return /* @__PURE__ */ jsxs12("div", { ref: rootRef, className: "ed-toolbar-tablemenu", children: [
5401
- /* @__PURE__ */ jsx13(
6352
+ return /* @__PURE__ */ jsxs15("div", { ref: rootRef, className: "ed-toolbar-tablemenu", children: [
6353
+ /* @__PURE__ */ jsx15(
5402
6354
  "button",
5403
6355
  {
5404
6356
  type: "button",
@@ -5413,7 +6365,7 @@ function AnswerLinesMenu() {
5413
6365
  children: "\u2630"
5414
6366
  }
5415
6367
  ),
5416
- open && /* @__PURE__ */ jsxs12(
6368
+ open && /* @__PURE__ */ jsxs15(
5417
6369
  "div",
5418
6370
  {
5419
6371
  className: "ed-table-popover ed-answerlines-popover",
@@ -5422,9 +6374,9 @@ function AnswerLinesMenu() {
5422
6374
  if (!keepsOwnMouseDown(e.target)) e.preventDefault();
5423
6375
  },
5424
6376
  children: [
5425
- /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label", children: [
6377
+ /* @__PURE__ */ jsxs15("label", { className: "ed-toolbar-label", children: [
5426
6378
  "Quantidade",
5427
- /* @__PURE__ */ jsx13(
6379
+ /* @__PURE__ */ jsx15(
5428
6380
  "input",
5429
6381
  {
5430
6382
  type: "number",
@@ -5436,23 +6388,23 @@ function AnswerLinesMenu() {
5436
6388
  }
5437
6389
  )
5438
6390
  ] }),
5439
- /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label", children: [
6391
+ /* @__PURE__ */ jsxs15("label", { className: "ed-toolbar-label", children: [
5440
6392
  "Entrelinha",
5441
- /* @__PURE__ */ jsxs12(
6393
+ /* @__PURE__ */ jsxs15(
5442
6394
  "select",
5443
6395
  {
5444
6396
  value: String(spacing),
5445
6397
  onChange: (e) => setSpacing(Number(e.target.value)),
5446
6398
  "aria-label": "Entrelinha",
5447
6399
  children: [
5448
- /* @__PURE__ */ jsx13("option", { value: "1", children: "Simples" }),
5449
- /* @__PURE__ */ jsx13("option", { value: "1.5", children: "1,5" }),
5450
- /* @__PURE__ */ jsx13("option", { value: "2", children: "Duplo" })
6400
+ /* @__PURE__ */ jsx15("option", { value: "1", children: "Simples" }),
6401
+ /* @__PURE__ */ jsx15("option", { value: "1.5", children: "1,5" }),
6402
+ /* @__PURE__ */ jsx15("option", { value: "2", children: "Duplo" })
5451
6403
  ]
5452
6404
  }
5453
6405
  )
5454
6406
  ] }),
5455
- /* @__PURE__ */ jsxs12(
6407
+ /* @__PURE__ */ jsxs15(
5456
6408
  "button",
5457
6409
  {
5458
6410
  type: "button",
@@ -5476,9 +6428,10 @@ function AnswerLinesMenu() {
5476
6428
  function TableMenu() {
5477
6429
  const editor = useEditorContext();
5478
6430
  const inTable = editor.isInTable();
5479
- const [open, setOpen] = useState11(false);
5480
- const rootRef = useRef12(null);
5481
- useEffect10(() => {
6431
+ const [open, setOpen] = useState12(false);
6432
+ const [bordersOpen, setBordersOpen] = useState12(false);
6433
+ const rootRef = useRef13(null);
6434
+ useEffect11(() => {
5482
6435
  if (!open) return;
5483
6436
  const onDown = (e) => {
5484
6437
  if (!rootRef.current) return;
@@ -5488,8 +6441,11 @@ function TableMenu() {
5488
6441
  document.addEventListener("mousedown", onDown);
5489
6442
  return () => document.removeEventListener("mousedown", onDown);
5490
6443
  }, [open]);
5491
- return /* @__PURE__ */ jsxs12("div", { ref: rootRef, className: "ed-toolbar-tablemenu", children: [
5492
- /* @__PURE__ */ jsx13(
6444
+ useEffect11(() => {
6445
+ if (!open) setBordersOpen(false);
6446
+ }, [open]);
6447
+ return /* @__PURE__ */ jsxs15("div", { ref: rootRef, className: "ed-toolbar-tablemenu", children: [
6448
+ /* @__PURE__ */ jsx15(
5493
6449
  "button",
5494
6450
  {
5495
6451
  type: "button",
@@ -5504,7 +6460,7 @@ function TableMenu() {
5504
6460
  children: "\u25A6"
5505
6461
  }
5506
6462
  ),
5507
- open && /* @__PURE__ */ jsxs12(
6463
+ open && /* @__PURE__ */ jsxs15(
5508
6464
  "div",
5509
6465
  {
5510
6466
  className: "ed-table-popover",
@@ -5513,7 +6469,7 @@ function TableMenu() {
5513
6469
  if (!keepsOwnMouseDown(e.target)) e.preventDefault();
5514
6470
  },
5515
6471
  children: [
5516
- /* @__PURE__ */ jsx13(
6472
+ /* @__PURE__ */ jsx15(
5517
6473
  TableSizePicker,
5518
6474
  {
5519
6475
  onPick: (rows, cols) => {
@@ -5522,34 +6478,34 @@ function TableMenu() {
5522
6478
  }
5523
6479
  }
5524
6480
  ),
5525
- inTable && /* @__PURE__ */ jsxs12("div", { className: "ed-table-actions", children: [
5526
- /* @__PURE__ */ jsx13("hr", {}),
5527
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => {
6481
+ inTable && /* @__PURE__ */ jsxs15("div", { className: "ed-table-actions", children: [
6482
+ /* @__PURE__ */ jsx15("hr", {}),
6483
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => {
5528
6484
  setOpen(false);
5529
6485
  editor.insertRowAbove();
5530
6486
  }, children: "Inserir linha acima" }),
5531
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => {
6487
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => {
5532
6488
  setOpen(false);
5533
6489
  editor.insertRowBelow();
5534
6490
  }, children: "Inserir linha abaixo" }),
5535
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => {
6491
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => {
5536
6492
  setOpen(false);
5537
6493
  editor.insertColumnLeft();
5538
6494
  }, children: "Inserir coluna \xE0 esquerda" }),
5539
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => {
6495
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => {
5540
6496
  setOpen(false);
5541
6497
  editor.insertColumnRight();
5542
6498
  }, children: "Inserir coluna \xE0 direita" }),
5543
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => {
6499
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => {
5544
6500
  setOpen(false);
5545
6501
  editor.deleteCurrentRow();
5546
6502
  }, children: "Excluir linha" }),
5547
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => {
6503
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => {
5548
6504
  setOpen(false);
5549
6505
  editor.deleteCurrentColumn();
5550
6506
  }, children: "Excluir coluna" }),
5551
- /* @__PURE__ */ jsx13("hr", {}),
5552
- editor.hasTableRectSelection() && /* @__PURE__ */ jsx13(
6507
+ /* @__PURE__ */ jsx15("hr", {}),
6508
+ editor.hasTableRectSelection() && /* @__PURE__ */ jsx15(
5553
6509
  "button",
5554
6510
  {
5555
6511
  type: "button",
@@ -5560,21 +6516,21 @@ function TableMenu() {
5560
6516
  children: "Mesclar c\xE9lulas selecionadas"
5561
6517
  }
5562
6518
  ),
5563
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => {
6519
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => {
5564
6520
  setOpen(false);
5565
6521
  editor.mergeRight();
5566
6522
  }, children: "Mesclar com c\xE9lula \xE0 direita" }),
5567
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => {
6523
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => {
5568
6524
  setOpen(false);
5569
6525
  editor.mergeDown();
5570
6526
  }, children: "Mesclar com c\xE9lula abaixo" }),
5571
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => {
6527
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => {
5572
6528
  setOpen(false);
5573
6529
  editor.splitCell();
5574
6530
  }, children: "Dividir c\xE9lulas mescladas" }),
5575
- /* @__PURE__ */ jsx13("hr", {}),
5576
- /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label ed-table-bgcolor", children: [
5577
- /* @__PURE__ */ jsx13(
6531
+ /* @__PURE__ */ jsx15("hr", {}),
6532
+ /* @__PURE__ */ jsxs15("label", { className: "ed-toolbar-label ed-table-bgcolor", children: [
6533
+ /* @__PURE__ */ jsx15(
5578
6534
  "span",
5579
6535
  {
5580
6536
  className: "ed-toolbar-swatch",
@@ -5583,7 +6539,7 @@ function TableMenu() {
5583
6539
  }
5584
6540
  ),
5585
6541
  "Cor de fundo da c\xE9lula",
5586
- /* @__PURE__ */ jsx13(
6542
+ /* @__PURE__ */ jsx15(
5587
6543
  "input",
5588
6544
  {
5589
6545
  type: "color",
@@ -5593,62 +6549,39 @@ function TableMenu() {
5593
6549
  }
5594
6550
  )
5595
6551
  ] }),
5596
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => {
6552
+ /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => {
5597
6553
  setOpen(false);
5598
6554
  editor.setCellBackground(null);
5599
6555
  }, children: "Remover cor de fundo" }),
5600
- /* @__PURE__ */ jsx13("hr", {}),
5601
- /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label", children: [
5602
- "Bordas",
5603
- /* @__PURE__ */ jsxs12(
5604
- "select",
5605
- {
5606
- value: editor.getTableBorderPreset(),
5607
- onChange: (e) => editor.setTableBorderPreset(e.target.value),
5608
- "aria-label": "Bordas da tabela",
5609
- children: [
5610
- /* @__PURE__ */ jsx13("option", { value: "all", children: "Todas" }),
5611
- /* @__PURE__ */ jsx13("option", { value: "outer", children: "S\xF3 externas" }),
5612
- /* @__PURE__ */ jsx13("option", { value: "horizontal", children: "S\xF3 horizontais" }),
5613
- /* @__PURE__ */ jsx13("option", { value: "vertical", children: "S\xF3 verticais" }),
5614
- /* @__PURE__ */ jsx13("option", { value: "none", children: "Nenhuma" })
5615
- ]
5616
- }
5617
- )
5618
- ] }),
5619
- /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label ed-table-bgcolor", children: [
5620
- /* @__PURE__ */ jsx13(
5621
- "span",
5622
- {
5623
- className: "ed-toolbar-swatch",
5624
- "aria-hidden": true,
5625
- style: { background: editor.getTableBorderColor() ?? TABLE_BORDER_COLOR }
5626
- }
5627
- ),
5628
- "Cor da borda",
5629
- /* @__PURE__ */ jsx13(
5630
- "input",
5631
- {
5632
- type: "color",
5633
- value: editor.getTableBorderColor() ?? TABLE_BORDER_COLOR,
5634
- onChange: (e) => editor.setTableBorderColor(e.target.value),
5635
- "aria-label": "Cor da borda"
5636
- }
5637
- )
5638
- ] }),
5639
- /* @__PURE__ */ jsx13(
6556
+ /* @__PURE__ */ jsx15("hr", {}),
6557
+ /* @__PURE__ */ jsx15(
5640
6558
  "button",
5641
6559
  {
5642
6560
  type: "button",
5643
- onClick: () => {
5644
- setOpen(false);
5645
- editor.setTableBorderColor(null);
6561
+ className: "ed-border-toggle",
6562
+ "aria-pressed": bordersOpen,
6563
+ "aria-expanded": bordersOpen,
6564
+ title: "Bordas da tabela",
6565
+ "aria-label": "Bordas da tabela",
6566
+ onClick: (e) => {
6567
+ e.preventDefault();
6568
+ setBordersOpen((v) => !v);
5646
6569
  },
5647
- children: "Restaurar cor padr\xE3o"
6570
+ children: BORDER_MENU_GLYPH
6571
+ }
6572
+ ),
6573
+ bordersOpen && /* @__PURE__ */ jsx15(
6574
+ TableBorderPanel,
6575
+ {
6576
+ preset: editor.getTableBorderPreset(),
6577
+ color: editor.getTableBorderColor() ?? TABLE_BORDER_COLOR,
6578
+ onPreset: (p) => editor.setTableBorderPreset(p),
6579
+ onColor: (c) => editor.setTableBorderColor(c),
6580
+ onResetColor: () => editor.setTableBorderColor(null)
5648
6581
  }
5649
6582
  ),
5650
- /* @__PURE__ */ jsx13("hr", {}),
5651
- /* @__PURE__ */ jsx13(
6583
+ /* @__PURE__ */ jsx15("hr", {}),
6584
+ /* @__PURE__ */ jsx15(
5652
6585
  "button",
5653
6586
  {
5654
6587
  type: "button",
@@ -5669,11 +6602,11 @@ function TableMenu() {
5669
6602
  var PICKER_MAX_ROWS = 8;
5670
6603
  var PICKER_MAX_COLS = 10;
5671
6604
  function TableSizePicker({ onPick }) {
5672
- const [hover, setHover] = useState11(null);
6605
+ const [hover, setHover] = useState12(null);
5673
6606
  const label = hover ? `${hover.r + 1} \xD7 ${hover.c + 1}` : "Inserir tabela";
5674
- return /* @__PURE__ */ jsxs12("div", { className: "ed-table-picker", children: [
5675
- /* @__PURE__ */ jsx13("div", { className: "ed-table-picker-label", children: label }),
5676
- /* @__PURE__ */ jsx13(
6607
+ return /* @__PURE__ */ jsxs15("div", { className: "ed-table-picker", children: [
6608
+ /* @__PURE__ */ jsx15("div", { className: "ed-table-picker-label", children: label }),
6609
+ /* @__PURE__ */ jsx15(
5677
6610
  "div",
5678
6611
  {
5679
6612
  className: "ed-table-picker-grid",
@@ -5686,7 +6619,7 @@ function TableSizePicker({ onPick }) {
5686
6619
  { length: PICKER_MAX_ROWS },
5687
6620
  (_, r) => Array.from({ length: PICKER_MAX_COLS }, (_2, c) => {
5688
6621
  const filled = hover != null && r <= hover.r && c <= hover.c;
5689
- return /* @__PURE__ */ jsx13(
6622
+ return /* @__PURE__ */ jsx15(
5690
6623
  "button",
5691
6624
  {
5692
6625
  type: "button",
@@ -5716,10 +6649,10 @@ function blockSelectValue(type, level) {
5716
6649
  return type;
5717
6650
  }
5718
6651
  function Group({ children }) {
5719
- return /* @__PURE__ */ jsx13("div", { className: "ed-toolbar-group", children });
6652
+ return /* @__PURE__ */ jsx15("div", { className: "ed-toolbar-group", children });
5720
6653
  }
5721
6654
  function MarkButton({ title, pressed, onClick, children }) {
5722
- return /* @__PURE__ */ jsx13(
6655
+ return /* @__PURE__ */ jsx15(
5723
6656
  "button",
5724
6657
  {
5725
6658
  type: "button",
@@ -5736,6 +6669,7 @@ export {
5736
6669
  A4_PAGE,
5737
6670
  Editor,
5738
6671
  EditorProvider,
6672
+ FormulaDialog,
5739
6673
  ImageCaptionDialog,
5740
6674
  LinkDialog,
5741
6675
  NodeView,