@prosekit/extensions 0.11.6 → 0.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/{enter-rule-RdhEA900.js → enter-rule-5tkoU2Ir.js} +2 -3
  2. package/dist/enter-rule-5tkoU2Ir.js.map +1 -0
  3. package/dist/{file-DVUhe5KJ.js → file-nRyo7PMB.js} +2 -4
  4. package/dist/{file-DVUhe5KJ.js.map → file-nRyo7PMB.js.map} +1 -1
  5. package/dist/index-DY6lIIYV.d.ts.map +1 -1
  6. package/dist/{input-rule-B17tpW4m.js → input-rule-DO_iy2aT.js} +4 -7
  7. package/dist/input-rule-DO_iy2aT.js.map +1 -0
  8. package/dist/{paste-rule-BIztzELg.js → paste-rule-Brej6cWi.js} +2 -3
  9. package/dist/{paste-rule-BIztzELg.js.map → paste-rule-Brej6cWi.js.map} +1 -1
  10. package/dist/prosekit-extensions-autocomplete.js +1 -2
  11. package/dist/prosekit-extensions-autocomplete.js.map +1 -1
  12. package/dist/prosekit-extensions-blockquote.js +1 -1
  13. package/dist/prosekit-extensions-bold.js +1 -1
  14. package/dist/prosekit-extensions-code-block.js +4 -5
  15. package/dist/prosekit-extensions-code-block.js.map +1 -1
  16. package/dist/prosekit-extensions-code.js +1 -1
  17. package/dist/prosekit-extensions-commit.js +2 -4
  18. package/dist/prosekit-extensions-commit.js.map +1 -1
  19. package/dist/prosekit-extensions-enter-rule.js +1 -1
  20. package/dist/prosekit-extensions-file.js +1 -1
  21. package/dist/prosekit-extensions-heading.js +1 -1
  22. package/dist/prosekit-extensions-horizontal-rule.js +1 -1
  23. package/dist/prosekit-extensions-image.d.ts +85 -29
  24. package/dist/prosekit-extensions-image.d.ts.map +1 -1
  25. package/dist/prosekit-extensions-image.js +93 -52
  26. package/dist/prosekit-extensions-image.js.map +1 -1
  27. package/dist/prosekit-extensions-input-rule.js +1 -1
  28. package/dist/prosekit-extensions-italic.js +1 -1
  29. package/dist/prosekit-extensions-link.js +3 -3
  30. package/dist/prosekit-extensions-list.js +3 -5
  31. package/dist/prosekit-extensions-list.js.map +1 -1
  32. package/dist/prosekit-extensions-paste-rule.js +1 -1
  33. package/dist/prosekit-extensions-placeholder.js +2 -3
  34. package/dist/prosekit-extensions-placeholder.js.map +1 -1
  35. package/dist/prosekit-extensions-search.js +1 -2
  36. package/dist/prosekit-extensions-search.js.map +1 -1
  37. package/dist/prosekit-extensions-strike.js +1 -1
  38. package/dist/prosekit-extensions-table.js +1 -1
  39. package/dist/{table-BNwuK7xg.js → table-DND_1127.js} +9 -18
  40. package/dist/{table-BNwuK7xg.js.map → table-DND_1127.js.map} +1 -1
  41. package/package.json +4 -4
  42. package/src/file/file-upload.ts +2 -10
  43. package/src/image/image-commands/insert-image.ts +14 -0
  44. package/src/image/image-commands/upload-image.ts +137 -0
  45. package/src/image/image-commands.ts +7 -12
  46. package/src/image/image-upload-handler.ts +10 -70
  47. package/src/image/index.ts +8 -3
  48. package/dist/enter-rule-RdhEA900.js.map +0 -1
  49. package/dist/input-rule-B17tpW4m.js.map +0 -1
@@ -42,19 +42,14 @@ function createEmptyTable(schema, row, col, header) {
42
42
  const tableCellType = getNodeType(schema, "tableCell");
43
43
  const tableHeaderCellType = getNodeType(schema, "tableHeaderCell");
44
44
  if (header) {
45
- const headerCell = tableHeaderCellType.createAndFill();
46
- const headerCells = repeat(headerCell, col);
45
+ const headerCells = repeat(tableHeaderCellType.createAndFill(), col);
47
46
  const headerRow = tableRowType.createAndFill(null, headerCells);
48
- const bodyCell = tableCellType.createAndFill();
49
- const bodyCells = repeat(bodyCell, col);
50
- const bodyRow = tableRowType.createAndFill(null, bodyCells);
51
- const bodyRows = repeat(bodyRow, row - 1);
47
+ const bodyCells = repeat(tableCellType.createAndFill(), col);
48
+ const bodyRows = repeat(tableRowType.createAndFill(null, bodyCells), row - 1);
52
49
  return tableType.createAndFill(null, [headerRow, ...bodyRows]);
53
50
  } else {
54
- const bodyCell = tableCellType.createAndFill();
55
- const bodyCells = repeat(bodyCell, col);
56
- const bodyRow = tableRowType.createAndFill(null, bodyCells);
57
- const bodyRows = repeat(bodyRow, row);
51
+ const bodyCells = repeat(tableCellType.createAndFill(), col);
52
+ const bodyRows = repeat(tableRowType.createAndFill(null, bodyCells), row);
58
53
  return tableType.createAndFill(null, bodyRows);
59
54
  }
60
55
  }
