@prosekit/extensions 0.11.6 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{drop-indicator-E7nCfdnR.js → drop-indicator-BMvWUDXz.js} +2 -2
- package/dist/{drop-indicator-E7nCfdnR.js.map → drop-indicator-BMvWUDXz.js.map} +1 -1
- package/dist/{enter-rule-RdhEA900.js → enter-rule-D-p4ykfv.js} +3 -4
- package/dist/enter-rule-D-p4ykfv.js.map +1 -0
- package/dist/{file-DVUhe5KJ.js → file-DKoIIa7q.js} +3 -5
- package/dist/{file-DVUhe5KJ.js.map → file-DKoIIa7q.js.map} +1 -1
- package/dist/{index-DY6lIIYV.d.ts → index-DdjnBeho.d.ts} +2 -2
- package/dist/index-DdjnBeho.d.ts.map +1 -0
- package/dist/{input-rule-B17tpW4m.js → input-rule-COGr_GBb.js} +5 -8
- package/dist/input-rule-COGr_GBb.js.map +1 -0
- package/dist/{mark-rule-CGmswjQ_.js → mark-rule-v2E7B4C0.js} +2 -2
- package/dist/{mark-rule-CGmswjQ_.js.map → mark-rule-v2E7B4C0.js.map} +1 -1
- package/dist/{paste-rule-BIztzELg.js → paste-rule-qSz46pqD.js} +3 -4
- package/dist/{paste-rule-BIztzELg.js.map → paste-rule-qSz46pqD.js.map} +1 -1
- package/dist/prosekit-extensions-autocomplete.d.ts.map +1 -1
- package/dist/prosekit-extensions-autocomplete.js +1 -2
- package/dist/prosekit-extensions-autocomplete.js.map +1 -1
- package/dist/prosekit-extensions-blockquote.d.ts.map +1 -1
- package/dist/prosekit-extensions-blockquote.js +1 -1
- package/dist/prosekit-extensions-bold.d.ts.map +1 -1
- package/dist/prosekit-extensions-bold.js +1 -1
- package/dist/prosekit-extensions-code-block.d.ts +1 -1
- package/dist/prosekit-extensions-code-block.d.ts.map +1 -1
- package/dist/prosekit-extensions-code-block.js +4 -5
- package/dist/prosekit-extensions-code-block.js.map +1 -1
- package/dist/prosekit-extensions-code.d.ts.map +1 -1
- package/dist/prosekit-extensions-code.js +1 -1
- package/dist/prosekit-extensions-commit.d.ts.map +1 -1
- package/dist/prosekit-extensions-commit.js +2 -4
- package/dist/prosekit-extensions-commit.js.map +1 -1
- package/dist/prosekit-extensions-doc.d.ts.map +1 -1
- package/dist/prosekit-extensions-drop-cursor.d.ts.map +1 -1
- package/dist/prosekit-extensions-drop-indicator.d.ts.map +1 -1
- package/dist/prosekit-extensions-drop-indicator.js +1 -1
- package/dist/prosekit-extensions-enter-rule.d.ts.map +1 -1
- package/dist/prosekit-extensions-enter-rule.js +1 -1
- package/dist/prosekit-extensions-file.d.ts +1 -1
- package/dist/prosekit-extensions-file.js +1 -1
- package/dist/prosekit-extensions-hard-break.d.ts.map +1 -1
- package/dist/prosekit-extensions-heading.d.ts.map +1 -1
- package/dist/prosekit-extensions-heading.js +1 -1
- package/dist/prosekit-extensions-horizontal-rule.d.ts.map +1 -1
- package/dist/prosekit-extensions-horizontal-rule.js +1 -1
- package/dist/prosekit-extensions-image.d.ts +86 -30
- package/dist/prosekit-extensions-image.d.ts.map +1 -1
- package/dist/prosekit-extensions-image.js +93 -52
- package/dist/prosekit-extensions-image.js.map +1 -1
- package/dist/prosekit-extensions-input-rule.d.ts.map +1 -1
- package/dist/prosekit-extensions-input-rule.js +1 -1
- package/dist/prosekit-extensions-italic.d.ts.map +1 -1
- package/dist/prosekit-extensions-italic.js +1 -1
- package/dist/prosekit-extensions-link.d.ts.map +1 -1
- package/dist/prosekit-extensions-link.js +4 -4
- package/dist/prosekit-extensions-list.d.ts.map +1 -1
- package/dist/prosekit-extensions-list.js +37 -10
- package/dist/prosekit-extensions-list.js.map +1 -1
- package/dist/prosekit-extensions-loro.d.ts.map +1 -1
- package/dist/prosekit-extensions-mark-rule.d.ts.map +1 -1
- package/dist/prosekit-extensions-mark-rule.js +1 -1
- package/dist/prosekit-extensions-mention.d.ts.map +1 -1
- package/dist/prosekit-extensions-paragraph.d.ts.map +1 -1
- package/dist/prosekit-extensions-paste-rule.d.ts.map +1 -1
- package/dist/prosekit-extensions-paste-rule.js +1 -1
- package/dist/prosekit-extensions-placeholder.d.ts.map +1 -1
- package/dist/prosekit-extensions-placeholder.js +3 -4
- package/dist/prosekit-extensions-placeholder.js.map +1 -1
- package/dist/prosekit-extensions-search.d.ts.map +1 -1
- package/dist/prosekit-extensions-search.js +1 -2
- package/dist/prosekit-extensions-search.js.map +1 -1
- package/dist/prosekit-extensions-strike.d.ts.map +1 -1
- package/dist/prosekit-extensions-strike.js +1 -1
- package/dist/prosekit-extensions-table.d.ts.map +1 -1
- package/dist/prosekit-extensions-table.js +2 -2
- package/dist/prosekit-extensions-text-align.d.ts.map +1 -1
- package/dist/prosekit-extensions-text.d.ts.map +1 -1
- package/dist/prosekit-extensions-underline.d.ts.map +1 -1
- package/dist/prosekit-extensions-yjs.d.ts.map +1 -1
- package/dist/{shiki-highlighter-chunk-Cwu1Jr9o.d.ts → shiki-highlighter-chunk-DcY3Ud8v.d.ts} +2 -2
- package/dist/shiki-highlighter-chunk-DcY3Ud8v.d.ts.map +1 -0
- package/dist/shiki-highlighter-chunk.d.ts +1 -1
- package/dist/{table-BNwuK7xg.js → table-BLjD91VB.js} +11 -20
- package/dist/{table-BNwuK7xg.js.map → table-BLjD91VB.js.map} +1 -1
- package/package.json +14 -14
- package/src/file/file-upload.ts +2 -10
- package/src/image/image-commands/insert-image.ts +14 -0
- package/src/image/image-commands/upload-image.ts +137 -0
- package/src/image/image-commands.ts +7 -12
- package/src/image/image-upload-handler.ts +10 -70
- package/src/image/index.ts +8 -3
- package/src/link/index.spec.ts +1 -1
- package/src/list/list-serializer.ts +57 -3
- package/src/list/list.spec.ts +129 -0
- package/src/testing/keyboard.ts +1 -1
- package/src/testing/markdown.ts +3 -0
- package/dist/enter-rule-RdhEA900.js.map +0 -1
- package/dist/index-DY6lIIYV.d.ts.map +0 -1
- package/dist/input-rule-B17tpW4m.js.map +0 -1
- package/dist/shiki-highlighter-chunk-Cwu1Jr9o.d.ts.map +0 -1
|
@@ -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-BLjD91VB.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.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "A collection of common extensions for ProseKit",
|
|
7
7
|
"author": {
|
|
@@ -207,18 +207,18 @@
|
|
|
207
207
|
"src"
|
|
208
208
|
],
|
|
209
209
|
"dependencies": {
|
|
210
|
-
"@ocavue/utils": "^0.
|
|
210
|
+
"@ocavue/utils": "^0.8.1",
|
|
211
211
|
"prosemirror-changeset": "^2.3.1",
|
|
212
|
+
"prosemirror-drop-indicator": "^0.1.2",
|
|
212
213
|
"prosemirror-dropcursor": "^1.8.2",
|
|
213
|
-
"prosemirror-flat-list": "^0.5.
|
|
214
|
-
"prosemirror-gapcursor": "^1.
|
|
214
|
+
"prosemirror-flat-list": "^0.5.8",
|
|
215
|
+
"prosemirror-gapcursor": "^1.4.0",
|
|
215
216
|
"prosemirror-highlight": "^0.13.0",
|
|
216
217
|
"prosemirror-search": "^1.1.0",
|
|
217
218
|
"prosemirror-tables": "^1.8.1",
|
|
218
|
-
"shiki": "^3.
|
|
219
|
+
"shiki": "^3.14.0",
|
|
219
220
|
"@prosekit/core": "^0.8.4",
|
|
220
|
-
"@prosekit/pm": "^0.1.
|
|
221
|
-
"prosemirror-drop-indicator": "^0.1.0"
|
|
221
|
+
"@prosekit/pm": "^0.1.13"
|
|
222
222
|
},
|
|
223
223
|
"peerDependencies": {
|
|
224
224
|
"loro-crdt": ">= 0.16.7",
|
|
@@ -242,21 +242,21 @@
|
|
|
242
242
|
},
|
|
243
243
|
"devDependencies": {
|
|
244
244
|
"@types/diffable-html": "^5.0.2",
|
|
245
|
-
"@vitest/browser": "^3.2.4",
|
|
246
245
|
"diffable-html": "^6.0.1",
|
|
247
246
|
"just-pick": "^4.2.0",
|
|
248
|
-
"loro-crdt": "^1.8.
|
|
249
|
-
"loro-prosemirror": "^0.3.
|
|
247
|
+
"loro-crdt": "^1.8.8",
|
|
248
|
+
"loro-prosemirror": "^0.3.6",
|
|
250
249
|
"rehype-parse": "^9.0.1",
|
|
251
250
|
"rehype-remark": "^10.0.1",
|
|
251
|
+
"remark-gfm": "^4.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.
|
|
256
|
-
"type-fest": "^5.0
|
|
257
|
-
"typescript": "~5.9.
|
|
255
|
+
"tsdown": "^0.15.12",
|
|
256
|
+
"type-fest": "^5.1.0",
|
|
257
|
+
"typescript": "~5.9.3",
|
|
258
258
|
"unified": "^11.0.5",
|
|
259
|
-
"vitest": "^
|
|
259
|
+
"vitest": "^4.0.5",
|
|
260
260
|
"y-prosemirror": "^1.3.7",
|
|
261
261
|
"y-protocols": "^1.0.6",
|
|
262
262
|
"yjs": "^13.6.27",
|
package/src/file/file-upload.ts
CHANGED
|
@@ -75,16 +75,8 @@ export class UploadTask<Result> {
|
|
|
75
75
|
const maybePromise = uploader({
|
|
76
76
|
file,
|
|
77
77
|
onProgress: (progress) => {
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
package/src/image/index.ts
CHANGED
|
@@ -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'
|
package/src/link/index.spec.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type PlainExtension,
|
|
5
5
|
} from '@prosekit/core'
|
|
6
6
|
import {
|
|
7
|
+
findCheckboxInListItem,
|
|
7
8
|
joinListElements,
|
|
8
9
|
listToDOM,
|
|
9
10
|
} from 'prosemirror-flat-list'
|
|
@@ -16,13 +17,15 @@ export function defineListSerializer(): PlainExtension {
|
|
|
16
17
|
serializeFragmentWrapper: (fn) => {
|
|
17
18
|
return (...args) => {
|
|
18
19
|
const dom = fn(...args)
|
|
19
|
-
return joinListElements(dom)
|
|
20
|
+
return normalizeElementTree(joinListElements(dom))
|
|
20
21
|
}
|
|
21
22
|
},
|
|
22
23
|
serializeNodeWrapper: (fn) => {
|
|
23
24
|
return (...args) => {
|
|
24
25
|
const dom = fn(...args)
|
|
25
|
-
return isElementLike(dom)
|
|
26
|
+
return isElementLike(dom)
|
|
27
|
+
? normalizeElementTree(joinListElements(dom))
|
|
28
|
+
: dom
|
|
26
29
|
}
|
|
27
30
|
},
|
|
28
31
|
nodesFromSchemaWrapper: (fn) => {
|
|
@@ -30,9 +33,60 @@ export function defineListSerializer(): PlainExtension {
|
|
|
30
33
|
const nodes = fn(...args)
|
|
31
34
|
return {
|
|
32
35
|
...nodes,
|
|
33
|
-
list: (node) => listToDOM({ node, nativeList: true
|
|
36
|
+
list: (node) => listToDOM({ node, nativeList: true }),
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
},
|
|
37
40
|
})
|
|
38
41
|
}
|
|
42
|
+
|
|
43
|
+
function normalizeElementTree<T extends Element | DocumentFragment>(
|
|
44
|
+
node: T,
|
|
45
|
+
): T {
|
|
46
|
+
if (isElementLike(node)) {
|
|
47
|
+
normalizeTaskList(node)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const child of node.children) {
|
|
51
|
+
normalizeElementTree(child)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return node
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Modifies the DOM tree for task lists to ensure that the output HTML can be
|
|
59
|
+
* parsed by rehype-remark.
|
|
60
|
+
*/
|
|
61
|
+
function normalizeTaskList(node: Element): void {
|
|
62
|
+
if (
|
|
63
|
+
!node.classList.contains('prosemirror-flat-list')
|
|
64
|
+
|| node.getAttribute('data-list-kind') !== 'task'
|
|
65
|
+
|| node.children.length !== 2
|
|
66
|
+
) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const marker = node.children.item(0)
|
|
71
|
+
if (!marker || !marker.classList.contains('list-marker')) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const checkbox = findCheckboxInListItem(marker)
|
|
76
|
+
if (!checkbox) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const content = node.children.item(1)
|
|
81
|
+
if (!content || !content.classList.contains('list-content')) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const textBlock = content.children.item(0)
|
|
86
|
+
if (!textBlock || !['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(textBlock.tagName)) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
node.replaceChildren(...content.children)
|
|
91
|
+
textBlock.prepend(checkbox)
|
|
92
|
+
}
|