@stll/folio-core 0.53.0 → 0.54.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.
Files changed (148) hide show
  1. package/dist/ai-edits/apply.js +390 -166
  2. package/dist/ai-edits/batch-claims.d.ts +21 -6
  3. package/dist/ai-edits/batch-claims.js +47 -16
  4. package/dist/ai-edits/headless.d.ts +20 -0
  5. package/dist/ai-edits/headless.js +140 -11
  6. package/dist/ai-edits/pending-suggestions.js +1 -1
  7. package/dist/ai-edits/read.d.ts +5 -3
  8. package/dist/ai-edits/read.js +97 -47
  9. package/dist/ai-edits/revisionStretches.d.ts +17 -0
  10. package/dist/ai-edits/revisionStretches.js +80 -0
  11. package/dist/ai-edits/snapshot.js +3 -2
  12. package/dist/ai-edits/table-cell-mutations.d.ts +3 -1
  13. package/dist/ai-edits/table-cell-mutations.js +2 -1
  14. package/dist/ai-edits/table-row-column-mutations.d.ts +6 -1
  15. package/dist/ai-edits/table-row-column-mutations.js +27 -10
  16. package/dist/compare/content-alignment.d.ts +5 -1
  17. package/dist/compare/content-alignment.js +39 -32
  18. package/dist/compare/inline-provenance.js +1 -1
  19. package/dist/controller/layoutPipeline.js +3 -2
  20. package/dist/docx/archiveInflation.d.ts +127 -0
  21. package/dist/docx/archiveInflation.js +181 -0
  22. package/dist/docx/metadataPrivacy.js +24 -1
  23. package/dist/docx/noteReferenceMark.d.ts +15 -0
  24. package/dist/docx/noteReferenceMark.js +48 -0
  25. package/dist/docx/paragraphParser.js +5 -3
  26. package/dist/docx/paragraphPropertySource.d.ts +1 -1
  27. package/dist/docx/paragraphPropertySource.js +3 -1
  28. package/dist/docx/parser.js +2 -2
  29. package/dist/docx/rezip.js +4 -2
  30. package/dist/docx/selectiveXmlPatch.d.ts +3 -1
  31. package/dist/docx/selectiveXmlPatch.js +81 -3
  32. package/dist/docx/server/boundedArchive.d.ts +8 -13
  33. package/dist/docx/server/boundedArchive.js +70 -59
  34. package/dist/docx/server/extractDocxText.d.ts +8 -2
  35. package/dist/docx/server/extractDocxText.js +2 -2
  36. package/dist/docx/server/validateDocxConformance.d.ts +1 -1
  37. package/dist/docx/server/validateDocxConformance.js +1 -0
  38. package/dist/docx/unzip.d.ts +19 -2
  39. package/dist/docx/unzip.js +135 -24
  40. package/dist/fonts/embeddedFonts.js +1 -1
  41. package/dist/i18n/messages/catalogs.gen.d.ts +34 -34
  42. package/dist/i18n/messages/catalogs.gen.js +34 -34
  43. package/dist/i18n/messages/messages.gen.d.ts +2 -2
  44. package/dist/internal/wholeStoryRevisionResolution.js +95 -25
  45. package/dist/layout-engine/measure/measureParagraph.js +2 -2
  46. package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +12 -1
  47. package/dist/layout-engine/measure/paragraphMeasureShared.js +14 -1
  48. package/dist/markdown/escape.d.ts +2 -2
  49. package/dist/markdown/escape.js +4 -5
  50. package/dist/markdown/renderBlock.js +91 -25
  51. package/dist/markdown/renderRuns.d.ts +10 -1
  52. package/dist/markdown/renderRuns.js +320 -59
  53. package/dist/prosemirror/clearRunColor.d.ts +6 -0
  54. package/dist/prosemirror/clearRunColor.js +46 -0
  55. package/dist/prosemirror/commands/comments.js +56 -21
  56. package/dist/prosemirror/commands/formatPainter.js +1 -1
  57. package/dist/prosemirror/commands/hyperlink.js +2 -1
  58. package/dist/prosemirror/commands/propertyChangeScope.d.ts +3 -2
  59. package/dist/prosemirror/commands/propertyChangeScope.js +24 -3
  60. package/dist/prosemirror/commands/resolveAllTableChanges.js +46 -41
  61. package/dist/prosemirror/commands/resolveParagraphProperties.js +20 -6
  62. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -1
  63. package/dist/prosemirror/commands/tableCellMergeResolution.js +23 -7
  64. package/dist/prosemirror/containerFinalParagraph.d.ts +11 -5
  65. package/dist/prosemirror/containerFinalParagraph.js +7 -6
  66. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
  67. package/dist/prosemirror/conversion/fromProseDoc.js +12 -590
  68. package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -11
  69. package/dist/prosemirror/conversion/toProseDoc.js +31 -150
  70. package/dist/prosemirror/documentSchema.d.ts +8 -0
  71. package/dist/prosemirror/documentSchema.js +18 -0
  72. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  73. package/dist/prosemirror/extensions/core/ParagraphExtension.js +104 -63
  74. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -1
  75. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +24 -3
  76. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.d.ts +20 -0
  77. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +43 -0
  78. package/dist/prosemirror/extensions/features/ListExtension.js +10 -9
  79. package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +1 -1
  80. package/dist/prosemirror/extensions/features/pastedHtmlLists.js +2 -4
  81. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +14 -8
  82. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +8 -4
  83. package/dist/prosemirror/extensions/marks/StrikeExtension.js +2 -2
  84. package/dist/prosemirror/extensions/marks/SubscriptExtension.js +2 -2
  85. package/dist/prosemirror/extensions/marks/SuperscriptExtension.js +2 -2
  86. package/dist/prosemirror/extensions/marks/TextColorExtension.js +4 -3
  87. package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -6
  88. package/dist/prosemirror/extensions/marks/markUtils.js +17 -8
  89. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +3 -1
  90. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +12 -1
  91. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +2 -2
  92. package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +5 -3
  93. package/dist/prosemirror/extensions/nodes/TableExtension.js +104 -210
  94. package/dist/prosemirror/extensions/types.d.ts +7 -0
  95. package/dist/prosemirror/hyperlinkRemoval.d.ts +9 -0
  96. package/dist/prosemirror/hyperlinkRemoval.js +129 -0
  97. package/dist/prosemirror/index.d.ts +2 -1
  98. package/dist/prosemirror/index.js +2 -1
  99. package/dist/prosemirror/listNumbering.d.ts +3 -21
  100. package/dist/prosemirror/listNumbering.js +10 -25
  101. package/dist/prosemirror/listRendering.d.ts +27 -0
  102. package/dist/prosemirror/listRendering.js +27 -0
  103. package/dist/prosemirror/markupViewNotes.d.ts +8 -0
  104. package/dist/prosemirror/markupViewNotes.js +49 -0
  105. package/dist/prosemirror/markupViewProjection.d.ts +3 -1
  106. package/dist/prosemirror/markupViewProjection.js +7 -2
  107. package/dist/prosemirror/noteReferenceReview.d.ts +77 -0
  108. package/dist/prosemirror/noteReferenceReview.js +332 -0
  109. package/dist/prosemirror/paragraphIndentation.js +5 -2
  110. package/dist/prosemirror/paragraphMarkJoin.d.ts +6 -4
  111. package/dist/prosemirror/paragraphMarkJoin.js +21 -19
  112. package/dist/prosemirror/paragraphPropertyCarry.d.ts +64 -0
  113. package/dist/prosemirror/paragraphPropertyCarry.js +158 -0
  114. package/dist/prosemirror/plugins/documentStyleState.d.ts +26 -0
  115. package/dist/prosemirror/plugins/documentStyleState.js +32 -0
  116. package/dist/prosemirror/plugins/documentStyles.d.ts +2 -21
  117. package/dist/prosemirror/plugins/documentStyles.js +66 -29
  118. package/dist/prosemirror/plugins/index.d.ts +2 -1
  119. package/dist/prosemirror/plugins/index.js +2 -1
  120. package/dist/prosemirror/plugins/paragraphStyleResolution.d.ts +15 -0
  121. package/dist/prosemirror/plugins/paragraphStyleResolution.js +230 -0
  122. package/dist/prosemirror/plugins/suggestionMode.d.ts +12 -2
  123. package/dist/prosemirror/plugins/suggestionMode.js +391 -28
  124. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +22 -2
  125. package/dist/prosemirror/rebaseParagraphRunFormatting.js +39 -13
  126. package/dist/prosemirror/rebaseParagraphRuns.d.ts +36 -0
  127. package/dist/prosemirror/rebaseParagraphRuns.js +99 -0
  128. package/dist/prosemirror/runFormattingFromMarks.d.ts +18 -0
  129. package/dist/prosemirror/runFormattingFromMarks.js +567 -0
  130. package/dist/prosemirror/runFormattingReconciliation.js +10 -9
  131. package/dist/prosemirror/schema/nodes.d.ts +2 -0
  132. package/dist/prosemirror/styles/paragraphStyleCascade.d.ts +59 -0
  133. package/dist/prosemirror/styles/paragraphStyleCascade.js +145 -0
  134. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +21 -2
  135. package/dist/prosemirror/styles/resolvedStyleAttrs.js +38 -1
  136. package/dist/prosemirror/styles/tableStyleRegions.d.ts +31 -0
  137. package/dist/prosemirror/styles/tableStyleRegions.js +55 -0
  138. package/dist/prosemirror/tableCellPaste.d.ts +74 -0
  139. package/dist/prosemirror/tableCellPaste.js +525 -0
  140. package/dist/prosemirror/tableGridMutation.d.ts +53 -14
  141. package/dist/prosemirror/tableGridMutation.js +166 -18
  142. package/dist/prosemirror/tableRunIn.d.ts +51 -0
  143. package/dist/prosemirror/tableRunIn.js +165 -0
  144. package/dist/prosemirror/textInput.js +11 -2
  145. package/dist/prosemirror/trackedRevisionPath.d.ts +17 -0
  146. package/dist/prosemirror/trackedRevisionPath.js +32 -0
  147. package/dist/server.d.ts +2 -2
  148. package/package.json +5 -5
