@sofereditor/core 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.d.cts CHANGED
@@ -128,11 +128,24 @@ interface BlockAttrs {
128
128
  /** Only meaningful when `type === "table"`. */
129
129
  cols?: number;
130
130
  /**
131
- * Only meaningful when `type === "table"`. Per-column widths in CSS px.
132
- * Length should equal `cols` when set; missing or short arrays fall back to
133
- * an auto distribution at render time.
131
+ * Only meaningful when `type === "table"`. Per-column widths in
132
+ * **proportion**, summing to 100. Old documents have absolute px and are
133
+ * normalized on read — see `normalizarLarguras`.
134
134
  */
135
135
  colWidths?: number[];
136
+ /**
137
+ * Only meaningful when `type === "table"`. Total table width as a
138
+ * percentage of the available page width. Missing = 100, so existing
139
+ * documents don't change appearance.
140
+ */
141
+ tableWidth?: number;
142
+ /**
143
+ * Only meaningful when `type === "table"`. Per-row height in px, one entry
144
+ * per row. It's a MINIMUM, not fixed — the row grows past it when content
145
+ * needs more room (same posture as `w:trHeight hRule="atLeast"` in DOCX).
146
+ * Missing = rows size to content, exactly like today.
147
+ */
148
+ rowHeights?: number[];
136
149
  /**
137
150
  * Só relevante quando `type === "table"`. Onde as linhas da grade aparecem.
138
151
  * Ausente = "all" — documentos existentes não mudam de aparência.
@@ -274,9 +287,46 @@ interface ImageEmbed {
274
287
  * caption using the paragraph's own alignment.
275
288
  */
276
289
  captionAlign?: "left" | "center" | "right";
290
+ /**
291
+ * Presente ⇒ este embed é uma fórmula. A imagem (`src`) é o RENDER; isto é
292
+ * a fonte. Reabrir o editor de fórmula relê `latex` daqui.
293
+ */
294
+ formula?: {
295
+ /** Fonte da verdade. O que o professor escreveu. */
296
+ latex: string;
297
+ /**
298
+ * Bloco (`\displaystyle`, centrado) em vez de inline. Guardado explícito
299
+ * em vez de derivado de `align === "center"`: derivar acoplaria o modo da
300
+ * fórmula a um campo de layout que o usuário mexe pelos botões de
301
+ * alinhamento da toolbar, e reabrir o modal cairia no modo errado.
302
+ */
303
+ display: boolean;
304
+ /**
305
+ * Alinhamento de base para fórmula inline, no formato que o MathJax
306
+ * devolve (ex.: "-0.781ex"). Aplicado como `vertical-align` no WRAPPER
307
+ * (<figure>) para a fórmula sentar na linha do texto. Ausente quando
308
+ * `display` é true.
309
+ */
310
+ vAlign?: string;
311
+ };
312
+ /**
313
+ * PNG data URL. Só o `export-docx` consome: o `ImageRun` de `type: "svg"`
314
+ * exige um `fallback` raster. Vale para qualquer embed cujo `src` seja SVG —
315
+ * não é exclusivo de fórmula.
316
+ */
317
+ svgFallback?: string;
277
318
  }
278
319
  type InsertContent = string | ImageEmbed;
279
320
  declare function isImageEmbed(v: unknown): v is ImageEmbed;