@@ -72,8 +67,7 @@ function repeat(node, length) {
72
67
  function insertTable(options) {
73
68
  return (state, dispatch, view) => {
74
69
  const { row, col, header = false } = options;
75
- const table = createEmptyTable(state.schema, row, col, header);
76
- return insertNode({ node: table })(state, dispatch, view);
70
+ return insertNode({ node: createEmptyTable(state.schema, row, col, header) })(state, dispatch, view);
77
71
  };
78
72
  }
79
73
 
@@ -95,8 +89,7 @@ function isCellSelection(value) {
95
89
  */
96
90
  function selectTable(options) {
97
91
  return (state, dispatch) => {
98
- const $pos = options?.pos ? state.doc.resolve(options.pos) : state.selection.$anchor;
99
- const table = findTable($pos);
92
+ const table = findTable(options?.pos ? state.doc.resolve(options.pos) : state.selection.$anchor);
100
93
  if (!table) return false;
101
94
  const map = TableMap.get(table.node);
102
95
  if (map.map.length === 0) return false;
@@ -106,9 +99,7 @@ function selectTable(options) {
106
99
  const lastCellPosInTable = map.map[map.map.length - 1];
107
100
  const firstCellPos = table.pos + firstCellPosInTable + 1;
108
101
  const lastCellPos = table.pos + lastCellPosInTable + 1;
109
- const $firstCellPos = tr.doc.resolve(firstCellPos);
110
- const $lastCellPos = tr.doc.resolve(lastCellPos);
111
- const selection = new CellSelection($firstCellPos, $lastCellPos);
102
+ const selection = new CellSelection(tr.doc.resolve(firstCellPos), tr.doc.resolve(lastCellPos));
112
103
  tr = tr.setSelection(selection);
113
104
  dispatch?.(tr);
114
105
  }
@@ -294,4 +285,4 @@ function defineTable() {
294
285
 
295
286
  //#endregion
296
287
  export { defineTable, defineTableCellSpec, defineTableCommands, defineTableDropIndicator, defineTableHeaderCellSpec, defineTablePlugins, defineTableRowSpec, defineTableSpec, exitTable, findTable, insertTable, isCellSelection, moveTableColumn, moveTableRow, selectTable, selectTableCell, selectTableColumn, selectTableRow };
297
- //# sourceMappingURL=table-BNwuK7xg.js.map
288
+ //# sourceMappingURL=table-DND_1127.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"table-BNwuK7xg.js","names":["exitTable: Command","onDrag: DragEventHandler","match: boolean"],"sources":["../src/table/table-commands/exit-table.ts","../src/table/table-commands/insert-table.ts","../src/table/table-utils.ts","../src/table/table-commands/select-table.ts","../src/table/table-commands/select-table-cell.ts","../src/table/table-commands/select-table-column.ts","../src/table/table-commands/select-table-row.ts","../src/table/table-commands.ts","../src/table/table-drop-indicator.ts","../src/table/table-plugins.ts","../src/table/table-spec.ts","../src/table/table.ts"],"sourcesContent":["import { defaultBlockAt } from '@prosekit/core'\nimport { TextSelection } from '@prosekit/pm/state'\nimport type { Command } from '@prosekit/pm/state'\nimport type { TableRole } from 'prosemirror-tables'\n\n/**\n * When the selection is in a table node, create a default block after the table\n * table, and move the cursor there.\n *\n * @public\n */\nexport const exitTable: Command = (state, dispatch) => {\n const { $head, $anchor } = state.selection\n\n if (!$head.sameParent($anchor)) {\n return false\n }\n\n let tableStart = -1\n let tableDepth = -1\n for (let depth = $head.depth; depth >= 0; depth--) {\n const node = $head.node(depth)\n if ((node.type.spec.tableRole as TableRole) === 'table') {\n tableStart = $head.before(depth)\n tableDepth = depth\n }\n }\n\n if (tableStart < 0 || tableDepth <= 0) {\n return false\n }\n\n const above = $head.node(tableDepth - 1)\n const after = $head.indexAfter(tableDepth - 1)\n const type = defaultBlockAt(above.contentMatchAt(after))\n const node = type?.createAndFill()\n\n if (!type || !node || !above.canReplaceWith(after, after, type)) {\n return false\n }\n\n if (dispatch) {\n const pos = $head.after(tableDepth)\n const tr = state.tr.replaceWith(pos, pos, node)\n tr.setSelection(TextSelection.near(tr.doc.resolve(pos), 1))\n dispatch(tr.scrollIntoView())\n }\n return true\n}\n","import {\n getNodeType,\n insertNode,\n} from '@prosekit/core'\nimport type { Schema } from '@prosekit/pm/model'\nimport type { Command } from '@prosekit/pm/state'\n\nfunction createEmptyTable(\n schema: Schema,\n row: number,\n col: number,\n header: boolean,\n) {\n const tableType = getNodeType(schema, 'table')\n const tableRowType = getNodeType(schema, 'tableRow')\n const tableCellType = getNodeType(schema, 'tableCell')\n const tableHeaderCellType = getNodeType(schema, 'tableHeaderCell')\n\n if (header) {\n const headerCell = tableHeaderCellType.createAndFill()!\n const headerCells = repeat(headerCell, col)\n const headerRow = tableRowType.createAndFill(null, headerCells)!\n\n const bodyCell = tableCellType.createAndFill()!\n const bodyCells = repeat(bodyCell, col)\n const bodyRow = tableRowType.createAndFill(null, bodyCells)!\n const bodyRows = repeat(bodyRow, row - 1)\n\n return tableType.createAndFill(null, [headerRow, ...bodyRows])!\n } else {\n const bodyCell = tableCellType.createAndFill()!\n const bodyCells = repeat(bodyCell, col)\n const bodyRow = tableRowType.createAndFill(null, bodyCells)!\n const bodyRows = repeat(bodyRow, row)\n\n return tableType.createAndFill(null, bodyRows)!\n }\n}\n\nfunction repeat<T>(node: T, length: number): T[] {\n return Array<T>(length).fill(node)\n}\n\n/**\n * @public\n */\nexport interface InsertTableOptions {\n /**\n * The number of rows in the table.\n */\n row: number\n\n /**\n * The number of columns in the table.\n */\n col: number\n\n /**\n * Whether the table has a header row.\n *\n * @default false\n */\n header?: boolean\n}\n\n/**\n * Insert a table node with the given number of rows and columns, and optionally\n * a header row.\n *\n * @param options\n *\n * @public\n */\nexport function insertTable(options: InsertTableOptions): Command {\n return (state, dispatch, view) => {\n const { row, col, header = false } = options\n const table = createEmptyTable(state.schema, row, col, header)\n return insertNode({ node: table })(state, dispatch, view)\n }\n}\n","import { CellSelection } from 'prosemirror-tables'\n\nexport {\n findCellPos,\n findCellRange,\n findTable,\n} from 'prosemirror-tables'\n\n/**\n * Checks if the given object is a `CellSelection` instance.\n *\n * @public\n */\nexport function isCellSelection(value: unknown): value is CellSelection {\n return value instanceof CellSelection\n}\n","import type { Command } from '@prosekit/pm/state'\nimport {\n CellSelection,\n TableMap,\n} from 'prosemirror-tables'\n\nimport { findTable } from '../table-utils'\n\n/**\n * @public\n */\nexport interface SelectTableOptions {\n /**\n * A hit position of the table to select from. By default, the selection\n * anchor will be used.\n */\n pos?: number\n}\n\n/**\n * @public\n */\nexport function selectTable(options?: SelectTableOptions): Command {\n return (state, dispatch) => {\n const $pos = options?.pos\n ? state.doc.resolve(options.pos)\n : state.selection.$anchor\n const table = findTable($pos)\n if (!table) {\n return false\n }\n const map = TableMap.get(table.node)\n if (map.map.length === 0) {\n return false\n }\n if (dispatch) {\n let tr = state.tr\n const firstCellPosInTable = map.map[0]\n const lastCellPosInTable = map.map[map.map.length - 1]\n const firstCellPos = table.pos + firstCellPosInTable + 1\n const lastCellPos = table.pos + lastCellPosInTable + 1\n const $firstCellPos = tr.doc.resolve(firstCellPos)\n const $lastCellPos = tr.doc.resolve(lastCellPos)\n const selection = new CellSelection($firstCellPos, $lastCellPos)\n tr = tr.setSelection(selection)\n dispatch?.(tr)\n }\n return true\n }\n}\n","import type { Command } from '@prosekit/pm/state'\nimport { CellSelection } from 'prosemirror-tables'\n\nimport { findCellPos } from '../table-utils'\n\n/**\n * @public\n */\nexport interface SelectTableCellOptions {\n /**\n * A hit position of the table cell to select from. By default, the selection\n * anchor will be used.\n */\n pos?: number\n}\n\n/**\n * @public\n */\nexport function selectTableCell(options?: SelectTableCellOptions): Command {\n return (state, dispatch) => {\n const $cellPos = findCellPos(\n state.doc,\n options?.pos ?? state.selection.anchor,\n )\n if (!$cellPos) {\n return false\n }\n if (dispatch) {\n const selection = new CellSelection($cellPos)\n dispatch(state.tr.setSelection(selection))\n }\n return true\n }\n}\n","import type { Command } from '@prosekit/pm/state'\nimport { CellSelection } from 'prosemirror-tables'\n\nimport { findCellRange } from '../table-utils'\n\n/**\n * @public\n */\nexport interface SelectTableColumnOptions {\n /**\n * A hit position of the table cell to select from. By default, the selection\n * anchor will be used.\n */\n anchor?: number\n\n /**\n * A hit position of the table cell to select to. By default, the selection\n * head will be used.\n */\n head?: number\n}\n\n/**\n * @public\n */\nexport function selectTableColumn(options?: SelectTableColumnOptions): Command {\n return (state, dispatch) => {\n const range = findCellRange(state.selection, options?.anchor, options?.head)\n if (!range) {\n return false\n }\n if (dispatch) {\n const [$anchorCell, $headCell] = range\n const selection = CellSelection.colSelection($anchorCell, $headCell)\n dispatch(state.tr.setSelection(selection))\n }\n return true\n }\n}\n","import type { Command } from '@prosekit/pm/state'\nimport { CellSelection } from 'prosemirror-tables'\n\nimport { findCellRange } from '../table-utils'\n\n/**\n * @public\n */\nexport interface SelectTableRowOptions {\n /**\n * A hit position of the table cell to select from. By default, the selection\n * anchor will be used.\n */\n anchor?: number\n\n /**\n * A hit position of the table cell to select to. By default, the selection\n * head will be used.\n */\n head?: number\n}\n\n/**\n * @public\n */\nexport function selectTableRow(options?: SelectTableRowOptions): Command {\n return (state, dispatch) => {\n const range = findCellRange(state.selection, options?.anchor, options?.head)\n if (!range) {\n return false\n }\n if (dispatch) {\n const [$anchorCell, $headCell] = range\n const selection = CellSelection.rowSelection($anchorCell, $headCell)\n dispatch(state.tr.setSelection(selection))\n }\n return true\n }\n}\n","import {\n defineCommands,\n type Extension,\n} from '@prosekit/core'\nimport {\n addColumnAfter,\n addColumnBefore,\n addRowAfter,\n addRowBefore,\n deleteColumn,\n deleteRow,\n deleteTable,\n mergeCells,\n splitCell,\n} from 'prosemirror-tables'\n\nimport { deleteCellSelection } from './table-commands/delete-cell-selection'\nimport { exitTable } from './table-commands/exit-table'\nimport {\n insertTable,\n type InsertTableOptions,\n} from './table-commands/insert-table'\nimport {\n moveTableColumn,\n type MoveTableColumnOptions,\n} from './table-commands/move-table-column'\nimport {\n moveTableRow,\n type MoveTableRowOptions,\n} from './table-commands/move-table-row'\nimport {\n selectTable,\n type SelectTableOptions,\n} from './table-commands/select-table'\nimport {\n selectTableCell,\n type SelectTableCellOptions,\n} from './table-commands/select-table-cell'\nimport {\n selectTableColumn,\n type SelectTableColumnOptions,\n} from './table-commands/select-table-column'\nimport {\n selectTableRow,\n type SelectTableRowOptions,\n} from './table-commands/select-table-row'\n\n/**\n * @internal\n */\nexport type TableCommandsExtension = Extension<{\n Commands: {\n insertTable: [options: InsertTableOptions]\n exitTable: []\n\n selectTable: [options?: SelectTableOptions]\n selectTableCell: [options?: SelectTableCellOptions]\n selectTableColumn: [options?: SelectTableColumnOptions]\n selectTableRow: [options?: SelectTableRowOptions]\n\n addTableColumnBefore: []\n addTableColumnAfter: []\n addTableRowAbove: []\n addTableRowBelow: []\n\n deleteTable: []\n deleteTableColumn: []\n deleteTableRow: []\n deleteCellSelection: []\n\n mergeTableCells: []\n splitTableCell: []\n\n moveTableRow: [options: MoveTableRowOptions]\n moveTableColumn: [options: MoveTableColumnOptions]\n }\n}>\n\n/**\n * Adds commands for working with `table` nodes.\n *\n * @public\n */\nexport function defineTableCommands(): TableCommandsExtension {\n return defineCommands({\n insertTable,\n exitTable: () => exitTable,\n\n selectTable,\n selectTableCell,\n selectTableColumn,\n selectTableRow,\n\n addTableColumnBefore: () => addColumnBefore,\n addTableColumnAfter: () => addColumnAfter,\n addTableRowAbove: () => addRowBefore,\n addTableRowBelow: () => addRowAfter,\n\n deleteTable: () => deleteTable,\n deleteTableColumn: () => deleteColumn,\n deleteTableRow: () => deleteRow,\n deleteCellSelection: () => deleteCellSelection,\n\n mergeTableCells: () => mergeCells,\n splitTableCell: () => splitCell,\n\n moveTableRow,\n moveTableColumn,\n })\n}\n","import type { PlainExtension } from '@prosekit/core'\n\nimport type { DragEventHandler } from '../drop-indicator'\nimport { defineDropIndicator } from '../drop-indicator'\n\n/**\n * Hides the drop indicator when dragging a table column or row by using the\n * table handle.\n *\n * @internal\n */\nexport function defineTableDropIndicator(): PlainExtension {\n return defineDropIndicator({\n onDrag,\n })\n}\n\nconst matchMap = new WeakMap<DataTransfer, boolean>()\n\nconst onDrag: DragEventHandler = ({ event }): boolean => {\n const dataTransfer = event.dataTransfer\n if (!dataTransfer) return true\n\n let match: boolean\n\n if (matchMap.has(dataTransfer)) {\n match = matchMap.get(dataTransfer)!\n } else {\n // On Safari, accessing `dataTransfer.types` is more than 10x slower than in\n // Chrome. This becomes a bottleneck when `onDrag` is called frequently, so\n // we cache the result in a WeakMap.\n const types = dataTransfer.types\n match = types.includes('application/x-prosekit-table-handle-drag')\n matchMap.set(dataTransfer, match)\n }\n\n // Don't show the drop indicator when the drag event has\n // \"application/x-prosekit-table-handle-drag\" type.\n return !match\n}\n","import {\n definePlugin,\n type PlainExtension,\n} from '@prosekit/core'\nimport {\n columnResizing,\n tableEditing,\n} from 'prosemirror-tables'\n\n/**\n * @public\n */\nexport function defineTablePlugins(): PlainExtension {\n return definePlugin([tableEditing(), columnResizing()])\n}\n","import {\n defineNodeSpec,\n type Extension,\n} from '@prosekit/core'\nimport type {\n AttributeSpec,\n Attrs,\n} from '@prosekit/pm/model'\nimport { tableNodes } from 'prosemirror-tables'\n\nconst cellContent = 'block+'\n\n/**\n * @public\n */\nexport interface CellAttrs {\n colspan?: number\n rowspan?: number\n colwidth?: number[] | null\n}\n\nconst cellAttrs = {\n colspan: { default: 1 },\n rowspan: { default: 1 },\n colwidth: { default: null },\n} satisfies Record<string, AttributeSpec>\n\n/**\n * @internal\n */\nexport type TableSpecExtension = Extension<{\n Nodes: {\n table: Attrs\n }\n}>\n\nconst specs = tableNodes({\n tableGroup: 'block',\n cellContent,\n cellAttributes: {},\n})\n\n/**\n * @internal\n */\nexport function defineTableSpec(): TableSpecExtension {\n return defineNodeSpec({\n ...specs['table'],\n content: 'tableRow+',\n name: 'table',\n })\n}\n\n/**\n * @internal\n */\nexport type TableRowSpecExtension = Extension<{\n Nodes: {\n tableRow: Attrs\n }\n}>\n\n/**\n * @internal\n */\nexport function defineTableRowSpec(): TableRowSpecExtension {\n return defineNodeSpec({\n ...specs['table_row'],\n content: '(tableCell | tableHeaderCell)*',\n name: 'tableRow',\n })\n}\n\n/**\n * @internal\n */\nexport type TableCellSpecExtension = Extension<{\n Nodes: {\n tableCell: CellAttrs\n }\n}>\n\n/**\n * @internal\n */\nexport function defineTableCellSpec(): TableCellSpecExtension {\n return defineNodeSpec({\n ...specs['table_cell'],\n name: 'tableCell',\n attrs: cellAttrs,\n })\n}\n\n/**\n * @internal\n */\nexport type TableHeaderCellSpecExtension = Extension<{\n Nodes: {\n tableHeaderCell: CellAttrs\n }\n}>\n\nexport function defineTableHeaderCellSpec(): TableHeaderCellSpecExtension {\n return defineNodeSpec({\n ...specs['table_header'],\n name: 'tableHeaderCell',\n attrs: cellAttrs,\n })\n}\n","import {\n union,\n type Union,\n} from '@prosekit/core'\n\nimport {\n defineTableCommands,\n type TableCommandsExtension,\n} from './table-commands'\nimport { defineTableDropIndicator } from './table-drop-indicator'\nimport { defineTablePlugins } from './table-plugins'\nimport {\n defineTableCellSpec,\n defineTableHeaderCellSpec,\n defineTableRowSpec,\n defineTableSpec,\n type TableCellSpecExtension,\n type TableHeaderCellSpecExtension,\n type TableRowSpecExtension,\n type TableSpecExtension,\n} from './table-spec'\n\n/**\n * @internal\n */\nexport type TableExtension = Union<\n [\n TableSpecExtension,\n TableRowSpecExtension,\n TableCellSpecExtension,\n TableHeaderCellSpecExtension,\n TableCommandsExtension,\n ]\n>\n\n/**\n * @public\n */\nexport function defineTable(): TableExtension {\n return union(\n defineTableSpec(),\n defineTableRowSpec(),\n defineTableCellSpec(),\n defineTableHeaderCellSpec(),\n defineTablePlugins(),\n defineTableCommands(),\n defineTableDropIndicator(),\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"table-DND_1127.js","names":["exitTable: Command","onDrag: DragEventHandler","match: boolean"],"sources":["../src/table/table-commands/exit-table.ts","../src/table/table-commands/insert-table.ts","../src/table/table-utils.ts","../src/table/table-commands/select-table.ts","../src/table/table-commands/select-table-cell.ts","../src/table/table-commands/select-table-column.ts","../src/table/table-commands/select-table-row.ts","../src/table/table-commands.ts","../src/table/table-drop-indicator.ts","../src/table/table-plugins.ts","../src/table/table-spec.ts","../src/table/table.ts"],"sourcesContent":["import { defaultBlockAt } from '@prosekit/core'\nimport { TextSelection } from '@prosekit/pm/state'\nimport type { Command } from '@prosekit/pm/state'\nimport type { TableRole } from 'prosemirror-tables'\n\n/**\n * When the selection is in a table node, create a default block after the table\n * table, and move the cursor there.\n *\n * @public\n */\nexport const exitTable: Command = (state, dispatch) => {\n const { $head, $anchor } = state.selection\n\n if (!$head.sameParent($anchor)) {\n return false\n }\n\n let tableStart = -1\n let tableDepth = -1\n for (let depth = $head.depth; depth >= 0; depth--) {\n const node = $head.node(depth)\n if ((node.type.spec.tableRole as TableRole) === 'table') {\n tableStart = $head.before(depth)\n tableDepth = depth\n }\n }\n\n if (tableStart < 0 || tableDepth <= 0) {\n return false\n }\n\n const above = $head.node(tableDepth - 1)\n const after = $head.indexAfter(tableDepth - 1)\n const type = defaultBlockAt(above.contentMatchAt(after))\n const node = type?.createAndFill()\n\n if (!type || !node || !above.canReplaceWith(after, after, type)) {\n return false\n }\n\n if (dispatch) {\n const pos = $head.after(tableDepth)\n const tr = state.tr.replaceWith(pos, pos, node)\n tr.setSelection(TextSelection.near(tr.doc.resolve(pos), 1))\n dispatch(tr.scrollIntoView())\n }\n return true\n}\n","import {\n getNodeType,\n insertNode,\n} from '@prosekit/core'\nimport type { Schema } from '@prosekit/pm/model'\nimport type { Command } from '@prosekit/pm/state'\n\nfunction createEmptyTable(\n schema: Schema,\n row: number,\n col: number,\n header: boolean,\n) {\n const tableType = getNodeType(schema, 'table')\n const tableRowType = getNodeType(schema, 'tableRow')\n const tableCellType = getNodeType(schema, 'tableCell')\n const tableHeaderCellType = getNodeType(schema, 'tableHeaderCell')\n\n if (header) {\n const headerCell = tableHeaderCellType.createAndFill()!\n const headerCells = repeat(headerCell, col)\n const headerRow = tableRowType.createAndFill(null, headerCells)!\n\n const bodyCell = tableCellType.createAndFill()!\n const bodyCells = repeat(bodyCell, col)\n const bodyRow = tableRowType.createAndFill(null, bodyCells)!\n const bodyRows = repeat(bodyRow, row - 1)\n\n return tableType.createAndFill(null, [headerRow, ...bodyRows])!\n } else {\n const bodyCell = tableCellType.createAndFill()!\n const bodyCells = repeat(bodyCell, col)\n const bodyRow = tableRowType.createAndFill(null, bodyCells)!\n const bodyRows = repeat(bodyRow, row)\n\n return tableType.createAndFill(null, bodyRows)!\n }\n}\n\nfunction repeat<T>(node: T, length: number): T[] {\n return Array<T>(length).fill(node)\n}\n\n/**\n * @public\n */\nexport interface InsertTableOptions {\n /**\n * The number of rows in the table.\n */\n row: number\n\n /**\n * The number of columns in the table.\n */\n col: number\n\n /**\n * Whether the table has a header row.\n *\n * @default false\n */\n header?: boolean\n}\n\n/**\n * Insert a table node with the given number of rows and columns, and optionally\n * a header row.\n *\n * @param options\n *\n * @public\n */\nexport function insertTable(options: InsertTableOptions): Command {\n return (state, dispatch, view) => {\n const { row, col, header = false } = options\n const table = createEmptyTable(state.schema, row, col, header)\n return insertNode({ node: table })(state, dispatch, view)\n }\n}\n","import { CellSelection } from 'prosemirror-tables'\n\nexport {\n findCellPos,\n findCellRange,\n findTable,\n} from 'prosemirror-tables'\n\n/**\n * Checks if the given object is a `CellSelection` instance.\n *\n * @public\n */\nexport function isCellSelection(value: unknown): value is CellSelection {\n return value instanceof CellSelection\n}\n","import type { Command } from '@prosekit/pm/state'\nimport {\n CellSelection,\n TableMap,\n} from 'prosemirror-tables'\n\nimport { findTable } from '../table-utils'\n\n/**\n * @public\n */\nexport interface SelectTableOptions {\n /**\n * A hit position of the table to select from. By default, the selection\n * anchor will be used.\n */\n pos?: number\n}\n\n/**\n * @public\n */\nexport function selectTable(options?: SelectTableOptions): Command {\n return (state, dispatch) => {\n const $pos = options?.pos\n ? state.doc.resolve(options.pos)\n : state.selection.$anchor\n const table = findTable($pos)\n if (!table) {\n return false\n }\n const map = TableMap.get(table.node)\n if (map.map.length === 0) {\n return false\n }\n if (dispatch) {\n let tr = state.tr\n const firstCellPosInTable = map.map[0]\n const lastCellPosInTable = map.map[map.map.length - 1]\n const firstCellPos = table.pos + firstCellPosInTable + 1\n const lastCellPos = table.pos + lastCellPosInTable + 1\n const $firstCellPos = tr.doc.resolve(firstCellPos)\n const $lastCellPos = tr.doc.resolve(lastCellPos)\n const selection = new CellSelection($firstCellPos, $lastCellPos)\n tr = tr.setSelection(selection)\n dispatch?.(tr)\n }\n return true\n }\n}\n","import type { Command } from '@prosekit/pm/state'\nimport { CellSelection } from 'prosemirror-tables'\n\nimport { findCellPos } from '../table-utils'\n\n/**\n * @public\n */\nexport interface SelectTableCellOptions {\n /**\n * A hit position of the table cell to select from. By default, the selection\n * anchor will be used.\n */\n pos?: number\n}\n\n/**\n * @public\n */\nexport function selectTableCell(options?: SelectTableCellOptions): Command {\n return (state, dispatch) => {\n const $cellPos = findCellPos(\n state.doc,\n options?.pos ?? state.selection.anchor,\n )\n if (!$cellPos) {\n return false\n }\n if (dispatch) {\n const selection = new CellSelection($cellPos)\n dispatch(state.tr.setSelection(selection))\n }\n return true\n }\n}\n","import type { Command } from '@prosekit/pm/state'\nimport { CellSelection } from 'prosemirror-tables'\n\nimport { findCellRange } from '../table-utils'\n\n/**\n * @public\n */\nexport interface SelectTableColumnOptions {\n /**\n * A hit position of the table cell to select from. By default, the selection\n * anchor will be used.\n */\n anchor?: number\n\n /**\n * A hit position of the table cell to select to. By default, the selection\n * head will be used.\n */\n head?: number\n}\n\n/**\n * @public\n */\nexport function selectTableColumn(options?: SelectTableColumnOptions): Command {\n return (state, dispatch) => {\n const range = findCellRange(state.selection, options?.anchor, options?.head)\n if (!range) {\n return false\n }\n if (dispatch) {\n const [$anchorCell, $headCell] = range\n const selection = CellSelection.colSelection($anchorCell, $headCell)\n dispatch(state.tr.setSelection(selection))\n }\n return true\n }\n}\n","import type { Command } from '@prosekit/pm/state'\nimport { CellSelection } from 'prosemirror-tables'\n\nimport { findCellRange } from '../table-utils'\n\n/**\n * @public\n */\nexport interface SelectTableRowOptions {\n /**\n * A hit position of the table cell to select from. By default, the selection\n * anchor will be used.\n */\n anchor?: number\n\n /**\n * A hit position of the table cell to select to. By default, the selection\n * head will be used.\n */\n head?: number\n}\n\n/**\n * @public\n */\nexport function selectTableRow(options?: SelectTableRowOptions): Command {\n return (state, dispatch) => {\n const range = findCellRange(state.selection, options?.anchor, options?.head)\n if (!range) {\n return false\n }\n if (dispatch) {\n const [$anchorCell, $headCell] = range\n const selection = CellSelection.rowSelection($anchorCell, $headCell)\n dispatch(state.tr.setSelection(selection))\n }\n return true\n }\n}\n","import {\n defineCommands,\n type Extension,\n} from '@prosekit/core'\nimport {\n addColumnAfter,\n addColumnBefore,\n addRowAfter,\n addRowBefore,\n deleteColumn,\n deleteRow,\n deleteTable,\n mergeCells,\n splitCell,\n} from 'prosemirror-tables'\n\nimport { deleteCellSelection } from './table-commands/delete-cell-selection'\nimport { exitTable } from './table-commands/exit-table'\nimport {\n insertTable,\n type InsertTableOptions,\n} from './table-commands/insert-table'\nimport {\n moveTableColumn,\n type MoveTableColumnOptions,\n} from './table-commands/move-table-column'\nimport {\n moveTableRow,\n type MoveTableRowOptions,\n} from './table-commands/move-table-row'\nimport {\n selectTable,\n type SelectTableOptions,\n} from './table-commands/select-table'\nimport {\n selectTableCell,\n type SelectTableCellOptions,\n} from './table-commands/select-table-cell'\nimport {\n selectTableColumn,\n type SelectTableColumnOptions,\n} from './table-commands/select-table-column'\nimport {\n selectTableRow,\n type SelectTableRowOptions,\n} from './table-commands/select-table-row'\n\n/**\n * @internal\n */\nexport type TableCommandsExtension = Extension<{\n Commands: {\n insertTable: [options: InsertTableOptions]\n exitTable: []\n\n selectTable: [options?: SelectTableOptions]\n selectTableCell: [options?: SelectTableCellOptions]\n selectTableColumn: [options?: SelectTableColumnOptions]\n selectTableRow: [options?: SelectTableRowOptions]\n\n addTableColumnBefore: []\n addTableColumnAfter: []\n addTableRowAbove: []\n addTableRowBelow: []\n\n deleteTable: []\n deleteTableColumn: []\n deleteTableRow: []\n deleteCellSelection: []\n\n mergeTableCells: []\n splitTableCell: []\n\n moveTableRow: [options: MoveTableRowOptions]\n moveTableColumn: [options: MoveTableColumnOptions]\n }\n}>\n\n/**\n * Adds commands for working with `table` nodes.\n *\n * @public\n */\nexport function defineTableCommands(): TableCommandsExtension {\n return defineCommands({\n insertTable,\n exitTable: () => exitTable,\n\n selectTable,\n selectTableCell,\n selectTableColumn,\n selectTableRow,\n\n addTableColumnBefore: () => addColumnBefore,\n addTableColumnAfter: () => addColumnAfter,\n addTableRowAbove: () => addRowBefore,\n addTableRowBelow: () => addRowAfter,\n\n deleteTable: () => deleteTable,\n deleteTableColumn: () => deleteColumn,\n deleteTableRow: () => deleteRow,\n deleteCellSelection: () => deleteCellSelection,\n\n mergeTableCells: () => mergeCells,\n splitTableCell: () => splitCell,\n\n moveTableRow,\n moveTableColumn,\n })\n}\n","import type { PlainExtension } from '@prosekit/core'\n\nimport type { DragEventHandler } from '../drop-indicator'\nimport { defineDropIndicator } from '../drop-indicator'\n\n/**\n * Hides the drop indicator when dragging a table column or row by using the\n * table handle.\n *\n * @internal\n */\nexport function defineTableDropIndicator(): PlainExtension {\n return defineDropIndicator({\n onDrag,\n })\n}\n\nconst matchMap = new WeakMap<DataTransfer, boolean>()\n\nconst onDrag: DragEventHandler = ({ event }): boolean => {\n const dataTransfer = event.dataTransfer\n if (!dataTransfer) return true\n\n let match: boolean\n\n if (matchMap.has(dataTransfer)) {\n match = matchMap.get(dataTransfer)!\n } else {\n // On Safari, accessing `dataTransfer.types` is more than 10x slower than in\n // Chrome. This becomes a bottleneck when `onDrag` is called frequently, so\n // we cache the result in a WeakMap.\n const types = dataTransfer.types\n match = types.includes('application/x-prosekit-table-handle-drag')\n matchMap.set(dataTransfer, match)\n }\n\n // Don't show the drop indicator when the drag event has\n // \"application/x-prosekit-table-handle-drag\" type.\n return !match\n}\n","import {\n definePlugin,\n type PlainExtension,\n} from '@prosekit/core'\nimport {\n columnResizing,\n tableEditing,\n} from 'prosemirror-tables'\n\n/**\n * @public\n */\nexport function defineTablePlugins(): PlainExtension {\n return definePlugin([tableEditing(), columnResizing()])\n}\n","import {\n defineNodeSpec,\n type Extension,\n} from '@prosekit/core'\nimport type {\n AttributeSpec,\n Attrs,\n} from '@prosekit/pm/model'\nimport { tableNodes } from 'prosemirror-tables'\n\nconst cellContent = 'block+'\n\n/**\n * @public\n */\nexport interface CellAttrs {\n colspan?: number\n rowspan?: number\n colwidth?: number[] | null\n}\n\nconst cellAttrs = {\n colspan: { default: 1 },\n rowspan: { default: 1 },\n colwidth: { default: null },\n} satisfies Record<string, AttributeSpec>\n\n/**\n * @internal\n */\nexport type TableSpecExtension = Extension<{\n Nodes: {\n table: Attrs\n }\n}>\n\nconst specs = tableNodes({\n tableGroup: 'block',\n cellContent,\n cellAttributes: {},\n})\n\n/**\n * @internal\n */\nexport function defineTableSpec(): TableSpecExtension {\n return defineNodeSpec({\n ...specs['table'],\n content: 'tableRow+',\n name: 'table',\n })\n}\n\n/**\n * @internal\n */\nexport type TableRowSpecExtension = Extension<{\n Nodes: {\n tableRow: Attrs\n }\n}>\n\n/**\n * @internal\n */\nexport function defineTableRowSpec(): TableRowSpecExtension {\n return defineNodeSpec({\n ...specs['table_row'],\n content: '(tableCell | tableHeaderCell)*',\n name: 'tableRow',\n })\n}\n\n/**\n * @internal\n */\nexport type TableCellSpecExtension = Extension<{\n Nodes: {\n tableCell: CellAttrs\n }\n}>\n\n/**\n * @internal\n */\nexport function defineTableCellSpec(): TableCellSpecExtension {\n return defineNodeSpec({\n ...specs['table_cell'],\n name: 'tableCell',\n attrs: cellAttrs,\n })\n}\n\n/**\n * @internal\n */\nexport type TableHeaderCellSpecExtension = Extension<{\n Nodes: {\n tableHeaderCell: CellAttrs\n }\n}>\n\nexport function defineTableHeaderCellSpec(): TableHeaderCellSpecExtension {\n return defineNodeSpec({\n ...specs['table_header'],\n name: 'tableHeaderCell',\n attrs: cellAttrs,\n })\n}\n","import {\n union,\n type Union,\n} from '@prosekit/core'\n\nimport {\n defineTableCommands,\n type TableCommandsExtension,\n} from './table-commands'\nimport { defineTableDropIndicator } from './table-drop-indicator'\nimport { defineTablePlugins } from './table-plugins'\nimport {\n defineTableCellSpec,\n defineTableHeaderCellSpec,\n defineTableRowSpec,\n defineTableSpec,\n type TableCellSpecExtension,\n type TableHeaderCellSpecExtension,\n type TableRowSpecExtension,\n type TableSpecExtension,\n} from './table-spec'\n\n/**\n * @internal\n */\nexport type TableExtension = Union<\n [\n TableSpecExtension,\n TableRowSpecExtension,\n TableCellSpecExtension,\n TableHeaderCellSpecExtension,\n TableCommandsExtension,\n ]\n>\n\n/**\n * @public\n */\nexport function defineTable(): TableExtension {\n return union(\n defineTableSpec(),\n defineTableRowSpec(),\n defineTableCellSpec(),\n defineTableHeaderCellSpec(),\n defineTablePlugins(),\n defineTableCommands(),\n defineTableDropIndicator(),\n )\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@prosekit/extensions",
3
3
  "type": "module",
4
- "version": "0.11.6",
4
+ "version": "0.11.7",
5
5
  "private": false,
6
6
  "description": "A collection of common extensions for ProseKit",
7
7
  "author": {
@@ -210,7 +210,7 @@
210
210
  "@ocavue/utils": "^0.7.1",
211
211
  "prosemirror-changeset": "^2.3.1",
212
212
  "prosemirror-dropcursor": "^1.8.2",
213
- "prosemirror-flat-list": "^0.5.5",
213
+ "prosemirror-flat-list": "^0.5.7",
214
214
  "prosemirror-gapcursor": "^1.3.2",
215
215
  "prosemirror-highlight": "^0.13.0",
216
216
  "prosemirror-search": "^1.1.0",
@@ -245,14 +245,14 @@
245
245
  "@vitest/browser": "^3.2.4",
246
246
  "diffable-html": "^6.0.1",
247
247
  "just-pick": "^4.2.0",
248
- "loro-crdt": "^1.8.0",
248
+ "loro-crdt": "^1.8.2",
249
249
  "loro-prosemirror": "^0.3.1",
250
250
  "rehype-parse": "^9.0.1",
251
251
  "rehype-remark": "^10.0.1",
252
252
  "remark-html": "^16.0.1",
253
253
  "remark-parse": "^11.0.0",
254
254
  "remark-stringify": "^11.0.0",
255
- "tsdown": "^0.15.4",
255
+ "tsdown": "^0.15.6",
256
256
  "type-fest": "^5.0.1",
257
257
  "typescript": "~5.9.2",
258
258
  "unified": "^11.0.5",
@@ -75,16 +75,8 @@ export class UploadTask<Result> {
75
75
  const maybePromise = uploader({
76
76
  file,
77
77
  onProgress: (progress) => {
78
- if (typeof progress.total !== 'number') {
79
- throw new TypeError('total must be a number')
80
- }
81
- if (typeof progress.loaded !== 'number') {
82
- throw new TypeError('loaded must be a number')
83
- }
84
- if (progress.loaded > 0) {
85
- for (const subscriber of this.subscribers) {
86
- subscriber(progress)
87
- }
78
+ for (const subscriber of this.subscribers) {
79
+ subscriber(progress)
88
80
  }
89
81
  },
90
82
  })
@@ -0,0 +1,14 @@
1
+ import { insertNode } from '@prosekit/core'
2
+ import type { Command } from '@prosekit/pm/state'
3
+
4
+ import type { ImageAttrs } from '../image-spec'
5
+
6
+ /**
7
+ * Returns a command that inserts an image node with the given attributes at the
8
+ * current selection position.
9
+ *
10
+ * @public
11
+ */
12
+ export function insertImage(attrs?: ImageAttrs): Command {
13
+ return insertNode({ type: 'image', attrs })
14
+ }
@@ -0,0 +1,137 @@
1
+ import {
2
+ insertNode,
3
+ ProseKitError,
4
+ } from '@prosekit/core'
5
+ import type { Command } from '@prosekit/pm/state'
6
+ import type { EditorView } from '@prosekit/pm/view'
7
+
8
+ import {
9
+ UploadTask,
10
+ type Uploader,
11
+ } from '../../file'
12
+ import type { ImageAttrs } from '../image-spec'
13
+
14
+ /**
15
+ * Options for {@link uploadImage}.
16
+ *
17
+ * @public
18
+ */
19
+ export interface UploadImageOptions {
20
+ /**
21
+ * The uploader used to upload the file. It should return a promise that
22
+ * resolves to the URL of the uploaded image.
23
+ */
24
+ uploader: Uploader<string>
25
+ /**
26
+ * The file that will be uploaded.
27
+ */
28
+ file: File
29
+ /**
30
+ * The position where the image should be inserted. If not provided, the
31
+ * image is inserted at the current selection.
32
+ */
33
+ pos?: number
34
+ /**
35
+ * A handler to be called when an error occurs during the upload.
36
+ */
37
+ onError?: ImageUploadErrorHandler
38
+ }
39
+
40
+ /**
41
+ * Options for the {@link ImageUploadErrorHandler} callback.
42
+ *
43
+ * @public
44
+ */
45
+ export interface ImageUploadErrorHandlerOptions {
46
+ /**
47
+ * The file that was uploaded.
48
+ */
49
+ file: File
50
+ /**
51
+ * The error that occurred during the upload.
52
+ */
53
+ error: unknown
54
+ /**
55
+ * The upload task that was used to upload the file.
56
+ */
57
+ uploadTask: UploadTask<string>
58
+ }
59
+
60
+ /**
61
+ * A handler to be called when an error occurs during the upload.
62
+ *
63
+ * @public
64
+ */
65
+ export type ImageUploadErrorHandler = (options: ImageUploadErrorHandlerOptions) => void
66
+
67
+ /**
68
+ * Returns a command that uploads an image file and inserts an image node with a
69
+ * temporary URL which is replaced once the upload completes.
70
+ *
71
+ * @param options
72
+ *
73
+ * @public
74
+ */
75
+ export function uploadImage({ uploader, file, pos, onError }: UploadImageOptions): Command {
76
+ return (state, dispatch, view) => {
77
+ const uploadTask = new UploadTask({ file, uploader })
78
+ const objectURL = uploadTask.objectURL
79
+ const attrs: ImageAttrs = { src: objectURL }
80
+
81
+ uploadTask.finished
82
+ .then((resultURL) => {
83
+ if (view && view.isDestroyed) {
84
+ return
85
+ } else if (typeof resultURL !== 'string') {
86
+ const error = new ProseKitError(
87
+ `Unexpected upload result. Expected a string but got ${typeof resultURL}`,
88
+ )
89
+ onError?.({ file, error, uploadTask })
90
+ } else if (!view) {
91
+ const error = new ProseKitError(
92
+ 'View must be available to replace the image URL',
93
+ )
94
+ onError?.({ file, error, uploadTask })
95
+ } else {
96
+ replaceImageURL(view, objectURL, resultURL)
97
+ UploadTask.delete(objectURL)
98
+ }
99
+ })
100
+ .catch((error) => {
101
+ onError?.({ file, error, uploadTask })
102
+ })
103
+
104
+ return insertNode({ type: 'image', attrs, pos })(state, dispatch, view)
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Replaces the temporary image URL with the final uploaded URL.
110
+ *
111
+ * @internal
112
+ */
113
+ export function replaceImageURL(
114
+ view: EditorView,
115
+ oldURL: string,
116
+ newURL: string,
117
+ ): void {
118
+ const positions: number[] = []
119
+ view.state.doc.descendants((node, pos) => {
120
+ if (node.type.name === 'image') {
121
+ const attrs = node.attrs as ImageAttrs
122
+ if (attrs.src === oldURL) {
123
+ positions.push(pos)
124
+ }
125
+ }
126
+ })
127
+
128
+ if (positions.length === 0) {
129
+ return
130
+ }
131
+
132
+ const tr = view.state.tr
133
+ for (const pos of positions) {
134
+ tr.setNodeAttribute(pos, 'src', newURL)
135
+ }
136
+ view.dispatch(tr)
137
+ }
@@ -1,10 +1,13 @@
1
1
  import {
2
2
  defineCommands,
3
- insertNode,
4
3
  type Extension,
5
4
  } from '@prosekit/core'
6
- import type { Command } from '@prosekit/pm/state'
7
5
 
6
+ import { insertImage } from './image-commands/insert-image'
7
+ import {
8
+ uploadImage,
9
+ type UploadImageOptions,
10
+ } from './image-commands/upload-image'
8
11
  import type { ImageAttrs } from './image-spec'
9
12
 
10
13
  /**
@@ -13,24 +16,16 @@ import type { ImageAttrs } from './image-spec'
13
16
  export type ImageCommandsExtension = Extension<{
14
17
  Commands: {
15
18
  insertImage: [attrs?: ImageAttrs]
19
+ uploadImage: [options: UploadImageOptions]
16
20
  }
17
21
  }>
18
22
 
19
- /**
20
- * Returns a command that inserts an image node with the given attributes at the
21
- * current selection position.
22
- *
23
- * @public
24
- */
25
- export function insertImage(attrs?: ImageAttrs): Command {
26
- return insertNode({ type: 'image', attrs })
27
- }
28
-
29
23
  /**
30
24
  * @internal
31
25
  */
32
26
  export function defineImageCommands(): ImageCommandsExtension {
33
27
  return defineCommands({
34
28
  insertImage,
29
+ uploadImage,
35
30
  })
36
31
  }
@@ -1,15 +1,11 @@
1
1
  import {
2
- insertNode,
3
- ProseKitError,
4
2
  union,
5
3
  type PlainExtension,
6
4
  } from '@prosekit/core'
7
- import type { EditorView } from '@prosekit/pm/view'
8
5
 
9
6
  import {
10
7
  defineFileDropHandler,
11
8
  defineFilePasteHandler,
12
- UploadTask,
13
9
  type FileDropHandler,
14
10
  type FileDropHandlerOptions,
15
11
  type FilePasteHandler,
@@ -17,7 +13,10 @@ import {
17
13
  type Uploader,
18
14
  } from '../file'
19
15
 
20
- import type { ImageAttrs } from './image-spec'
16
+ import {
17
+ uploadImage,
18
+ type ImageUploadErrorHandler,
19
+ } from './image-commands/upload-image'
21
20
 
22
21
  /**
23
22
  * A predicate to determine if the pasted file should be uploaded and inserted as an image.
@@ -32,29 +31,6 @@ export type ImageCanDropPredicate = (options: FileDropHandlerOptions) => boolean
32
31
  /**
33
32
  * A handler to be called when an error occurs during the upload.
34
33
  */
35
- export type ImageUploadErrorHandler = (options: ImageUploadErrorHandlerOptions) => void
36
-
37
- /**
38
- * Options for the {@link ImageUploadErrorHandler} callback.
39
- */
40
- export interface ImageUploadErrorHandlerOptions {
41
- /**
42
- * The file that was uploaded.
43
- */
44
- file: File
45
- /**
46
- * The error that occurred during the upload.
47
- */
48
- error: unknown
49
- /**
50
- * The upload task that was used to upload the file.
51
- */
52
- uploadTask: UploadTask<string>
53
- }
54
-
55
- /**
56
- * Options for {@link defineImageUploadHandler}.
57
- */
58
34
  export interface ImageUploadHandlerOptions {
59
35
  /**
60
36
  * The uploader used to upload the file. It should return a promise that
@@ -99,35 +75,18 @@ export function defineImageUploadHandler({
99
75
  canDrop = defaultCanUpload,
100
76
  onError = defaultOnError,
101
77
  }: ImageUploadHandlerOptions): PlainExtension {
102
- const handleInsert = (view: EditorView, file: File, pos?: number): boolean => {
103
- const uploadTask = new UploadTask({ file, uploader })
104
- const objectURL = uploadTask.objectURL
105
- const attrs: ImageAttrs = { src: objectURL }
106
- uploadTask.finished.then((resultURL) => {
107
- if (view.isDestroyed) {
108
- return
109
- } else if (typeof resultURL !== 'string') {
110
- const error = new ProseKitError(`Unexpected upload result. Expected a string but got ${typeof resultURL}`)
111
- onError({ file, error, uploadTask })
112
- } else {
113
- replaceImageURL(view, objectURL, resultURL)
114
- UploadTask.delete(objectURL)
115
- }
116
- }).catch((error) => {
117
- onError({ file, error, uploadTask })
118
- })
119
- const command = insertNode({ type: 'image', attrs, pos })
120
- return command(view.state, view.dispatch, view)
121
- }
122
-
123
78
  const handlePaste: FilePasteHandler = (options) => {
124
79
  if (!canPaste(options)) return false
125
- return handleInsert(options.view, options.file)
80
+ const { view, file } = options
81
+ const command = uploadImage({ uploader, file, onError })
82
+ return command(view.state, view.dispatch, view)
126
83
  }
127
84
 
128
85
  const handleDrop: FileDropHandler = (options) => {
129
86
  if (!canDrop(options)) return false
130
- return handleInsert(options.view, options.file, options.pos)
87
+ const { view, file, pos } = options
88
+ const command = uploadImage({ uploader, file, onError, pos })
89
+ return command(view.state, view.dispatch, view)
131
90
  }
132
91
 
133
92
  return union(
@@ -135,22 +94,3 @@ export function defineImageUploadHandler({
135
94
  defineFileDropHandler(handleDrop),
136
95
  )
137
96
  }
138
-
139
- function replaceImageURL(view: EditorView, oldURL: string, newURL: string) {
140
- const positions: number[] = []
141
- view.state.doc.descendants((node, pos) => {
142
- if (node.type.name === 'image') {
143
- const attrs = node.attrs as ImageAttrs
144
- if (attrs.src === oldURL) {
145
- positions.push(pos)
146
- }
147
- }
148
- })
149
- if (positions.length > 0) {
150
- const tr = view.state.tr
151
- for (const pos of positions) {
152
- tr.setNodeAttribute(pos, 'src', newURL)
153
- }
154
- view.dispatch(tr)
155
- }
156
- }
@@ -4,9 +4,16 @@ export {
4
4
  } from './image'
5
5
  export {
6
6
  defineImageCommands,
7
- insertImage,
8
7
  type ImageCommandsExtension,
9
8
  } from './image-commands'
9
+ export { insertImage } from './image-commands/insert-image'
10
+ export {
11
+ replaceImageURL,
12
+ uploadImage,
13
+ type ImageUploadErrorHandler,
14
+ type ImageUploadErrorHandlerOptions,
15
+ type UploadImageOptions,
16
+ } from './image-commands/upload-image'
10
17
  export {
11
18
  defineImageSpec,
12
19
  type ImageAttrs,
@@ -16,7 +23,5 @@ export {
16
23
  defineImageUploadHandler,
17
24
  type ImageCanDropPredicate,
18
25
  type ImageCanPastePredicate,
19
- type ImageUploadErrorHandler,
20
- type ImageUploadErrorHandlerOptions,
21
26
  type ImageUploadHandlerOptions,
22
27
  } from './image-upload-handler'
@@ -1 +0,0 @@
1
- {"version":3,"file":"enter-rule-RdhEA900.js","names":["rule: EnterRule","regex: RegExp","handler: EnterRuleHandler","stop: boolean","rules: EnterRule[]","command: Command"],"sources":["../src/enter-rule/index.ts"],"sourcesContent":["import {\n defineFacet,\n defineFacetPayload,\n getNodeType,\n isTextSelection,\n maybeRun,\n OBJECT_REPLACEMENT_CHARACTER,\n pluginFacet,\n type PlainExtension,\n type PluginPayload,\n} from '@prosekit/core'\nimport { keydownHandler } from '@prosekit/pm/keymap'\nimport type {\n Attrs,\n NodeType,\n} from '@prosekit/pm/model'\nimport {\n PluginKey,\n ProseMirrorPlugin,\n type Command,\n type EditorState,\n type Transaction,\n} from '@prosekit/pm/state'\nimport type { EditorView } from '@prosekit/pm/view'\n\n/**\n * @public\n *\n * Options for {@link EnterRuleHandler}.\n */\nexport interface EnterRuleHandlerOptions {\n /**\n * The current editor state.\n */\n state: EditorState\n\n /**\n * The start position of the matched text.\n */\n from: number\n\n /**\n * The end position of the matched text.\n */\n to: number\n\n /**\n * The matched result from the regular expression.\n */\n match: RegExpExecArray\n}\n\n/**\n * @public\n */\nexport type EnterRuleHandler = (options: EnterRuleHandlerOptions) => Transaction | null\n\n/**\n * Options for {@link defineEnterRule}.\n *\n * @public\n */\nexport type EnterRuleOptions = {\n /**\n * The regular expression to match against. It should end with `$`.\n */\n regex: RegExp\n\n /**\n * A function to be called when an enter rule is triggered.\n */\n handler: EnterRuleHandler\n\n /**\n * Whether to stop further handlers from being called if this rule is triggered.\n *\n * @default false\n */\n stop?: boolean\n}\n\n/**\n * Options for {@link defineTextBlockEnterRule}.\n *\n * @public\n */\nexport interface TextBlockEnterRuleOptions {\n /**\n * The regular expression to match against. It should end with `$`.\n */\n regex: RegExp\n\n /**\n * The node type to replace the matched text with.\n */\n type: string | NodeType\n\n /**\n * Attributes to set on the node. If a function is provided, it will be called\n * with the matched result from the regular expression.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n\n /**\n * Whether to stop further handlers from being called if this rule is triggered.\n *\n * @default true\n */\n stop?: boolean\n}\n\n/**\n * Defines an enter rule. An enter rule applies when the text directly in front of\n * the cursor matches `regex` and user presses Enter. The `regex` should end\n * with `$`.\n *\n * @param options\n *\n * @public\n */\nexport function defineEnterRule({\n regex,\n handler,\n stop = false,\n}: EnterRuleOptions): PlainExtension {\n const rule: EnterRule = new EnterRule(regex, handler, stop)\n return defineFacetPayload(enterRule, [rule]) as PlainExtension\n}\n\n/**\n * Defines an enter rule that replaces the matched text with a block node.\n *\n * See also {@link defineEnterRule}.\n *\n * @param options\n *\n * @public\n */\nexport function defineTextBlockEnterRule({\n regex,\n type,\n attrs,\n stop = true,\n}: TextBlockEnterRuleOptions): PlainExtension {\n return defineEnterRule({\n regex,\n handler: ({ state, from, to, match }) => {\n const nodeType = getNodeType(state.schema, type)\n const $start = state.doc.resolve(from)\n\n if (\n !$start\n .node(-1)\n .canReplaceWith($start.index(-1), $start.indexAfter(-1), nodeType)\n ) {\n return null\n }\n\n const nodeAttrs = maybeRun(attrs, match)\n return state.tr\n .delete(from, to)\n .setBlockType(from, from, nodeType, nodeAttrs)\n },\n stop,\n })\n}\n\n/**\n * @internal\n */\nclass EnterRule {\n constructor(\n readonly regex: RegExp,\n readonly handler: EnterRuleHandler,\n readonly stop: boolean,\n ) {}\n}\n\nconst enterRule = defineFacet<EnterRule, PluginPayload>({\n reduce: () => {\n let rules: EnterRule[] = []\n\n const command: Command = (state, dispatch, view) => {\n if (!view) return false\n return execRules(view, rules, dispatch)\n }\n const handler = keydownHandler({ Enter: command })\n const plugin = new ProseMirrorPlugin({\n key: new PluginKey('prosekit-enter-rule'),\n props: { handleKeyDown: handler },\n })\n\n return function reducer(inputs) {\n rules = inputs\n return plugin\n }\n },\n\n parent: pluginFacet,\n})\n\nfunction execRules(\n view: EditorView,\n rules: readonly EnterRule[],\n dispatch?: (tr: Transaction) => void,\n): boolean {\n if (view.composing) return false\n const state = view.state\n const selection = state.selection\n if (!isTextSelection(selection)) return false\n const $cursor = selection.$cursor\n if (!$cursor || $cursor.parent.type.spec.code) return false\n\n const textBefore = $cursor.parent.textBetween(\n Math.max(0, $cursor.parentOffset - MAX_MATCH),\n $cursor.parentOffset,\n null,\n OBJECT_REPLACEMENT_CHARACTER,\n )\n\n for (const rule of rules) {\n rule.regex.lastIndex = 0\n const match = rule.regex.exec(textBefore)\n const tr = match\n && rule.handler({\n state,\n from: $cursor.pos - match[0].length,\n to: $cursor.pos,\n match,\n })\n if (!tr) continue\n dispatch?.(tr)\n\n if (rule.stop) {\n return true\n }\n }\n return false\n}\n\nconst MAX_MATCH = 200\n"],"mappings":";;;;;;;;;;;;;;AAwHA,SAAgB,gBAAgB,EAC9B,OACA,SACA,OAAO,SAC4B;CACnC,MAAMA,OAAkB,IAAI,UAAU,OAAO,SAAS,KAAK;AAC3D,QAAO,mBAAmB,WAAW,CAAC,KAAK,CAAC;;;;;;;;;;;AAY9C,SAAgB,yBAAyB,EACvC,OACA,MACA,OACA,OAAO,QACqC;AAC5C,QAAO,gBAAgB;EACrB;EACA,UAAU,EAAE,OAAO,MAAM,IAAI,YAAY;GACvC,MAAM,WAAW,YAAY,MAAM,QAAQ,KAAK;GAChD,MAAM,SAAS,MAAM,IAAI,QAAQ,KAAK;AAEtC,OACE,CAAC,OACE,KAAK,GAAG,CACR,eAAe,OAAO,MAAM,GAAG,EAAE,OAAO,WAAW,GAAG,EAAE,SAAS,CAEpE,QAAO;GAGT,MAAM,YAAY,SAAS,OAAO,MAAM;AACxC,UAAO,MAAM,GACV,OAAO,MAAM,GAAG,CAChB,aAAa,MAAM,MAAM,UAAU,UAAU;;EAElD;EACD,CAAC;;;;;AAMJ,IAAM,YAAN,MAAgB;CACd,YACE,AAASC,OACT,AAASC,SACT,AAASC,MACT;EAHS;EACA;EACA;;;AAIb,MAAM,YAAY,YAAsC;CACtD,cAAc;EACZ,IAAIC,QAAqB,EAAE;EAE3B,MAAMC,WAAoB,OAAO,UAAU,SAAS;AAClD,OAAI,CAAC,KAAM,QAAO;AAClB,UAAO,UAAU,MAAM,OAAO,SAAS;;EAEzC,MAAM,UAAU,eAAe,EAAE,OAAO,SAAS,CAAC;EAClD,MAAM,SAAS,IAAI,kBAAkB;GACnC,KAAK,IAAI,UAAU,sBAAsB;GACzC,OAAO,EAAE,eAAe,SAAS;GAClC,CAAC;AAEF,SAAO,SAAS,QAAQ,QAAQ;AAC9B,WAAQ;AACR,UAAO;;;CAIX,QAAQ;CACT,CAAC;AAEF,SAAS,UACP,MACA,OACA,UACS;AACT,KAAI,KAAK,UAAW,QAAO;CAC3B,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,MAAM;AACxB,KAAI,CAAC,gBAAgB,UAAU,CAAE,QAAO;CACxC,MAAM,UAAU,UAAU;AAC1B,KAAI,CAAC,WAAW,QAAQ,OAAO,KAAK,KAAK,KAAM,QAAO;CAEtD,MAAM,aAAa,QAAQ,OAAO,YAChC,KAAK,IAAI,GAAG,QAAQ,eAAe,UAAU,EAC7C,QAAQ,cACR,MACA,6BACD;AAED,MAAK,MAAM,QAAQ,OAAO;AACxB,OAAK,MAAM,YAAY;EACvB,MAAM,QAAQ,KAAK,MAAM,KAAK,WAAW;EACzC,MAAM,KAAK,SACN,KAAK,QAAQ;GACd;GACA,MAAM,QAAQ,MAAM,MAAM,GAAG;GAC7B,IAAI,QAAQ;GACZ;GACD,CAAC;AACJ,MAAI,CAAC,GAAI;AACT,aAAW,GAAG;AAEd,MAAI,KAAK,KACP,QAAO;;AAGX,QAAO;;AAGT,MAAM,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"input-rule-B17tpW4m.js","names":["rules: InputRule[]"],"sources":["../src/input-rule/index.ts"],"sourcesContent":["import {\n defineFacet,\n defineFacetPayload,\n getMarkType,\n getNodeType,\n isMarkAbsent,\n maybeRun,\n pluginFacet,\n type PlainExtension,\n type PluginPayload,\n} from '@prosekit/core'\nimport {\n InputRule,\n inputRules,\n textblockTypeInputRule,\n wrappingInputRule,\n} from '@prosekit/pm/inputrules'\nimport type {\n Attrs,\n MarkType,\n NodeType,\n ProseMirrorNode,\n Schema,\n} from '@prosekit/pm/model'\nimport type { Plugin } from '@prosekit/pm/state'\n\n/**\n * Defines an input rule extension.\n *\n * @param rule - The ProseMirror input rule to add.\n *\n * @public\n */\nexport function defineInputRule(rule: InputRule): PlainExtension {\n return defineInputRuleFacetPayload(() => rule)\n}\n\n/**\n * Options for {@link defineMarkInputRule}.\n *\n * @public\n */\nexport interface MarkInputRuleOptions {\n /**\n * The regular expression to match against, which should end with `$` and has\n * exactly one capture group. All other matched text outside the capture group\n * will be deleted.\n */\n regex: RegExp\n\n /**\n * The type of mark to set.\n */\n type: string | MarkType\n\n /**\n * Attributes to set on the mark.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n\n /**\n * Whether this rule should fire inside marks marked as [code](https://prosemirror.net/docs/ref/#model.MarkSpec.code).\n *\n * @default `false`\n */\n inCodeMark?: boolean\n}\n\n/**\n * @internal\n */\nexport function createMarkInputRule({\n regex,\n type,\n attrs = null,\n inCodeMark = false,\n}: MarkInputRuleOptions): InputRule {\n const rule = new InputRule(regex, (state, match, start, end) => {\n const { tr, schema } = state\n const [fullText, markText] = match\n\n if (!markText) {\n return null\n }\n\n const markStart = start + fullText.indexOf(markText)\n const markEnd = markStart + markText.length\n\n if (!(start <= markStart && markStart < markEnd && markEnd <= end)) {\n // Incorrect regex.\n return null\n }\n\n const markType = getMarkType(schema, type)\n const mark = markType.create(maybeRun(attrs, match))\n\n if (!isMarkAbsent(tr.doc, markStart, markEnd, markType, attrs)) {\n // The mark is already active.\n return null\n }\n\n const initialStoredMarks = tr.storedMarks ?? []\n\n tr.addMark(markStart, markEnd, mark)\n\n if (markEnd < end) {\n tr.delete(markEnd, end)\n }\n if (start < markStart) {\n tr.delete(start, markStart)\n }\n\n // Make sure not to reactivate any marks which had previously been\n // deactivated. By keeping track of the initial stored marks we are able to\n // discard any unintended consequences of deleting text and adding it again.\n tr.setStoredMarks(initialStoredMarks)\n\n return tr\n }, { inCodeMark })\n\n return rule\n}\n\n/**\n * Defines an input rule for automatically adding inline marks when a given\n * pattern is typed.\n *\n * @public\n */\nexport function defineMarkInputRule(\n options: MarkInputRuleOptions,\n): PlainExtension {\n return defineInputRule(createMarkInputRule(options))\n}\n\n/**\n * Defines an input rule that changes the type of a textblock when the matched\n * text is typed into it.\n *\n * See also [textblockTypeInputRule](https://prosemirror.net/docs/ref/#inputrules.textblockTypeInputRule)\n *\n * @param options\n *\n * @public\n */\nexport function defineTextBlockInputRule({\n regex,\n type,\n attrs,\n}: {\n /**\n * The regular expression to match against, which should end with `$`. It\n * usually also starts with `^` to that it is only matched at the start of a\n * textblock.\n */\n regex: RegExp\n\n /**\n * The node type to replace the matched text with.\n */\n type: string | NodeType\n\n /**\n * Attributes to set on the node.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n}): PlainExtension {\n return defineInputRuleFacetPayload(({ schema }): InputRule => {\n const nodeType = getNodeType(schema, type)\n return textblockTypeInputRule(regex, nodeType, attrs)\n })\n}\n\n/**\n * Defines an input rule for automatically wrapping a textblock when a given\n * string is typed.\n *\n * See also [wrappingInputRule](https://prosemirror.net/docs/ref/#inputrules.wrappingInputRule)\n *\n * @param options\n *\n * @public\n */\nexport function defineWrappingInputRule({\n regex,\n type,\n attrs,\n join,\n}: {\n /**\n * The regular expression to match against, which should end with `$`. It\n * usually also starts with `^` to that it is only matched at the start of a\n * textblock.\n */\n regex: RegExp\n\n /**\n * The type of node to wrap in.\n */\n type: string | NodeType\n\n /**\n * Attributes to set on the node.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n\n /**\n * By default, if there's a node with the same type above the newly wrapped\n * node, the rule will try to\n * [join](https://prosemirror.net/docs/ref/#transform.Transform.join) those\n * two nodes. You can pass a join predicate, which takes a regular expression\n * match and the node before the wrapped node, and can return a boolean to\n * indicate whether a join should happen.\n */\n join?: (match: RegExpMatchArray, node: ProseMirrorNode) => boolean\n}): PlainExtension {\n return defineInputRuleFacetPayload(({ schema }): InputRule => {\n const nodeType = getNodeType(schema, type)\n return wrappingInputRule(regex, nodeType, attrs, join)\n })\n}\n\nfunction defineInputRuleFacetPayload(input: InputRulePayload): PlainExtension {\n return defineFacetPayload(inputRuleFacet, [input]) as PlainExtension\n}\n\ntype InputRulePayload = (context: { schema: Schema }) => InputRule\n\nconst inputRuleFacet = defineFacet<InputRulePayload, PluginPayload>({\n reducer: (inputs: InputRulePayload[]): PluginPayload => {\n return (context): Plugin[] => {\n const rules: InputRule[] = inputs.flatMap((callback) => callback(context))\n return [inputRules({ rules })]\n }\n },\n parent: pluginFacet,\n})\n"],"mappings":";;;;;;;;;;;AAiCA,SAAgB,gBAAgB,MAAiC;AAC/D,QAAO,kCAAkC,KAAK;;;;;AAqChD,SAAgB,oBAAoB,EAClC,OACA,MACA,QAAQ,MACR,aAAa,SACqB;AA4ClC,QA3Ca,IAAI,UAAU,QAAQ,OAAO,OAAO,OAAO,QAAQ;EAC9D,MAAM,EAAE,IAAI,WAAW;EACvB,MAAM,CAAC,UAAU,YAAY;AAE7B,MAAI,CAAC,SACH,QAAO;EAGT,MAAM,YAAY,QAAQ,SAAS,QAAQ,SAAS;EACpD,MAAM,UAAU,YAAY,SAAS;AAErC,MAAI,EAAE,SAAS,aAAa,YAAY,WAAW,WAAW,KAE5D,QAAO;EAGT,MAAM,WAAW,YAAY,QAAQ,KAAK;EAC1C,MAAM,OAAO,SAAS,OAAO,SAAS,OAAO,MAAM,CAAC;AAEpD,MAAI,CAAC,aAAa,GAAG,KAAK,WAAW,SAAS,UAAU,MAAM,CAE5D,QAAO;EAGT,MAAM,qBAAqB,GAAG,eAAe,EAAE;AAE/C,KAAG,QAAQ,WAAW,SAAS,KAAK;AAEpC,MAAI,UAAU,IACZ,IAAG,OAAO,SAAS,IAAI;AAEzB,MAAI,QAAQ,UACV,IAAG,OAAO,OAAO,UAAU;AAM7B,KAAG,eAAe,mBAAmB;AAErC,SAAO;IACN,EAAE,YAAY,CAAC;;;;;;;;AAWpB,SAAgB,oBACd,SACgB;AAChB,QAAO,gBAAgB,oBAAoB,QAAQ,CAAC;;;;;;;;;;;;AAatD,SAAgB,yBAAyB,EACvC,OACA,MACA,SAkBiB;AACjB,QAAO,6BAA6B,EAAE,aAAwB;EAC5D,MAAM,WAAW,YAAY,QAAQ,KAAK;AAC1C,SAAO,uBAAuB,OAAO,UAAU,MAAM;GACrD;;;;;;;;;;;;AAaJ,SAAgB,wBAAwB,EACtC,OACA,MACA,OACA,QA4BiB;AACjB,QAAO,6BAA6B,EAAE,aAAwB;EAC5D,MAAM,WAAW,YAAY,QAAQ,KAAK;AAC1C,SAAO,kBAAkB,OAAO,UAAU,OAAO,KAAK;GACtD;;AAGJ,SAAS,4BAA4B,OAAyC;AAC5E,QAAO,mBAAmB,gBAAgB,CAAC,MAAM,CAAC;;AAKpD,MAAM,iBAAiB,YAA6C;CAClE,UAAU,WAA8C;AACtD,UAAQ,YAAsB;GAC5B,MAAMA,QAAqB,OAAO,SAAS,aAAa,SAAS,QAAQ,CAAC;AAC1E,UAAO,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;;;CAGlC,QAAQ;CACT,CAAC"}