@@ -5,11 +5,13 @@ import { isValidHexColor, resolveColor } from "../../../utils/colorResolver.js";
5
5
  import { resolveTablePlacementAlignment } from "../../../utils/tablePlacement.js";
6
6
  import { textFlowCss } from "../../../utils/textDirectionFlow.js";
7
7
  import { expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, mergeTableAttrs, mergeTableCellAttrs, mergeTableRowAttrs } from "../../attrs/index.js";
8
- import { removeTableRow } from "../../tableGridMutation.js";
8
+ import { makeRevisionInfo } from "../../plugins/suggestionMode.js";
9
+ import { pasteTableCells, pastedSliceWithNewTableCells } from "../../tableCellPaste.js";
10
+ import { insertTableColumn, removeTableColumns, removeTableRow } from "../../tableGridMutation.js";
9
11
  import { createExtension, createNodeExtension } from "../create.js";
10
12
  import { panic } from "better-result";
11
13
  import { statesNoBorder } from "@stll/docx-core/model";
12
- import { CellSelection, TableMap, columnResizing, mergeCells, selectedRect, splitCell, tableEditing } from "prosemirror-tables";
14
+ import { CellSelection, TableMap, columnResizing, mergeCells, selectedRect, splitCell, tableEditing, tableNodeTypes } from "prosemirror-tables";
13
15
  import { Plugin, PluginKey, Selection, TextSelection } from "prosemirror-state";