321
+ /**
322
+ * Um embed de fórmula é um embed de IMAGEM que carrega seu LaTeX. Só dois
323
+ * lugares precisam distinguir: a toolbar (trocar "Legenda" por "Editar
324
+ * fórmula") e o duplo clique que reabre o modal. Todo o resto trata como
325
+ * imagem, de propósito.
326
+ */
327
+ declare function isFormulaEmbed(v: unknown): v is ImageEmbed & {
328
+ formula: NonNullable<ImageEmbed["formula"]>;
329
+ };
280
330
  interface DeltaOp {
281
331
  insert: InsertContent;
282
332
  attributes?: MarkAttrs;
@@ -648,6 +698,22 @@ declare function insertText(ctx: CommandContext, text: string, marks?: Partial<R
648
698
  declare function insertParagraph(ctx: CommandContext): void;
649
699
  declare function deleteBackward(ctx: CommandContext): void;
650
700
  declare function deleteForward(ctx: CommandContext): void;
701
+ /**
702
+ * `true` quando o caractere imediatamente ANTES (`"backward"`) ou DEPOIS
703
+ * (`"forward"`) da posição atual do caret é um embed de imagem. Só considera
704
+ * seleção COLAPSADA — o caso de um embed inteiro SELECIONADO (range de 1
705
+ * caractere) é responsabilidade de `getSelectedEmbed` no chamador.
706
+ *
707
+ * Existe para `Editor.tsx` decidir se Backspace/Delete precisam ser tratados
708
+ * no `onKeyDown` em vez de esperar `beforeinput`: o `<figure>` de um embed é
709
+ * `contenteditable=false`, e quando a seleção do DOM pousa nele (o que
710
+ * acontece depois de inserir ou clicar numa imagem) o navegador NUNCA
711
+ * dispara `beforeinput` — o caminho normal de delete fica mudo. Resolvido
712
+ * consultando o MODELO em vez de inspecionar a seleção do DOM (que pode
713
+ * pousar de formas diferentes por navegador): determinístico e testável sem
714
+ * montar um editor real.
715
+ */
716
+ declare function isEmbedAdjacentToCaret(ctx: CommandContext, direction: "backward" | "forward"): boolean;
651
717
  /**
652
718
  * Toggle a mark across the current selection.
653
719
  * - If the selection is collapsed: no-op (the UI layer handles pendingMarks).
@@ -860,8 +926,81 @@ declare function mergeDown(ctx: CommandContext, blockIndex: number, row: number,
860
926
  * something was split. No-op (returns false) on a cell with no spans.
861
927
  */
862
928
  declare function splitCell(ctx: CommandContext, blockIndex: number, row: number, col: number): boolean;
863
- /** Set the width (in CSS px) of a specific column. Auto-initializes `colWidths`. */
864
- declare function setColumnWidth(ctx: CommandContext, blockIndex: number, col: number, widthPx: number): void;
929
+ /**
930
+ * Devolve proporções somando 100.
931
+ *
932
+ * Documento antigo grava px absoluto. Como a tabela sempre renderiza em
933
+ * `width: 100%`, esses px já eram interpretados PROPORCIONALMENTE pelo
934
+ * navegador — então normalizar preserva exatamente o que o professor via.
935
+ * Não é conversão destrutiva.
936
+ *
937
+ * A heurística "a soma já é ~100, então já é proporção" é segura na prática:
938
+ * para uma tabela em px cair nela, a média por coluna teria que ser 100/n —
939
+ * 25 px cada em 4 colunas. O código antigo gravava largura renderizada, que
940
+ * numa A4 dá ~150 px por coluna. É heurística, não prova.
941
+ */
942
+ declare function normalizarLarguras(widths: number[] | undefined, cols: number): number[];
943
+ /**
944
+ * Move a divisa `boundary` (entre a coluna `boundary` e a `boundary+1`) por
945
+ * `deltaPct` pontos percentuais: uma cresce, a vizinha encolhe na mesma
946
+ * medida. A soma permanece 100 **por construção**, não por normalização —
947
+ * é o que faz o arrasto colar no cursor.
948
+ *
949
+ * A divisa da última coluna não tem vizinha: ela é a borda direita da tabela
950
+ * e é tratada por `setTableWidth`, não aqui.
951
+ */
952
+ declare function setColumnBoundary(ctx: CommandContext, blockIndex: number, boundary: number, deltaPct: number,
953
+ /**
954
+ * Proporções do início do arrasto. O overlay lê o modelo UMA vez no
955
+ * `pointerdown` e passa aqui a cada movimento, para o delta ser sempre
956
+ * relativo àquele instante. Sem a base, cada `pointermove` aplicaria o
957
+ * delta sobre o resultado do anterior e o arrasto aceleraria.
958
+ */
959
+ base?: number[]): void;
960
+ /** Piso arbitrário e assumido: com `table-layout: fixed` a tabela encolhe
961
+ * abaixo do conteúdo sem resistência, então não há "mínimo do conteúdo"
962
+ * para ancorar. */
963
+ declare const MIN_TABELA_PCT = 20;
964
+ /** Teto: a tabela não passa da margem da página. */
965
+ declare const MAX_TABELA_PCT = 100;
966
+ /**
967
+ * Trava `pct` na faixa válida. Exportada porque o import de DOCX precisa da
968
+ * MESMA faixa que o comando: o Word aceita largura de tabela maior que a
969
+ * página, e sem esta trava um `w:tblW` de 7500 pct entrava como 150% — a
970
+ * tabela transbordava a área imprimível e a última coluna sumia do papel.
971
+ */
972
+ declare function travaLarguraTabela(pct: number): number;
973
+ /**
974
+ * Define a largura total da tabela como percentual da largura útil da
975
+ * página. Ausente = 100 (comportamento de hoje).
976
+ */
977
+ declare function setTableWidth(ctx: CommandContext, blockIndex: number, pct: number): void;
978
+ /** Piso de altura de linha, em px. Abaixo disso a linha some da tela. */
979
+ declare const MIN_LINHA_PX = 16;
980
+ /**
981
+ * Grava a altura de UMA linha, em px. É um MÍNIMO, não fixo — o CSS trata
982
+ * `height` do `<tr>` como piso, e conteúdo maior empurra a linha para além
983
+ * do valor gravado (mesma postura de `w:trHeight hRule="atLeast"` no DOCX).
984
+ *
985
+ * Só materializa `rowHeights` quando o array já existe ou está sendo
986
+ * inicializado aqui — uma tabela nunca redimensionada não ganha o atributo
987
+ * à toa, senão suas linhas parariam de crescer livremente com o conteúdo.
988
+ */
989
+ declare function setRowHeight(ctx: CommandContext, blockIndex: number, row: number, px: number): void;
990
+ /**
991
+ * Grava o array de alturas INTEIRO numa única transação — para o arrasto da
992
+ * Task 5 escrever, a cada `pointermove`, o resultado final calculado a
993
+ * partir da base do `pointerdown` (mesmo padrão de `setColumnBoundary`),
994
+ * numa chamada só, em vez de N chamadas de `setRowHeight` que teriam que ser
995
+ * reconciliadas depois. NÃO verificado aqui se isso muda o agrupamento de
996
+ * undo do `Y.UndoManager` (que já agrupa por `captureTimeout` independente
997
+ * de quantas transações ocorrem) — só a exportação e a escrita atômica do
998
+ * array são o que este teste cobre.
999
+ *
1000
+ * Cada altura passa pelo mesmo piso de `setRowHeight`. O array precisa
1001
+ * cobrir todas as linhas da tabela; tamanho errado é rejeitado (no-op).
1002
+ */
1003
+ declare function setRowHeights(ctx: CommandContext, blockIndex: number, heights: number[]): void;
865
1004
  /**
866
1005
  * Move the caret to the next cell of the same table (Tab). At the last cell of
867
1006
  * the last row, creates a new row and lands in its first cell. Returns true
@@ -916,4 +1055,4 @@ declare class EditorHistory {
916
1055
  destroy(): void;
917
1056
  }
918
1057
 
919
- export { ANSWER_LINE_COLOR, ANSWER_LINE_MAX, type AlignValue, type AnswerLineSpacing, BLANK_MIN_RUN, BLANK_STYLE, BLOCK_SPECS, type BlockAttrs, type BlockType, CLEAR_ALL_MARKS, COMMAND_ORIGIN, type CellAttrs, type CellBorderColors, type CellBorderPos, type ClipboardSlice, type CommandContext, type CommentAttr, DEFAULT_PAGE_SETTINGS, type DeltaOp, type DirValue, EditorDocument, EditorHistory, type EmbedLoc, type EncodedCursor, type EncodedCursorPoint, type ImageEmbed, type ImageLayout, type InsertContent, type LegacySerializedDocument, type LinkAttr, type ListKind, type ListStyleType, MARGIN_PRESETS, MAX_LIST_LEVEL, type MarkAttrs, type MarkName, type MarkValue, PAGE_PRESETS, type PagePreset, type PageSettings, type Position, SOFER_MIME, type Selection, type SerializedBlock, type SerializedCell, type SerializedDocument, type StyleRecord, TABLE_BORDER_COLOR, TABLE_GUIDE_COLOR, TRACKED_ORIGINS, type TableBorderPreset, type TableLocation, type TableRect, answerLineStyle, cellBorderColors, cellBorderStyle, cellsInRect, clearRectCells, collapsedSelection, comparePositions, createBlock, createCell, createTableBlock, decodeSelection, dedentList, defaultAttrsFor, deleteBackward, deleteForward, deleteSelection, deleteTable, deleteTableColumn, deleteTableRow, deltaLength, detectPreset, encodeSelection, expandTableRect, getBlockId, getMarksInRange, indentList, insertAnswerLines, insertImage, insertParagraph, insertSlice, insertTable, insertTableColumn, insertTableRow, insertText, isCollapsed, isCommandOrigin, isImageEmbed, isKnownBlockType, isLegacySerializedDocument, isMarkUniformInRange, mergeDown, mergeRight, mergeSelection, mmToPx, moveEmbedAnchor, moveToNextCell, moveToPrevCell, newBlockId, orderedRange, positionsEqual, pxToMm, removeMark, sameTextRun, serializeSelection, setBlockAttr, setBlockAttrAtIndex, setBlockType, setCellAttr, setColumnWidth, setImageAttrs, setMark, sliceDelta, sliceToInlineDelta, sliceToText, spanOf, splitCell, splitListItem, splitUnderscoreRuns, styleToCssText, tableLocationOf, tableRectSelection, toggleList, toggleMark, withDefaults };
1058
+ export { ANSWER_LINE_COLOR, ANSWER_LINE_MAX, type AlignValue, type AnswerLineSpacing, BLANK_MIN_RUN, BLANK_STYLE, BLOCK_SPECS, type BlockAttrs, type BlockType, CLEAR_ALL_MARKS, COMMAND_ORIGIN, type CellAttrs, type CellBorderColors, type CellBorderPos, type ClipboardSlice, type CommandContext, type CommentAttr, DEFAULT_PAGE_SETTINGS, type DeltaOp, type DirValue, EditorDocument, EditorHistory, type EmbedLoc, type EncodedCursor, type EncodedCursorPoint, type ImageEmbed, type ImageLayout, type InsertContent, type LegacySerializedDocument, type LinkAttr, type ListKind, type ListStyleType, MARGIN_PRESETS, MAX_LIST_LEVEL, MAX_TABELA_PCT, MIN_LINHA_PX, MIN_TABELA_PCT, type MarkAttrs, type MarkName, type MarkValue, PAGE_PRESETS, type PagePreset, type PageSettings, type Position, SOFER_MIME, type Selection, type SerializedBlock, type SerializedCell, type SerializedDocument, type StyleRecord, TABLE_BORDER_COLOR, TABLE_GUIDE_COLOR, TRACKED_ORIGINS, type TableBorderPreset, type TableLocation, type TableRect, answerLineStyle, cellBorderColors, cellBorderStyle, cellsInRect, clearRectCells, collapsedSelection, comparePositions, createBlock, createCell, createTableBlock, decodeSelection, dedentList, defaultAttrsFor, deleteBackward, deleteForward, deleteSelection, deleteTable, deleteTableColumn, deleteTableRow, deltaLength, detectPreset, encodeSelection, expandTableRect, getBlockId, getMarksInRange, indentList, insertAnswerLines, insertImage, insertParagraph, insertSlice, insertTable, insertTableColumn, insertTableRow, insertText, isCollapsed, isCommandOrigin, isEmbedAdjacentToCaret, isFormulaEmbed, isImageEmbed, isKnownBlockType, isLegacySerializedDocument, isMarkUniformInRange, mergeDown, mergeRight, mergeSelection, mmToPx, moveEmbedAnchor, moveToNextCell, moveToPrevCell, newBlockId, normalizarLarguras, orderedRange, positionsEqual, pxToMm, removeMark, sameTextRun, serializeSelection, setBlockAttr, setBlockAttrAtIndex, setBlockType, setCellAttr, setColumnBoundary, setImageAttrs, setMark, setRowHeight, setRowHeights, setTableWidth, sliceDelta, sliceToInlineDelta, sliceToText, spanOf, splitCell, splitListItem, splitUnderscoreRuns, styleToCssText, tableLocationOf, tableRectSelection, toggleList, toggleMark, travaLarguraTabela, withDefaults };
package/dist/index.d.ts CHANGED
@@ -128,11 +128,24 @@ interface BlockAttrs {
128
128
  /** Only meaningful when `type === "table"`. */
129
129
  cols?: number;
130
130
  /**
131
- * Only meaningful when `type === "table"`. Per-column widths in CSS px.
132
- * Length should equal `cols` when set; missing or short arrays fall back to
133
- * an auto distribution at render time.
131
+ * Only meaningful when `type === "table"`. Per-column widths in
132
+ * **proportion**, summing to 100. Old documents have absolute px and are
133
+ * normalized on read — see `normalizarLarguras`.
134
134
  */
135
135
  colWidths?: number[];
136
+ /**
137
+ * Only meaningful when `type === "table"`. Total table width as a
138
+ * percentage of the available page width. Missing = 100, so existing
139
+ * documents don't change appearance.
140
+ */
141
+ tableWidth?: number;
142
+ /**
143
+ * Only meaningful when `type === "table"`. Per-row height in px, one entry
144
+ * per row. It's a MINIMUM, not fixed — the row grows past it when content
145
+ * needs more room (same posture as `w:trHeight hRule="atLeast"` in DOCX).
146
+ * Missing = rows size to content, exactly like today.
147
+ */
148
+ rowHeights?: number[];
136
149
  /**
137
150
  * Só relevante quando `type === "table"`. Onde as linhas da grade aparecem.
138
151
  * Ausente = "all" — documentos existentes não mudam de aparência.
@@ -274,9 +287,46 @@ interface ImageEmbed {
274
287
  * caption using the paragraph's own alignment.
275
288
  */
276
289
  captionAlign?: "left" | "center" | "right";
290
+ /**
291
+ * Presente ⇒ este embed é uma fórmula. A imagem (`src`) é o RENDER; isto é
292
+ * a fonte. Reabrir o editor de fórmula relê `latex` daqui.
293
+ */
294
+ formula?: {
295
+ /** Fonte da verdade. O que o professor escreveu. */
296
+ latex: string;
297
+ /**
298
+ * Bloco (`\displaystyle`, centrado) em vez de inline. Guardado explícito
299
+ * em vez de derivado de `align === "center"`: derivar acoplaria o modo da
300
+ * fórmula a um campo de layout que o usuário mexe pelos botões de
301
+ * alinhamento da toolbar, e reabrir o modal cairia no modo errado.
302
+ */
303
+ display: boolean;
304
+ /**
305
+ * Alinhamento de base para fórmula inline, no formato que o MathJax
306
+ * devolve (ex.: "-0.781ex"). Aplicado como `vertical-align` no WRAPPER
307
+ * (<figure>) para a fórmula sentar na linha do texto. Ausente quando
308
+ * `display` é true.
309
+ */
310
+ vAlign?: string;
311
+ };
312
+ /**
313
+ * PNG data URL. Só o `export-docx` consome: o `ImageRun` de `type: "svg"`
314
+ * exige um `fallback` raster. Vale para qualquer embed cujo `src` seja SVG —
315
+ * não é exclusivo de fórmula.
316
+ */
317
+ svgFallback?: string;
277
318
  }
278
319
  type InsertContent = string | ImageEmbed;
279
320
  declare function isImageEmbed(v: unknown): v is ImageEmbed;
321
+ /**
322
+ * Um embed de fórmula é um embed de IMAGEM que carrega seu LaTeX. Só dois
323
+ * lugares precisam distinguir: a toolbar (trocar "Legenda" por "Editar
324
+ * fórmula") e o duplo clique que reabre o modal. Todo o resto trata como
325
+ * imagem, de propósito.
326
+ */
327
+ declare function isFormulaEmbed(v: unknown): v is ImageEmbed & {
328
+ formula: NonNullable<ImageEmbed["formula"]>;
329
+ };
280
330
  interface DeltaOp {
281
331
  insert: InsertContent;
282
332
  attributes?: MarkAttrs;
@@ -648,6 +698,22 @@ declare function insertText(ctx: CommandContext, text: string, marks?: Partial<R
648
698
  declare function insertParagraph(ctx: CommandContext): void;
649
699
  declare function deleteBackward(ctx: CommandContext): void;
650
700
  declare function deleteForward(ctx: CommandContext): void;
701
+ /**
702
+ * `true` quando o caractere imediatamente ANTES (`"backward"`) ou DEPOIS
703
+ * (`"forward"`) da posição atual do caret é um embed de imagem. Só considera
704
+ * seleção COLAPSADA — o caso de um embed inteiro SELECIONADO (range de 1
705
+ * caractere) é responsabilidade de `getSelectedEmbed` no chamador.
706
+ *
707
+ * Existe para `Editor.tsx` decidir se Backspace/Delete precisam ser tratados
708
+ * no `onKeyDown` em vez de esperar `beforeinput`: o `<figure>` de um embed é
709
+ * `contenteditable=false`, e quando a seleção do DOM pousa nele (o que
710
+ * acontece depois de inserir ou clicar numa imagem) o navegador NUNCA
711
+ * dispara `beforeinput` — o caminho normal de delete fica mudo. Resolvido
712
+ * consultando o MODELO em vez de inspecionar a seleção do DOM (que pode
713
+ * pousar de formas diferentes por navegador): determinístico e testável sem
714
+ * montar um editor real.
715
+ */
716
+ declare function isEmbedAdjacentToCaret(ctx: CommandContext, direction: "backward" | "forward"): boolean;
651
717
  /**
652
718
  * Toggle a mark across the current selection.
653
719
  * - If the selection is collapsed: no-op (the UI layer handles pendingMarks).
@@ -860,8 +926,81 @@ declare function mergeDown(ctx: CommandContext, blockIndex: number, row: number,
860
926
  * something was split. No-op (returns false) on a cell with no spans.
861
927
  */
862
928
  declare function splitCell(ctx: CommandContext, blockIndex: number, row: number, col: number): boolean;
863
- /** Set the width (in CSS px) of a specific column. Auto-initializes `colWidths`. */
864
- declare function setColumnWidth(ctx: CommandContext, blockIndex: number, col: number, widthPx: number): void;
929
+ /**
930
+ * Devolve proporções somando 100.
931
+ *
932
+ * Documento antigo grava px absoluto. Como a tabela sempre renderiza em
933
+ * `width: 100%`, esses px já eram interpretados PROPORCIONALMENTE pelo
934
+ * navegador — então normalizar preserva exatamente o que o professor via.
935
+ * Não é conversão destrutiva.
936
+ *
937
+ * A heurística "a soma já é ~100, então já é proporção" é segura na prática:
938
+ * para uma tabela em px cair nela, a média por coluna teria que ser 100/n —
939
+ * 25 px cada em 4 colunas. O código antigo gravava largura renderizada, que
940
+ * numa A4 dá ~150 px por coluna. É heurística, não prova.
941
+ */
942
+ declare function normalizarLarguras(widths: number[] | undefined, cols: number): number[];
943
+ /**
944
+ * Move a divisa `boundary` (entre a coluna `boundary` e a `boundary+1`) por
945
+ * `deltaPct` pontos percentuais: uma cresce, a vizinha encolhe na mesma
946
+ * medida. A soma permanece 100 **por construção**, não por normalização —
947
+ * é o que faz o arrasto colar no cursor.
948
+ *
949
+ * A divisa da última coluna não tem vizinha: ela é a borda direita da tabela
950
+ * e é tratada por `setTableWidth`, não aqui.
951
+ */
952
+ declare function setColumnBoundary(ctx: CommandContext, blockIndex: number, boundary: number, deltaPct: number,
953
+ /**
954
+ * Proporções do início do arrasto. O overlay lê o modelo UMA vez no
955
+ * `pointerdown` e passa aqui a cada movimento, para o delta ser sempre
956
+ * relativo àquele instante. Sem a base, cada `pointermove` aplicaria o
957
+ * delta sobre o resultado do anterior e o arrasto aceleraria.
958
+ */
959
+ base?: number[]): void;
960
+ /** Piso arbitrário e assumido: com `table-layout: fixed` a tabela encolhe
961
+ * abaixo do conteúdo sem resistência, então não há "mínimo do conteúdo"
962
+ * para ancorar. */
963
+ declare const MIN_TABELA_PCT = 20;
964
+ /** Teto: a tabela não passa da margem da página. */
965
+ declare const MAX_TABELA_PCT = 100;
966
+ /**
967
+ * Trava `pct` na faixa válida. Exportada porque o import de DOCX precisa da
968
+ * MESMA faixa que o comando: o Word aceita largura de tabela maior que a
969
+ * página, e sem esta trava um `w:tblW` de 7500 pct entrava como 150% — a
970
+ * tabela transbordava a área imprimível e a última coluna sumia do papel.
971
+ */
972
+ declare function travaLarguraTabela(pct: number): number;
973
+ /**
974
+ * Define a largura total da tabela como percentual da largura útil da
975
+ * página. Ausente = 100 (comportamento de hoje).
976
+ */
977
+ declare function setTableWidth(ctx: CommandContext, blockIndex: number, pct: number): void;
978
+ /** Piso de altura de linha, em px. Abaixo disso a linha some da tela. */
979
+ declare const MIN_LINHA_PX = 16;
980
+ /**
981
+ * Grava a altura de UMA linha, em px. É um MÍNIMO, não fixo — o CSS trata
982
+ * `height` do `<tr>` como piso, e conteúdo maior empurra a linha para além
983
+ * do valor gravado (mesma postura de `w:trHeight hRule="atLeast"` no DOCX).
984
+ *
985
+ * Só materializa `rowHeights` quando o array já existe ou está sendo
986
+ * inicializado aqui — uma tabela nunca redimensionada não ganha o atributo
987
+ * à toa, senão suas linhas parariam de crescer livremente com o conteúdo.
988
+ */
989
+ declare function setRowHeight(ctx: CommandContext, blockIndex: number, row: number, px: number): void;
990
+ /**
991
+ * Grava o array de alturas INTEIRO numa única transação — para o arrasto da
992
+ * Task 5 escrever, a cada `pointermove`, o resultado final calculado a
993
+ * partir da base do `pointerdown` (mesmo padrão de `setColumnBoundary`),
994
+ * numa chamada só, em vez de N chamadas de `setRowHeight` que teriam que ser
995
+ * reconciliadas depois. NÃO verificado aqui se isso muda o agrupamento de
996
+ * undo do `Y.UndoManager` (que já agrupa por `captureTimeout` independente
997
+ * de quantas transações ocorrem) — só a exportação e a escrita atômica do
998
+ * array são o que este teste cobre.
999
+ *
1000
+ * Cada altura passa pelo mesmo piso de `setRowHeight`. O array precisa
1001
+ * cobrir todas as linhas da tabela; tamanho errado é rejeitado (no-op).
1002
+ */
1003
+ declare function setRowHeights(ctx: CommandContext, blockIndex: number, heights: number[]): void;
865
1004
  /**
866
1005
  * Move the caret to the next cell of the same table (Tab). At the last cell of
867
1006
  * the last row, creates a new row and lands in its first cell. Returns true
@@ -916,4 +1055,4 @@ declare class EditorHistory {
916
1055
  destroy(): void;
917
1056
  }
918
1057
 
919
- export { ANSWER_LINE_COLOR, ANSWER_LINE_MAX, type AlignValue, type AnswerLineSpacing, BLANK_MIN_RUN, BLANK_STYLE, BLOCK_SPECS, type BlockAttrs, type BlockType, CLEAR_ALL_MARKS, COMMAND_ORIGIN, type CellAttrs, type CellBorderColors, type CellBorderPos, type ClipboardSlice, type CommandContext, type CommentAttr, DEFAULT_PAGE_SETTINGS, type DeltaOp, type DirValue, EditorDocument, EditorHistory, type EmbedLoc, type EncodedCursor, type EncodedCursorPoint, type ImageEmbed, type ImageLayout, type InsertContent, type LegacySerializedDocument, type LinkAttr, type ListKind, type ListStyleType, MARGIN_PRESETS, MAX_LIST_LEVEL, type MarkAttrs, type MarkName, type MarkValue, PAGE_PRESETS, type PagePreset, type PageSettings, type Position, SOFER_MIME, type Selection, type SerializedBlock, type SerializedCell, type SerializedDocument, type StyleRecord, TABLE_BORDER_COLOR, TABLE_GUIDE_COLOR, TRACKED_ORIGINS, type TableBorderPreset, type TableLocation, type TableRect, answerLineStyle, cellBorderColors, cellBorderStyle, cellsInRect, clearRectCells, collapsedSelection, comparePositions, createBlock, createCell, createTableBlock, decodeSelection, dedentList, defaultAttrsFor, deleteBackward, deleteForward, deleteSelection, deleteTable, deleteTableColumn, deleteTableRow, deltaLength, detectPreset, encodeSelection, expandTableRect, getBlockId, getMarksInRange, indentList, insertAnswerLines, insertImage, insertParagraph, insertSlice, insertTable, insertTableColumn, insertTableRow, insertText, isCollapsed, isCommandOrigin, isImageEmbed, isKnownBlockType, isLegacySerializedDocument, isMarkUniformInRange, mergeDown, mergeRight, mergeSelection, mmToPx, moveEmbedAnchor, moveToNextCell, moveToPrevCell, newBlockId, orderedRange, positionsEqual, pxToMm, removeMark, sameTextRun, serializeSelection, setBlockAttr, setBlockAttrAtIndex, setBlockType, setCellAttr, setColumnWidth, setImageAttrs, setMark, sliceDelta, sliceToInlineDelta, sliceToText, spanOf, splitCell, splitListItem, splitUnderscoreRuns, styleToCssText, tableLocationOf, tableRectSelection, toggleList, toggleMark, withDefaults };
1058
+ export { ANSWER_LINE_COLOR, ANSWER_LINE_MAX, type AlignValue, type AnswerLineSpacing, BLANK_MIN_RUN, BLANK_STYLE, BLOCK_SPECS, type BlockAttrs, type BlockType, CLEAR_ALL_MARKS, COMMAND_ORIGIN, type CellAttrs, type CellBorderColors, type CellBorderPos, type ClipboardSlice, type CommandContext, type CommentAttr, DEFAULT_PAGE_SETTINGS, type DeltaOp, type DirValue, EditorDocument, EditorHistory, type EmbedLoc, type EncodedCursor, type EncodedCursorPoint, type ImageEmbed, type ImageLayout, type InsertContent, type LegacySerializedDocument, type LinkAttr, type ListKind, type ListStyleType, MARGIN_PRESETS, MAX_LIST_LEVEL, MAX_TABELA_PCT, MIN_LINHA_PX, MIN_TABELA_PCT, type MarkAttrs, type MarkName, type MarkValue, PAGE_PRESETS, type PagePreset, type PageSettings, type Position, SOFER_MIME, type Selection, type SerializedBlock, type SerializedCell, type SerializedDocument, type StyleRecord, TABLE_BORDER_COLOR, TABLE_GUIDE_COLOR, TRACKED_ORIGINS, type TableBorderPreset, type TableLocation, type TableRect, answerLineStyle, cellBorderColors, cellBorderStyle, cellsInRect, clearRectCells, collapsedSelection, comparePositions, createBlock, createCell, createTableBlock, decodeSelection, dedentList, defaultAttrsFor, deleteBackward, deleteForward, deleteSelection, deleteTable, deleteTableColumn, deleteTableRow, deltaLength, detectPreset, encodeSelection, expandTableRect, getBlockId, getMarksInRange, indentList, insertAnswerLines, insertImage, insertParagraph, insertSlice, insertTable, insertTableColumn, insertTableRow, insertText, isCollapsed, isCommandOrigin, isEmbedAdjacentToCaret, isFormulaEmbed, isImageEmbed, isKnownBlockType, isLegacySerializedDocument, isMarkUniformInRange, mergeDown, mergeRight, mergeSelection, mmToPx, moveEmbedAnchor, moveToNextCell, moveToPrevCell, newBlockId, normalizarLarguras, orderedRange, positionsEqual, pxToMm, removeMark, sameTextRun, serializeSelection, setBlockAttr, setBlockAttrAtIndex, setBlockType, setCellAttr, setColumnBoundary, setImageAttrs, setMark, setRowHeight, setRowHeights, setTableWidth, sliceDelta, sliceToInlineDelta, sliceToText, spanOf, splitCell, splitListItem, splitUnderscoreRuns, styleToCssText, tableLocationOf, tableRectSelection, toggleList, toggleMark, travaLarguraTabela, withDefaults };
package/dist/index.js CHANGED
@@ -3,6 +3,9 @@ var MAX_LIST_LEVEL = 5;
3
3
  function isImageEmbed(v) {
4
4
  return typeof v === "object" && v !== null && v.type === "image" && typeof v.src === "string";
5
5
  }
6
+ function isFormulaEmbed(v) {
7
+ return isImageEmbed(v) && v.formula != null;
8
+ }
6
9
  function isLegacySerializedDocument(v) {
7
10
  return Array.isArray(v);
8
11
  }
@@ -1044,6 +1047,24 @@ function deleteForward(ctx) {
1044
1047
  }
1045
1048
  });
1046
1049
  }
1050
+ function isEmbedAdjacentToCaret(ctx, direction) {
1051
+ const sel = ctx.getSelection();
1052
+ if (!isCollapsed(sel)) return false;
1053
+ const pos = sel.focus;
1054
+ const yText = ctx.doc.textAt(pos.blockIndex, pos.cellIndex);
1055
+ if (!yText) return false;
1056
+ const delta = yText.toDelta();
1057
+ let cursor = 0;
1058
+ for (const op of delta) {
1059
+ const len = typeof op.insert === "string" ? op.insert.length : 1;
1060
+ const opStart = cursor;
1061
+ const opEnd = cursor + len;
1062
+ cursor = opEnd;
1063
+ const matches = direction === "backward" ? opEnd === pos.offset : opStart === pos.offset;
1064
+ if (matches) return isImageEmbed(op.insert);
1065
+ }
1066
+ return false;
1067
+ }
1047
1068
  var booleanMarks = /* @__PURE__ */ new Set(["bold", "italic", "underline", "strike"]);
1048
1069
  function toggleMark(ctx, name, value) {
1049
1070
  const sel = ctx.getSelection();
@@ -1535,6 +1556,12 @@ function insertTableRow(ctx, blockIndex, row, where) {
1535
1556
  }
1536
1557
  cells.insert(P * cols, fresh);
1537
1558
  attrs.set("rows", rows + 1);
1559
+ const heights = attrs.get("rowHeights");
1560
+ if (Array.isArray(heights) && heights.length === rows) {
1561
+ const next = heights.slice();
1562
+ next.splice(P, 0, MIN_LINHA_PX);
1563
+ attrs.set("rowHeights", next);
1564
+ }
1538
1565
  const firstReal = newRowCovered.findIndex((v) => !v);
1539
1566
  const targetCellIdx = P * cols + (firstReal < 0 ? 0 : firstReal);
1540
1567
  ctx.setSelection(
@@ -1580,6 +1607,12 @@ function deleteTableRow(ctx, blockIndex, row) {
1580
1607
  }
1581
1608
  cells.delete(R * cols, cols);
1582
1609
  attrs.set("rows", rows - 1);
1610
+ const heights = attrs.get("rowHeights");
1611
+ if (Array.isArray(heights) && heights.length === rows) {
1612
+ const next = heights.slice();
1613
+ next.splice(R, 1);
1614
+ attrs.set("rowHeights", next);
1615
+ }
1583
1616
  for (const p of promotions) {
1584
1617
  const m = ctx.doc.getCellAttrsMap(blockIndex, p.flatNew);
1585
1618
  if (!m) continue;
@@ -1639,9 +1672,12 @@ function insertTableColumn(ctx, blockIndex, col, where) {
1639
1672
  attrs.set("cols", cols + 1);
1640
1673
  const widths = attrs.get("colWidths");
1641
1674
  if (Array.isArray(widths) && widths.length === cols) {
1642
- const next = widths.slice();
1643
- next.splice(C, 0, 100);
1644
- attrs.set("colWidths", next);
1675
+ const base = normalizarLarguras(widths, cols);
1676
+ const media = arredonda(100 / (cols + 1));
1677
+ const escala = cols / (cols + 1);
1678
+ const next = base.map((w) => arredonda(w * escala));
1679
+ next.splice(C, 0, media);
1680
+ attrs.set("colWidths", normalizarLarguras(next, cols + 1));
1645
1681
  }
1646
1682
  const newCols = cols + 1;
1647
1683
  const focusRow = 0;
@@ -1714,7 +1750,7 @@ function deleteTableColumn(ctx, blockIndex, col) {
1714
1750
  if (Array.isArray(widths) && widths.length === cols) {
1715
1751
  const next = widths.slice();
1716
1752
  next.splice(C, 1);
1717
- attrs.set("colWidths", next);
1753
+ attrs.set("colWidths", normalizarLarguras(next, cols - 1));
1718
1754
  }
1719
1755
  const newCol = Math.min(C, newCols - 1);
1720
1756
  const target = findNextRealCell(ctx.doc, blockIndex, newCol, rows * newCols) ?? 0;
@@ -2002,20 +2038,81 @@ function splitCell(ctx, blockIndex, row, col) {
2002
2038
  });
2003
2039
  return true;
2004
2040
  }
2005
- function setColumnWidth(ctx, blockIndex, col, widthPx) {
2041
+ var SOMA_OK = (s) => Math.abs(s - 100) < 0.5;
2042
+ var arredonda = (n) => Math.round(n * 1e3) / 1e3;
2043
+ function normalizarLarguras(widths, cols) {
2044
+ const igual = () => new Array(cols).fill(100 / cols);
2045
+ if (!Array.isArray(widths) || widths.length !== cols || cols <= 0) {
2046
+ return igual();
2047
+ }
2048
+ if (!widths.every((w) => typeof w === "number" && isFinite(w) && w > 0)) {
2049
+ return igual();
2050
+ }
2051
+ const soma = widths.reduce((a, b) => a + b, 0);
2052
+ if (SOMA_OK(soma)) return widths.slice();
2053
+ return widths.map((w) => arredonda(w / soma * 100));
2054
+ }
2055
+ var MIN_COLUNA_PCT = 3;
2056
+ function setColumnBoundary(ctx, blockIndex, boundary, deltaPct, base) {
2006
2057
  if (!ctx.doc.isTable(blockIndex)) return;
2007
2058
  const { cols } = ctx.doc.getTableSize(blockIndex);
2008
- if (col < 0 || col >= cols) return;
2009
- const w = Math.max(20, Math.round(widthPx));
2059
+ if (boundary < 0 || boundary >= cols - 1) return;
2010
2060
  transact(ctx.doc, () => {
2011
2061
  const attrsMap = ctx.doc.getBlockAttrsMap(blockIndex);
2012
2062
  if (!attrsMap) return;
2013
- const current = attrsMap.get("colWidths");
2014
- const next = Array.isArray(current) && current.length === cols ? current.slice() : new Array(cols).fill(120);
2015
- next[col] = w;
2063
+ const larguras = normalizarLarguras(
2064
+ base ?? attrsMap.get("colWidths"),
2065
+ cols
2066
+ );
2067
+ const a = larguras[boundary];
2068
+ const b = larguras[boundary + 1];
2069
+ const piso = Math.min(MIN_COLUNA_PCT, (a + b) / 2);
2070
+ const d = Math.max(piso - a, Math.min(deltaPct, b - piso));
2071
+ const next = larguras.slice();
2072
+ next[boundary] = arredonda(a + d);
2073
+ next[boundary + 1] = arredonda(b - d);
2016
2074
  attrsMap.set("colWidths", next);
2017
2075
  });
2018
2076
  }
2077
+ var MIN_TABELA_PCT = 20;
2078
+ var MAX_TABELA_PCT = 100;
2079
+ function travaLarguraTabela(pct) {
2080
+ return Math.max(MIN_TABELA_PCT, Math.min(MAX_TABELA_PCT, pct));
2081
+ }
2082
+ function setTableWidth(ctx, blockIndex, pct) {
2083
+ if (!ctx.doc.isTable(blockIndex)) return;
2084
+ const v = travaLarguraTabela(arredonda(pct));
2085
+ transact(ctx.doc, () => {
2086
+ ctx.doc.getBlockAttrsMap(blockIndex)?.set("tableWidth", v);
2087
+ });
2088
+ }
2089
+ var MIN_LINHA_PX = 16;
2090
+ function setRowHeight(ctx, blockIndex, row, px) {
2091
+ if (!ctx.doc.isTable(blockIndex)) return;
2092
+ const { rows } = ctx.doc.getTableSize(blockIndex);
2093
+ if (row < 0 || row >= rows) return;
2094
+ transact(ctx.doc, () => {
2095
+ const attrsMap = ctx.doc.getBlockAttrsMap(blockIndex);
2096
+ if (!attrsMap) return;
2097
+ const atual = attrsMap.get("rowHeights");
2098
+ const base = Array.isArray(atual) && atual.length === rows ? atual.slice() : new Array(rows).fill(MIN_LINHA_PX);
2099
+ base[row] = Math.max(MIN_LINHA_PX, Math.round(px));
2100
+ attrsMap.set("rowHeights", base);
2101
+ });
2102
+ }
2103
+ function setRowHeights(ctx, blockIndex, heights) {
2104
+ if (!ctx.doc.isTable(blockIndex)) return;
2105
+ const { rows } = ctx.doc.getTableSize(blockIndex);
2106
+ if (heights.length !== rows) return;
2107
+ transact(ctx.doc, () => {
2108
+ const attrsMap = ctx.doc.getBlockAttrsMap(blockIndex);
2109
+ if (!attrsMap) return;
2110
+ attrsMap.set(
2111
+ "rowHeights",
2112
+ heights.map((h) => Math.max(MIN_LINHA_PX, Math.round(h)))
2113
+ );
2114
+ });
2115
+ }
2019
2116
  function moveToNextCell(ctx) {
2020
2117
  const sel = ctx.getSelection();
2021
2118
  const loc = tableLocationOf(ctx.doc, sel.focus);
@@ -2207,6 +2304,9 @@ export {
2207
2304
  EditorHistory,
2208
2305
  MARGIN_PRESETS,
2209
2306
  MAX_LIST_LEVEL,
2307
+ MAX_TABELA_PCT,
2308
+ MIN_LINHA_PX,
2309
+ MIN_TABELA_PCT,
2210
2310
  PAGE_PRESETS,
2211
2311
  SOFER_MIME,
2212
2312
  TABLE_BORDER_COLOR,
@@ -2249,6 +2349,8 @@ export {
2249
2349
  insertText,
2250
2350
  isCollapsed,
2251
2351
  isCommandOrigin,
2352
+ isEmbedAdjacentToCaret,
2353
+ isFormulaEmbed,
2252
2354
  isImageEmbed,
2253
2355
  isKnownBlockType,
2254
2356
  isLegacySerializedDocument,
@@ -2261,6 +2363,7 @@ export {
2261
2363
  moveToNextCell,
2262
2364
  moveToPrevCell,
2263
2365
  newBlockId,
2366
+ normalizarLarguras,
2264
2367
  orderedRange,
2265
2368
  positionsEqual,
2266
2369
  pxToMm,
@@ -2271,9 +2374,12 @@ export {
2271
2374
  setBlockAttrAtIndex,
2272
2375
  setBlockType,
2273
2376
  setCellAttr,
2274
- setColumnWidth,
2377
+ setColumnBoundary,
2275
2378
  setImageAttrs,
2276
2379
  setMark,
2380
+ setRowHeight,
2381
+ setRowHeights,
2382
+ setTableWidth,
2277
2383
  sliceDelta,
2278
2384
  sliceToInlineDelta,
2279
2385
  sliceToText,
@@ -2286,6 +2392,7 @@ export {
2286
2392
  tableRectSelection,
2287
2393
  toggleList,
2288
2394
  toggleMark,
2395
+ travaLarguraTabela,
2289
2396
  withDefaults
2290
2397
  };
2291
2398
  //# sourceMappingURL=index.js.map