diff --git a/editors/demo/config-overrides.js b/editors/demo/config-overrides.js
index 6bc5ae421f27f4825e4d4bb5a6f9b17606d5a728..1b6da38784b41f0271147cdcda1f60d309c1b5e0 100644
--- a/editors/demo/config-overrides.js
+++ b/editors/demo/config-overrides.js
@@ -15,6 +15,10 @@ module.exports = function override(config, env) {
         __dirname,
         '../../wax-questions-service/index',
       ),
+      'wax-table-service': path.resolve(
+        __dirname,
+        '../../wax-table-service/index',
+      ),
     },
   };
   config.module = {
diff --git a/editors/demo/package.json b/editors/demo/package.json
index c4b6a5adf5e52e195a13976944a549c7699c22bf..466716b9595e29bce16a03706ef38d5e955f45de 100644
--- a/editors/demo/package.json
+++ b/editors/demo/package.json
@@ -6,7 +6,6 @@
     "@guardian/prosemirror-invisibles": "^1.2.0",
     "@pubsweet/ui-toolkit": "^2.3.1",
     "fontsource-merriweather": "^3.0.9",
-    "prosemirror-tables": "1.3.4",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
     "react-panelgroup": "^1.0.10",
@@ -16,7 +15,8 @@
     "typeface-vollkorn": "^0.0.54",
     "wax-prosemirror-core": "^0.8.9",
     "wax-prosemirror-services": "^0.8.9",
-    "wax-questions-service": "^0.8.9"
+    "wax-questions-service": "^0.8.9",
+    "wax-table-service": "^0.8.9"
   },
   "scripts": {
     "start": "react-app-rewired start",
diff --git a/editors/demo/src/Editoria/config/config.js b/editors/demo/src/Editoria/config/config.js
index 7b700096ab6376d8e66db976c055e584a9dec632..a11306ffd7d8e5dd138a1816c8b6b3ef7d357c13 100644
--- a/editors/demo/src/Editoria/config/config.js
+++ b/editors/demo/src/Editoria/config/config.js
@@ -1,6 +1,6 @@
 import { emDash, ellipsis } from 'prosemirror-inputrules';
 import { debounce } from 'lodash';
-import { columnResizing, tableEditing } from 'prosemirror-tables';
+
 import {
   InlineAnnotationsService,
   AnnotationToolGroupService,
@@ -9,7 +9,6 @@ import {
   LinkService,
   ListsService,
   ListToolGroupService,
-  TablesService,
   BaseService,
   BaseToolGroupService,
   DisplayBlockLevelService,
@@ -48,6 +47,8 @@ import {
   // TitleToolGroupService,
 } from 'wax-prosemirror-services';
 
+import { TablesService, tableEditing, columnResizing } from 'wax-table-service';
+
 import { EditoriaSchema } from 'wax-prosemirror-core';
 
 import invisibles, {
@@ -150,8 +151,8 @@ export default {
     },
   },
   PmPlugins: [
-    columnResizing(),
     tableEditing(),
+    columnResizing(),
     invisibles([hardBreak()]),
     disallowPasteImagesPlugin(() =>
       onWarning(
diff --git a/editors/demo/src/Editoria/config/configMobile.js b/editors/demo/src/Editoria/config/configMobile.js
index f873786b51460a573a0a6acb6b427deeecc68950..a87d5ec5e72338992b4c82cba17085f1fd71d4d5 100644
--- a/editors/demo/src/Editoria/config/configMobile.js
+++ b/editors/demo/src/Editoria/config/configMobile.js
@@ -1,5 +1,4 @@
 import { emDash, ellipsis } from 'prosemirror-inputrules';
-import { columnResizing, tableEditing } from 'prosemirror-tables';
 import {
   AnnotationToolGroupService,
   ImageService,
@@ -7,7 +6,6 @@ import {
   LinkService,
   ListsService,
   ListToolGroupService,
-  TablesService,
   BaseService,
   BaseToolGroupService,
   DisplayBlockLevelService,
@@ -35,6 +33,8 @@ import {
   CustomTagService,
 } from 'wax-prosemirror-services';
 
+import { TablesService, tableEditing, columnResizing } from 'wax-table-service';
+
 import { DefaultSchema } from 'wax-prosemirror-core';
 
 import invisibles, {
@@ -79,7 +79,7 @@ export default {
   TitleService: { updateTitle },
   EnableTrackChangeService: { enabled: false },
 
-  PmPlugins: [columnResizing(), tableEditing(), invisibles([hardBreak()])],
+  PmPlugins: [invisibles([hardBreak()])],
   CustomTagService: {
     tags: [
       { label: 'custom-tag-label-1', tagType: 'inline' },
diff --git a/editors/demo/src/HHMI/config/config.js b/editors/demo/src/HHMI/config/config.js
index 0d12462bb71bdfd696edc9592803da5aeb70990e..7fc649f31ced9483756acb011a2040eceeaa1626 100644
--- a/editors/demo/src/HHMI/config/config.js
+++ b/editors/demo/src/HHMI/config/config.js
@@ -1,5 +1,4 @@
 import { emDash, ellipsis } from 'prosemirror-inputrules';
-import { columnResizing, tableEditing } from 'prosemirror-tables';
 import {
   InlineAnnotationsService,
   AnnotationToolGroupService,
@@ -8,7 +7,6 @@ import {
   LinkService,
   ListsService,
   ListToolGroupService,
-  TablesService,
   BaseService,
   BaseToolGroupService,
   DisplayTextToolGroupService,
@@ -19,6 +17,7 @@ import {
 } from 'wax-prosemirror-services';
 
 import { QuestionsService } from 'wax-questions-service';
+import { TablesService, tableEditing, columnResizing } from 'wax-table-service';
 
 import { DefaultSchema } from 'wax-prosemirror-core';
 import invisibles, { hardBreak } from '@guardian/prosemirror-invisibles';
@@ -130,7 +129,7 @@ export default {
   RulesService: [emDash, ellipsis],
   ImageService: { showAlt: true },
 
-  PmPlugins: [columnResizing(), tableEditing(), invisibles([hardBreak()])],
+  PmPlugins: [invisibles([hardBreak()])],
   services: [
     // new ExternalAPIContentService(),
     new QuestionsService(),
diff --git a/editors/demo/src/OEN/config/config.js b/editors/demo/src/OEN/config/config.js
index 0a148460a1b3adadf8606e1e18ff9c7c5c96ef4e..159cad8845432ee10f071910d351c663ccc6c38b 100644
--- a/editors/demo/src/OEN/config/config.js
+++ b/editors/demo/src/OEN/config/config.js
@@ -1,6 +1,6 @@
 import { emDash, ellipsis } from 'prosemirror-inputrules';
 import { debounce } from 'lodash';
-import { columnResizing, tableEditing } from 'prosemirror-tables';
+
 import {
   InlineAnnotationsService,
   AnnotationToolGroupService,
@@ -9,7 +9,6 @@ import {
   LinkService,
   ListsService,
   ListToolGroupService,
-  TablesService,
   BaseService,
   BaseToolGroupService,
   DisplayBlockLevelService,
@@ -47,6 +46,7 @@ import {
   OENContainersToolGroupService,
   OENAsideToolGroupService,
 } from 'wax-prosemirror-services';
+import { TablesService, tableEditing, columnResizing } from 'wax-table-service';
 
 import { EditoriaSchema } from 'wax-prosemirror-core';
 
@@ -191,7 +191,7 @@ export default {
       reject: true,
     },
   },
-  PmPlugins: [columnResizing(), tableEditing(), invisibles([hardBreak()])],
+  PmPlugins: [invisibles([hardBreak()])],
   CustomTagService: {
     tags: [
       { label: 'custom-tag-label-1', tagType: 'inline' },
diff --git a/package.json b/package.json
index 00e25850c1b7483f52364fbc897b77ddbc5aa938..f728163b96123cbbe005841875a99a48e99e4bf6 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
     "wax-prosemirror-core",
     "wax-prosemirror-services",
     "wax-questions-service",
+    "wax-table-service",
     "editors/*"
   ],
   "scripts": {
diff --git a/wax-prosemirror-core/rollup.config.js b/wax-prosemirror-core/rollup.config.js
index 8d739e1e70f3370014fc922a2484df7d7b0a70b7..3acc3b132c6a82ce28d33771d7ce965c5bc9d2c9 100644
--- a/wax-prosemirror-core/rollup.config.js
+++ b/wax-prosemirror-core/rollup.config.js
@@ -30,11 +30,5 @@ export default {
     commonjs(),
     terser(),
   ],
-  external: [
-    'react-dropdown',
-    'uuid',
-    'prop-types',
-    'prosemirror-tables',
-    'wax-prosemirror-services',
-  ],
+  external: [],
 };
diff --git a/wax-prosemirror-core/src/utilities/commands/Commands.js b/wax-prosemirror-core/src/utilities/commands/Commands.js
index 6bbab9026b399afbb2a3c74a2ca16173a7c6f33a..64d14487222f59565c4f1c32d85e76a0f8aa9db2 100644
--- a/wax-prosemirror-core/src/utilities/commands/Commands.js
+++ b/wax-prosemirror-core/src/utilities/commands/Commands.js
@@ -96,8 +96,11 @@ const createTable = (colsRows, state, dispatch) => {
     rows.push(state.config.schema.nodes.table_row.createAndFill(null, cells));
   }
 
-  const table = state.config.schema.nodes.table.createAndFill(null, rows);
-  dispatch(state.tr.replaceSelectionWith(table));
+  const tableBody = state.config.schema.nodes.table_body.createAndFill(
+    null,
+    rows,
+  );
+  dispatch(state.tr.replaceSelectionWith(tableBody));
 };
 
 const createLink = (state, dispatch) => {
diff --git a/wax-prosemirror-services/index.js b/wax-prosemirror-services/index.js
index 9a7293e2c8b371641de385165d9645405f2a62c7..47ca65e64e54e53b8e39e16f8cb81719d5f210d6 100644
--- a/wax-prosemirror-services/index.js
+++ b/wax-prosemirror-services/index.js
@@ -5,7 +5,6 @@ export { default as BaseService } from './src/BaseService/BaseService';
 export { default as ImageService } from './src/ImageService/ImageService';
 export { default as InlineAnnotationsService } from './src/InlineAnnotations/InlineAnnotationsService';
 export { default as ListsService } from './src/ListsService/ListsService';
-export { default as TablesService } from './src/TablesService/TablesService';
 export { default as TextBlockLevelService } from './src/TextBlockLevel/TextBlockLevelService';
 export { default as DisplayBlockLevelService } from './src/DisplayBlockLevel/DisplayBlockLevelService';
 export { default as NoteService } from './src/NoteService/NoteService';
diff --git a/wax-prosemirror-services/package.json b/wax-prosemirror-services/package.json
index a13ea0bdca63a5c8d703e7e1dbf7465b4f9fd666..00f9eded28091a7e79726f72c3db6c9a6606b5ae 100644
--- a/wax-prosemirror-services/package.json
+++ b/wax-prosemirror-services/package.json
@@ -29,7 +29,6 @@
     "prosemirror-model": "1.19.0",
     "prosemirror-schema-list": "1.3.0",
     "prosemirror-state": "1.4.2",
-    "prosemirror-tables": "1.3.4",
     "prosemirror-transform": "1.7.1",
     "prosemirror-view": "1.30.2",
     "react-dropdown": "^1.6.2",
diff --git a/wax-prosemirror-services/rollup.config.js b/wax-prosemirror-services/rollup.config.js
index 4fda92e215d51ab73865ffa00c2e288dd304467b..46fa70aa6cb0fad1e51b5c258de9273c7f13de85 100644
--- a/wax-prosemirror-services/rollup.config.js
+++ b/wax-prosemirror-services/rollup.config.js
@@ -40,12 +40,7 @@ export default {
     'uuid',
     'react',
     'react-dom',
-    'react-dropdown',
     'lodash',
-    'prosemirror-tables',
-    'prosemirror-schema-list',
-    'prosemirror-model',
-    'prosemirror-inputrules',
     '@babel/runtime/regenerator',
   ],
 };
diff --git a/wax-table-service/README.md b/wax-table-service/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b8d971e91fb0d7bf6970c3267f60dd6e2baefe9e
--- /dev/null
+++ b/wax-table-service/README.md
@@ -0,0 +1 @@
+## Wax table package
diff --git a/wax-table-service/index.js b/wax-table-service/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..556e566daeb211f238884deb9475c02a66667344
--- /dev/null
+++ b/wax-table-service/index.js
@@ -0,0 +1,2 @@
+export { tableEditing, columnResizing } from './src/tableSrc';
+export { default as TablesService } from './src/TablesService';
diff --git a/wax-table-service/package.json b/wax-table-service/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..4f51a4ae7829d93cf1af5899b0e02509fb4a5cb6
--- /dev/null
+++ b/wax-table-service/package.json
@@ -0,0 +1,37 @@
+{
+  "name": "wax-table-service",
+  "author": "Christos Kokosias & Giannis Kopanas",
+  "version": "0.8.9",
+  "description": "Wax Table Service",
+  "license": "MIT",
+  "main": "dist/index.js",
+  "style": "dist/index.css",
+  "files": [
+    "dist"
+  ],
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1",
+    "build": "BABEL_ENV=production rollup -c"
+  },
+  "dependencies": {
+    "@pubsweet/ui-toolkit": "^2.3.1",
+    "inversify": "^5.0.1",
+    "lodash": "^4.17.4",
+    "uuid": "^7.0.3",
+    "prosemirror-keymap": "1.2.1",
+    "prosemirror-state": "1.4.2",
+    "prosemirror-view": "1.30.2",
+    "prosemirror-transform": "1.7.1",
+    "wax-prosemirror-core": "^0.8.9",
+    "wax-prosemirror-services": "^0.8.9"
+  },
+  "devDependencies": {
+    "mocha": "^3.4.2",
+    "prosemirror-test-builder": "^1.0.1"
+  },
+  "peerDependencies": {
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
+    "styled-components": "^5.3.1"
+  }
+}
diff --git a/wax-table-service/rollup.config.js b/wax-table-service/rollup.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..7db020053fa745910cb74f40a152cdd7dad19eb6
--- /dev/null
+++ b/wax-table-service/rollup.config.js
@@ -0,0 +1,40 @@
+import babel from 'rollup-plugin-babel';
+import commonjs from '@rollup/plugin-commonjs';
+import external from 'rollup-plugin-peer-deps-external';
+import css from 'rollup-plugin-import-css';
+import { terser } from 'rollup-plugin-terser';
+
+export default {
+  input: 'index.js',
+  output: [
+    {
+      file: 'dist/index.js',
+      format: 'cjs',
+      sourcemap: false,
+    },
+  ],
+  plugins: [
+    css(),
+    external({
+      includeDependencies: true,
+    }),
+    babel({
+      presets: ['react-app'],
+      plugins: [
+        [
+          '@babel/plugin-transform-runtime',
+          {
+            regenerator: true,
+          },
+        ],
+        ['@babel/plugin-proposal-decorators', { legacy: true }],
+        'babel-plugin-parameter-decorator',
+      ],
+      exclude: 'node_modules/**',
+      runtimeHelpers: true,
+    }),
+    commonjs(),
+    terser(),
+  ],
+  external: ['uuid', 'react', 'react-dom', 'lodash', 'prosemirror-model'],
+};
diff --git a/wax-prosemirror-services/src/TablesService/EditTableService/EditTableService.js b/wax-table-service/src/EditTableService/EditTableService.js
similarity index 100%
rename from wax-prosemirror-services/src/TablesService/EditTableService/EditTableService.js
rename to wax-table-service/src/EditTableService/EditTableService.js
diff --git a/wax-prosemirror-services/src/TablesService/EditTableService/TableDropDownOptions.js b/wax-table-service/src/EditTableService/TableDropDownOptions.js
similarity index 100%
rename from wax-prosemirror-services/src/TablesService/EditTableService/TableDropDownOptions.js
rename to wax-table-service/src/EditTableService/TableDropDownOptions.js
diff --git a/wax-prosemirror-services/src/TablesService/InsertTableService/InsertTableService.js b/wax-table-service/src/InsertTableService/InsertTableService.js
similarity index 70%
rename from wax-prosemirror-services/src/TablesService/InsertTableService/InsertTableService.js
rename to wax-table-service/src/InsertTableService/InsertTableService.js
index beed96a3269f970063efbb9a1845de32a900e8e0..c722bea85aa8bb5366a58a4b82c4861675c58827 100644
--- a/wax-prosemirror-services/src/TablesService/InsertTableService/InsertTableService.js
+++ b/wax-table-service/src/InsertTableService/InsertTableService.js
@@ -1,5 +1,6 @@
+/* eslint-disable camelcase */
 import { Service } from 'wax-prosemirror-core';
-import { tableNodes, goToNextCell } from 'prosemirror-tables';
+import { tableNodes, goToNextCell } from '../tableSrc';
 import Table from './Table';
 
 class InsertTableService extends Service {
@@ -8,7 +9,14 @@ class InsertTableService extends Service {
     const CreateShortCut = this.container.get('CreateShortCut');
 
     // eslint-disable-next-line camelcase
-    const { table, table_row, table_cell, table_header } = tableNodes({
+    const {
+      table,
+      table_row,
+      table_caption,
+      table_body,
+      table_cell,
+      table_header,
+    } = tableNodes({
       tableGroup: 'block',
       cellContent: 'block+',
     });
@@ -17,6 +25,12 @@ class InsertTableService extends Service {
     createNode({
       table,
     });
+    createNode({
+      table_caption,
+    });
+    createNode({
+      table_body,
+    });
     createNode({
       table_row,
     });
diff --git a/wax-prosemirror-services/src/TablesService/InsertTableService/Table.js b/wax-table-service/src/InsertTableService/Table.js
similarity index 100%
rename from wax-prosemirror-services/src/TablesService/InsertTableService/Table.js
rename to wax-table-service/src/InsertTableService/Table.js
diff --git a/wax-prosemirror-services/src/TablesService/TableToolGroupService/TableToolGroupService.js b/wax-table-service/src/TableToolGroupService/TableToolGroupService.js
similarity index 100%
rename from wax-prosemirror-services/src/TablesService/TableToolGroupService/TableToolGroupService.js
rename to wax-table-service/src/TableToolGroupService/TableToolGroupService.js
diff --git a/wax-prosemirror-services/src/TablesService/TableToolGroupService/Tables.js b/wax-table-service/src/TableToolGroupService/Tables.js
similarity index 100%
rename from wax-prosemirror-services/src/TablesService/TableToolGroupService/Tables.js
rename to wax-table-service/src/TableToolGroupService/Tables.js
diff --git a/wax-prosemirror-services/src/TablesService/TablesService.js b/wax-table-service/src/TablesService.js
similarity index 100%
rename from wax-prosemirror-services/src/TablesService/TablesService.js
rename to wax-table-service/src/TablesService.js
diff --git a/wax-prosemirror-services/src/TablesService/components/CreateTable.js b/wax-table-service/src/components/CreateTable.js
similarity index 100%
rename from wax-prosemirror-services/src/TablesService/components/CreateTable.js
rename to wax-table-service/src/components/CreateTable.js
diff --git a/wax-prosemirror-services/src/TablesService/components/InsertTableTool.js b/wax-table-service/src/components/InsertTableTool.js
similarity index 100%
rename from wax-prosemirror-services/src/TablesService/components/InsertTableTool.js
rename to wax-table-service/src/components/InsertTableTool.js
diff --git a/wax-prosemirror-services/src/TablesService/components/TableDropDown.js b/wax-table-service/src/components/TableDropDown.js
similarity index 96%
rename from wax-prosemirror-services/src/TablesService/components/TableDropDown.js
rename to wax-table-service/src/components/TableDropDown.js
index e7736d78f976241cdbe3a2c0c35f7542a56cf2aa..1d9b573a9bc17ed7fe9e467e1005cf3d6743db72 100644
--- a/wax-prosemirror-services/src/TablesService/components/TableDropDown.js
+++ b/wax-table-service/src/components/TableDropDown.js
@@ -8,8 +8,8 @@ import React, {
   createRef,
 } from 'react';
 import styled from 'styled-components';
-import * as tablesFn from 'prosemirror-tables';
 import { WaxContext, Icon, useOnClickOutside } from 'wax-prosemirror-core';
+import * as tablesFn from '../tableSrc';
 
 const Wrapper = styled.div`
   opacity: ${props => (props.disabled ? '0.4' : '1')};
@@ -41,7 +41,7 @@ const DropDownMenu = styled.div`
   margin: 10px auto auto;
   position: absolute;
   width: 170px;
-  max-height: 150px;
+  max-height: 180px;
   overflow-y: scroll;
   z-index: 2;
 
@@ -64,13 +64,15 @@ const StyledIcon = styled(Icon)`
 
 const TableDropDown = ({ item }) => {
   const dropDownOptions = [
+    { label: 'Delete table', value: 'deleteTable' },
+    { label: 'Add Caption', value: 'addCaption' },
+    { label: 'Delete Caption', value: 'deleteCaption' },
     { label: 'Add column before', value: 'addColumnBefore' },
     { label: 'Add column after', value: 'addColumnAfter' },
     { label: 'Delete column', value: 'deleteColumn' },
     { label: 'Insert row before', value: 'addRowBefore' },
     { label: 'Insert row after', value: 'addRowAfter' },
     { label: 'Delete row', value: 'deleteRow' },
-    { label: 'Delete table', value: 'deleteTable' },
     { label: 'Merge cells', value: 'mergeCells' },
     { label: 'Split cell', value: 'splitCell' },
     { label: 'Toggle header column', value: 'toggleHeaderColumn' },
diff --git a/wax-prosemirror-services/src/TablesService/index.js b/wax-table-service/src/index.js
similarity index 100%
rename from wax-prosemirror-services/src/TablesService/index.js
rename to wax-table-service/src/index.js
diff --git a/wax-prosemirror-services/src/TablesService/table.css b/wax-table-service/src/table.css
similarity index 90%
rename from wax-prosemirror-services/src/TablesService/table.css
rename to wax-table-service/src/table.css
index 1737e4ac5e6407dddce1c2eaa0be6147117d96e6..692234ff6bbbc53e65594158e2335533a209335c 100644
--- a/wax-prosemirror-services/src/TablesService/table.css
+++ b/wax-table-service/src/table.css
@@ -12,6 +12,12 @@
     width: 100%;
   }
 
+   table>caption {
+      background-color: #535E76;
+      color: white;
+      font-weight: bold;
+    }
+
   th,
   td {
     border: 1px solid #eee;
diff --git a/wax-table-service/src/tableSrc/index.js b/wax-table-service/src/tableSrc/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..4b193e70c180a62918e623b67356300507e848f2
--- /dev/null
+++ b/wax-table-service/src/tableSrc/index.js
@@ -0,0 +1,3402 @@
+/* eslint-disable*/
+
+// src/index.ts
+import { Plugin as Plugin2 } from 'prosemirror-state';
+
+// src/cellselection.ts
+import { Fragment, Slice } from 'prosemirror-model';
+import {
+  NodeSelection as NodeSelection2,
+  Selection,
+  SelectionRange,
+  TextSelection,
+} from 'prosemirror-state';
+import { Decoration, DecorationSet } from 'prosemirror-view';
+
+// src/util.ts
+import { PluginKey } from 'prosemirror-state';
+
+// src/schema.ts
+function getCellAttrs(dom, extraAttrs) {
+  if (typeof dom === 'string') {
+    return {};
+  }
+  const widthAttr = dom.getAttribute('data-colwidth');
+  const widths =
+    widthAttr && /^\d+(,\d+)*$/.test(widthAttr)
+      ? widthAttr.split(',').map(s => Number(s))
+      : null;
+  const colspan = Number(dom.getAttribute('colspan') || 1);
+  const result = {
+    colspan,
+    rowspan: Number(dom.getAttribute('rowspan') || 1),
+    colwidth: widths && widths.length == colspan ? widths : null,
+  };
+  for (const prop in extraAttrs) {
+    const getter = extraAttrs[prop].getFromDOM;
+    const value = getter && getter(dom);
+    if (value != null) {
+      result[prop] = value;
+    }
+  }
+  return result;
+}
+function setCellAttrs(node, extraAttrs) {
+  const attrs = {};
+  if (node.attrs.colspan != 1) attrs.colspan = node.attrs.colspan;
+  if (node.attrs.rowspan != 1) attrs.rowspan = node.attrs.rowspan;
+  if (node.attrs.colwidth)
+    attrs['data-colwidth'] = node.attrs.colwidth.join(',');
+  for (const prop in extraAttrs) {
+    const setter = extraAttrs[prop].setDOMAttr;
+    if (setter) setter(node.attrs[prop], attrs);
+  }
+  return attrs;
+}
+function tableNodes(options) {
+  const extraAttrs = options.cellAttributes || {};
+  const cellAttrs = {
+    colspan: { default: 1 },
+    rowspan: { default: 1 },
+    colwidth: { default: null },
+  };
+  for (const prop in extraAttrs)
+    cellAttrs[prop] = { default: extraAttrs[prop].default };
+  return {
+    table: {
+      // content: 'table_caption? table_head? table_body* table_foot?',
+      content: 'table_caption? table_body*',
+      tableRole: 'table',
+      isolating: true,
+      group: options.tableGroup,
+      parseDOM: [{ tag: 'table' }],
+      toDOM() {
+        return ['table', 0];
+      },
+    },
+    table_caption: {
+      content: 'block+',
+      tableRole: 'caption',
+      isolating: true,
+      parseDOM: [{ tag: 'caption' }],
+      toDOM() {
+        return ['caption', 0];
+      },
+    },
+    table_head: {
+      content: 'table_row+',
+      tableRole: 'head',
+      isolating: true,
+      parseDOM: [{ tag: 'thead' }],
+      toDOM() {
+        return ['thead', 0];
+      },
+    },
+    table_foot: {
+      content: 'table_row+',
+      tableRole: 'foot',
+      isolating: true,
+      parseDOM: [{ tag: 'tfoot' }],
+      toDOM() {
+        return ['tfoot', 0];
+      },
+    },
+    table_body: {
+      content: 'table_row+',
+      tableRole: 'body',
+      isolating: true,
+      parseDOM: [{ tag: 'tbody' }],
+      toDOM() {
+        return ['tbody', 0];
+      },
+    },
+    table_row: {
+      content: '(table_cell | table_header)*',
+      tableRole: 'row',
+      parseDOM: [{ tag: 'tr' }],
+      toDOM() {
+        return ['tr', 0];
+      },
+    },
+    table_cell: {
+      content: options.cellContent,
+      attrs: cellAttrs,
+      tableRole: 'cell',
+      isolating: true,
+      parseDOM: [{ tag: 'td', getAttrs: dom => getCellAttrs(dom, extraAttrs) }],
+      toDOM(node) {
+        return ['td', setCellAttrs(node, extraAttrs), 0];
+      },
+    },
+    table_header: {
+      content: options.cellContent,
+      attrs: cellAttrs,
+      tableRole: 'header_cell',
+      isolating: true,
+      parseDOM: [{ tag: 'th', getAttrs: dom => getCellAttrs(dom, extraAttrs) }],
+      toDOM(node) {
+        return ['th', setCellAttrs(node, extraAttrs), 0];
+      },
+    },
+  };
+}
+function tableNodeTypes(schema) {
+  let result = schema.cached.tableNodeTypes;
+  if (!result) {
+    result = schema.cached.tableNodeTypes = {};
+    for (const name in schema.nodes) {
+      const type = schema.nodes[name],
+        role = type.spec.tableRole;
+      if (role) result[role] = type;
+    }
+  }
+  return result;
+}
+function isTableSection(node) {
+  const role = node.type.spec.tableRole;
+  return role === 'body' || role === 'head' || role === 'foot';
+}
+function isTableSectionRole(role) {
+  return role === 'body' || role === 'head' || role === 'foot';
+}
+
+// src/tablemap.ts
+var readFromCache;
+var addToCache;
+if (typeof WeakMap != 'undefined') {
+  let cache = /* @__PURE__ */ new WeakMap();
+  readFromCache = key => cache.get(key);
+  addToCache = (key, value) => {
+    cache.set(key, value);
+    return value;
+  };
+} else {
+  const cache = [];
+  const cacheSize = 10;
+  let cachePos = 0;
+  readFromCache = key => {
+    for (let i = 0; i < cache.length; i += 2)
+      if (cache[i] == key) return cache[i + 1];
+  };
+  addToCache = (key, value) => {
+    if (cachePos == cacheSize) cachePos = 0;
+    cache[cachePos++] = key;
+    return (cache[cachePos++] = value);
+  };
+}
+var TableMap = class {
+  constructor(width, height, map, sectionRows, problems) {
+    this.width = width;
+    this.height = height;
+    this.map = map;
+    this.sectionRows = sectionRows;
+    this.problems = problems;
+  }
+  // Find the dimensions of the cell at the given position.
+  findCell(pos) {
+    for (let i = 0; i < this.map.length; i++) {
+      const curPos = this.map[i];
+      if (curPos != pos) continue;
+      const left = i % this.width;
+      const top = (i / this.width) | 0;
+      let right = left + 1;
+      let bottom = top + 1;
+      for (let j = 1; right < this.width && this.map[i + j] == curPos; j++) {
+        right++;
+      }
+      for (
+        let j = 1;
+        bottom < this.height && this.map[i + this.width * j] == curPos;
+        j++
+      ) {
+        bottom++;
+      }
+      return { left, top, right, bottom };
+    }
+    throw new RangeError(`No cell with offset ${pos} found`);
+  }
+  // Find the left side of the cell at the given position.
+  colCount(pos) {
+    for (let i = 0; i < this.map.length; i++) {
+      if (this.map[i] == pos) {
+        return i % this.width;
+      }
+    }
+    throw new RangeError(`No cell with offset ${pos} found`);
+  }
+  // Find the next cell in the given direction, starting from the cell
+  // at `pos`, if any.
+  nextCell(pos, axis, dir) {
+    const { left, right, top, bottom } = this.findCell(pos);
+    if (axis == 'horiz') {
+      if (dir < 0 ? left == 0 : right == this.width) return null;
+      return this.map[top * this.width + (dir < 0 ? left - 1 : right)];
+    } else {
+      if (dir < 0 ? top == 0 : bottom == this.height) return null;
+      return this.map[left + this.width * (dir < 0 ? top - 1 : bottom)];
+    }
+  }
+  // Get the rectangle spanning the two given cells.
+  rectBetween(a, b) {
+    const {
+      left: leftA,
+      right: rightA,
+      top: topA,
+      bottom: bottomA,
+    } = this.findCell(a);
+    const {
+      left: leftB,
+      right: rightB,
+      top: topB,
+      bottom: bottomB,
+    } = this.findCell(b);
+    return {
+      left: Math.min(leftA, leftB),
+      top: Math.min(topA, topB),
+      right: Math.max(rightA, rightB),
+      bottom: Math.max(bottomA, bottomB),
+    };
+  }
+  // Return the position of all cells that have the top left corner in
+  // the given rectangle.
+  cellsInRect(rect) {
+    const result = [];
+    const seen = {};
+    for (let row = rect.top; row < rect.bottom; row++) {
+      for (let col = rect.left; col < rect.right; col++) {
+        const index = row * this.width + col;
+        const pos = this.map[index];
+        if (seen[pos]) continue;
+        seen[pos] = true;
+        if (
+          (col == rect.left && col && this.map[index - 1] == pos) ||
+          (row == rect.top && row && this.map[index - this.width] == pos)
+        ) {
+          continue;
+        }
+        result.push(pos);
+      }
+    }
+    return result;
+  }
+  // Return the indices of all sections that are touched (overlapped, even partially)
+  // by the given rectangle.
+  // Indices start from 0 and don't consider the caption, so if there's a caption
+  // section n is table.child(n+1), otherwise it's table.child(n)
+  sectionsInRect(rect) {
+    const result = [];
+    const sectionRows = this.sectionRows;
+    let top = 0,
+      bottom = 0;
+    for (let i = 0; i < sectionRows.length; i++) {
+      bottom += sectionRows[i];
+      if (rect.top < bottom && rect.bottom > top) result.push(i);
+      top = bottom;
+    }
+    return result;
+  }
+  isLastRowInSection(row) {
+    const srows = this.sectionRows;
+    let lastRow = 0;
+    for (let s = 0; s < srows.length; s++) {
+      lastRow += srows[s];
+      if (lastRow === row) return true;
+      if (lastRow > row) return false;
+    }
+    return false;
+  }
+  // Return the position at which the cell at the given row and column
+  // starts, or would start, if a cell started there.
+  positionAt(row, col, table) {
+    for (let i = 0; ; i++) {
+      const { node, pos: rowStart } = getRow(table, row);
+      const rowEnd = rowStart + node.nodeSize;
+      if (i == row) {
+        let index = col + row * this.width;
+        const rowEndIndex = (row + 1) * this.width;
+        while (index < rowEndIndex && this.map[index] < rowStart) index++;
+        return index == rowEndIndex ? rowEnd - 1 : this.map[index];
+      }
+    }
+  }
+  findSection(pos) {
+    const { top } = this.findCell(pos);
+    let rows = 0,
+      nextRows = 0;
+    for (let s = 0; s < this.sectionRows.length; s++) {
+      const nextRows2 = rows + this.sectionRows[s];
+      if (top < rows)
+        return {
+          left: 0,
+          top: rows,
+          right: this.width,
+          bottom: nextRows2,
+        };
+      rows = nextRows2;
+    }
+    return {
+      left: 0,
+      top: 0,
+      right: this.width,
+      bottom: this.height,
+    };
+  }
+  sectionOfRow(row) {
+    let countRows = 0;
+    for (let i = 0; i < this.sectionRows.length; i++) {
+      countRows += this.sectionRows[i];
+      if (row < countRows) return i;
+    }
+    return -1;
+  }
+  rectOverOneSection(rect) {
+    const topSection = this.sectionOfRow(rect.top);
+    return topSection >= 0 && topSection == this.sectionOfRow(rect.bottom - 1);
+  }
+  // Find the table map for the given table node.
+  static get(table) {
+    return readFromCache(table) || addToCache(table, computeMap(table));
+  }
+};
+function computeMap(table) {
+  if (table.type.spec.tableRole != 'table')
+    throw new RangeError('Not a table node: ' + table.type.name);
+  const width = findWidth(table);
+  const height = findHeight(table);
+  const tmap = new TableMap(width, height, [], [], null);
+  let offset = 0;
+  let colWidths = [];
+  for (let c = 0; c < table.childCount; c++) {
+    const section = table.child(c);
+    if (isTableSection(section)) {
+      tmap.sectionRows.push(section.childCount);
+      let smap = computeSectionMap(section, width, offset + 1, colWidths);
+      tmap.map = tmap.map.concat(smap.map);
+      if (smap.problems) {
+        tmap.problems = (tmap.problems || []).concat(smap.problems);
+      }
+    }
+    offset += section.nodeSize;
+  }
+  let badWidths = false;
+  for (let i = 0; !badWidths && i < colWidths.length; i += 2)
+    if (colWidths[i] != null && colWidths[i + 1] < height) badWidths = true;
+  if (badWidths) findBadColWidths(tmap, colWidths, table);
+  return tmap;
+}
+function computeSectionMap(section, width, offset, colWidths) {
+  if (!isTableSection(section))
+    throw new RangeError('Not a table section node: ' + section.type.name);
+  const height = section.childCount;
+  const map = [];
+  let mapPos = 0;
+  let problems = null;
+  for (let i = 0, e = width * height; i < e; i++) map[i] = 0;
+  for (let row = 0, pos = offset; row < height; row++) {
+    const rowNode = section.child(row);
+    pos++;
+    for (let i = 0; ; i++) {
+      while (mapPos < map.length && map[mapPos] != 0) mapPos++;
+      if (i == rowNode.childCount) break;
+      const cellNode = rowNode.child(i);
+      const { colspan, rowspan, colwidth } = cellNode.attrs;
+      for (let h = 0; h < rowspan; h++) {
+        if (h + row >= height) {
+          (problems || (problems = [])).push({
+            type: 'overlong_rowspan',
+            pos,
+            n: rowspan - h,
+          });
+          break;
+        }
+        const start = mapPos + h * width;
+        for (let w = 0; w < colspan; w++) {
+          if (map[start + w] == 0) map[start + w] = pos;
+          else
+            (problems || (problems = [])).push({
+              type: 'collision',
+              row,
+              pos,
+              n: colspan - w,
+            });
+          const colW = colwidth && colwidth[w];
+          if (colW) {
+            const widthIndex = ((start + w) % width) * 2,
+              prev = colWidths[widthIndex];
+            if (
+              prev == null ||
+              (prev != colW && colWidths[widthIndex + 1] == 1)
+            ) {
+              colWidths[widthIndex] = colW;
+              colWidths[widthIndex + 1] = 1;
+            } else if (prev == colW) {
+              colWidths[widthIndex + 1]++;
+            }
+          }
+        }
+      }
+      mapPos += colspan;
+      pos += cellNode.nodeSize;
+    }
+    const expectedPos = (row + 1) * width;
+    let missing = 0;
+    while (mapPos < expectedPos) if (map[mapPos++] == 0) missing++;
+    if (missing)
+      (problems || (problems = [])).push({ type: 'missing', row, n: missing });
+    pos++;
+  }
+  const tableMap = new TableMap(width, height, map, [], problems);
+  return tableMap;
+}
+function findWidth(table) {
+  let width = -1;
+  let hasRowSpan = false;
+  for (let cIndex = 0; cIndex < table.childCount; cIndex++) {
+    const sectionNode = table.child(cIndex);
+    if (isTableSection(sectionNode)) {
+      for (let row = 0; row < sectionNode.childCount; row++) {
+        const rowNode = sectionNode.child(row);
+        let rowWidth = 0;
+        if (hasRowSpan)
+          for (let j = 0; j < row; j++) {
+            const prevRow = sectionNode.child(j);
+            for (let i = 0; i < prevRow.childCount; i++) {
+              const cell = prevRow.child(i);
+              if (j + cell.attrs.rowspan > row) rowWidth += cell.attrs.colspan;
+            }
+          }
+        for (let i = 0; i < rowNode.childCount; i++) {
+          const cell = rowNode.child(i);
+          rowWidth += cell.attrs.colspan;
+          if (cell.attrs.rowspan > 1) hasRowSpan = true;
+        }
+        if (width == -1) width = rowWidth;
+        else if (width != rowWidth) width = Math.max(width, rowWidth);
+      }
+    }
+  }
+  return width;
+}
+function findHeight(table) {
+  let height = 0;
+  for (let cIndex = 0; cIndex < table.childCount; cIndex++) {
+    const sectionNode = table.child(cIndex);
+    if (isTableSection(sectionNode)) {
+      height += sectionNode.childCount;
+    }
+  }
+  return height;
+}
+function findBadColWidths(map, colWidths, table) {
+  if (!map.problems) map.problems = [];
+  const seen = {};
+  for (let i = 0; i < map.map.length; i++) {
+    const pos = map.map[i];
+    if (seen[pos]) continue;
+    seen[pos] = true;
+    const node = table.nodeAt(pos);
+    if (!node) {
+      throw new RangeError(`No cell with offset ${pos} found`);
+    }
+    let updated = null;
+    const attrs = node.attrs;
+    for (let j = 0; j < attrs.colspan; j++) {
+      const col = (i + j) % map.width;
+      const colWidth = colWidths[col * 2];
+      if (
+        colWidth != null &&
+        (!attrs.colwidth || attrs.colwidth[j] != colWidth)
+      )
+        (updated || (updated = freshColWidth(attrs)))[j] = colWidth;
+    }
+    if (updated) {
+      map.problems.unshift({
+        type: 'colwidth mismatch',
+        pos,
+        colwidth: updated,
+      });
+    }
+  }
+}
+function freshColWidth(attrs) {
+  if (attrs.colwidth) return attrs.colwidth.slice();
+  const result = [];
+  for (let i = 0; i < attrs.colspan; i++) result.push(0);
+  return result;
+}
+
+// src/util.ts
+var tableEditingKey = new PluginKey('selectingCells');
+function cellAround($pos) {
+  for (let d = $pos.depth - 1; d > 0; d--)
+    if ($pos.node(d).type.spec.tableRole == 'row')
+      return $pos.node(0).resolve($pos.before(d + 1));
+  return null;
+}
+function cellWrapping($pos) {
+  for (let d = $pos.depth; d > 0; d--) {
+    const role = $pos.node(d).type.spec.tableRole;
+    if (role === 'cell' || role === 'header_cell') return $pos.node(d);
+  }
+  return null;
+}
+function isInTable(state) {
+  const $head = state.selection.$head;
+  for (let d = $head.depth; d > 0; d--)
+    if ($head.node(d).type.spec.tableRole == 'row') return true;
+  return false;
+}
+function tableDepth($pos) {
+  for (let d = $pos.depth; d >= 0; d--)
+    if ($pos.node(d).type.spec.tableRole == 'table') return d;
+  return -1;
+}
+function selectionCell(state) {
+  const sel = state.selection;
+  if ('$anchorCell' in sel && sel.$anchorCell) {
+    return sel.$anchorCell.pos > sel.$headCell.pos
+      ? sel.$anchorCell
+      : sel.$headCell;
+  } else if (
+    'node' in sel &&
+    sel.node &&
+    sel.node.type.spec.tableRole == 'cell'
+  ) {
+    return sel.$anchor;
+  }
+  const $cell = cellAround(sel.$head) || cellNear(sel.$head);
+  if ($cell) {
+    return $cell;
+  }
+  throw new RangeError(`No cell found around position ${sel.head}`);
+}
+function cellNear($pos) {
+  for (
+    let after = $pos.nodeAfter, pos = $pos.pos;
+    after;
+    after = after.firstChild, pos++
+  ) {
+    const role = after.type.spec.tableRole;
+    if (role == 'cell' || role == 'header_cell') return $pos.doc.resolve(pos);
+  }
+  for (
+    let before = $pos.nodeBefore, pos = $pos.pos;
+    before;
+    before = before.lastChild, pos--
+  ) {
+    const role = before.type.spec.tableRole;
+    if (role == 'cell' || role == 'header_cell')
+      return $pos.doc.resolve(pos - before.nodeSize);
+  }
+}
+function pointsAtCell($pos) {
+  return $pos.parent.type.spec.tableRole == 'row' && !!$pos.nodeAfter;
+}
+function moveCellForward($pos) {
+  return $pos.node(0).resolve($pos.pos + $pos.nodeAfter.nodeSize);
+}
+function inSameTable($cellA, $cellB) {
+  return (
+    $cellA.depth == $cellB.depth &&
+    $cellA.pos >= $cellB.start(-2) &&
+    $cellA.pos <= $cellB.end(-2)
+  );
+}
+function findCell($pos) {
+  return TableMap.get($pos.node(-2)).findCell($pos.pos - $pos.start(-2));
+}
+function colCount($pos) {
+  return TableMap.get($pos.node(-2)).colCount($pos.pos - $pos.start(-2));
+}
+function nextCell($pos, axis, dir) {
+  const table = $pos.node(-2);
+  const map = TableMap.get(table);
+  const tableStart = $pos.start(-2);
+  const moved = map.nextCell($pos.pos - tableStart, axis, dir);
+  return moved == null ? null : $pos.node(0).resolve(tableStart + moved);
+}
+function removeColSpan(attrs, pos, n = 1) {
+  const result = { ...attrs, colspan: attrs.colspan - n };
+  if (result.colwidth) {
+    result.colwidth = result.colwidth.slice();
+    result.colwidth.splice(pos, n);
+    if (!result.colwidth.some(w => w > 0)) result.colwidth = null;
+  }
+  return result;
+}
+function addColSpan(attrs, pos, n = 1) {
+  const result = { ...attrs, colspan: attrs.colspan + n };
+  if (result.colwidth) {
+    result.colwidth = result.colwidth.slice();
+    for (let i = 0; i < n; i++) result.colwidth.splice(pos, 0, 0);
+  }
+  return result;
+}
+function columnIsHeader(map, table, col) {
+  const headerCell = tableNodeTypes(table.type.schema).header_cell;
+  for (let row = 0; row < map.height; row++)
+    if (table.nodeAt(map.map[col + row * map.width]).type != headerCell)
+      return false;
+  return true;
+}
+function rowsCount(table) {
+  let count = 0;
+  for (let c = 0; c < table.childCount; c++) {
+    const section = table.child(c);
+    if (isTableSection(section)) count += section.childCount;
+  }
+  return count;
+}
+function getRow(table, row) {
+  let rPos = 0;
+  let prevSectionsRows = 0;
+  let sectionIndex = -1;
+  for (let tc = 0; tc < table.childCount; tc++) {
+    const section = table.child(tc);
+    if (isTableSection(section)) {
+      sectionIndex++;
+      const sectionRows = section.childCount;
+      if (sectionRows > 0) {
+        if (prevSectionsRows + sectionRows <= row) {
+          if (tc === table.childCount - 1) {
+            return {
+              node: null,
+              pos: rPos + section.nodeSize - 1,
+              section: sectionIndex,
+            };
+          }
+          rPos += section.nodeSize;
+          prevSectionsRows += sectionRows;
+        } else {
+          rPos++;
+          let r = 0;
+          while (r < sectionRows) {
+            if (prevSectionsRows + r === row) break;
+            rPos += section.child(r).nodeSize;
+            r++;
+          }
+          if (r === sectionRows) rPos++;
+          return {
+            node: r >= sectionRows ? null : section.child(r),
+            pos: rPos,
+            section: sectionIndex,
+          };
+        }
+      }
+    } else {
+      rPos += section.nodeSize;
+    }
+  }
+  return { node: null, pos: rPos, section: sectionIndex };
+}
+function rowPos(table, row) {
+  return getRow(
+    table,
+    row,
+    /* debug */
+  ).pos;
+}
+function rowAtPos(table, pos) {
+  let rpos = 0;
+  let row = 0;
+  for (let c = 0; c < table.childCount; c++) {
+    const section = table.child(c);
+    if (isTableSection(section)) {
+      rpos++;
+      for (let r = 0; r < section.childCount; r++) {
+        rpos += section.child(r).nodeSize;
+        if (pos < rpos) return row;
+        row++;
+      }
+      rpos++;
+    } else {
+      rpos += section.nodeSize;
+    }
+  }
+  return row;
+}
+function tableHasCaption(table) {
+  if (table && table.type.spec.tableRole === 'table') {
+    return table.child(0).type.spec.tableRole === 'caption';
+  }
+  return false;
+}
+function tableHasHead(table) {
+  if (table && table.type.spec.tableRole === 'table') {
+    for (let i = 0; i < table.childCount; i++)
+      if (table.child(i).type.spec.tableRole === 'head') return true;
+  }
+  return false;
+}
+function tableHasFoot(table) {
+  if (table && table.type.spec.tableRole === 'table') {
+    for (let i = table.childCount - 1; i > 0; i--)
+      if (table.child(i).type.spec.tableRole === 'foot') return true;
+  }
+  return false;
+}
+function tableBodiesCount(table) {
+  let count = 0;
+  if (table && table.type.spec.tableRole === 'table') {
+    for (let i = 0; i < table.childCount; i++)
+      if (table.child(i).type.spec.tableRole === 'body') count++;
+  }
+  return count;
+}
+function tableSectionsCount(table) {
+  let count = 0;
+  if (table && table.type.spec.tableRole === 'table') {
+    for (let i = 0; i < table.childCount; i++)
+      if (isTableSection(table.child(i))) count++;
+  }
+  return count;
+}
+function isRowLastInSection(table, row) {
+  const { height, sectionRows } = TableMap.get(table);
+  if (row >= height || row < 0) return false;
+  let rowsMinusOne = -1;
+  for (let i = 0; i < sectionRows.length; i++) {
+    rowsMinusOne += sectionRows[i];
+    if (row === rowsMinusOne) return true;
+    if (row < rowsMinusOne) return false;
+  }
+  return false;
+}
+
+// src/cellselection.ts
+var CellSelection = class extends Selection {
+  // A table selection is identified by its anchor and head cells. The
+  // positions given to this constructor should point _before_ two
+  // cells in the same table. They may be the same, to select a single
+  // cell.
+  constructor($anchorCell, $headCell = $anchorCell) {
+    const table = $anchorCell.node(-2);
+    const map = TableMap.get(table);
+    const tableStart = $anchorCell.start(-2);
+    const rect = map.rectBetween(
+      $anchorCell.pos - tableStart,
+      $headCell.pos - tableStart,
+    );
+    const doc = $anchorCell.node(0);
+    const cells = map
+      .cellsInRect(rect)
+      .filter(p => p != $headCell.pos - tableStart);
+    cells.unshift($headCell.pos - tableStart);
+    const ranges = cells.map(pos => {
+      const cell = table.nodeAt(pos);
+      if (!cell) {
+        throw RangeError(`No cell with offset ${pos} found`);
+      }
+      const from = tableStart + pos + 1;
+      return new SelectionRange(
+        doc.resolve(from),
+        doc.resolve(from + cell.content.size),
+      );
+    });
+    super(ranges[0].$from, ranges[0].$to, ranges);
+    this.$anchorCell = $anchorCell;
+    this.$headCell = $headCell;
+  }
+  map(doc, mapping) {
+    const $anchorCell = doc.resolve(mapping.map(this.$anchorCell.pos));
+    const $headCell = doc.resolve(mapping.map(this.$headCell.pos));
+    if (
+      pointsAtCell($anchorCell) &&
+      pointsAtCell($headCell) &&
+      inSameTable($anchorCell, $headCell)
+    ) {
+      const tableChanged = this.$anchorCell.node(-2) != $anchorCell.node(-2);
+      if (tableChanged && this.isRowSelection())
+        return CellSelection.rowSelection($anchorCell, $headCell);
+      else if (tableChanged && this.isColSelection())
+        return CellSelection.colSelection($anchorCell, $headCell);
+      else return new CellSelection($anchorCell, $headCell);
+    }
+    return TextSelection.between($anchorCell, $headCell);
+  }
+  // Returns a rectangular slice of table rows containing the selected
+  // cells.
+  content() {
+    const table = this.$anchorCell.node(-2);
+    const map = TableMap.get(table);
+    const tableStart = this.$anchorCell.start(-2);
+    const rect = map.rectBetween(
+      this.$anchorCell.pos - tableStart,
+      this.$headCell.pos - tableStart,
+    );
+    const seen = {};
+    const rows = [];
+    for (let row = rect.top; row < rect.bottom; row++) {
+      const rowContent = [];
+      for (
+        let index = row * map.width + rect.left, col = rect.left;
+        col < rect.right;
+        col++, index++
+      ) {
+        const pos = map.map[index];
+        if (seen[pos]) continue;
+        seen[pos] = true;
+        const cellRect = map.findCell(pos);
+        let cell = table.nodeAt(pos);
+        if (!cell) {
+          throw RangeError(`No cell with offset ${pos} found`);
+        }
+        const extraLeft = rect.left - cellRect.left;
+        const extraRight = cellRect.right - rect.right;
+        if (extraLeft > 0 || extraRight > 0) {
+          let attrs = cell.attrs;
+          if (extraLeft > 0) {
+            attrs = removeColSpan(attrs, 0, extraLeft);
+          }
+          if (extraRight > 0) {
+            attrs = removeColSpan(
+              attrs,
+              attrs.colspan - extraRight,
+              extraRight,
+            );
+          }
+          if (cellRect.left < rect.left) {
+            cell = cell.type.createAndFill(attrs);
+            if (!cell) {
+              throw RangeError(
+                `Could not create cell with attrs ${JSON.stringify(attrs)}`,
+              );
+            }
+          } else {
+            cell = cell.type.create(attrs, cell.content);
+          }
+        }
+        if (cellRect.top < rect.top || cellRect.bottom > rect.bottom) {
+          const attrs = {
+            ...cell.attrs,
+            rowspan:
+              Math.min(cellRect.bottom, rect.bottom) -
+              Math.max(cellRect.top, rect.top),
+          };
+          if (cellRect.top < rect.top) {
+            cell = cell.type.createAndFill(attrs);
+          } else {
+            cell = cell.type.create(attrs, cell.content);
+          }
+        }
+        rowContent.push(cell);
+      }
+      const rowNode = getRow(table, row).node;
+      rows.push(rowNode.copy(Fragment.from(rowContent)));
+    }
+    const fragment =
+      this.isColSelection(map) && this.isRowSelection() ? table : rows;
+    return new Slice(Fragment.from(fragment), 1, 1);
+  }
+  replace(tr, content = Slice.empty) {
+    const mapFrom = tr.steps.length,
+      ranges = this.ranges;
+    for (let i = 0; i < ranges.length; i++) {
+      const { $from, $to } = ranges[i],
+        mapping = tr.mapping.slice(mapFrom);
+      tr.replace(
+        mapping.map($from.pos),
+        mapping.map($to.pos),
+        i ? Slice.empty : content,
+      );
+    }
+    const sel = Selection.findFrom(
+      tr.doc.resolve(tr.mapping.slice(mapFrom).map(this.to)),
+      -1,
+    );
+    if (sel) tr.setSelection(sel);
+  }
+  replaceWith(tr, node) {
+    this.replace(tr, new Slice(Fragment.from(node), 0, 0));
+  }
+  forEachCell(f) {
+    const table = this.$anchorCell.node(-2);
+    const map = TableMap.get(table);
+    const tableStart = this.$anchorCell.start(-2);
+    const cells = map.cellsInRect(
+      map.rectBetween(
+        this.$anchorCell.pos - tableStart,
+        this.$headCell.pos - tableStart,
+      ),
+    );
+    for (let i = 0; i < cells.length; i++) {
+      f(table.nodeAt(cells[i]), tableStart + cells[i]);
+    }
+  }
+  // True if this selection goes all the way from the top to the
+  // bottom of the table.
+  isColSelection(tableMap) {
+    const table = this.$anchorCell.node(-2);
+    const tableStart = this.$anchorCell.start(-2);
+    const anchorTop = rowAtPos(table, this.$anchorCell.pos - tableStart);
+    const headTop = rowAtPos(table, this.$headCell.pos - tableStart);
+    if (Math.min(anchorTop, headTop) > 0) return false;
+    const anchorBottom = anchorTop + this.$anchorCell.nodeAfter.attrs.rowspan;
+    const headBottom = headTop + this.$headCell.nodeAfter.attrs.rowspan;
+    return Math.max(anchorBottom, headBottom) == rowsCount(table);
+  }
+  // Returns the smallest column selection that covers the given anchor
+  // and head cell.
+  static colSelection($anchorCell, $headCell = $anchorCell) {
+    const table = $anchorCell.node(-2);
+    const map = TableMap.get(table);
+    const tableStart = $anchorCell.start(-2);
+    const anchorRect = map.findCell($anchorCell.pos - tableStart);
+    const headRect = map.findCell($headCell.pos - tableStart);
+    const doc = $anchorCell.node(0);
+    if (anchorRect.top <= headRect.top) {
+      if (anchorRect.top > 0)
+        $anchorCell = doc.resolve(tableStart + map.map[anchorRect.left]);
+      if (headRect.bottom < map.height)
+        $headCell = doc.resolve(
+          tableStart +
+            map.map[map.width * (map.height - 1) + headRect.right - 1],
+        );
+    } else {
+      if (headRect.top > 0)
+        $headCell = doc.resolve(tableStart + map.map[headRect.left]);
+      if (anchorRect.bottom < map.height)
+        $anchorCell = doc.resolve(
+          tableStart +
+            map.map[map.width * (map.height - 1) + anchorRect.right - 1],
+        );
+    }
+    return new CellSelection($anchorCell, $headCell);
+  }
+  // True if this selection goes all the way from the left to the
+  // right of the table.
+  isRowSelection() {
+    const table = this.$anchorCell.node(-2);
+    const map = TableMap.get(table);
+    const tableStart = this.$anchorCell.start(-2);
+    const anchorLeft = map.colCount(this.$anchorCell.pos - tableStart);
+    const headLeft = map.colCount(this.$headCell.pos - tableStart);
+    if (Math.min(anchorLeft, headLeft) > 0) return false;
+    const anchorRight = anchorLeft + this.$anchorCell.nodeAfter.attrs.colspan;
+    const headRight = headLeft + this.$headCell.nodeAfter.attrs.colspan;
+    return Math.max(anchorRight, headRight) == map.width;
+  }
+  eq(other) {
+    return (
+      other instanceof CellSelection &&
+      other.$anchorCell.pos == this.$anchorCell.pos &&
+      other.$headCell.pos == this.$headCell.pos
+    );
+  }
+  // Returns the smallest row selection that covers the given anchor
+  // and head cell.
+  static rowSelection($anchorCell, $headCell = $anchorCell) {
+    const table = $anchorCell.node(-2);
+    const map = TableMap.get(table);
+    const tableStart = $anchorCell.start(-2);
+    const anchorRect = map.findCell($anchorCell.pos - tableStart);
+    const headRect = map.findCell($headCell.pos - tableStart);
+    const doc = $anchorCell.node(0);
+    if (anchorRect.left <= headRect.left) {
+      if (anchorRect.left > 0)
+        $anchorCell = doc.resolve(
+          tableStart + map.map[anchorRect.top * map.width],
+        );
+      if (headRect.right < map.width)
+        $headCell = doc.resolve(
+          tableStart + map.map[map.width * (headRect.top + 1) - 1],
+        );
+    } else {
+      if (headRect.left > 0)
+        $headCell = doc.resolve(tableStart + map.map[headRect.top * map.width]);
+      if (anchorRect.right < map.width)
+        $anchorCell = doc.resolve(
+          tableStart + map.map[map.width * (anchorRect.top + 1) - 1],
+        );
+    }
+    return new CellSelection($anchorCell, $headCell);
+  }
+  // Returns the smallest section selection that covers the given anchor
+  // and head cell.
+  static sectionSelection($anchorCell, $headCell = $anchorCell) {
+    const table = $anchorCell.node(-2);
+    const map = TableMap.get(table);
+    const tableStart = $anchorCell.start(-2);
+    const sectionStart = $anchorCell.start(-1);
+    const anchorSection = map.findSection($anchorCell.pos - sectionStart + 1);
+    const headSection = map.findSection($headCell.pos - sectionStart + 1);
+    const doc = $anchorCell.node(0);
+    if (anchorSection.top <= headSection.top) {
+      $anchorCell = doc.resolve(
+        tableStart + map.map[map.width * anchorSection.top],
+      );
+      $headCell = doc.resolve(
+        tableStart + map.map[map.width * headSection.bottom - 1],
+      );
+    } else {
+      $anchorCell = doc.resolve(
+        tableStart + map.map[map.width * headSection.top],
+      );
+      $headCell = doc.resolve(
+        tableStart + map.map[map.width * anchorSection.bottom - 1],
+      );
+    }
+    return new CellSelection($anchorCell, $headCell);
+  }
+  toJSON() {
+    return {
+      type: 'cell',
+      anchor: this.$anchorCell.pos,
+      head: this.$headCell.pos,
+    };
+  }
+  static fromJSON(doc, json) {
+    return new CellSelection(doc.resolve(json.anchor), doc.resolve(json.head));
+  }
+  static create(doc, anchorCell, headCell = anchorCell) {
+    return new CellSelection(doc.resolve(anchorCell), doc.resolve(headCell));
+  }
+  getBookmark() {
+    return new CellBookmark(this.$anchorCell.pos, this.$headCell.pos);
+  }
+};
+CellSelection.prototype.visible = false;
+Selection.jsonID('cell', CellSelection);
+var CellBookmark = class {
+  constructor(anchor, head) {
+    this.anchor = anchor;
+    this.head = head;
+  }
+  map(mapping) {
+    return new CellBookmark(mapping.map(this.anchor), mapping.map(this.head));
+  }
+  resolve(doc) {
+    const $anchorCell = doc.resolve(this.anchor),
+      $headCell = doc.resolve(this.head);
+    if (
+      $anchorCell.parent.type.spec.tableRole == 'row' &&
+      $headCell.parent.type.spec.tableRole == 'row' &&
+      $anchorCell.index() < $anchorCell.parent.childCount &&
+      $headCell.index() < $headCell.parent.childCount &&
+      inSameTable($anchorCell, $headCell)
+    )
+      return new CellSelection($anchorCell, $headCell);
+    else return Selection.near($headCell, 1);
+  }
+};
+function drawCellSelection(state) {
+  if (!(state.selection instanceof CellSelection)) return null;
+  const cells = [];
+  state.selection.forEachCell((node, pos) => {
+    cells.push(
+      Decoration.node(pos, pos + node.nodeSize, { class: 'selectedCell' }),
+    );
+  });
+  return DecorationSet.create(state.doc, cells);
+}
+function isCellBoundarySelection({ $from, $to }) {
+  if ($from.pos == $to.pos || $from.pos < $from.pos - 6) return false;
+  let afterFrom = $from.pos;
+  let beforeTo = $to.pos;
+  let depth = $from.depth;
+  for (; depth >= 0; depth--, afterFrom++)
+    if ($from.after(depth + 1) < $from.end(depth)) break;
+  for (let d = $to.depth; d >= 0; d--, beforeTo--)
+    if ($to.before(d + 1) > $to.start(d)) break;
+  return (
+    afterFrom == beforeTo &&
+    /^(row|body|table|head|foot)$/.test($from.node(depth).type.spec.tableRole)
+  );
+}
+function isTextSelectionAcrossCells({ $from, $to }) {
+  let fromCellBoundaryNode;
+  let toCellBoundaryNode;
+  for (let i = $from.depth; i > 0; i--) {
+    const node = $from.node(i);
+    if (
+      node.type.spec.tableRole === 'cell' ||
+      node.type.spec.tableRole === 'header_cell'
+    ) {
+      fromCellBoundaryNode = node;
+      break;
+    }
+  }
+  for (let i = $to.depth; i > 0; i--) {
+    const node = $to.node(i);
+    if (
+      node.type.spec.tableRole === 'cell' ||
+      node.type.spec.tableRole === 'header_cell'
+    ) {
+      toCellBoundaryNode = node;
+      break;
+    }
+  }
+  return fromCellBoundaryNode !== toCellBoundaryNode && $to.parentOffset === 0;
+}
+function normalizeSelection(state, tr, allowTableNodeSelection) {
+  const sel = (tr || state).selection;
+  const doc = (tr || state).doc;
+  let normalize;
+  let role;
+  if (sel instanceof NodeSelection2 && (role = sel.node.type.spec.tableRole)) {
+    if (role == 'cell' || role == 'header_cell') {
+      normalize = CellSelection.create(doc, sel.from);
+    } else if (role == 'row') {
+      const $cell = doc.resolve(sel.from + 1);
+      normalize = CellSelection.rowSelection($cell, $cell);
+    } else if (isTableSectionRole(role)) {
+      const $cell = doc.resolve(sel.from + 2);
+      normalize = CellSelection.sectionSelection($cell, $cell);
+    } else if (!allowTableNodeSelection) {
+      const map = TableMap.get(sel.node);
+      const start = sel.from + 1;
+      const lastCell = start + map.map[map.width * map.height - 1];
+      normalize = CellSelection.create(doc, start + 2, lastCell);
+    }
+  } else if (sel instanceof TextSelection && isCellBoundarySelection(sel)) {
+    normalize = TextSelection.create(doc, sel.from);
+  } else if (sel instanceof TextSelection && isTextSelectionAcrossCells(sel)) {
+    normalize = TextSelection.create(doc, sel.$from.start(), sel.$from.end());
+  }
+  if (normalize) (tr || (tr = state.tr)).setSelection(normalize);
+  return tr;
+}
+
+// src/fixtables.ts
+import { PluginKey as PluginKey2 } from 'prosemirror-state';
+var fixTablesKey = new PluginKey2('fix-tables');
+function changedDescendants(old, cur, offset, f) {
+  const oldSize = old.childCount,
+    curSize = cur.childCount;
+  outer: for (let i = 0, j = 0; i < curSize; i++) {
+    const child = cur.child(i);
+    for (let scan = j, e = Math.min(oldSize, i + 3); scan < e; scan++) {
+      if (old.child(scan) == child) {
+        j = scan + 1;
+        offset += child.nodeSize;
+        continue outer;
+      }
+    }
+    f(child, offset);
+    if (j < oldSize && old.child(j).sameMarkup(child))
+      changedDescendants(old.child(j), child, offset + 1, f);
+    else child.nodesBetween(0, child.content.size, f, offset + 1);
+    offset += child.nodeSize;
+  }
+}
+function fixTables(state, oldState) {
+  let tr;
+  const check = (node, pos) => {
+    if (node.type.spec.tableRole == 'table')
+      tr = fixTable(state, node, pos, tr);
+  };
+  if (!oldState) state.doc.descendants(check);
+  else if (oldState.doc != state.doc)
+    changedDescendants(oldState.doc, state.doc, 0, check);
+  return tr;
+}
+function fixTable(state, table, tablePos, tr) {
+  if (hasFreeRows(table)) tr = fixFreeRows(state, table, tablePos, tr);
+  const map = TableMap.get(table);
+  if (!map.problems) return tr;
+  if (!tr) tr = state.tr;
+  const mustAdd = [];
+  for (let i = 0; i < map.height; i++) mustAdd.push(0);
+  for (let i = 0; i < map.problems.length; i++) {
+    const prob = map.problems[i];
+    if (prob.type == 'collision') {
+      const cell = table.nodeAt(prob.pos);
+      if (!cell) continue;
+      const attrs = cell.attrs;
+      for (let j = 0; j < attrs.rowspan; j++) mustAdd[prob.row + j] += prob.n;
+      tr.setNodeMarkup(
+        tr.mapping.map(tablePos + 1 + prob.pos),
+        null,
+        removeColSpan(attrs, attrs.colspan - prob.n, prob.n),
+      );
+    } else if (prob.type == 'missing') {
+      mustAdd[prob.row] += prob.n;
+    } else if (prob.type == 'overlong_rowspan') {
+      const cell = table.nodeAt(prob.pos);
+      if (!cell) continue;
+      tr.setNodeMarkup(tr.mapping.map(tablePos + 1 + prob.pos), null, {
+        ...cell.attrs,
+        rowspan: cell.attrs.rowspan - prob.n,
+      });
+    } else if (prob.type == 'colwidth mismatch') {
+      const cell = table.nodeAt(prob.pos);
+      if (!cell) continue;
+      tr.setNodeMarkup(tr.mapping.map(tablePos + 1 + prob.pos), null, {
+        ...cell.attrs,
+        colwidth: prob.colwidth,
+      });
+    }
+  }
+  let first, last;
+  for (let i = 0; i < mustAdd.length; i++)
+    if (mustAdd[i]) {
+      if (first == null) first = i;
+      last = i;
+    }
+  for (let i = 0; i < map.height; i++) {
+    const { node: row, pos } = getRow(table, i);
+    const end = pos + row.nodeSize;
+    const add = mustAdd[i];
+    if (add > 0) {
+      let role = 'cell';
+      if (row.firstChild) {
+        role = row.firstChild.type.spec.tableRole;
+      }
+      const nodes = [];
+      for (let j = 0; j < add; j++) {
+        const node = tableNodeTypes(state.schema)[role].createAndFill();
+        if (node) nodes.push(node);
+      }
+      const side = (i == 0 || first == i - 1) && last == i ? pos + 1 : end - 1;
+      tr.insert(tr.mapping.map(side + 1), nodes);
+    }
+  }
+  return tr.setMeta(fixTablesKey, { fixTables: true });
+}
+function hasFreeRows(table) {
+  for (let i = 0; i < table.childCount; i++)
+    if (table.child(i).type.spec.tableRole === 'row') return true;
+  return false;
+}
+function fixFreeRows(state, table, tablePos, tr) {
+  let freeRows = [];
+  let freeRowsFound = false;
+  const sections = [];
+  const types = tableNodeTypes(state.schema);
+  for (let i = 0; i < table.childCount; i++) {
+    const child = table.child(i);
+    if (child.type.spec.tableRole === 'row') {
+      freeRowsFound = true;
+      freeRows.push(child);
+    } else {
+      if (freeRows.length > 0) {
+        sections.push(types.body.createAndFill(null, freeRows));
+        freeRows = [];
+      }
+      sections.push(child);
+    }
+  }
+  if (freeRows.length > 0) {
+    sections.push(types.body.createAndFill(null, freeRows));
+    freeRows = [];
+  }
+  if (!freeRowsFound) return tr;
+  return (tr || state.tr).replaceWith(
+    tablePos,
+    tablePos + table.nodeSize,
+    types.table.createAndFill(table.attrs, sections),
+  );
+}
+
+// src/input.ts
+import { Fragment as Fragment3, Slice as Slice3 } from 'prosemirror-model';
+import {
+  Selection as Selection2,
+  TextSelection as TextSelection2,
+} from 'prosemirror-state';
+import { keydownHandler } from 'prosemirror-keymap';
+
+// src/copypaste.ts
+import { Fragment as Fragment2, Slice as Slice2 } from 'prosemirror-model';
+import { Transform } from 'prosemirror-transform';
+function pastedCells(slice) {
+  if (!slice.size) return null;
+  let { content, openStart, openEnd } = slice;
+  while (
+    content.childCount == 1 &&
+    ((openStart > 0 && openEnd > 0) ||
+      content.child(0).type.spec.tableRole == 'table')
+  ) {
+    openStart--;
+    openEnd--;
+    content = content.child(0).content;
+  }
+  const first = content.child(0);
+  const role = first.type.spec.tableRole;
+  const schema = first.type.schema;
+  const rows = [];
+  if (isTableSectionRole(role)) {
+    for (let s = 0; s < content.childCount; s++) {
+      const section = content.child(s);
+      if (isTableSection(section)) {
+        for (let i = 0; i < section.childCount; i++) {
+          let cells = section.child(i).content;
+          const left = i != 0 ? 0 : Math.max(0, openStart - 1);
+          const right =
+            i < section.childCount - 1 ? 0 : Math.max(0, openEnd - 1);
+          if (left || right)
+            cells = fitSlice(
+              tableNodeTypes(schema).row,
+              new Slice2(cells, left, right),
+            ).content;
+          rows.push(cells);
+        }
+      }
+    }
+  } else if (role == 'row') {
+    for (let i = 0; i < content.childCount; i++) {
+      let cells = content.child(i).content;
+      const left = i ? 0 : Math.max(0, openStart - 1);
+      const right = i < content.childCount - 1 ? 0 : Math.max(0, openEnd - 1);
+      if (left || right)
+        cells = fitSlice(
+          tableNodeTypes(schema).row,
+          new Slice2(cells, left, right),
+        ).content;
+      rows.push(cells);
+    }
+  } else if (role == 'cell' || role == 'header_cell') {
+    rows.push(
+      openStart || openEnd
+        ? fitSlice(
+            tableNodeTypes(schema).row,
+            new Slice2(content, openStart, openEnd),
+          ).content
+        : content,
+    );
+  } else {
+    return null;
+  }
+  return ensureRectangular(schema, rows);
+}
+function ensureRectangular(schema, rows) {
+  const widths = [];
+  for (let i = 0; i < rows.length; i++) {
+    const row = rows[i];
+    for (let j = row.childCount - 1; j >= 0; j--) {
+      const { rowspan, colspan } = row.child(j).attrs;
+      for (let r = i; r < i + rowspan; r++)
+        widths[r] = (widths[r] || 0) + colspan;
+    }
+  }
+  let width = 0;
+  for (let r = 0; r < widths.length; r++) width = Math.max(width, widths[r]);
+  for (let r = 0; r < widths.length; r++) {
+    if (r >= rows.length) rows.push(Fragment2.empty);
+    if (widths[r] < width) {
+      const empty = tableNodeTypes(schema).cell.createAndFill();
+      const cells = [];
+      for (let i = widths[r]; i < width; i++) {
+        cells.push(empty);
+      }
+      rows[r] = rows[r].append(Fragment2.from(cells));
+    }
+  }
+  return { height: rows.length, width, rows };
+}
+function fitSlice(nodeType, slice) {
+  const node = nodeType.createAndFill();
+  const tr = new Transform(node).replace(0, node.content.size, slice);
+  return tr.doc;
+}
+function clipCells({ width, height, rows }, newWidth, newHeight) {
+  if (width != newWidth) {
+    const added = [];
+    const newRows = [];
+    for (let row = 0; row < rows.length; row++) {
+      const frag = rows[row],
+        cells = [];
+      for (let col = added[row] || 0, i = 0; col < newWidth; i++) {
+        let cell = frag.child(i % frag.childCount);
+        if (col + cell.attrs.colspan > newWidth)
+          cell = cell.type.createChecked(
+            removeColSpan(
+              cell.attrs,
+              cell.attrs.colspan,
+              col + cell.attrs.colspan - newWidth,
+            ),
+            cell.content,
+          );
+        cells.push(cell);
+        col += cell.attrs.colspan;
+        for (let j = 1; j < cell.attrs.rowspan; j++)
+          added[row + j] = (added[row + j] || 0) + cell.attrs.colspan;
+      }
+      newRows.push(Fragment2.from(cells));
+    }
+    rows = newRows;
+    width = newWidth;
+  }
+  if (height != newHeight) {
+    const newRows = [];
+    for (let row = 0, i = 0; row < newHeight; row++, i++) {
+      const cells = [],
+        source = rows[i % height];
+      for (let j = 0; j < source.childCount; j++) {
+        let cell = source.child(j);
+        if (row + cell.attrs.rowspan > newHeight)
+          cell = cell.type.create(
+            {
+              ...cell.attrs,
+              rowspan: Math.max(1, newHeight - cell.attrs.rowspan),
+            },
+            cell.content,
+          );
+        cells.push(cell);
+      }
+      newRows.push(Fragment2.from(cells));
+    }
+    rows = newRows;
+    height = newHeight;
+  }
+  return { width, height, rows };
+}
+function growTable(tr, map, table, start, width, height, mapFrom) {
+  const schema = tr.doc.type.schema;
+  const types = tableNodeTypes(schema);
+  let empty;
+  let emptyHead;
+  if (width > map.width) {
+    const lastCellOfRow = [];
+    for (let row = 0; row < map.height; row++) {
+      const lastCell = table.nodeAt(map.map[(row + 1) * map.width - 1]);
+      if (lastCell == null || lastCell.type == types.cell) {
+        lastCellOfRow.push(empty || (empty = types.cell.createAndFill()));
+      } else {
+        lastCellOfRow.push(
+          emptyHead || (emptyHead = types.header_cell.createAndFill()),
+        );
+      }
+    }
+    for (let row = 0; row < map.height; row++) {
+      const { node: rowNode, pos: rowPos2 } = getRow(table, row);
+      const rowEnd = rowPos2 + rowNode.nodeSize - 1;
+      const cells = [];
+      for (let i = map.width; i < width; i++) cells.push(lastCellOfRow[row]);
+      tr.insert(tr.mapping.slice(mapFrom).map(rowEnd + start), cells);
+    }
+  }
+  if (height > map.height) {
+    const cells = [];
+    for (
+      let i = 0, start2 = (map.height - 1) * map.width;
+      i < Math.max(map.width, width);
+      i++
+    ) {
+      const header =
+        i >= map.width
+          ? false
+          : table.nodeAt(map.map[start2 + i]).type == types.header_cell;
+      cells.push(
+        header
+          ? emptyHead || (emptyHead = types.header_cell.createAndFill())
+          : empty || (empty = types.cell.createAndFill()),
+      );
+    }
+    const emptyRow = types.row.create(null, Fragment2.from(cells)),
+      rows = [];
+    for (let i = map.height; i < height; i++) rows.push(emptyRow);
+    tr.insert(tr.mapping.slice(mapFrom).map(start + table.nodeSize - 3), rows);
+  }
+  return !!(empty || emptyHead);
+}
+function isolateHorizontal(tr, map, table, start, left, right, top, mapFrom) {
+  if (top == 0 || top == map.height) return false;
+  let found = false;
+  for (let col = left; col < right; col++) {
+    const index = top * map.width + col,
+      pos = map.map[index];
+    if (map.map[index - map.width] == pos) {
+      found = true;
+      const cell = table.nodeAt(pos);
+      const { top: cellTop, left: cellLeft } = map.findCell(pos);
+      tr.setNodeMarkup(tr.mapping.slice(mapFrom).map(pos + start), null, {
+        ...cell.attrs,
+        rowspan: top - cellTop,
+      });
+      tr.insert(
+        tr.mapping.slice(mapFrom).map(map.positionAt(top, cellLeft, table)),
+        cell.type.createAndFill({
+          ...cell.attrs,
+          rowspan: cellTop + cell.attrs.rowspan - top,
+        }),
+      );
+      col += cell.attrs.colspan - 1;
+    }
+  }
+  return found;
+}
+function isolateVertical(tr, map, table, start, top, bottom, left, mapFrom) {
+  if (left == 0 || left == map.width) return false;
+  let found = false;
+  for (let row = top; row < bottom; row++) {
+    const index = row * map.width + left,
+      pos = map.map[index];
+    if (map.map[index - 1] == pos) {
+      found = true;
+      const cell = table.nodeAt(pos);
+      const cellLeft = map.colCount(pos);
+      const updatePos = tr.mapping.slice(mapFrom).map(pos + start);
+      tr.setNodeMarkup(
+        updatePos,
+        null,
+        removeColSpan(
+          cell.attrs,
+          left - cellLeft,
+          cell.attrs.colspan - (left - cellLeft),
+        ),
+      );
+      tr.insert(
+        updatePos + cell.nodeSize,
+        cell.type.createAndFill(removeColSpan(cell.attrs, 0, left - cellLeft)),
+      );
+      row += cell.attrs.rowspan - 1;
+    }
+  }
+  return found;
+}
+function insertCells(state, dispatch, tableStart, rect, cells) {
+  let table = tableStart ? state.doc.nodeAt(tableStart - 1) : state.doc;
+  if (!table) {
+    throw new Error('No table found');
+  }
+  let map = TableMap.get(table);
+  const { top, left } = rect;
+  const right = left + cells.width,
+    bottom = top + cells.height;
+  const tr = state.tr;
+  let mapFrom = 0;
+  function recomp() {
+    table = tableStart ? tr.doc.nodeAt(tableStart - 1) : tr.doc;
+    if (!table) {
+      throw new Error('No table found');
+    }
+    map = TableMap.get(table);
+    mapFrom = tr.mapping.maps.length;
+  }
+  if (growTable(tr, map, table, tableStart, right, bottom, mapFrom)) recomp();
+  if (isolateHorizontal(tr, map, table, tableStart, left, right, top, mapFrom))
+    recomp();
+  if (
+    isolateHorizontal(tr, map, table, tableStart, left, right, bottom, mapFrom)
+  )
+    recomp();
+  if (isolateVertical(tr, map, table, tableStart, top, bottom, left, mapFrom))
+    recomp();
+  if (isolateVertical(tr, map, table, tableStart, top, bottom, right, mapFrom))
+    recomp();
+  for (let row = top; row < bottom; row++) {
+    const from = map.positionAt(row, left, table),
+      to = map.positionAt(row, right, table);
+    tr.replace(
+      tr.mapping.slice(mapFrom).map(from + tableStart),
+      tr.mapping.slice(mapFrom).map(to + tableStart),
+      new Slice2(cells.rows[row - top], 0, 0),
+    );
+  }
+  recomp();
+  tr.setSelection(
+    new CellSelection(
+      tr.doc.resolve(tableStart + map.positionAt(top, left, table)),
+      tr.doc.resolve(tableStart + map.positionAt(bottom - 1, right - 1, table)),
+    ),
+  );
+  dispatch(tr);
+}
+
+// src/input.ts
+var handleKeyDown = keydownHandler({
+  ArrowLeft: arrow('horiz', -1),
+  ArrowRight: arrow('horiz', 1),
+  ArrowUp: arrow('vert', -1),
+  ArrowDown: arrow('vert', 1),
+  Tab: tabulation(1),
+  'Shift-Tab': tabulation(-1),
+  'Shift-ArrowLeft': shiftArrow('horiz', -1),
+  'Shift-ArrowRight': shiftArrow('horiz', 1),
+  'Shift-ArrowUp': shiftArrow('vert', -1),
+  'Shift-ArrowDown': shiftArrow('vert', 1),
+  Backspace: deleteCellSelection,
+  'Mod-Backspace': deleteCellSelection,
+  Delete: deleteCellSelection,
+  'Mod-Delete': deleteCellSelection,
+});
+function maybeSetSelection(state, dispatch, selection) {
+  if (selection.eq(state.selection)) return false;
+  if (dispatch) dispatch(state.tr.setSelection(selection).scrollIntoView());
+  return true;
+}
+function tabulation(dir) {
+  return (state, dispatch, view) => {
+    if (!view) return false;
+    const sel = state.selection;
+    const r = state.doc.resolve(sel.head);
+    let d = r.depth;
+    let inCaption = false;
+    for (; d > 0; d--) {
+      const role = r.node(d).type.spec.tableRole;
+      if (role === 'row') break;
+      if (role === 'caption' && dir > 0) {
+        inCaption = true;
+        break;
+      }
+    }
+    const tableDepth2 = d - (inCaption ? 1 : 2);
+    const table = r.node(tableDepth2);
+    if (!table || table.type.spec.tableRole != 'table') return false;
+    const tableStart = r.start(tableDepth2);
+    const tmap = TableMap.get(table);
+    let nextCellPos;
+    if (inCaption) {
+      nextCellPos = tmap.map[0];
+    } else {
+      const map = tmap.map;
+      const cellStart = inCaption
+        ? tmap.positionAt(0, 0, table)
+        : r.start(d + 1);
+      const cellPos = cellStart - tableStart - 1;
+      let i;
+      for (
+        i = dir < 0 ? 0 : map.length - 1;
+        i >= 0 && i < map.length;
+        i -= dir
+      ) {
+        if (cellPos == map[i]) break;
+      }
+      if (i < 0 || i >= map.length) return false;
+      i += dir;
+      if (i < 0 || i >= map.length) return false;
+      nextCellPos = map[i];
+    }
+    if (nextCellPos) {
+      const cell = table.nodeAt(nextCellPos);
+      if (!cell) return false;
+      if (dispatch) {
+        const from = tableStart + nextCellPos;
+        const to = from + cell.nodeSize - 1;
+        dispatch(
+          state.tr.setSelection(TextSelection2.create(state.doc, from, to)),
+        );
+      }
+      return true;
+    }
+    return false;
+  };
+}
+function arrow(axis, dir) {
+  return (state, dispatch, view) => {
+    if (!view) return false;
+    const sel = state.selection;
+    if (sel instanceof CellSelection) {
+      return maybeSetSelection(
+        state,
+        dispatch,
+        Selection2.near(sel.$headCell, dir),
+      );
+    }
+    if (axis != 'horiz' && !sel.empty) return false;
+    const end = atEndOfCell(view, axis, dir, true);
+    if (axis == 'horiz') {
+      return maybeSetSelection(
+        state,
+        dispatch,
+        Selection2.near(state.doc.resolve(sel.head + dir), dir),
+      );
+    } else {
+      let newSel;
+      if (end) {
+        const $cell = state.doc.resolve(end);
+        if ($cell.node().type.spec.tableRole === 'row') {
+          const $next = nextCell($cell, axis, dir);
+          if ($next) newSel = Selection2.near($next, 1);
+          else if (dir < 0) {
+            const table = $cell.node(-2);
+            if (tableHasCaption(table))
+              newSel = Selection2.near(state.doc.resolve($cell.start(-2)), 1);
+            else
+              newSel = Selection2.near(state.doc.resolve($cell.before(-2)), -1);
+          } else
+            newSel = Selection2.near(state.doc.resolve($cell.after(-2)), 1);
+        } else {
+          if (dir < 0) {
+            newSel = Selection2.near(state.doc.resolve($cell.before()), -1);
+          } else {
+            const table = $cell.node();
+            const map = TableMap.get(table);
+            const pos = $cell.start() + map.positionAt(0, 0, table);
+            newSel = Selection2.near(state.doc.resolve(pos), 1);
+          }
+        }
+      } else {
+        if (dir > 0) {
+          const pos = sel.$anchor.after();
+          const table = state.doc.nodeAt(pos);
+          if (table && table.type.spec.tableRole === 'table')
+            newSel = Selection2.near(state.doc.resolve(pos), 1);
+        } else {
+          newSel = Selection2.near(state.doc.resolve(sel.$anchor.before()), -1);
+          const d = tableDepth(newSel.$anchor);
+          if (d >= 0) {
+            const table = newSel.$anchor.node(d);
+            const map = TableMap.get(table);
+            const pos =
+              newSel.$anchor.start(d) +
+              map.positionAt(map.height - 1, 0, table);
+            newSel = Selection2.near(state.doc.resolve(pos), 1);
+          }
+        }
+      }
+      return newSel ? maybeSetSelection(state, dispatch, newSel) : false;
+    }
+  };
+}
+function shiftArrow(axis, dir) {
+  return (state, dispatch, view) => {
+    if (!view) return false;
+    const sel = state.selection;
+    let cellSel;
+    if (sel instanceof CellSelection) {
+      cellSel = sel;
+    } else {
+      const end = atEndOfCell(view, axis, dir);
+      if (end == null) return false;
+      cellSel = new CellSelection(state.doc.resolve(end));
+    }
+    const $head = nextCell(cellSel.$headCell, axis, dir);
+    if (!$head) return false;
+    return maybeSetSelection(
+      state,
+      dispatch,
+      new CellSelection(cellSel.$anchorCell, $head),
+    );
+  };
+}
+function deleteCellSelection(state, dispatch) {
+  const sel = state.selection;
+  if (!(sel instanceof CellSelection)) return false;
+  if (dispatch) {
+    const tr = state.tr;
+    const baseContent = tableNodeTypes(state.schema).cell.createAndFill()
+      .content;
+    sel.forEachCell((cell, pos) => {
+      if (!cell.content.eq(baseContent))
+        tr.replace(
+          tr.mapping.map(pos + 1),
+          tr.mapping.map(pos + cell.nodeSize - 1),
+          new Slice3(baseContent, 0, 0),
+        );
+    });
+    if (tr.docChanged) dispatch(tr);
+  }
+  return true;
+}
+function handleTripleClick(view, pos) {
+  const doc = view.state.doc,
+    $cell = cellAround(doc.resolve(pos));
+  if (!$cell) return false;
+  view.dispatch(view.state.tr.setSelection(new CellSelection($cell)));
+  return true;
+}
+function handlePaste(view, _, slice) {
+  if (!isInTable(view.state)) return false;
+  let cells = pastedCells(slice);
+  const sel = view.state.selection;
+  if (sel instanceof CellSelection) {
+    if (!cells)
+      cells = {
+        width: 1,
+        height: 1,
+        rows: [
+          Fragment3.from(
+            fitSlice(tableNodeTypes(view.state.schema).cell, slice),
+          ),
+        ],
+      };
+    const table = sel.$anchorCell.node(-2);
+    const start = sel.$anchorCell.start(-2);
+    const rect = TableMap.get(table).rectBetween(
+      sel.$anchorCell.pos - start,
+      sel.$headCell.pos - start,
+    );
+    cells = clipCells(cells, rect.right - rect.left, rect.bottom - rect.top);
+    insertCells(view.state, view.dispatch, start, rect, cells);
+    return true;
+  } else if (cells) {
+    const $cell = selectionCell(view.state);
+    const start = $cell.start(-2);
+    insertCells(
+      view.state,
+      view.dispatch,
+      start,
+      TableMap.get($cell.node(-2)).findCell($cell.pos - start),
+      cells,
+    );
+    return true;
+  } else {
+    return false;
+  }
+}
+function handleMouseDown(view, startEvent) {
+  var _a;
+  if (startEvent.ctrlKey || startEvent.metaKey) return;
+  const startDOMCell = domInCell(view, startEvent.target);
+  let $anchor;
+  if (startEvent.shiftKey && view.state.selection instanceof CellSelection) {
+    setCellSelection(view.state.selection.$anchorCell, startEvent);
+    startEvent.preventDefault();
+  } else if (
+    startEvent.shiftKey &&
+    startDOMCell &&
+    ($anchor = cellAround(view.state.selection.$anchor)) != null &&
+    ((_a = cellUnderMouse(view, startEvent)) == null ? void 0 : _a.pos) !=
+      $anchor.pos
+  ) {
+    setCellSelection($anchor, startEvent);
+    startEvent.preventDefault();
+  } else if (!startDOMCell) {
+    return;
+  }
+  function setCellSelection($anchor2, event) {
+    let $head = cellUnderMouse(view, event);
+    const starting = tableEditingKey.getState(view.state) == null;
+    if (!$head || !inSameTable($anchor2, $head)) {
+      if (starting) $head = $anchor2;
+      else return;
+    }
+    const selection = new CellSelection($anchor2, $head);
+    if (starting || !view.state.selection.eq(selection)) {
+      const tr = view.state.tr.setSelection(selection);
+      if (starting) tr.setMeta(tableEditingKey, $anchor2.pos);
+      view.dispatch(tr);
+    }
+  }
+  function stop() {
+    view.root.removeEventListener('mouseup', stop);
+    view.root.removeEventListener('dragstart', stop);
+    view.root.removeEventListener('mousemove', move);
+    if (tableEditingKey.getState(view.state) != null)
+      view.dispatch(view.state.tr.setMeta(tableEditingKey, -1));
+  }
+  function move(_event) {
+    const event = _event;
+    const anchor = tableEditingKey.getState(view.state);
+    let $anchor2;
+    if (anchor != null) {
+      $anchor2 = view.state.doc.resolve(anchor);
+    } else if (domInCell(view, event.target) != startDOMCell) {
+      $anchor2 = cellUnderMouse(view, startEvent);
+      if (!$anchor2) return stop();
+    }
+    if ($anchor2) setCellSelection($anchor2, event);
+  }
+  view.root.addEventListener('mouseup', stop);
+  view.root.addEventListener('dragstart', stop);
+  view.root.addEventListener('mousemove', move);
+}
+function atEndOfCell(view, axis, dir, checkCaption = false) {
+  if (!(view.state.selection instanceof TextSelection2)) return null;
+  const { $head } = view.state.selection;
+  for (let d = $head.depth - 1; d >= 0; d--) {
+    const parent = $head.node(d),
+      index = dir < 0 ? $head.index(d) : $head.indexAfter(d);
+    if (index != (dir < 0 ? 0 : parent.childCount)) return null;
+    const alsoInCaption =
+      checkCaption && parent.type.spec.tableRole == 'caption';
+    if (
+      parent.type.spec.tableRole == 'cell' ||
+      parent.type.spec.tableRole == 'header_cell' ||
+      alsoInCaption
+    ) {
+      const cellPos = $head.before(d);
+      const dirStr =
+        axis == 'vert' ? (dir > 0 ? 'down' : 'up') : dir > 0 ? 'right' : 'left';
+      return view.endOfTextblock(dirStr) ? cellPos : null;
+    }
+  }
+  return null;
+}
+function domInCell(view, dom) {
+  for (; dom && dom != view.dom; dom = dom.parentNode) {
+    if (dom.nodeName == 'TD' || dom.nodeName == 'TH') {
+      return dom;
+    }
+  }
+  return null;
+}
+function cellUnderMouse(view, event) {
+  const mousePos = view.posAtCoords({
+    left: event.clientX,
+    top: event.clientY,
+  });
+  if (!mousePos) return null;
+  return mousePos ? cellAround(view.state.doc.resolve(mousePos.pos)) : null;
+}
+
+// src/columnresizing.ts
+import { Plugin, PluginKey as PluginKey3 } from 'prosemirror-state';
+import {
+  Decoration as Decoration2,
+  DecorationSet as DecorationSet2,
+} from 'prosemirror-view';
+var columnResizingPluginKey = new PluginKey3('tableColumnResizing');
+var SPEC_COL_WIDTHS = 'colgroup';
+var SPEC_TABLE_WIDTH = 'tablewidth';
+var DEFAULT_HANDLE_WIDTH = 5;
+var DEFAULT_CELL_MIN_WIDTH = 25;
+var DEFAULT_LAST_COLUMN_RESIZABLE = true;
+function columnResizing({
+  handleWidth = DEFAULT_HANDLE_WIDTH,
+  cellMinWidth = DEFAULT_CELL_MIN_WIDTH,
+  lastColumnResizable = DEFAULT_LAST_COLUMN_RESIZABLE,
+} = {}) {
+  const plugin = new Plugin({
+    options: {
+      handleWidth,
+      cellMinWidth,
+      lastColumnResizable,
+    },
+    key: columnResizingPluginKey,
+    state: {
+      init(_, state) {
+        return new ResizeState(
+          -1,
+          false,
+          DecorationSet2.create(
+            state.doc,
+            createTableDecorations(state.doc, cellMinWidth),
+          ),
+        );
+      },
+      apply(tr, prev) {
+        return prev.apply(tr);
+      },
+    },
+    props: {
+      attributes: state => {
+        const pluginState = columnResizingPluginKey.getState(state);
+        return pluginState && pluginState.activeHandle > -1
+          ? { class: 'resize-cursor' }
+          : {};
+      },
+      handleDOMEvents: {
+        mousemove: (view, event) => {
+          handleMouseMove(
+            view,
+            event,
+            handleWidth,
+            cellMinWidth,
+            lastColumnResizable,
+          );
+        },
+        mouseleave: view => {
+          handleMouseLeave(view);
+        },
+        mousedown: (view, event) => {
+          handleMouseDown2(view, event, cellMinWidth);
+        },
+      },
+      decorations: state => {
+        const pluginState = columnResizingPluginKey.getState(state);
+        let decos = DecorationSet2.empty;
+        if (pluginState) {
+          decos = decos.add(
+            state.doc,
+            pluginState.tableDecos.find(void 0, void 0, () => true),
+          );
+          if (pluginState.activeHandle > -1) {
+            decos = decos.add(
+              state.doc,
+              handleDecorations(state, pluginState.activeHandle),
+            );
+          }
+        }
+        return decos;
+      },
+      // nodeViews: {},
+    },
+  });
+  return plugin;
+}
+var ResizeState = class {
+  constructor(activeHandle, dragging, tableDecos) {
+    this.activeHandle = activeHandle;
+    this.dragging = dragging;
+    this.tableDecos = tableDecos;
+  }
+  apply(tr) {
+    const state = this;
+    if (tr.docChanged) {
+      state.tableDecos = state.tableDecos.map(tr.mapping, tr.doc);
+    }
+    const action = tr.getMeta(columnResizingPluginKey);
+    if (action) {
+      if (action.setHandle != null)
+        return new ResizeState(action.setHandle, false, state.tableDecos);
+      if (action.setDragging !== void 0)
+        return new ResizeState(
+          state.activeHandle,
+          action.setDragging,
+          state.tableDecos,
+        );
+      let decos = state.tableDecos;
+      if (action.setColWidths) {
+        const scws = action.setColWidths;
+        scws.forEach(scw => {
+          const removed = decos.find(
+            scw.tableStart - 1,
+            scw.tableStart,
+            spec => spec.type === SPEC_COL_WIDTHS,
+          );
+          if (removed) decos = decos.remove(removed);
+          const deco = colgroupDecoration(scw.tableStart, scw.colWidths);
+          decos = decos.add(tr.doc, [deco]);
+        });
+      }
+      if (action.setTableWidth) {
+        let decos2 = state.tableDecos;
+        const stws = action.setTableWidth;
+        stws.forEach(stw => {
+          const removed = decos2.find(
+            stw.pos,
+            stw.pos + 1,
+            spec => spec.type === SPEC_TABLE_WIDTH,
+          );
+          if (removed) {
+            const newDecos = [];
+            removed.forEach(r => {
+              const pos = tr.mapping.map(stw.pos);
+              const table = tr.doc.nodeAt(pos);
+              if (
+                (table == null ? void 0 : table.type.spec.tableRole) === 'table'
+              ) {
+                newDecos.push(
+                  tableWidthDecoration(pos, pos + table.nodeSize, stw.css),
+                );
+              }
+            });
+            if (newDecos) decos2 = decos2.remove(removed).add(tr.doc, newDecos);
+          }
+        });
+      }
+      if (decos !== state.tableDecos)
+        return new ResizeState(state.activeHandle, state.dragging, decos);
+    }
+    if (tr.docChanged && state.activeHandle > -1) {
+      let handle = tr.mapping.map(state.activeHandle, -1);
+      if (!pointsAtCell(tr.doc.resolve(handle))) {
+        handle = -1;
+      }
+      return new ResizeState(handle, state.dragging, state.tableDecos);
+    }
+    return state;
+  }
+};
+function handleMouseMove(
+  view,
+  event,
+  handleWidth,
+  cellMinWidth,
+  lastColumnResizable,
+) {
+  const pluginState = columnResizingPluginKey.getState(view.state);
+  if (!pluginState) return;
+  if (!pluginState.dragging) {
+    const target = domCellAround(event.target);
+    let cell = -1;
+    if (target) {
+      const { left, right } = target.getBoundingClientRect();
+      if (event.clientX - left <= handleWidth)
+        cell = edgeCell(view, event, 'left');
+      else if (right - event.clientX <= handleWidth)
+        cell = edgeCell(view, event, 'right');
+    }
+    if (cell != pluginState.activeHandle) {
+      if (!lastColumnResizable && cell !== -1) {
+        const $cell = view.state.doc.resolve(cell);
+        const table = $cell.node(-2);
+        const map = TableMap.get(table);
+        const tableStart = $cell.start(-2);
+        const col =
+          map.colCount($cell.pos - tableStart) +
+          $cell.nodeAfter.attrs.colspan -
+          1;
+        if (col == map.width - 1) {
+          return;
+        }
+      }
+      updateHandle(view, cell);
+    }
+  }
+}
+function handleMouseLeave(view) {
+  const pluginState = columnResizingPluginKey.getState(view.state);
+  if (pluginState && pluginState.activeHandle > -1 && !pluginState.dragging)
+    updateHandle(view, -1);
+}
+function handleMouseDown2(view, event, cellMinWidth) {
+  const pluginState = columnResizingPluginKey.getState(view.state);
+  if (!pluginState || pluginState.activeHandle == -1 || pluginState.dragging)
+    return false;
+  const cell = view.state.doc.nodeAt(pluginState.activeHandle);
+  const width = currentColWidth(view, pluginState.activeHandle, cell.attrs);
+  view.dispatch(
+    view.state.tr.setMeta(columnResizingPluginKey, {
+      setDragging: { startX: event.clientX, startWidth: width },
+    }),
+  );
+  function finish(event2) {
+    window.removeEventListener('mouseup', finish);
+    window.removeEventListener('mousemove', move);
+    const pluginState2 = columnResizingPluginKey.getState(view.state);
+    if (pluginState2 == null ? void 0 : pluginState2.dragging) {
+      updateColumnWidth(
+        view,
+        pluginState2.activeHandle,
+        draggedWidth(pluginState2.dragging, event2, cellMinWidth),
+      );
+      view.dispatch(
+        view.state.tr.setMeta(columnResizingPluginKey, { setDragging: null }),
+      );
+    }
+  }
+  function move(event2) {
+    if (!event2.which) return finish(event2);
+    const pluginState2 = columnResizingPluginKey.getState(view.state);
+    if (!pluginState2) return;
+    if (pluginState2.dragging) {
+      const dragged = draggedWidth(pluginState2.dragging, event2, cellMinWidth);
+      displayColumnWidth(
+        view,
+        pluginState2.activeHandle,
+        dragged,
+        cellMinWidth,
+      );
+    }
+  }
+  window.addEventListener('mouseup', finish);
+  window.addEventListener('mousemove', move);
+  event.preventDefault();
+  return true;
+}
+function currentColWidth(view, cellPos, { colspan, colwidth }) {
+  const width = colwidth && colwidth[colwidth.length - 1];
+  if (width) return width;
+  const dom = view.domAtPos(cellPos);
+  const node = dom.node.childNodes[dom.offset];
+  let domWidth = node.offsetWidth,
+    parts = colspan;
+  if (colwidth) {
+    for (let i = 0; i < colspan; i++)
+      if (colwidth[i]) {
+        domWidth -= colwidth[i];
+        parts--;
+      }
+  }
+  return domWidth / parts;
+}
+function domCellAround(target) {
+  while (target && target.nodeName != 'TD' && target.nodeName != 'TH')
+    target =
+      target.classList && target.classList.contains('ProseMirror')
+        ? null
+        : target.parentNode;
+  return target;
+}
+function edgeCell(view, event, side) {
+  const found = view.posAtCoords({ left: event.clientX, top: event.clientY });
+  if (!found) return -1;
+  const { pos } = found;
+  const $cell = cellAround(view.state.doc.resolve(pos));
+  if (!$cell) return -1;
+  if (side == 'right') return $cell.pos;
+  const map = TableMap.get($cell.node(-2)),
+    start = $cell.start(-2);
+  const index = map.map.indexOf($cell.pos - start);
+  return index % map.width == 0 ? -1 : start + map.map[index - 1];
+}
+function draggedWidth(dragging, event, cellMinWidth) {
+  const offset = event.clientX - dragging.startX;
+  return Math.max(cellMinWidth, dragging.startWidth + offset);
+}
+function updateHandle(view, value) {
+  view.dispatch(
+    view.state.tr.setMeta(columnResizingPluginKey, { setHandle: value }),
+  );
+}
+function updateColumnWidth(view, cell, width) {
+  const $cell = view.state.doc.resolve(cell);
+  const table = $cell.node(-2),
+    map = TableMap.get(table),
+    start = $cell.start(-2);
+  const col =
+    map.colCount($cell.pos - start) + $cell.nodeAfter.attrs.colspan - 1;
+  const tr = view.state.tr;
+  for (let row = 0; row < map.height; row++) {
+    const mapIndex = row * map.width + col;
+    if (row && map.map[mapIndex] == map.map[mapIndex - map.width]) continue;
+    const pos = map.map[mapIndex];
+    const attrs = table.nodeAt(pos).attrs;
+    const index = attrs.colspan == 1 ? 0 : col - map.colCount(pos);
+    if (attrs.colwidth && attrs.colwidth[index] == width) continue;
+    const colwidth = attrs.colwidth
+      ? attrs.colwidth.slice()
+      : zeroes(attrs.colspan);
+    colwidth[index] = width;
+    tr.setNodeMarkup(start + pos, null, { ...attrs, colwidth });
+  }
+  if (tr.docChanged) view.dispatch(tr);
+}
+function displayColumnWidth(view, cell, width, cellMinWidth) {
+  const $cell = view.state.doc.resolve(cell);
+  const table = $cell.node(-2),
+    tableStart = $cell.start(-2);
+  const col =
+    TableMap.get(table).colCount($cell.pos - tableStart) +
+    $cell.nodeAfter.attrs.colspan -
+    1;
+  let dom = view.domAtPos($cell.start(-2)).node;
+  while (dom && dom.nodeName != 'TABLE') {
+    dom = dom.parentNode;
+  }
+  if (!dom) return;
+  updateColumnsOnResize(view, table, tableStart, cellMinWidth, col, width);
+}
+function zeroes(n) {
+  return Array(n).fill(0);
+}
+function handleDecorations(state, cell) {
+  const decorations = [];
+  const $cell = state.doc.resolve(cell);
+  const table = $cell.node(-2);
+  if (!table) {
+    return [];
+  }
+  const map = TableMap.get(table);
+  const start = $cell.start(-2);
+  const col = map.colCount($cell.pos - start) + $cell.nodeAfter.attrs.colspan;
+  for (let row = 0; row < map.height; row++) {
+    const index = col + row * map.width - 1;
+    if (
+      (col == map.width || map.map[index] != map.map[index + 1]) &&
+      (row == 0 || map.map[index - 1] != map.map[index - 1 - map.width])
+    ) {
+      const cellPos = map.map[index];
+      const pos = start + cellPos + table.nodeAt(cellPos).nodeSize - 1;
+      const dom = document.createElement('div');
+      dom.className = 'column-resize-handle';
+      decorations.push(Decoration2.widget(pos, dom));
+    }
+  }
+  return decorations;
+}
+function colgroupDecoration(tableStart, colWidths) {
+  return Decoration2.widget(
+    tableStart,
+    (view, getPos) => {
+      const colgroup = document.createElement('colgroup');
+      for (let c = 0; c < colWidths.length; c++) {
+        const colElement = document.createElement('col');
+        colElement.style.width = colWidths[c];
+        colgroup.appendChild(colElement);
+      }
+      return colgroup;
+    },
+    {
+      type: SPEC_COL_WIDTHS,
+      colWidths,
+    },
+  );
+}
+function tableWidthDecoration(from, to, css) {
+  const style = Object.entries(css)
+    .map(([prop, value]) => `${prop}: ${value}`)
+    .join('; ');
+  return Decoration2.node(from, to, { style }, { type: SPEC_TABLE_WIDTH });
+}
+function tableDecorationsCallback(doc, decos, cellMinWidth) {
+  return (node, pos) => {
+    if (node.type.spec.tableRole === 'table') {
+      const tableStart = pos + 1;
+      const resolved = doc.resolve(tableStart);
+      decos.push(tableWidthDecoration(resolved.before(), resolved.after(), {}));
+      const { colWidths } = updateColumnsOnResize(
+        null,
+        node,
+        tableStart,
+        cellMinWidth,
+      );
+      decos.push(colgroupDecoration(tableStart, colWidths));
+      return false;
+    }
+    return true;
+  };
+}
+function createTableDecorations(doc, cellMinWidth = 0, from, to) {
+  let decos = [];
+  if (from && to)
+    doc.nodesBetween(
+      from,
+      to,
+      tableDecorationsCallback(doc, decos, cellMinWidth),
+    );
+  else doc.descendants(tableDecorationsCallback(doc, decos, cellMinWidth));
+  return decos;
+}
+function updateColumnsOnResize(
+  view,
+  table,
+  tableStart,
+  cellMinWidth,
+  overrideCol,
+  overrideValue,
+) {
+  const { setColWidths, setTableWidth } = getTableWidths(
+    table,
+    tableStart,
+    cellMinWidth,
+    overrideCol,
+    overrideValue,
+  );
+  const colWidths = setColWidths[0].colWidths;
+  const tableWidth = setTableWidth[0].width + 'px';
+  if (view) {
+    view.dispatch(
+      view.state.tr.setMeta(columnResizingPluginKey, {
+        setColWidths,
+        setTableWidth,
+      }),
+    );
+  }
+  return { colWidths, tableWidth };
+}
+function getCellMinWidth(state) {
+  const plugin = columnResizingPluginKey.get(state);
+  return (plugin && plugin.spec.options.cellMinWidth) || 25;
+}
+function getTableWidths(
+  table,
+  tableStart,
+  cellMinWidth,
+  overrideCol,
+  overrideValue,
+) {
+  let totalWidth = 0;
+  let fixedWidth = true;
+  const row = getRow(table, 0).node;
+  if (!row) return;
+  const colWidths = [];
+  for (let i = 0, col = 0; i < row.childCount; i++) {
+    const { colspan, colwidth } = row.child(i).attrs;
+    for (let j = 0; j < colspan; j++, col++) {
+      const hasWidth =
+        overrideCol == col ? overrideValue : colwidth && colwidth[j];
+      colWidths.push(hasWidth ? hasWidth + 'px' : '');
+      totalWidth += hasWidth || cellMinWidth;
+      if (!hasWidth) fixedWidth = false;
+    }
+  }
+  const setColWidths = [{ tableStart, colWidths }];
+  const pos = tableStart - 1;
+  const tableWidth = totalWidth + 'px';
+  const setTableWidth = [
+    fixedWidth
+      ? { pos, width: totalWidth, css: { 'min-width': '', width: tableWidth } }
+      : { pos, width: totalWidth, css: { 'min-width': tableWidth, width: '' } },
+  ];
+  return { setColWidths, setTableWidth };
+}
+
+// src/commands.ts
+import { Fragment as Fragment4 } from 'prosemirror-model';
+import {
+  NodeSelection as NodeSelection4,
+  TextSelection as TextSelection3,
+} from 'prosemirror-state';
+function selectedRect(state) {
+  const sel = state.selection;
+  const $pos = selectionCell(state);
+  const table = $pos.node(-2);
+  const tableStart = $pos.start(-2);
+  const map = TableMap.get(table);
+  const rect =
+    sel instanceof CellSelection
+      ? map.rectBetween(
+          sel.$anchorCell.pos - tableStart,
+          sel.$headCell.pos - tableStart,
+        )
+      : map.findCell($pos.pos - tableStart);
+  return { ...rect, tableStart, map, table };
+}
+function addColumn(tr, { map, tableStart, table }, col) {
+  let refColumn = col > 0 ? -1 : 0;
+  if (columnIsHeader(map, table, col + refColumn)) {
+    refColumn = col == 0 || col == map.width ? null : 0;
+  }
+  for (let row = 0; row < map.height; row++) {
+    const index = row * map.width + col;
+    if (col > 0 && col < map.width && map.map[index - 1] == map.map[index]) {
+      const pos = map.map[index];
+      const cell = table.nodeAt(pos);
+      tr.setNodeMarkup(
+        tr.mapping.map(tableStart + pos),
+        null,
+        addColSpan(cell.attrs, col - map.colCount(pos)),
+      );
+      row += cell.attrs.rowspan - 1;
+    } else {
+      const type =
+        refColumn == null
+          ? tableNodeTypes(table.type.schema).cell
+          : table.nodeAt(map.map[index + refColumn]).type;
+      const pos = map.positionAt(row, col, table);
+      tr.insert(tr.mapping.map(tableStart + pos), type.createAndFill());
+    }
+  }
+  return tr;
+}
+function addColumnBefore(state, dispatch, view) {
+  if (!isInTable(state)) return false;
+  if (dispatch) {
+    const rect = selectedRect(state);
+    dispatch(addColumn(state.tr, rect, rect.left));
+  }
+  return true;
+}
+function addColumnAfter(state, dispatch, view) {
+  if (!isInTable(state)) return false;
+  if (dispatch) {
+    const rect = selectedRect(state);
+    dispatch(addColumn(state.tr, rect, rect.right));
+  }
+  return true;
+}
+function removeColumn(tr, { map, table, tableStart }, col) {
+  const mapStart = tr.mapping.maps.length;
+  for (let row = 0; row < map.height; ) {
+    const index = row * map.width + col;
+    const pos = map.map[index];
+    const cell = table.nodeAt(pos);
+    const attrs = cell.attrs;
+    if (
+      (col > 0 && map.map[index - 1] == pos) ||
+      (col < map.width - 1 && map.map[index + 1] == pos)
+    ) {
+      tr.setNodeMarkup(
+        tr.mapping.slice(mapStart).map(tableStart + pos),
+        null,
+        removeColSpan(attrs, col - map.colCount(pos)),
+      );
+    } else {
+      const start = tr.mapping.slice(mapStart).map(tableStart + pos);
+      tr.delete(start, start + cell.nodeSize);
+    }
+    row += attrs.rowspan;
+  }
+}
+function deleteColumn(state, dispatch, view) {
+  if (!isInTable(state)) return false;
+  if (dispatch) {
+    const rect = selectedRect(state);
+    const tr = state.tr;
+    if (rect.left == 0 && rect.right == rect.map.width) return false;
+    for (let i = rect.right - 1; ; i--) {
+      removeColumn(tr, rect, i);
+      if (i == rect.left) break;
+      const table = rect.tableStart
+        ? tr.doc.nodeAt(rect.tableStart - 1)
+        : tr.doc;
+      if (!table) {
+        throw RangeError('No table found');
+      }
+      rect.table = table;
+      rect.map = TableMap.get(table);
+    }
+    dispatch(tr);
+  }
+  return true;
+}
+function rowIsHeader(map, table, row) {
+  var _a;
+  const headerCell = tableNodeTypes(table.type.schema).header_cell;
+  for (let col = 0; col < map.width; col++)
+    if (
+      ((_a = table.nodeAt(map.map[col + row * map.width])) == null
+        ? void 0
+        : _a.type) != headerCell
+    )
+      return false;
+  return true;
+}
+function addRow(tr, { bottom, map, tableStart, table }, row) {
+  var _a;
+  let rPos = rowPos(table, row) + tableStart;
+  if (bottom === row && isRowLastInSection(table, row - 1)) rPos -= 2;
+  const cells = [];
+  let refRow = row > 0 ? -1 : 0;
+  if (rowIsHeader(map, table, row + refRow))
+    refRow = row == 0 || row == map.height ? null : 0;
+  const srows = map.sectionRows;
+  for (let s = 0, acc = 0; s < srows.length; s++) {
+    acc += srows[s];
+    if (row < acc || s === srows.length - 1) {
+      srows[s]++;
+      break;
+    }
+  }
+  for (let col = 0, index = map.width * row; col < map.width; col++, index++) {
+    if (
+      row > 0 &&
+      row < map.height &&
+      map.map[index] == map.map[index - map.width]
+    ) {
+      const pos = map.map[index];
+      const attrs = table.nodeAt(pos).attrs;
+      tr.setNodeMarkup(tableStart + pos, null, {
+        ...attrs,
+        rowspan: attrs.rowspan + 1,
+      });
+      col += attrs.colspan - 1;
+    } else {
+      const type =
+        refRow == null
+          ? tableNodeTypes(table.type.schema).cell
+          : (_a = table.nodeAt(map.map[index + refRow * map.width])) == null
+          ? void 0
+          : _a.type;
+      const node = type == null ? void 0 : type.createAndFill();
+      if (node) cells.push(node);
+    }
+  }
+  tr.insert(rPos, tableNodeTypes(table.type.schema).row.create(null, cells));
+  return tr;
+}
+function addRowBefore(state, dispatch) {
+  if (!isInTable(state)) return false;
+  if (dispatch) {
+    const rect = selectedRect(state);
+    dispatch(addRow(state.tr, rect, rect.top));
+  }
+  return true;
+}
+function addRowAfter(state, dispatch) {
+  if (!isInTable(state)) return false;
+  if (dispatch) {
+    const rect = selectedRect(state);
+    dispatch(addRow(state.tr, rect, rect.bottom));
+  }
+  return true;
+}
+function removeRow(tr, { map, table, tableStart }, row) {
+  const { node: rNode, pos: rPos } = getRow(table, row);
+  const mapFrom = tr.mapping.maps.length;
+  const from = rPos + tableStart;
+  const to = from + rNode.nodeSize - 1;
+  tr.delete(from, to);
+  for (let col = 0, index = row * map.width; col < map.width; col++, index++) {
+    const pos = map.map[index];
+    if (row > 0 && pos == map.map[index - map.width]) {
+      const attrs = table.nodeAt(pos).attrs;
+      tr.setNodeMarkup(tr.mapping.slice(mapFrom).map(pos + tableStart), null, {
+        ...attrs,
+        rowspan: attrs.rowspan - 1,
+      });
+      col += attrs.colspan - 1;
+    } else if (row < map.width && pos == map.map[index + map.width]) {
+      const cell = table.nodeAt(pos);
+      const attrs = cell.attrs;
+      const copy = cell.type.create(
+        { ...attrs, rowspan: cell.attrs.rowspan - 1 },
+        cell.content,
+      );
+      const newPos = map.positionAt(row + 1, col, table);
+      tr.insert(tr.mapping.slice(mapFrom).map(tableStart + newPos), copy);
+      col += attrs.colspan - 1;
+    }
+  }
+}
+function removeSection(tr, { map, table, tableStart }, section) {
+  let pos = 0;
+  let s = -1;
+  for (let i = 0; i < table.childCount; i++) {
+    const child = table.child(i);
+    if (isTableSection(child)) {
+      s++;
+      if (s == section) {
+        tr.delete(tableStart + pos, tableStart + pos + child.nodeSize);
+        return;
+      }
+    }
+    pos += child.nodeSize;
+  }
+}
+function deleteRow(state, dispatch) {
+  if (!isInTable(state)) return false;
+  if (dispatch) {
+    const rect = selectedRect(state),
+      tr = state.tr;
+    if (rect.top == 0 && rect.bottom == rect.map.height) return false;
+    const sectionRows = rect.map.sectionRows;
+    const sectionBottom = [sectionRows[0] || 0];
+    for (let s2 = 1; s2 < sectionRows.length; s2++)
+      sectionBottom[s2] = sectionBottom[s2 - 1] + sectionRows[s2];
+    let s = sectionRows.length - 1;
+    while (s > 0 && sectionBottom[s] > rect.bottom) s--;
+    for (let i = rect.bottom - 1; ; i--) {
+      const firstRowOfSection = sectionBottom[s] - sectionRows[s];
+      if (i + 1 === sectionBottom[s] && rect.top <= firstRowOfSection) {
+        removeSection(tr, rect, s);
+        i = firstRowOfSection;
+        s--;
+      } else {
+        removeRow(tr, rect, i);
+      }
+      if (i <= rect.top) break;
+      const table = rect.tableStart
+        ? tr.doc.nodeAt(rect.tableStart - 1)
+        : tr.doc;
+      if (!table) {
+        throw RangeError('No table found');
+      }
+      rect.table = table;
+      rect.map = TableMap.get(rect.table);
+    }
+    dispatch(tr);
+  }
+  return true;
+}
+function addCaption(state, dispatch) {
+  const $anchor = state.selection.$anchor;
+  const d = tableDepth($anchor);
+  if (d < 0) return false;
+  const table = $anchor.node(d);
+  if (tableHasCaption(table)) return false;
+  if (dispatch) {
+    let pos = $anchor.start(d);
+    const types = tableNodeTypes(state.schema);
+    const caption = types.caption.createAndFill();
+    dispatch(state.tr.insert(pos, caption));
+  }
+  return true;
+}
+function deleteCaption(state, dispatch) {
+  const $anchor = state.selection.$anchor;
+  const d = tableDepth($anchor);
+  if (d < 0) return false;
+  const table = $anchor.node(d);
+  if (!tableHasCaption(table)) return false;
+  if (dispatch) {
+    let pos = $anchor.start(d);
+    const size = table.firstChild.nodeSize;
+    dispatch(state.tr.delete(pos, pos + size));
+  }
+  return true;
+}
+function createSection(schema, role, width, cellRole) {
+  const types = tableNodeTypes(schema);
+  const cells = [];
+  const cellType =
+    (cellRole && types[cellRole]) || types.cell || types.header_cell;
+  for (let i = 0; i < width; i++) cells.push(cellType.createAndFill());
+  return types[role].createAndFill(null, types.row.createAndFill(null, cells));
+}
+function addTableHead(state, dispatch) {
+  const $anchor = state.selection.$anchor;
+  const d = tableDepth($anchor);
+  if (d < 0) return false;
+  const table = $anchor.node(d);
+  if (tableHasHead(table)) return false;
+  if (dispatch) {
+    let pos = $anchor.start(d);
+    const firstChild = table.child(0);
+    if (firstChild && firstChild.type.spec.tableRole === 'caption')
+      pos += firstChild.nodeSize;
+    const map = TableMap.get(table);
+    const head = createSection(state.schema, 'head', map.width, 'header_cell');
+    dispatch(state.tr.insert(pos, head));
+  }
+  return true;
+}
+function addTableFoot(state, dispatch) {
+  const $anchor = state.selection.$anchor;
+  const d = tableDepth($anchor);
+  if (d < 0) return false;
+  const table = $anchor.node(d);
+  if (tableHasFoot(table)) return false;
+  if (dispatch) {
+    const pos = $anchor.end(d);
+    const map = TableMap.get(table);
+    const foot = createSection(state.schema, 'foot', map.width, 'header_cell');
+    dispatch(state.tr.insert(pos, foot));
+  }
+  return true;
+}
+function addBodyBefore(state, dispatch) {
+  if (!isInTable(state)) return false;
+  const rect = selectedRect(state);
+  const { map, table, tableStart } = rect;
+  const firstSection = map.sectionsInRect(rect)[0];
+  if (firstSection === void 0 || (firstSection === 0 && tableHasHead(table)))
+    return false;
+  if (dispatch) {
+    let pos = tableStart,
+      s = -1;
+    for (let i = 0; i < table.childCount; i++) {
+      const child = table.child(i);
+      if (child.type.spec.tableRole != 'caption') s++;
+      if (s === firstSection) break;
+      pos += child.nodeSize;
+    }
+    const map2 = TableMap.get(table);
+    const body = createSection(state.schema, 'body', map2.width);
+    dispatch(state.tr.insert(pos, body));
+  }
+  return true;
+}
+function addBodyAfter(state, dispatch) {
+  if (!isInTable(state)) return false;
+  const rect = selectedRect(state);
+  const { map, table, tableStart } = rect;
+  const sections = map.sectionsInRect(rect);
+  const lastSection = sections[sections.length - 1];
+  if (lastSection === map.sectionRows.length - 1 && tableHasFoot(table))
+    return false;
+  if (dispatch) {
+    let pos = tableStart - 1,
+      s = -1;
+    for (let i = 0; i < table.childCount; i++) {
+      const child = table.child(i);
+      pos += child.nodeSize;
+      if (child.type.spec.tableRole != 'caption') s++;
+      if (s === lastSection) break;
+    }
+    const map2 = TableMap.get(table);
+    const body = createSection(state.schema, 'body', map2.width);
+    dispatch(state.tr.insert(pos, body));
+  }
+  return true;
+}
+function fixRowCells(row, headerCellType) {
+  const newCells = [];
+  for (let i = 0; i < row.childCount; i++) {
+    const cell = row.child(i);
+    newCells.push(
+      cell.type.spec.tableRole === 'header_cell'
+        ? cell
+        : headerCellType.create(cell.attrs, cell.content),
+    );
+  }
+  return row.copy(Fragment4.from(newCells));
+}
+function makeSection(role, state, dispatch) {
+  if (!isInTable(state)) return false;
+  const rect = selectedRect(state);
+  const { map, table, tableStart, top, bottom } = rect;
+  if (role === 'head' && top > 0) return false;
+  if (role === 'foot' && bottom < map.height) return false;
+  const tableTypes = tableNodeTypes(state.schema);
+  const newSectionType = tableTypes[role];
+  if (!newSectionType) return false;
+  const fixCellsType =
+    (role === 'head' || role === 'foot') &&
+    tableTypes.cell &&
+    tableTypes.header_cell;
+  if (dispatch) {
+    let newTableContents = Fragment4.empty;
+    let refSection = null;
+    let rowIndex = 0;
+    let inSelection = false;
+    let accSectionRows = Fragment4.empty;
+    for (let i = 0; i < table.childCount; i++) {
+      const section = table.child(i);
+      const sectionRole = section.type.spec.tableRole;
+      if (isTableSection(section)) {
+        const sectionRowsCount = section.childCount;
+        const lastRow = rowIndex + sectionRowsCount - 1;
+        if (
+          rowIndex === top &&
+          lastRow + 1 === bottom &&
+          sectionRole === role
+        ) {
+          return false;
+        }
+        if (rowIndex >= bottom || lastRow < top) {
+          newTableContents = newTableContents.addToEnd(section);
+        } else {
+          if (!refSection) refSection = section;
+          for (let j = 0; j < section.childCount; j++) {
+            if (rowIndex + j === top) {
+              if (accSectionRows.childCount > 0) {
+                newTableContents = newTableContents.addToEnd(
+                  refSection.copy(accSectionRows),
+                );
+                accSectionRows = Fragment4.empty;
+              }
+              inSelection = true;
+            }
+            const row =
+              inSelection && fixCellsType
+                ? fixRowCells(section.child(j), tableTypes.header_cell)
+                : section.child(j);
+            accSectionRows = accSectionRows.addToEnd(row);
+            if (rowIndex + j === bottom - 1) {
+              if (refSection.type.spec.tableRole !== role) refSection = section;
+              const newSection =
+                refSection.type.spec.tableRole !== role
+                  ? newSectionType.create(null, accSectionRows)
+                  : refSection.copy(accSectionRows);
+              newTableContents = newTableContents.addToEnd(newSection);
+              accSectionRows = Fragment4.empty;
+              refSection = section;
+              inSelection = false;
+            }
+          }
+          if (!inSelection && accSectionRows.childCount > 0) {
+            newTableContents = newTableContents.addToEnd(
+              refSection.copy(accSectionRows),
+            );
+            accSectionRows = Fragment4.empty;
+          }
+        }
+        rowIndex = lastRow + 1;
+      } else {
+        newTableContents = newTableContents.addToEnd(section);
+      }
+    }
+    const { doc, tr } = state;
+    tr.setSelection(new NodeSelection4(doc.resolve(tableStart - 1)));
+    const newTable = table.copy(newTableContents);
+    tr.replaceSelectionWith(newTable);
+    const cellsPositions = TableMap.get(newTable).cellsInRect(rect);
+    const $anchorCell = tr.doc.resolve(tableStart + cellsPositions[0]);
+    const $headCell = tr.doc.resolve(
+      tableStart + cellsPositions[cellsPositions.length - 1],
+    );
+    tr.setSelection(new CellSelection($anchorCell, $headCell));
+    tr.setMeta(
+      columnResizingPluginKey,
+      getTableWidths(table, tableStart, getCellMinWidth(state)),
+    );
+    dispatch(tr);
+  }
+  return true;
+}
+function makeBody(state, dispatch) {
+  return makeSection('body', state, dispatch);
+}
+function makeHead(state, dispatch) {
+  return makeSection('head', state, dispatch);
+}
+function makeFoot(state, dispatch) {
+  return makeSection('foot', state, dispatch);
+}
+function deleteSection(state, dispatch) {
+  if (!isInTable(state)) return false;
+  const rect = selectedRect(state),
+    tr = state.tr;
+  if (rect.top == 0 && rect.bottom == rect.map.height) return false;
+  if (dispatch) {
+    const { map, table, tableStart } = rect;
+    const sections = map.sectionsInRect(rect);
+    if (sections.length >= tableSectionsCount(table) || sections.length == 0)
+      return false;
+    const firstSectionIndex = tableHasCaption(table) ? 1 : 0;
+    const sectionPosAndSize = [];
+    let pos = tableStart;
+    for (let i = 0; i < table.childCount; i++) {
+      const size = table.child(i).nodeSize;
+      if (i >= firstSectionIndex) sectionPosAndSize.push([pos, size]);
+      pos += size;
+    }
+    for (let i = sections.length - 1; i >= 0; i--) {
+      const [pos2, size] = sectionPosAndSize[sections[i]];
+      tr.delete(pos2, pos2 + size);
+    }
+    dispatch(tr);
+  }
+  return true;
+}
+function isEmpty(cell) {
+  const c = cell.content;
+  return (
+    c.childCount == 1 && c.child(0).isTextblock && c.child(0).childCount == 0
+  );
+}
+function cellsOverlapRectangle({ width, height, map }, rect) {
+  let indexTop = rect.top * width + rect.left,
+    indexLeft = indexTop;
+  let indexBottom = (rect.bottom - 1) * width + rect.left,
+    indexRight = indexTop + (rect.right - rect.left - 1);
+  for (let i = rect.top; i < rect.bottom; i++) {
+    if (
+      (rect.left > 0 && map[indexLeft] == map[indexLeft - 1]) ||
+      (rect.right < width && map[indexRight] == map[indexRight + 1])
+    )
+      return true;
+    indexLeft += width;
+    indexRight += width;
+  }
+  for (let i = rect.left; i < rect.right; i++) {
+    if (
+      (rect.top > 0 && map[indexTop] == map[indexTop - width]) ||
+      (rect.bottom < height && map[indexBottom] == map[indexBottom + width])
+    )
+      return true;
+    indexTop++;
+    indexBottom++;
+  }
+  return false;
+}
+function mergeCells(state, dispatch) {
+  const sel = state.selection;
+  if (
+    !(sel instanceof CellSelection) ||
+    sel.$anchorCell.pos == sel.$headCell.pos
+  )
+    return false;
+  const rect = selectedRect(state),
+    { map } = rect;
+  if (!map.rectOverOneSection(rect)) return false;
+  if (cellsOverlapRectangle(map, rect)) return false;
+  if (dispatch) {
+    const tr = state.tr;
+    const seen = {};
+    let content = Fragment4.empty;
+    let mergedPos;
+    let mergedCell;
+    for (let row = rect.top; row < rect.bottom; row++) {
+      for (let col = rect.left; col < rect.right; col++) {
+        const cellPos = map.map[row * map.width + col];
+        const cell = rect.table.nodeAt(cellPos);
+        if (seen[cellPos] || !cell) continue;
+        seen[cellPos] = true;
+        if (mergedPos == null) {
+          mergedPos = cellPos;
+          mergedCell = cell;
+        } else {
+          if (!isEmpty(cell)) content = content.append(cell.content);
+          const mapped = tr.mapping.map(cellPos + rect.tableStart);
+          tr.delete(mapped, mapped + cell.nodeSize);
+        }
+      }
+    }
+    if (mergedPos == null || mergedCell == null) {
+      return true;
+    }
+    tr.setNodeMarkup(mergedPos + rect.tableStart, null, {
+      ...addColSpan(
+        mergedCell.attrs,
+        mergedCell.attrs.colspan,
+        rect.right - rect.left - mergedCell.attrs.colspan,
+      ),
+      rowspan: rect.bottom - rect.top,
+    });
+    if (content.size) {
+      const end = mergedPos + 1 + mergedCell.content.size;
+      const start = isEmpty(mergedCell) ? mergedPos + 1 : end;
+      tr.replaceWith(start + rect.tableStart, end + rect.tableStart, content);
+    }
+    tr.setSelection(
+      new CellSelection(tr.doc.resolve(mergedPos + rect.tableStart)),
+    );
+    dispatch(tr);
+  }
+  return true;
+}
+function splitCell(state, dispatch) {
+  const nodeTypes = tableNodeTypes(state.schema);
+  return splitCellWithType(({ node }) => {
+    return nodeTypes[node.type.spec.tableRole];
+  })(state, dispatch);
+}
+function splitCellWithType(getCellType) {
+  return (state, dispatch) => {
+    var _a;
+    const sel = state.selection;
+    let cellNode;
+    let cellPos;
+    if (!(sel instanceof CellSelection)) {
+      cellNode = cellWrapping(sel.$from);
+      if (!cellNode) return false;
+      cellPos = (_a = cellAround(sel.$from)) == null ? void 0 : _a.pos;
+    } else {
+      if (sel.$anchorCell.pos != sel.$headCell.pos) return false;
+      cellNode = sel.$anchorCell.nodeAfter;
+      cellPos = sel.$anchorCell.pos;
+    }
+    if (cellNode == null || cellPos == null) {
+      return false;
+    }
+    if (cellNode.attrs.colspan == 1 && cellNode.attrs.rowspan == 1) {
+      return false;
+    }
+    if (dispatch) {
+      let baseAttrs = cellNode.attrs;
+      const attrs = [];
+      const colwidth = baseAttrs.colwidth;
+      if (baseAttrs.rowspan > 1) baseAttrs = { ...baseAttrs, rowspan: 1 };
+      if (baseAttrs.colspan > 1) baseAttrs = { ...baseAttrs, colspan: 1 };
+      const rect = selectedRect(state),
+        tr = state.tr;
+      for (let i = 0; i < rect.right - rect.left; i++)
+        attrs.push(
+          colwidth
+            ? {
+                ...baseAttrs,
+                colwidth: colwidth && colwidth[i] ? [colwidth[i]] : null,
+              }
+            : baseAttrs,
+        );
+      let lastCell;
+      for (let row = rect.top; row < rect.bottom; row++) {
+        let pos = rect.map.positionAt(row, rect.left, rect.table);
+        if (row == rect.top) pos += cellNode.nodeSize;
+        for (let col = rect.left, i = 0; col < rect.right; col++, i++) {
+          if (col == rect.left && row == rect.top) continue;
+          tr.insert(
+            (lastCell = tr.mapping.map(pos + rect.tableStart, 1)),
+            getCellType({ node: cellNode, row, col }).createAndFill(attrs[i]),
+          );
+        }
+      }
+      tr.setNodeMarkup(
+        cellPos,
+        getCellType({ node: cellNode, row: rect.top, col: rect.left }),
+        attrs[0],
+      );
+      if (sel instanceof CellSelection)
+        tr.setSelection(
+          new CellSelection(
+            tr.doc.resolve(sel.$anchorCell.pos),
+            lastCell ? tr.doc.resolve(lastCell) : void 0,
+          ),
+        );
+      dispatch(tr);
+    }
+    return true;
+  };
+}
+function setCellAttr(name, value) {
+  return function (state, dispatch) {
+    if (!isInTable(state)) return false;
+    const $cell = selectionCell(state);
+    if ($cell.nodeAfter.attrs[name] === value) return false;
+    if (dispatch) {
+      const tr = state.tr;
+      if (state.selection instanceof CellSelection)
+        state.selection.forEachCell((node, pos) => {
+          if (node.attrs[name] !== value)
+            tr.setNodeMarkup(pos, null, {
+              ...node.attrs,
+              [name]: value,
+            });
+        });
+      else
+        tr.setNodeMarkup($cell.pos, null, {
+          ...$cell.nodeAfter.attrs,
+          [name]: value,
+        });
+      dispatch(tr);
+    }
+    return true;
+  };
+}
+function deprecated_toggleHeader(type) {
+  return function (state, dispatch) {
+    if (!isInTable(state)) return false;
+    if (dispatch) {
+      const types = tableNodeTypes(state.schema);
+      const rect = selectedRect(state),
+        tr = state.tr;
+      const cells = rect.map.cellsInRect(
+        type == 'column'
+          ? {
+              left: rect.left,
+              top: 0,
+              right: rect.right,
+              bottom: rect.map.height,
+            }
+          : type == 'row'
+          ? {
+              left: 0,
+              top: rect.top,
+              right: rect.map.width,
+              bottom: rect.bottom,
+            }
+          : rect,
+      );
+      const nodes = cells.map(pos => rect.table.nodeAt(pos));
+      for (let i = 0; i < cells.length; i++)
+        if (nodes[i].type == types.header_cell)
+          tr.setNodeMarkup(
+            rect.tableStart + cells[i],
+            types.cell,
+            nodes[i].attrs,
+          );
+      if (tr.steps.length == 0)
+        for (let i = 0; i < cells.length; i++)
+          tr.setNodeMarkup(
+            rect.tableStart + cells[i],
+            types.header_cell,
+            nodes[i].attrs,
+          );
+      dispatch(tr);
+    }
+    return true;
+  };
+}
+function isHeaderEnabledByType(type, rect, types) {
+  const cellPositions = rect.map.cellsInRect({
+    left: 0,
+    top: 0,
+    right: type == 'row' ? rect.map.width : 1,
+    bottom: type == 'column' ? rect.map.height : 1,
+  });
+  for (let i = 0; i < cellPositions.length; i++) {
+    const cell = rect.table.nodeAt(cellPositions[i]);
+    if (cell && cell.type !== types.header_cell) {
+      return false;
+    }
+  }
+  return true;
+}
+function toggleHeader(type, options) {
+  options = options || { useDeprecatedLogic: false };
+  if (options.useDeprecatedLogic) return deprecated_toggleHeader(type);
+  return function (state, dispatch) {
+    if (!isInTable(state)) return false;
+    if (dispatch) {
+      const types = tableNodeTypes(state.schema);
+      const rect = selectedRect(state),
+        tr = state.tr;
+      const isHeaderRowEnabled = isHeaderEnabledByType('row', rect, types);
+      const isHeaderColumnEnabled = isHeaderEnabledByType(
+        'column',
+        rect,
+        types,
+      );
+      const isHeaderEnabled =
+        type === 'column'
+          ? isHeaderRowEnabled
+          : type === 'row'
+          ? isHeaderColumnEnabled
+          : false;
+      const selectionStartsAt = isHeaderEnabled ? 1 : 0;
+      const cellsRect =
+        type == 'column'
+          ? {
+              left: 0,
+              top: selectionStartsAt,
+              right: 1,
+              bottom: rect.map.height,
+            }
+          : type == 'row'
+          ? {
+              left: selectionStartsAt,
+              top: 0,
+              right: rect.map.width,
+              bottom: 1,
+            }
+          : rect;
+      const newType =
+        type == 'column'
+          ? isHeaderColumnEnabled
+            ? types.cell
+            : types.header_cell
+          : type == 'row'
+          ? isHeaderRowEnabled
+            ? types.cell
+            : types.header_cell
+          : types.cell;
+      rect.map.cellsInRect(cellsRect).forEach(relativeCellPos => {
+        const cellPos = relativeCellPos + rect.tableStart;
+        const cell = tr.doc.nodeAt(cellPos);
+        if (cell) {
+          tr.setNodeMarkup(cellPos, newType, cell.attrs);
+        }
+      });
+      dispatch(tr);
+    }
+    return true;
+  };
+}
+var toggleHeaderRow = toggleHeader('row', {
+  useDeprecatedLogic: true,
+});
+var toggleHeaderColumn = toggleHeader('column', {
+  useDeprecatedLogic: true,
+});
+var toggleHeaderCell = toggleHeader('cell', {
+  useDeprecatedLogic: true,
+});
+function findNextCell($cell, dir) {
+  const table = $cell.node(-2);
+  const tableStart = $cell.start(-2);
+  if (dir < 0) {
+    const before = $cell.nodeBefore;
+    if (before) {
+      return $cell.pos - before.nodeSize;
+    }
+    for (
+      let row = $cell.index(-2) - 1, rowEnd = $cell.before();
+      row >= 0;
+      row--
+    ) {
+      const rowNode = $cell.node(-2).child(row);
+      const lastChild = rowNode.lastChild;
+      if (lastChild) {
+        return rowEnd - 1 - lastChild.nodeSize;
+      }
+      rowEnd -= rowNode.nodeSize;
+    }
+  } else {
+    if ($cell.index() < $cell.parent.childCount - 1) {
+      return $cell.pos + $cell.nodeAfter.nodeSize;
+    }
+    for (
+      let row = $cell.indexAfter(-2), rowStart = $cell.after();
+      row < table.childCount;
+      row++
+    ) {
+      const rowNode = table.child(row);
+      if (rowNode.childCount) return rowStart + 1;
+      rowStart += rowNode.nodeSize;
+    }
+  }
+  return null;
+}
+function goToNextCell(direction) {
+  return function (state, dispatch) {
+    if (!isInTable(state)) return false;
+    const cell = findNextCell(selectionCell(state), direction);
+    if (cell == null) return false;
+    if (dispatch) {
+      const $cell = state.doc.resolve(cell);
+      dispatch(
+        state.tr
+          .setSelection(TextSelection3.between($cell, moveCellForward($cell)))
+          .scrollIntoView(),
+      );
+    }
+    return true;
+  };
+}
+function deleteTable(state, dispatch) {
+  const $pos = state.selection.$anchor;
+  for (let d = $pos.depth; d > 0; d--) {
+    const node = $pos.node(d);
+    if (node.type.spec.tableRole == 'table') {
+      if (dispatch)
+        dispatch(
+          state.tr.delete($pos.before(d), $pos.after(d)).scrollIntoView(),
+        );
+      return true;
+    }
+  }
+  return false;
+}
+
+// src/index.ts
+function tableEditing({ allowTableNodeSelection = false } = {}) {
+  return new Plugin2({
+    key: tableEditingKey,
+    // This piece of state is used to remember when a mouse-drag
+    // cell-selection is happening, so that it can continue even as
+    // transactions (which might move its anchor cell) come in.
+    state: {
+      init() {
+        return null;
+      },
+      apply(tr, cur) {
+        const set = tr.getMeta(tableEditingKey);
+        if (set != null) return set == -1 ? null : set;
+        if (cur == null || !tr.docChanged) return cur;
+        const { deleted, pos } = tr.mapping.mapResult(cur);
+        return deleted ? null : pos;
+      },
+    },
+    props: {
+      decorations: drawCellSelection,
+      handleDOMEvents: {
+        mousedown: handleMouseDown,
+      },
+      createSelectionBetween(view) {
+        return tableEditingKey.getState(view.state) != null
+          ? view.state.selection
+          : null;
+      },
+      handleTripleClick,
+      handleKeyDown,
+      handlePaste,
+    },
+    appendTransaction(_, oldState, state) {
+      return normalizeSelection(
+        state,
+        fixTables(state, oldState),
+        allowTableNodeSelection,
+      );
+    },
+  });
+}
+export {
+  CellBookmark,
+  CellSelection,
+  ResizeState,
+  TableMap,
+  clipCells as __clipCells,
+  insertCells as __insertCells,
+  pastedCells as __pastedCells,
+  addBodyAfter,
+  addBodyBefore,
+  addCaption,
+  addColSpan,
+  addColumn,
+  addColumnAfter,
+  addColumnBefore,
+  addRow,
+  addRowAfter,
+  addRowBefore,
+  addTableFoot,
+  addTableHead,
+  cellAround,
+  colCount,
+  columnIsHeader,
+  columnResizing,
+  columnResizingPluginKey,
+  deleteCaption,
+  deleteColumn,
+  deleteRow,
+  deleteSection,
+  deleteTable,
+  findCell,
+  fixTables,
+  fixTablesKey,
+  getRow,
+  goToNextCell,
+  handlePaste,
+  inSameTable,
+  isInTable,
+  isRowLastInSection,
+  makeBody,
+  makeFoot,
+  makeHead,
+  mergeCells,
+  moveCellForward,
+  nextCell,
+  pointsAtCell,
+  removeColSpan,
+  removeColumn,
+  removeRow,
+  removeSection,
+  rowAtPos,
+  rowIsHeader,
+  rowPos,
+  rowsCount,
+  selectedRect,
+  selectionCell,
+  setCellAttr,
+  splitCell,
+  splitCellWithType,
+  tableBodiesCount,
+  tableEditing,
+  tableEditingKey,
+  tableHasCaption,
+  tableHasFoot,
+  tableHasHead,
+  tableNodeTypes,
+  tableNodes,
+  tableSectionsCount,
+  toggleHeader,
+  toggleHeaderCell,
+  toggleHeaderColumn,
+  toggleHeaderRow,
+};
diff --git a/yarn.lock b/yarn.lock
index 3704c03086dbc22cebbb47a2869d9064fcfec6a8..e4804eabb494b0685ffd5075bb567b3e906ba176 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12053,7 +12053,7 @@ prosemirror-keymap@1.2.1:
     prosemirror-state "^1.0.0"
     w3c-keyname "^2.2.0"
 
-prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2:
+prosemirror-keymap@^1.0.0:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.1.4.tgz#8b481bf8389a5ac40d38dbd67ec3da2c7eac6a6d"
   integrity sha512-Al8cVUOnDFL4gcI5IDlG6xbZ0aOD/i3B17VT+1JbHWDguCgt/lBHVTHUBcKvvbSg6+q/W4Nj1Fu6bwZSca3xjg==
@@ -12061,7 +12061,7 @@ prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2:
     prosemirror-state "^1.0.0"
     w3c-keyname "^2.2.0"
 
-prosemirror-model@1.19.0, prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.2.0, prosemirror-model@^1.8.1:
+prosemirror-model@1.19.0, prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.2.0:
   version "1.19.0"
   resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.0.tgz#d7ad9a65ada0bb12196f64fe0dd4fc392c841c29"
   integrity sha512-/CvFGJnwc41EJSfDkQLly1cAJJJmBpZwwUJtwZPTjY2RqZJfM8HVbCreOY/jti8wTRbVyjagcylyGoeJH/g/3w==
@@ -12092,7 +12092,7 @@ prosemirror-schema-list@^1.0.0:
     prosemirror-model "^1.0.0"
     prosemirror-transform "^1.0.0"
 
-prosemirror-state@1.4.2, prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1:
+prosemirror-state@1.4.2, prosemirror-state@^1.0.0, prosemirror-state@^1.2.2:
   version "1.4.2"
   resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.4.2.tgz#f93bd8a33a4454efab917ba9b738259d828db7e5"
   integrity sha512-puuzLD2mz/oTdfgd8msFbe0A42j5eNudKAAPDB0+QJRw8cO1ygjLmhLrg9RvDpf87Dkd6D4t93qdef00KKNacQ==
@@ -12101,17 +12101,6 @@ prosemirror-state@1.4.2, prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, pro
     prosemirror-transform "^1.0.0"
     prosemirror-view "^1.27.0"
 
-prosemirror-tables@1.3.4:
-  version "1.3.4"
-  resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.3.4.tgz#0b7cc16d49f90c5b834c9f29291c545478ce9ab0"
-  integrity sha512-z6uLSQ1BLC3rgbGwZmpfb+xkdvD7W/UOsURDfognZFYaTtc0gsk7u/t71Yijp2eLflVpffMk6X0u0+u+MMDvIw==
-  dependencies:
-    prosemirror-keymap "^1.1.2"
-    prosemirror-model "^1.8.1"
-    prosemirror-state "^1.3.1"
-    prosemirror-transform "^1.2.1"
-    prosemirror-view "^1.13.3"
-
 prosemirror-test-builder@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/prosemirror-test-builder/-/prosemirror-test-builder-1.0.3.tgz#9706b6e021efe82830b479797923d835f4ca12a3"
@@ -12121,7 +12110,7 @@ prosemirror-test-builder@^1.0.1:
     prosemirror-schema-basic "^1.0.0"
     prosemirror-schema-list "^1.0.0"
 
-prosemirror-transform@1.7.1, prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1:
+prosemirror-transform@1.7.1, prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0:
   version "1.7.1"
   resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.7.1.tgz#b516e818c3add0bdf960f4ca8ccb9d057a3ba21b"
   integrity sha512-VteoifAfpt46z0yEt6Fc73A5OID9t/y2QIeR5MgxEwTuitadEunD/V0c9jQW8ziT8pbFM54uTzRLJ/nLuQjMxg==
@@ -12135,7 +12124,7 @@ prosemirror-transform@^1.7.3:
   dependencies:
     prosemirror-model "^1.0.0"
 
-prosemirror-view@1.30.2, prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.27.0, prosemirror-view@^1.5.1:
+prosemirror-view@1.30.2, prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.5.1:
   version "1.30.2"
   resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.30.2.tgz#57a9d15c5baa454f0d0f4a3028ddbd9be1e8ed9b"
   integrity sha512-nTNzZvalQf9kHeEyO407LiV6DoOs/pXsid88UqW9Vvybo4ozJW2PJhkfZUxCUF1hR/9vJLdhxX84wuw9P9HsXA==