14
16
  import { Decoration, DecorationSet } from "prosemirror-view";
15
17
  //#region src/prosemirror/extensions/nodes/TableExtension.ts
@@ -277,6 +279,7 @@ const tableRowSpec = {
277
279
  heightRule: { default: null },
278
280
  isHeader: { default: false },
279
281
  hidden: { default: null },
282
+ _batchRowCleanup: { default: null },
280
283
  _resolvedJustification: { default: null },
281
284
  _originalFormatting: { default: null },
282
285
  _tablePropertyExceptions: { default: null },
@@ -518,12 +521,12 @@ function getTableContext(state) {
518
521
  let rowIndex;
519
522
  let columnIndex;
520
523
  let cellNode;
524
+ let cellPos;
521
525
  for (let d = $from.depth; d > 0; d--) {
522
526
  const node = $from.node(d);
523
527
  if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
524
528
  cellNode = node;
525
- const rowNode = $from.node(d - 1);
526
- if (rowNode.type.name === "tableRow") columnIndex = getColumnIndex(rowNode, $from.index(d - 1));
529
+ cellPos = $from.before(d);
527
530
  } else if (node.type.name === "tableRow") {
528
531
  if ($from.node(d - 1).type.name === "table") rowIndex = $from.index(d - 1);
529
532
  } else if (node.type.name === "table") {
@@ -533,18 +536,10 @@ function getTableContext(state) {
533
536
  }
534
537
  }
535
538
  if (!table) return { isInTable: false };
536
- let rowCount = 0;
537
- let columnCount = 0;
538
- table.forEach((row) => {
539
- if (row.type.name === "tableRow") {
540
- rowCount++;
541
- let cols = 0;
542
- row.forEach((cell) => {
543
- cols += Number(cell.attrs["colspan"]) || 1;
544
- });
545
- columnCount = Math.max(columnCount, cols);
546
- }
547
- });
539
+ const map = TableMap.get(table);
540
+ const rowCount = table.childCount;
541
+ const columnCount = map.width;
542
+ if (cellPos !== void 0 && tablePos !== void 0) columnIndex = map.findCell(cellPos - tablePos - 1).left;
548
543
  const canSplitCell = cellNode !== void 0 && ((Number(cellNode.attrs["colspan"]) || 1) > 1 || (Number(cellNode.attrs["rowspan"]) || 1) > 1);
549
544
  let cellBorderColor;
550
545
  let cellBackgroundColor;
@@ -579,19 +574,6 @@ function getTableContext(state) {
579
574
  ...cellBackgroundColor !== void 0 ? { cellBackgroundColor } : {}
580
575
  };
581
576
  }
582
- const getColumnIndex = (rowNode, targetIndex) => {
583
- let columnIndex = 0;
584
- let foundColumnIndex;
585
- rowNode.forEach((child, _offset, index) => {
586
- if (foundColumnIndex !== void 0) return;
587
- if (index === targetIndex) {
588
- foundColumnIndex = columnIndex;
589
- return;
590
- }
591
- columnIndex += Number(child.attrs["colspan"]) || 1;
592
- });
593
- return foundColumnIndex;
594
- };
595
577
  function isInTableCell(state) {
596
578
  const { $from } = state.selection;
597
579
  for (let d = $from.depth; d > 0; d--) {
@@ -689,10 +671,12 @@ const TablePluginExtension = createExtension({
689
671
  if (!schema.nodes["tableCell"]) panic("Missing node type: tableCell");
690
672
  if (!schema.nodes["tableRow"]) panic("Missing node type: tableRow");
691
673
  if (!schema.nodes["table"]) panic("Missing node type: table");
692
- const nodeTypeParagraph = schema.nodes["paragraph"];
693
- const nodeTypeTableCell = schema.nodes["tableCell"];
694
- const nodeTypeTableRow = schema.nodes["tableRow"];
695
- const nodeTypeTable = schema.nodes["table"];
674
+ const documentTableTypes = (documentSchema) => ({
675
+ paragraph: documentSchema.nodes["paragraph"] ?? panic("Missing node type: paragraph"),
676
+ tableCell: documentSchema.nodes["tableCell"] ?? panic("Missing node type: tableCell"),
677
+ tableRow: documentSchema.nodes["tableRow"] ?? panic("Missing node type: tableRow"),
678
+ table: documentSchema.nodes["table"] ?? panic("Missing node type: table")
679
+ });
696
680
  function chainCommands(...commands) {
697
681
  return (state, dispatch, view) => {
698
682
  for (const cmd of commands) if (cmd(state, dispatch, view)) return true;
@@ -717,7 +701,8 @@ const TablePluginExtension = createExtension({
717
701
  ...overrides
718
702
  };
719
703
  }
720
- function createTable(rows, cols, borderColor = "000000", contentWidthTwips = 9360) {
704
+ function createTable(documentSchema, rows, cols, borderColor = "000000", contentWidthTwips = 9360) {
705
+ const types = documentTableTypes(documentSchema);
721
706
  const tableRows = [];
722
707
  const colWidthTwips = Math.floor(contentWidthTwips / cols);
723
708
  const defaultRowHeightTwips = 360;
@@ -736,22 +721,22 @@ const TablePluginExtension = createExtension({
736
721
  for (let r = 0; r < rows; r++) {
737
722
  const cells = [];
738
723
  for (let c = 0; c < cols; c++) {
739
- const paragraph = nodeTypeParagraph.create();
724
+ const paragraph = types.paragraph.create();
740
725
  const cellAttrs = {
741
726
  colspan: 1,
742
727
  rowspan: 1,
743
728
  borders: defaultBorders,
744
729
  ...statedCellWidth(colWidthTwips, "dxa")
745
730
  };
746
- cells.push(nodeTypeTableCell.create(cellAttrs, paragraph));
731
+ cells.push(types.tableCell.create(cellAttrs, paragraph));
747
732
  }
748
- tableRows.push(nodeTypeTableRow.create({
733
+ tableRows.push(types.tableRow.create({
749
734
  height: defaultRowHeightTwips,
750
735
  heightRule: defaultRowHeightRule
751
736
  }, cells));
752
737
  }
753
738
  const columnWidths = Array.from({ length: cols }, () => colWidthTwips);
754
- return nodeTypeTable.create({
739
+ return types.table.create({
755
740
  columnWidths,
756
741
  width: contentWidthTwips,
757
742
  widthType: "dxa"
@@ -784,8 +769,31 @@ const TablePluginExtension = createExtension({
784
769
  break;
785
770
  }
786
771
  }
787
- const table = createTable(rows, cols, borderColor, contentWidthTwips);
788
- const emptyParagraph = nodeTypeParagraph.create();
772
+ let table = createTable(state.schema, rows, cols, borderColor, contentWidthTwips);
773
+ let emptyParagraph = documentTableTypes(state.schema).paragraph.create();
774
+ const revision = makeRevisionInfo(state);
775
+ if (revision) {
776
+ const rowRevision = {
777
+ revisionId: revision.id,
778
+ author: revision.author,
779
+ date: revision.date
780
+ };
781
+ const rowsWithRevision = [];
782
+ table.forEach((row) => {
783
+ rowsWithRevision.push(row.type.create({
784
+ ...row.attrs,
785
+ trIns: rowRevision
786
+ }, row.content));
787
+ });
788
+ table = table.type.create(table.attrs, rowsWithRevision);
789
+ emptyParagraph = emptyParagraph.type.create({
790
+ ...emptyParagraph.attrs,
791
+ pPrMark: {
792
+ kind: "ins",
793
+ info: revision
794
+ }
795
+ });
796
+ }
789
797
  const needsLeadingParagraph = state.doc.resolve(insertPos).nodeBefore?.type.name === "table";
790
798
  const insertContent = needsLeadingParagraph ? [
791
799
  emptyParagraph,
@@ -821,11 +829,13 @@ const TablePluginExtension = createExtension({
821
829
  const context = getTableContext(state);
822
830
  if (!context.isInTable || context.rowIndex === void 0 || !context.table || context.tablePos === void 0 || hasOmittedGridSlots(context.table)) return false;
823
831
  if (!dispatch) return true;
824
- const { table, tablePos, rowIndex } = context;
825
- const boundaryRow = rowIndex + boundaryOffset;
826
- const map = TableMap.get(table);
832
+ const { table, tablePos } = context;
833
+ const rect = selectedRect(state);
834
+ const boundaryRow = boundaryOffset === 0 ? rect.top : rect.bottom;
835
+ const { map } = rect;
827
836
  const tableStart = tablePos + 1;
828
837
  const tr = state.tr;
838
+ const paragraphType = state.schema.nodes["paragraph"];
829
839
  const cells = [];
830
840
  const extended = /* @__PURE__ */ new Set();
831
841
  let lastTemplatePos = -1;
@@ -846,10 +856,11 @@ const TablePluginExtension = createExtension({
846
856
  const templatePos = below ?? above;
847
857
  if (templatePos === void 0 || templatePos === lastTemplatePos) continue;
848
858
  lastTemplatePos = templatePos;
849
- cells.push(nodeTypeTableCell.create(buildCellAttrsFromTemplate(table.nodeAt(templatePos)), nodeTypeParagraph.create()));
859
+ const template = table.nodeAt(templatePos);
860
+ cells.push((template?.type ?? tableNodeTypes(state.schema).cell).create(buildCellAttrsFromTemplate(template), paragraphType?.create() ?? null));
850
861
  }
851
- const rowNode = table.child(rowIndex);
852
- const newRow = nodeTypeTableRow.create({
862
+ const rowNode = table.child(boundaryOffset === 0 ? rect.top : rect.bottom - 1);
863
+ const newRow = rowNode.type.create({
853
864
  height: rowNode.attrs["height"] ?? 360,
854
865
  heightRule: rowNode.attrs["heightRule"] ?? "atLeast"
855
866
  }, cells);
@@ -911,176 +922,58 @@ const TablePluginExtension = createExtension({
911
922
  }
912
923
  return true;
913
924
  }
914
- function addColumnLeft(state, dispatch) {
925
+ /**
926
+ * Insert a column beside the selected cells: left of the first column
927
+ * they cover, or right of the last. The table map places the new cells,
928
+ * so a row whose cell in that column is held by a merge from a row above
929
+ * still gains its cell at the right grid column, and `w:tblGrid` gains
930
+ * one column the width of its neighbour instead of being rebuilt.
931
+ */
932
+ function insertColumnBeside(state, dispatch, side) {
915
933
  const context = getTableContext(state);
916
934
  if (!context.isInTable || context.columnIndex === void 0 || !context.table || context.tablePos === void 0 || hasOmittedGridSlots(context.table)) return false;
917
935
  if (dispatch) {
918
- let tr = state.tr;
919
- const newColumnCount = (context.columnCount ?? 1) + 1;
920
- const newColWidthPercent = Math.floor(100 / newColumnCount);
921
- let rowPos = context.tablePos + 1;
922
- context.table.forEach((row) => {
923
- if (row.type.name === "tableRow") {
924
- let cellPos = rowPos + 1;
925
- let colIdx = 0;
926
- row.forEach((cell) => {
927
- if (colIdx === context.columnIndex) {
928
- const paragraph = nodeTypeParagraph.create();
929
- const cellAttrs = buildCellAttrsFromTemplate(cell, {
930
- colspan: 1,
931
- rowspan: 1
932
- });
933
- Object.assign(cellAttrs, statedCellWidth(newColWidthPercent, "pct"));
934
- const newCell = nodeTypeTableCell.create(cellAttrs, paragraph);
935
- tr = tr.insert(tr.mapping.map(cellPos), newCell);
936
- }
937
- cellPos += cell.nodeSize;
938
- colIdx += Number(cell.attrs["colspan"]) || 1;
936
+ const rect = selectedRect(state);
937
+ const tr = state.tr;
938
+ const paragraphType = state.schema.nodes["paragraph"];
939
+ insertTableColumn({
940
+ tr,
941
+ tablePosition: rect.tableStart - 1,
942
+ column: side === "left" ? rect.left : rect.right,
943
+ templateColumn: side === "left" ? rect.left : rect.right - 1,
944
+ createCell: (template) => {
945
+ const cellType = template?.type ?? tableNodeTypes(state.schema).cell;
946
+ const attrs = buildCellAttrsFromTemplate(template && (Number(template.attrs["colspan"]) || 1) === 1 ? template : null, {
947
+ colspan: 1,
948
+ rowspan: 1
939
949
  });
940
- if (context.columnIndex !== void 0 && colIdx <= context.columnIndex) {
941
- const paragraph = nodeTypeParagraph.create();
942
- const cellAttrs = buildCellAttrsFromTemplate(row.child(row.childCount - 1), {
943
- colspan: 1,
944
- rowspan: 1
945
- });
946
- Object.assign(cellAttrs, statedCellWidth(newColWidthPercent, "pct"));
947
- const newCell = nodeTypeTableCell.create(cellAttrs, paragraph);
948
- tr = tr.insert(tr.mapping.map(cellPos), newCell);
949
- }
950
+ return cellType.create(attrs, paragraphType?.create() ?? null);
950
951
  }
951
- rowPos += row.nodeSize;
952
952
  });
953
- const updatedTable = tr.doc.nodeAt(context.tablePos);
954
- if (updatedTable && updatedTable.type.name === "table") {
955
- const firstRow = updatedTable.child(0);
956
- if (firstRow.type.name === "tableRow") {
957
- let cellPos = context.tablePos + 2;
958
- firstRow.forEach((cell) => {
959
- if (cell.type.name === "tableCell" || cell.type.name === "tableHeader") tr = tr.setNodeMarkup(cellPos, void 0, mergeTableCellAttrs(cell, {
960
- width: newColWidthPercent,
961
- widthType: "pct"
962
- }));
963
- cellPos += cell.nodeSize;
964
- });
965
- }
966
- const colCount = firstRow.childCount;
967
- const tableWidthTwips = expectTableAttrs(updatedTable).width ?? 9360;
968
- const colWidthTwips = Math.floor(tableWidthTwips / Math.max(1, colCount));
969
- tr = tr.setNodeMarkup(context.tablePos, void 0, mergeTableAttrs(updatedTable, { columnWidths: Array.from({ length: colCount }, () => colWidthTwips) }));
970
- }
971
953
  dispatch(tr.scrollIntoView());
972
954
  }
973
955
  return true;
974
956
  }
957
+ function addColumnLeft(state, dispatch) {
958
+ return insertColumnBeside(state, dispatch, "left");
959
+ }
975
960
  function addColumnRight(state, dispatch) {
976
- const context = getTableContext(state);
977
- if (!context.isInTable || context.columnIndex === void 0 || !context.table || context.tablePos === void 0 || hasOmittedGridSlots(context.table)) return false;
978
- if (dispatch) {
979
- let tr = state.tr;
980
- const newColumnCount = (context.columnCount ?? 1) + 1;
981
- const newColWidthPercent = Math.floor(100 / newColumnCount);
982
- let rowPos = context.tablePos + 1;
983
- context.table.forEach((row) => {
984
- if (row.type.name === "tableRow") {
985
- let cellPos = rowPos + 1;
986
- let colIdx = 0;
987
- let insertedCount = 0;
988
- row.forEach((cell) => {
989
- cellPos += cell.nodeSize;
990
- colIdx += Number(cell.attrs["colspan"]) || 1;
991
- if (insertedCount === 0 && context.columnIndex !== void 0 && colIdx > context.columnIndex) {
992
- const paragraph = nodeTypeParagraph.create();
993
- const cellAttrs = buildCellAttrsFromTemplate(cell, {
994
- colspan: 1,
995
- rowspan: 1
996
- });
997
- Object.assign(cellAttrs, statedCellWidth(newColWidthPercent, "pct"));
998
- const newCell = nodeTypeTableCell.create(cellAttrs, paragraph);
999
- tr = tr.insert(tr.mapping.map(cellPos), newCell);
1000
- insertedCount += 1;
1001
- }
1002
- });
1003
- if (insertedCount === 0) {
1004
- const paragraph = nodeTypeParagraph.create();
1005
- const cellAttrs = buildCellAttrsFromTemplate(row.child(row.childCount - 1), {
1006
- colspan: 1,
1007
- rowspan: 1
1008
- });
1009
- Object.assign(cellAttrs, statedCellWidth(newColWidthPercent, "pct"));
1010
- const newCell = nodeTypeTableCell.create(cellAttrs, paragraph);
1011
- tr = tr.insert(tr.mapping.map(cellPos), newCell);
1012
- }
1013
- }
1014
- rowPos += row.nodeSize;
1015
- });
1016
- const updatedTable = tr.doc.nodeAt(context.tablePos);
1017
- if (updatedTable && updatedTable.type.name === "table") {
1018
- const firstRow = updatedTable.child(0);
1019
- if (firstRow.type.name === "tableRow") {
1020
- let cellPos = context.tablePos + 2;
1021
- firstRow.forEach((cell) => {
1022
- if (cell.type.name === "tableCell" || cell.type.name === "tableHeader") tr = tr.setNodeMarkup(cellPos, void 0, mergeTableCellAttrs(cell, {
1023
- width: newColWidthPercent,
1024
- widthType: "pct"
1025
- }));
1026
- cellPos += cell.nodeSize;
1027
- });
1028
- }
1029
- const colCount = firstRow.childCount;
1030
- const tableWidthTwips = expectTableAttrs(updatedTable).width ?? 9360;
1031
- const colWidthTwips = Math.floor(tableWidthTwips / Math.max(1, colCount));
1032
- tr = tr.setNodeMarkup(context.tablePos, void 0, mergeTableAttrs(updatedTable, { columnWidths: Array.from({ length: colCount }, () => colWidthTwips) }));
1033
- }
1034
- dispatch(tr.scrollIntoView());
1035
- }
1036
- return true;
961
+ return insertColumnBeside(state, dispatch, "right");
1037
962
  }
963
+ /**
964
+ * Delete the columns the selected cells cover. The table map finds each
965
+ * row's cell in them, a merged cell reaching past them narrows, a row
966
+ * left with no cell of its own goes, and `w:tblGrid` loses exactly those
967
+ * columns.
968
+ */
1038
969
  function deleteColumn(state, dispatch) {
1039
970
  const context = getTableContext(state);
1040
971
  if (!context.isInTable || context.columnIndex === void 0 || !context.table || context.tablePos === void 0 || (context.columnCount ?? 0) <= 1 || hasOmittedGridSlots(context.table)) return false;
972
+ const rect = selectedRect(state);
973
+ if (rect.right - rect.left >= rect.map.width) return false;
1041
974
  if (dispatch) {
1042
- let tr = state.tr;
1043
- const newColumnCount = (context.columnCount ?? 2) - 1;
1044
- const newColWidthPercent = Math.floor(100 / newColumnCount);
1045
- const deleteOps = [];
1046
- let rowPos = context.tablePos + 1;
1047
- context.table.forEach((row) => {
1048
- if (row.type.name === "tableRow") {
1049
- let cellPos = rowPos + 1;
1050
- let colIdx = 0;
1051
- row.forEach((cell) => {
1052
- const cellStart = cellPos;
1053
- const cellEnd = cellPos + cell.nodeSize;
1054
- const cellColspan = Number(cell.attrs["colspan"]) || 1;
1055
- if (context.columnIndex !== void 0 && colIdx <= context.columnIndex && context.columnIndex < colIdx + cellColspan) deleteOps.push({
1056
- start: cellStart,
1057
- end: cellEnd
1058
- });
1059
- cellPos = cellEnd;
1060
- colIdx += cellColspan;
1061
- });
1062
- }
1063
- rowPos += row.nodeSize;
1064
- });
1065
- for (const { start, end } of deleteOps.toReversed()) tr = tr.delete(start, end);
1066
- const updatedTable = tr.doc.nodeAt(context.tablePos);
1067
- if (updatedTable && updatedTable.type.name === "table") {
1068
- const firstRow = updatedTable.child(0);
1069
- if (firstRow.type.name === "tableRow") {
1070
- let cellPos = context.tablePos + 2;
1071
- firstRow.forEach((cell) => {
1072
- if (cell.type.name === "tableCell" || cell.type.name === "tableHeader") tr = tr.setNodeMarkup(cellPos, void 0, mergeTableCellAttrs(cell, {
1073
- width: newColWidthPercent,
1074
- widthType: "pct"
1075
- }));
1076
- cellPos += cell.nodeSize;
1077
- });
1078
- }
1079
- const colCount = firstRow.childCount;
1080
- const tableWidthTwips = expectTableAttrs(updatedTable).width ?? 9360;
1081
- const colWidthTwips = Math.floor(tableWidthTwips / Math.max(1, colCount));
1082
- tr = tr.setNodeMarkup(context.tablePos, void 0, mergeTableAttrs(updatedTable, { columnWidths: Array.from({ length: colCount }, () => colWidthTwips) }));
1083
- }
975
+ const tr = state.tr;
976
+ removeTableColumns(tr, rect.tableStart - 1, rect.left, rect.right);
1084
977
  dispatch(tr.scrollIntoView());
1085
978
  }
1086
979
  return true;
@@ -1125,16 +1018,9 @@ const TablePluginExtension = createExtension({
1125
1018
  if (!context.isInTable || context.tablePos === void 0 || !context.table || context.columnIndex === void 0) return false;
1126
1019
  if (dispatch) {
1127
1020
  const tableStart = context.tablePos + 1;
1128
- const firstRow = context.table.child(0);
1129
- const lastRow = context.table.child(context.table.childCount - 1);
1130
- let firstCellPos = tableStart + 1;
1131
- for (let c = 0; c < context.columnIndex && c < firstRow.childCount; c++) firstCellPos += firstRow.child(c).nodeSize;
1132
- let lastRowPos = tableStart;
1133
- for (let r = 0; r < context.table.childCount - 1; r++) lastRowPos += context.table.child(r).nodeSize;
1134
- let lastCellPos = lastRowPos + 1;
1135
- for (let c = 0; c < context.columnIndex && c < lastRow.childCount; c++) lastCellPos += lastRow.child(c).nodeSize;
1136
- const cellSel = CellSelection.create(state.doc, firstCellPos, lastCellPos);
1137
- dispatch(state.tr.setSelection(cellSel));
1021
+ const firstCell = TableMap.get(context.table).map[context.columnIndex];
1022
+ if (firstCell === void 0) return false;
1023
+ dispatch(state.tr.setSelection(CellSelection.colSelection(state.doc.resolve(tableStart + firstCell))));
1138
1024
  }
1139
1025
  return true;
1140
1026
  }
@@ -2017,6 +1903,13 @@ const TablePluginExtension = createExtension({
2017
1903
  return DecorationSet.empty;
2018
1904
  } }
2019
1905
  });
1906
+ const tableCellPastePlugin = new Plugin({
1907
+ key: new PluginKey("tableCellPaste"),
1908
+ props: {
1909
+ transformPasted: (slice) => pastedSliceWithNewTableCells(slice),
1910
+ handlePaste: (view, _event, slice) => pasteTableCells(view.state, slice, view.dispatch)
1911
+ }
1912
+ });
2020
1913
  return {
2021
1914
  plugins: [
2022
1915
  columnResizing({
@@ -2024,6 +1917,7 @@ const TablePluginExtension = createExtension({
2024
1917
  cellMinWidth: 25,
2025
1918
  lastColumnResizable: true
2026
1919
  }),
1920
+ tableCellPastePlugin,
2027
1921
  tableEditing(),
2028
1922
  activeCellPlugin
2029
1923
  ],
@@ -63,6 +63,13 @@ declare const Priority: {
63
63
  readonly Lowest: 200;
64
64
  };
65
65
  type ExtensionContext = {
66
+ /**
67
+ * The schema this runtime was built with. The documents the runtime edits
68
+ * may be of another instance of it (header, footer and note editors each
69
+ * build a runtime, and every document is parsed into the shared schema), so
70
+ * commands, key bindings and plugins build and compare nodes and marks with
71
+ * the types of the state they act on, never with types taken from here.
72
+ */
66
73
  schema: Schema;
67
74
  };
68
75
  type FolioCommandArguments = {
@@ -0,0 +1,9 @@
1
+ import { EditorState, Transaction } from "prosemirror-state";
2
+ //#region src/prosemirror/hyperlinkRemoval.d.ts
3
+ /**
4
+ * Remove the hyperlinks from [`from`, `to`) of `tr.doc`: directly, or, with
5
+ * suggestions on, as a tracked replacement of the linked text by plain text.
6
+ */
7
+ declare const removeHyperlinkInRange: (state: EditorState, tr: Transaction, from: number, to: number) => Transaction;
8
+ //#endregion
9
+ export { removeHyperlinkInRange };
@@ -0,0 +1,129 @@
1
+ import { BUILT_IN_STYLE_NAME } from "../docx/builtInStyles.js";
2
+ import { getDocumentBuiltInStyles, getDocumentStyleResolver } from "./plugins/documentStyleState.js";
3
+ import { isSuggestionModeActive, makeRevisionInfo, suggestRangeDeletion } from "./plugins/suggestionMode.js";
4
+ import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "./runFormattingReconciliation.js";
5
+ import { paragraphRunStyleContextAt } from "./runStyleFormatting.js";
6
+ //#region src/prosemirror/hyperlinkRemoval.ts
7
+ /** Contiguous stretches of linked, not yet deleted, text in [`from`, `to`) of `doc`. */
8
+ const linkedRanges = (doc, from, to) => {
9
+ const ranges = [];
10
+ doc.nodesBetween(from, to, (node, position) => {
11
+ if (!node.isText || !node.marks.some(({ type }) => type.name === "hyperlink") || node.marks.some(({ type }) => type.name === "deletion")) return true;
12
+ const start = Math.max(position, from);
13
+ const end = Math.min(position + node.nodeSize, to);
14
+ if (start >= end) return true;
15
+ const piece = {
16
+ from: start,
17
+ to: end,
18
+ node: node.cut(start - position, end - position)
19
+ };
20
+ const last = ranges.at(-1);
21
+ if (last && last.to === start) {
22
+ last.to = end;
23
+ last.pieces.push(piece);
24
+ } else ranges.push({
25
+ from: start,
26
+ to: end,
27
+ pieces: [piece]
28
+ });
29
+ return true;
30
+ });
31
+ return ranges;
32
+ };
33
+ /** A piece's marks once the Hyperlink character style is taken off it. */
34
+ const withoutHyperlinkStyle = (state, doc, piece) => {
35
+ const styleResolver = getDocumentStyleResolver(state);
36
+ const builtInStyles = getDocumentBuiltInStyles(state);
37
+ const context = paragraphRunStyleContextAt({
38
+ doc,
39
+ pos: piece.from,
40
+ styleResolver
41
+ });
42
+ const previousFormatting = readAuthoredRunFormatting({
43
+ context,
44
+ marks: piece.node.marks,
45
+ styleResolver
46
+ });
47
+ const styleId = previousFormatting.styleId;
48
+ if (!(styleId !== void 0 && (builtInStyles.builtInNameOf(styleId) === BUILT_IN_STYLE_NAME.hyperlink || styleId === "Hyperlink"))) return {
49
+ marks: piece.node.marks,
50
+ previousFormatting: null
51
+ };
52
+ const { styleId: _removed, ...formatting } = previousFormatting;
53
+ return {
54
+ marks: reconcileRunFormattingMarks({
55
+ authoredFormatting: formatting,
56
+ context,
57
+ node: piece.node,
58
+ styleResolver
59
+ }),
60
+ previousFormatting
61
+ };
62
+ };
63
+ /** Replace the marks of the text at [`from`, `to`) of `tr.doc` with `marks`. */
64
+ const setTextMarks = (tr, from, to, current, marks) => {
65
+ for (const mark of current) if (!mark.isInSet(marks)) tr.removeMark(from, to, mark);
66
+ for (const mark of marks) if (!mark.isInSet(current)) tr.addMark(from, to, mark);
67
+ };
68
+ /**
69
+ * Remove the hyperlinks from [`from`, `to`) of `tr.doc`: directly, or, with
70
+ * suggestions on, as a tracked replacement of the linked text by plain text.
71
+ */
72
+ const removeHyperlinkInRange = (state, tr, from, to) => {
73
+ const { schema } = state;
74
+ const hyperlinkType = schema.marks["hyperlink"];
75
+ if (!hyperlinkType) return tr;
76
+ const insertionType = schema.marks["insertion"];
77
+ const deletionType = schema.marks["deletion"];
78
+ const propertyChangeType = schema.marks["runPropertyChange"];
79
+ const tracked = isSuggestionModeActive(state) && insertionType !== void 0 && deletionType !== void 0 && propertyChangeType !== void 0;
80
+ const revisionAttrs = () => {
81
+ const revision = makeRevisionInfo(state);
82
+ return revision ? {
83
+ id: revision.id,
84
+ author: revision.author,
85
+ date: revision.date
86
+ } : null;
87
+ };
88
+ const insertionInfo = tracked ? revisionAttrs() : null;
89
+ const insertion = insertionType && insertionInfo ? insertionType.create({
90
+ revisionId: insertionInfo.id,
91
+ author: insertionInfo.author,
92
+ date: insertionInfo.date
93
+ }) : null;
94
+ const propertyChange = (previousFormatting) => {
95
+ const info = revisionAttrs();
96
+ if (!propertyChangeType || !info) return null;
97
+ const change = {
98
+ type: "runPropertyChange",
99
+ info,
100
+ previousFormatting
101
+ };
102
+ return propertyChangeType.create({ changes: [change] });
103
+ };
104
+ const hasPendingPropertyChange = (marks) => marks.some(({ type }) => type === propertyChangeType);
105
+ for (const range of linkedRanges(tr.doc, from, to).toReversed()) {
106
+ const unlinked = range.pieces.map((piece) => ({
107
+ piece,
108
+ ...withoutHyperlinkStyle(state, tr.doc, piece)
109
+ }));
110
+ if (!insertion || !deletionType) {
111
+ for (const { piece, marks } of unlinked) setTextMarks(tr, piece.from, piece.to, piece.node.marks, marks.filter(({ type }) => type !== hyperlinkType));
112
+ continue;
113
+ }
114
+ const plain = unlinked.map(({ piece, marks, previousFormatting }) => {
115
+ const change = previousFormatting && !hasPendingPropertyChange(marks) ? propertyChange(previousFormatting) : null;
116
+ const kept = marks.filter(({ type }) => type !== hyperlinkType && type !== insertionType && type !== deletionType);
117
+ return piece.node.mark(insertion.addToSet(change ? change.addToSet(kept) : kept));
118
+ });
119
+ tr.insert(range.to, plain);
120
+ for (const { piece, marks, previousFormatting } of unlinked.toReversed()) {
121
+ const change = previousFormatting && !hasPendingPropertyChange(piece.node.marks) ? propertyChange(previousFormatting) : null;
122
+ setTextMarks(tr, piece.from, piece.to, piece.node.marks, change ? change.addToSet(marks) : marks);
123
+ }
124
+ suggestRangeDeletion(state, tr, range.from, range.to);
125
+ }
126
+ return tr;
127
+ };
128
+ //#endregion
129
+ export { removeHyperlinkInRange };
@@ -25,7 +25,8 @@ import { SelectionChangeCallback, SelectionContext, createSelectionTrackerPlugin
25
25
  import { ensureParaIdsInState } from "./extensions/features/ParaIdAllocatorExtension.js";
26
26
  import "./styles/index.js";
27
27
  import { SelectionState, extractSelectionState } from "./selectionState.js";
28
- import { createDocumentStylesPlugin, documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleResolver } from "./plugins/documentStyles.js";
28
+ import { documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleResolver } from "./plugins/documentStyleState.js";
29
+ import { createDocumentStylesPlugin } from "./plugins/documentStyles.js";
29
30
  import "./plugins/index.js";
30
31
  import { insertPageBreakInView, insertTableInView, insertTableOfContentsInView } from "./insertOperations.js";
31
32
  import { TextSelection } from "prosemirror-state";
@@ -15,7 +15,8 @@ import { getTableContext, isInTable as isInTableCell } from "./extensions/nodes/
15
15
  import { insertPageBreakInView, insertTableInView, insertTableOfContentsInView } from "./insertOperations.js";
16
16
  import { NO_LIST_NUMBERING_MENU, listNumberingMenuState } from "./listNumbering.js";
17
17
  import { NO_LIST_STATE, isInListState, listStateLevel, resolveListState, sameListState } from "./listState.js";
18
- import { createDocumentStylesPlugin, documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleResolver } from "./plugins/documentStyles.js";
18
+ import { documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleResolver } from "./plugins/documentStyleState.js";
19
+ import { createDocumentStylesPlugin } from "./plugins/documentStyles.js";
19
20
  import { createSelectionTrackerPlugin, extractSelectionContext, getSelectionContext, selectionTrackerKey } from "./plugins/selectionTracker.js";
20
21
  import { schema, singletonManager } from "./schema/index.js";
21
22
  import { extractSelectionState } from "./selectionState.js";