@prosekit/extensions 0.11.4 → 0.11.6
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/commit/style.css +2 -0
- package/dist/commit/style.css.map +1 -0
- package/dist/commit/style.js +1 -0
- package/dist/drop-indicator-E7nCfdnR.js +58 -0
- package/dist/drop-indicator-E7nCfdnR.js.map +1 -0
- package/dist/enter-rule-RdhEA900.js +2 -1
- package/dist/enter-rule-RdhEA900.js.map +1 -0
- package/dist/file-DVUhe5KJ.js +134 -0
- package/dist/file-DVUhe5KJ.js.map +1 -0
- package/dist/gap-cursor/style.css +2 -0
- package/dist/gap-cursor/style.css.map +1 -0
- package/dist/gap-cursor/style.js +1 -0
- package/dist/index-DY6lIIYV.d.ts +134 -0
- package/dist/index-DY6lIIYV.d.ts.map +1 -0
- package/dist/{input-rule-Gji4N7Oe.js → input-rule-B17tpW4m.js} +3 -3
- package/dist/input-rule-B17tpW4m.js.map +1 -0
- package/dist/list/style.css +2 -0
- package/dist/list/style.css.map +1 -0
- package/dist/list/style.js +1 -0
- package/dist/loro/style.css +2 -0
- package/dist/loro/style.css.map +1 -0
- package/dist/loro/style.js +1 -0
- package/dist/{mark-rule-D7zaa32n.js → mark-rule-CGmswjQ_.js} +3 -3
- package/dist/mark-rule-CGmswjQ_.js.map +1 -0
- package/dist/{paste-rule-Cca3n5TA.js → paste-rule-BIztzELg.js} +5 -15
- package/dist/paste-rule-BIztzELg.js.map +1 -0
- package/dist/placeholder/style.css +2 -0
- package/dist/placeholder/style.css.map +1 -0
- package/dist/placeholder/style.js +1 -0
- package/dist/prosekit-extensions-autocomplete.d.ts +2 -1
- package/dist/prosekit-extensions-autocomplete.d.ts.map +1 -0
- package/dist/prosekit-extensions-autocomplete.js +2 -9
- package/dist/prosekit-extensions-autocomplete.js.map +1 -0
- package/dist/prosekit-extensions-blockquote.d.ts +2 -1
- package/dist/prosekit-extensions-blockquote.d.ts.map +1 -0
- package/dist/prosekit-extensions-blockquote.js +4 -4
- package/dist/prosekit-extensions-blockquote.js.map +1 -0
- package/dist/prosekit-extensions-bold.d.ts +2 -1
- package/dist/prosekit-extensions-bold.d.ts.map +1 -0
- package/dist/prosekit-extensions-bold.js +3 -2
- package/dist/prosekit-extensions-bold.js.map +1 -0
- package/dist/prosekit-extensions-code-block.d.ts +3 -2
- package/dist/prosekit-extensions-code-block.d.ts.map +1 -0
- package/dist/prosekit-extensions-code-block.js +5 -6
- package/dist/prosekit-extensions-code-block.js.map +1 -0
- package/dist/prosekit-extensions-code.d.ts +2 -1
- package/dist/prosekit-extensions-code.d.ts.map +1 -0
- package/dist/prosekit-extensions-code.js +3 -2
- package/dist/prosekit-extensions-code.js.map +1 -0
- package/dist/prosekit-extensions-commit.d.ts +2 -1
- package/dist/prosekit-extensions-commit.d.ts.map +1 -0
- package/dist/prosekit-extensions-commit.js +11 -13
- package/dist/prosekit-extensions-commit.js.map +1 -0
- package/dist/prosekit-extensions-doc.d.ts +2 -1
- package/dist/prosekit-extensions-doc.d.ts.map +1 -0
- package/dist/prosekit-extensions-doc.js +2 -1
- package/dist/prosekit-extensions-doc.js.map +1 -0
- package/dist/prosekit-extensions-drop-cursor.d.ts +2 -1
- package/dist/prosekit-extensions-drop-cursor.d.ts.map +1 -0
- package/dist/prosekit-extensions-drop-cursor.js +2 -1
- package/dist/prosekit-extensions-drop-cursor.js.map +1 -0
- package/dist/prosekit-extensions-drop-indicator.d.ts +5 -107
- package/dist/prosekit-extensions-drop-indicator.d.ts.map +1 -0
- package/dist/prosekit-extensions-drop-indicator.js +1 -1
- package/dist/prosekit-extensions-enter-rule.d.ts +2 -1
- package/dist/prosekit-extensions-enter-rule.d.ts.map +1 -0
- package/dist/prosekit-extensions-file.d.ts +2 -125
- package/dist/prosekit-extensions-file.js +1 -139
- package/dist/prosekit-extensions-gap-cursor.d.ts +2 -2
- package/dist/prosekit-extensions-gap-cursor.d.ts.map +1 -0
- package/dist/prosekit-extensions-gap-cursor.js +2 -1
- package/dist/prosekit-extensions-gap-cursor.js.map +1 -0
- package/dist/prosekit-extensions-hard-break.d.ts +2 -5
- package/dist/prosekit-extensions-hard-break.d.ts.map +1 -0
- package/dist/prosekit-extensions-hard-break.js +2 -1
- package/dist/prosekit-extensions-hard-break.js.map +1 -0
- package/dist/prosekit-extensions-heading.d.ts +2 -1
- package/dist/prosekit-extensions-heading.d.ts.map +1 -0
- package/dist/prosekit-extensions-heading.js +5 -6
- package/dist/prosekit-extensions-heading.js.map +1 -0
- package/dist/prosekit-extensions-horizontal-rule.d.ts +2 -1
- package/dist/prosekit-extensions-horizontal-rule.d.ts.map +1 -0
- package/dist/prosekit-extensions-horizontal-rule.js +5 -6
- package/dist/prosekit-extensions-horizontal-rule.js.map +1 -0
- package/dist/prosekit-extensions-image.d.ts +80 -3
- package/dist/prosekit-extensions-image.d.ts.map +1 -0
- package/dist/prosekit-extensions-image.js +90 -10
- package/dist/prosekit-extensions-image.js.map +1 -0
- package/dist/prosekit-extensions-input-rule.d.ts +2 -1
- package/dist/prosekit-extensions-input-rule.d.ts.map +1 -0
- package/dist/prosekit-extensions-input-rule.js +1 -1
- package/dist/prosekit-extensions-italic.d.ts +2 -1
- package/dist/prosekit-extensions-italic.d.ts.map +1 -0
- package/dist/prosekit-extensions-italic.js +3 -2
- package/dist/prosekit-extensions-italic.js.map +1 -0
- package/dist/prosekit-extensions-link.d.ts +2 -1
- package/dist/prosekit-extensions-link.d.ts.map +1 -0
- package/dist/prosekit-extensions-link.js +6 -5
- package/dist/prosekit-extensions-link.js.map +1 -0
- package/dist/prosekit-extensions-list.d.ts +22 -21
- package/dist/prosekit-extensions-list.d.ts.map +1 -0
- package/dist/prosekit-extensions-list.js +7 -8
- package/dist/prosekit-extensions-list.js.map +1 -0
- package/dist/prosekit-extensions-loro.d.ts +14 -13
- package/dist/prosekit-extensions-loro.d.ts.map +1 -0
- package/dist/prosekit-extensions-loro.js +2 -1
- package/dist/prosekit-extensions-loro.js.map +1 -0
- package/dist/prosekit-extensions-mark-rule.d.ts +2 -1
- package/dist/prosekit-extensions-mark-rule.d.ts.map +1 -0
- package/dist/prosekit-extensions-mark-rule.js +1 -1
- package/dist/prosekit-extensions-mention.d.ts +2 -1
- package/dist/prosekit-extensions-mention.d.ts.map +1 -0
- package/dist/prosekit-extensions-mention.js +2 -1
- package/dist/prosekit-extensions-mention.js.map +1 -0
- package/dist/prosekit-extensions-mod-click-prevention.d.ts +2 -1
- package/dist/prosekit-extensions-mod-click-prevention.d.ts.map +1 -0
- package/dist/prosekit-extensions-mod-click-prevention.js +2 -1
- package/dist/prosekit-extensions-mod-click-prevention.js.map +1 -0
- package/dist/prosekit-extensions-paragraph.d.ts +2 -5
- package/dist/prosekit-extensions-paragraph.d.ts.map +1 -0
- package/dist/prosekit-extensions-paragraph.js +2 -1
- package/dist/prosekit-extensions-paragraph.js.map +1 -0
- package/dist/prosekit-extensions-paste-rule.d.ts +2 -1
- package/dist/prosekit-extensions-paste-rule.d.ts.map +1 -0
- package/dist/prosekit-extensions-paste-rule.js +1 -1
- package/dist/prosekit-extensions-placeholder.d.ts +2 -1
- package/dist/prosekit-extensions-placeholder.d.ts.map +1 -0
- package/dist/prosekit-extensions-placeholder.js +5 -5
- package/dist/prosekit-extensions-placeholder.js.map +1 -0
- package/dist/prosekit-extensions-readonly.d.ts +2 -1
- package/dist/prosekit-extensions-readonly.d.ts.map +1 -0
- package/dist/prosekit-extensions-readonly.js +2 -1
- package/dist/prosekit-extensions-readonly.js.map +1 -0
- package/dist/prosekit-extensions-search.d.ts +2 -1
- package/dist/prosekit-extensions-search.d.ts.map +1 -0
- package/dist/prosekit-extensions-search.js +3 -3
- package/dist/prosekit-extensions-search.js.map +1 -0
- package/dist/prosekit-extensions-strike.d.ts +2 -1
- package/dist/prosekit-extensions-strike.d.ts.map +1 -0
- package/dist/prosekit-extensions-strike.js +3 -2
- package/dist/prosekit-extensions-strike.js.map +1 -0
- package/dist/prosekit-extensions-table.d.ts +47 -114
- package/dist/prosekit-extensions-table.d.ts.map +1 -0
- package/dist/prosekit-extensions-table.js +2 -2
- package/dist/prosekit-extensions-text-align.d.ts +2 -1
- package/dist/prosekit-extensions-text-align.d.ts.map +1 -0
- package/dist/prosekit-extensions-text-align.js +2 -1
- package/dist/prosekit-extensions-text-align.js.map +1 -0
- package/dist/prosekit-extensions-text.d.ts +2 -1
- package/dist/prosekit-extensions-text.d.ts.map +1 -0
- package/dist/prosekit-extensions-text.js +2 -1
- package/dist/prosekit-extensions-text.js.map +1 -0
- package/dist/prosekit-extensions-underline.d.ts +2 -1
- package/dist/prosekit-extensions-underline.d.ts.map +1 -0
- package/dist/prosekit-extensions-underline.js +2 -1
- package/dist/prosekit-extensions-underline.js.map +1 -0
- package/dist/prosekit-extensions-virtual-selection.d.ts +2 -1
- package/dist/prosekit-extensions-virtual-selection.d.ts.map +1 -0
- package/dist/prosekit-extensions-virtual-selection.js +3 -3
- package/dist/prosekit-extensions-virtual-selection.js.map +1 -0
- package/dist/prosekit-extensions-yjs.d.ts +2 -1
- package/dist/prosekit-extensions-yjs.d.ts.map +1 -0
- package/dist/prosekit-extensions-yjs.js +2 -1
- package/dist/prosekit-extensions-yjs.js.map +1 -0
- package/dist/prosekit-extensions.js +1 -0
- package/dist/search/style.css +2 -0
- package/dist/search/style.css.map +1 -0
- package/dist/search/style.js +1 -0
- package/dist/{shiki-highlighter-chunk-DSPM0T27.d.ts → shiki-highlighter-chunk-Cwu1Jr9o.d.ts} +2 -1
- package/dist/shiki-highlighter-chunk-Cwu1Jr9o.d.ts.map +1 -0
- package/dist/shiki-highlighter-chunk.d.ts +1 -1
- package/dist/shiki-highlighter-chunk.js +3 -5
- package/dist/shiki-highlighter-chunk.js.map +1 -0
- package/dist/table/style.css +2 -0
- package/dist/table/style.css.map +1 -0
- package/dist/table/style.js +1 -0
- package/dist/table-BNwuK7xg.js +297 -0
- package/dist/table-BNwuK7xg.js.map +1 -0
- package/dist/virtual-selection/style.css +2 -0
- package/dist/virtual-selection/style.css.map +1 -0
- package/dist/virtual-selection/style.js +1 -0
- package/dist/yjs/style.css +2 -0
- package/dist/yjs/style.css.map +1 -0
- package/dist/yjs/style.js +1 -0
- package/package.json +12 -10
- package/src/autocomplete/autocomplete-helpers.ts +74 -0
- package/src/autocomplete/autocomplete-plugin.ts +186 -0
- package/src/autocomplete/autocomplete-rule.ts +117 -0
- package/src/autocomplete/autocomplete.spec.ts +132 -0
- package/src/autocomplete/autocomplete.ts +29 -0
- package/src/autocomplete/index.ts +9 -0
- package/src/blockquote/blockquote-commands.ts +32 -0
- package/src/blockquote/blockquote-input-rule.ts +14 -0
- package/src/blockquote/blockquote-keymap.spec.ts +45 -0
- package/src/blockquote/blockquote-keymap.ts +31 -0
- package/src/blockquote/blockquote-spec.ts +24 -0
- package/src/blockquote/blockquote.ts +34 -0
- package/src/blockquote/index.ts +14 -0
- package/src/bold/bold-commands.ts +23 -0
- package/src/bold/bold-input-rule.spec.ts +51 -0
- package/src/bold/bold-input-rule.ts +18 -0
- package/src/bold/bold-keymap.ts +14 -0
- package/src/bold/bold-spec.ts +53 -0
- package/src/bold/bold.ts +32 -0
- package/src/bold/index.ts +14 -0
- package/src/code/code-commands.ts +23 -0
- package/src/code/code-input-rule.ts +18 -0
- package/src/code/code-keymap.ts +14 -0
- package/src/code/code-spec.ts +28 -0
- package/src/code/code.ts +32 -0
- package/src/code/index.ts +14 -0
- package/src/code-block/code-block-commands.ts +44 -0
- package/src/code-block/code-block-highlight.ts +40 -0
- package/src/code-block/code-block-input-rule.ts +36 -0
- package/src/code-block/code-block-keymap.ts +61 -0
- package/src/code-block/code-block-shiki.ts +58 -0
- package/src/code-block/code-block-spec.spec.ts +164 -0
- package/src/code-block/code-block-spec.ts +71 -0
- package/src/code-block/code-block-types.ts +8 -0
- package/src/code-block/code-block.ts +46 -0
- package/src/code-block/index.ts +32 -0
- package/src/code-block/shiki-bundle.ts +8 -0
- package/src/code-block/shiki-highlighter-chunk.ts +84 -0
- package/src/code-block/shiki-highlighter.ts +22 -0
- package/src/code-block/shiki-parser.ts +36 -0
- package/src/commit/index.ts +330 -0
- package/src/commit/style.css +7 -0
- package/src/doc/index.ts +21 -0
- package/src/drop-cursor/drop-cursor.ts +46 -0
- package/src/drop-cursor/index.ts +5 -0
- package/src/drop-indicator/drop-indicator-facet.ts +62 -0
- package/src/drop-indicator/drop-indicator.ts +35 -0
- package/src/drop-indicator/index.ts +14 -0
- package/src/enter-rule/index.ts +241 -0
- package/src/file/file-drop-handler.ts +75 -0
- package/src/file/file-paste-handler.spec.ts +95 -0
- package/src/file/file-paste-handler.ts +59 -0
- package/src/file/file-upload.ts +140 -0
- package/src/file/helpers.ts +39 -0
- package/src/file/index.ts +16 -0
- package/src/gap-cursor/gap-cursor.ts +28 -0
- package/src/gap-cursor/index.ts +4 -0
- package/src/gap-cursor/style.css +25 -0
- package/src/hard-break/hard-break-commands.ts +31 -0
- package/src/hard-break/hard-break-keymap.spec.ts +45 -0
- package/src/hard-break/hard-break-keymap.ts +16 -0
- package/src/hard-break/hard-break-spec.ts +31 -0
- package/src/hard-break/hard-break.ts +32 -0
- package/src/hard-break/index.ts +13 -0
- package/src/heading/heading-commands.ts +37 -0
- package/src/heading/heading-input-rule.ts +22 -0
- package/src/heading/heading-keymap.spec.ts +53 -0
- package/src/heading/heading-keymap.ts +40 -0
- package/src/heading/heading-spec.ts +39 -0
- package/src/heading/heading-types.ts +3 -0
- package/src/heading/heading.ts +34 -0
- package/src/heading/index.ts +15 -0
- package/src/horizontal-rule/horizontal-rule-commands.spec.ts +61 -0
- package/src/horizontal-rule/horizontal-rule-commands.ts +37 -0
- package/src/horizontal-rule/horizontal-rule-input-rule.spec.ts +61 -0
- package/src/horizontal-rule/horizontal-rule-input-rule.ts +26 -0
- package/src/horizontal-rule/horizontal-rule-spec.ts +21 -0
- package/src/horizontal-rule/horizontal-rule.ts +29 -0
- package/src/horizontal-rule/index.ts +14 -0
- package/src/image/image-commands.ts +36 -0
- package/src/image/image-spec.ts +72 -0
- package/src/image/image-upload-handler.ts +156 -0
- package/src/image/image.ts +25 -0
- package/src/image/index.ts +22 -0
- package/src/index.ts +1 -0
- package/src/input-rule/index.ts +237 -0
- package/src/italic/index.ts +14 -0
- package/src/italic/italic-commands.spec.ts +75 -0
- package/src/italic/italic-commands.ts +23 -0
- package/src/italic/italic-input-rule.spec.ts +25 -0
- package/src/italic/italic-input-rule.ts +18 -0
- package/src/italic/italic-keymap.ts +14 -0
- package/src/italic/italic-spec.ts +35 -0
- package/src/italic/italic.ts +34 -0
- package/src/link/index.spec.ts +88 -0
- package/src/link/index.ts +156 -0
- package/src/link/link-paste-rule.spec.ts +194 -0
- package/src/link/link-paste-rule.ts +22 -0
- package/src/link/link-regex.spec.ts +82 -0
- package/src/link/link-regex.ts +79 -0
- package/src/link/link-types.ts +8 -0
- package/src/list/index.ts +25 -0
- package/src/list/list-commands.ts +61 -0
- package/src/list/list-drop-indicator.ts +37 -0
- package/src/list/list-input-rules.ts +14 -0
- package/src/list/list-keymap.spec.ts +39 -0
- package/src/list/list-keymap.ts +48 -0
- package/src/list/list-plugins.ts +35 -0
- package/src/list/list-serializer.ts +38 -0
- package/src/list/list-spec.ts +60 -0
- package/src/list/list-types.spec.ts +10 -0
- package/src/list/list-types.ts +23 -0
- package/src/list/list.spec.ts +134 -0
- package/src/list/list.ts +38 -0
- package/src/list/style.css +128 -0
- package/src/loro/index.ts +17 -0
- package/src/loro/loro-commands.ts +27 -0
- package/src/loro/loro-cursor-plugin.ts +28 -0
- package/src/loro/loro-keymap.ts +23 -0
- package/src/loro/loro-sync-plugin.ts +14 -0
- package/src/loro/loro-undo-plugin.ts +12 -0
- package/src/loro/loro.ts +75 -0
- package/src/loro/style.css +33 -0
- package/src/mark-rule/apply.ts +129 -0
- package/src/mark-rule/index.ts +2 -0
- package/src/mark-rule/mark-rule.spec.ts +123 -0
- package/src/mark-rule/mark-rule.ts +48 -0
- package/src/mark-rule/range.ts +107 -0
- package/src/mark-rule/types.ts +30 -0
- package/src/mention/index.ts +90 -0
- package/src/mod-click-prevention/index.ts +35 -0
- package/src/paragraph/index.ts +7 -0
- package/src/paragraph/paragraph-commands.ts +29 -0
- package/src/paragraph/paragraph-keymap.ts +15 -0
- package/src/paragraph/paragraph-spec.ts +31 -0
- package/src/paragraph/paragraph.ts +37 -0
- package/src/paste-rule/index.ts +10 -0
- package/src/paste-rule/mark-paste-rule.spec.ts +112 -0
- package/src/paste-rule/mark-paste-rule.ts +194 -0
- package/src/paste-rule/paste-rule-plugin.ts +53 -0
- package/src/paste-rule/paste-rule.spec.ts +96 -0
- package/src/paste-rule/paste-rule.ts +60 -0
- package/src/paste-rule/split-text-by-regex.spec.ts +97 -0
- package/src/paste-rule/split-text-by-regex.ts +44 -0
- package/src/placeholder/index.ts +113 -0
- package/src/placeholder/style.css +7 -0
- package/src/readonly/index.ts +22 -0
- package/src/search/index.ts +140 -0
- package/src/search/style.css +13 -0
- package/src/strike/index.ts +101 -0
- package/src/table/index.ts +53 -0
- package/src/table/style.css +42 -0
- package/src/table/table-commands/delete-cell-selection.spec.ts +41 -0
- package/src/table/table-commands/delete-cell-selection.ts +1 -0
- package/src/table/table-commands/exit-table.spec.ts +45 -0
- package/src/table/table-commands/exit-table.ts +49 -0
- package/src/table/table-commands/insert-table.spec.ts +39 -0
- package/src/table/table-commands/insert-table.ts +80 -0
- package/src/table/table-commands/move-table-column.spec.ts +618 -0
- package/src/table/table-commands/move-table-column.ts +4 -0
- package/src/table/table-commands/move-table-row.spec.ts +380 -0
- package/src/table/table-commands/move-table-row.ts +4 -0
- package/src/table/table-commands/select-table-cell.spec.ts +34 -0
- package/src/table/table-commands/select-table-cell.ts +35 -0
- package/src/table/table-commands/select-table-column.spec.ts +33 -0
- package/src/table/table-commands/select-table-column.ts +39 -0
- package/src/table/table-commands/select-table-row.spec.ts +32 -0
- package/src/table/table-commands/select-table-row.ts +39 -0
- package/src/table/table-commands/select-table.spec.ts +36 -0
- package/src/table/table-commands/select-table.ts +50 -0
- package/src/table/table-commands.ts +110 -0
- package/src/table/table-drop-indicator.ts +40 -0
- package/src/table/table-plugins.ts +15 -0
- package/src/table/table-spec.spec.ts +113 -0
- package/src/table/table-spec.ts +109 -0
- package/src/table/table-utils.ts +16 -0
- package/src/table/table.ts +49 -0
- package/src/table/test-utils.ts +28 -0
- package/src/testing/clipboard.ts +58 -0
- package/src/testing/format-html.ts +5 -0
- package/src/testing/index.ts +161 -0
- package/src/testing/keyboard.ts +36 -0
- package/src/testing/markdown.ts +23 -0
- package/src/text/index.ts +24 -0
- package/src/text-align/index.ts +133 -0
- package/src/types/assert-type-equal.ts +8 -0
- package/src/underline/index.ts +83 -0
- package/src/virtual-selection/index.ts +100 -0
- package/src/virtual-selection/style.css +5 -0
- package/src/yjs/index.ts +22 -0
- package/src/yjs/style.css +31 -0
- package/src/yjs/yjs-commands.ts +27 -0
- package/src/yjs/yjs-cursor-plugin.ts +25 -0
- package/src/yjs/yjs-keymap.ts +23 -0
- package/src/yjs/yjs-sync-plugin.ts +23 -0
- package/src/yjs/yjs-undo-plugin.ts +87 -0
- package/src/yjs/yjs.ts +84 -0
- package/dist/drop-indicator-dB9rZn8e.js +0 -267
- package/dist/table-CPI9ZxbK.js +0 -760
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineFacet,
|
|
3
|
+
defineFacetPayload,
|
|
4
|
+
getNodeType,
|
|
5
|
+
isTextSelection,
|
|
6
|
+
maybeRun,
|
|
7
|
+
OBJECT_REPLACEMENT_CHARACTER,
|
|
8
|
+
pluginFacet,
|
|
9
|
+
type PlainExtension,
|
|
10
|
+
type PluginPayload,
|
|
11
|
+
} from '@prosekit/core'
|
|
12
|
+
import { keydownHandler } from '@prosekit/pm/keymap'
|
|
13
|
+
import type {
|
|
14
|
+
Attrs,
|
|
15
|
+
NodeType,
|
|
16
|
+
} from '@prosekit/pm/model'
|
|
17
|
+
import {
|
|
18
|
+
PluginKey,
|
|
19
|
+
ProseMirrorPlugin,
|
|
20
|
+
type Command,
|
|
21
|
+
type EditorState,
|
|
22
|
+
type Transaction,
|
|
23
|
+
} from '@prosekit/pm/state'
|
|
24
|
+
import type { EditorView } from '@prosekit/pm/view'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @public
|
|
28
|
+
*
|
|
29
|
+
* Options for {@link EnterRuleHandler}.
|
|
30
|
+
*/
|
|
31
|
+
export interface EnterRuleHandlerOptions {
|
|
32
|
+
/**
|
|
33
|
+
* The current editor state.
|
|
34
|
+
*/
|
|
35
|
+
state: EditorState
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The start position of the matched text.
|
|
39
|
+
*/
|
|
40
|
+
from: number
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The end position of the matched text.
|
|
44
|
+
*/
|
|
45
|
+
to: number
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The matched result from the regular expression.
|
|
49
|
+
*/
|
|
50
|
+
match: RegExpExecArray
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
export type EnterRuleHandler = (options: EnterRuleHandlerOptions) => Transaction | null
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Options for {@link defineEnterRule}.
|
|
60
|
+
*
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
63
|
+
export type EnterRuleOptions = {
|
|
64
|
+
/**
|
|
65
|
+
* The regular expression to match against. It should end with `$`.
|
|
66
|
+
*/
|
|
67
|
+
regex: RegExp
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A function to be called when an enter rule is triggered.
|
|
71
|
+
*/
|
|
72
|
+
handler: EnterRuleHandler
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Whether to stop further handlers from being called if this rule is triggered.
|
|
76
|
+
*
|
|
77
|
+
* @default false
|
|
78
|
+
*/
|
|
79
|
+
stop?: boolean
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Options for {@link defineTextBlockEnterRule}.
|
|
84
|
+
*
|
|
85
|
+
* @public
|
|
86
|
+
*/
|
|
87
|
+
export interface TextBlockEnterRuleOptions {
|
|
88
|
+
/**
|
|
89
|
+
* The regular expression to match against. It should end with `$`.
|
|
90
|
+
*/
|
|
91
|
+
regex: RegExp
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The node type to replace the matched text with.
|
|
95
|
+
*/
|
|
96
|
+
type: string | NodeType
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Attributes to set on the node. If a function is provided, it will be called
|
|
100
|
+
* with the matched result from the regular expression.
|
|
101
|
+
*/
|
|
102
|
+
attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Whether to stop further handlers from being called if this rule is triggered.
|
|
106
|
+
*
|
|
107
|
+
* @default true
|
|
108
|
+
*/
|
|
109
|
+
stop?: boolean
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Defines an enter rule. An enter rule applies when the text directly in front of
|
|
114
|
+
* the cursor matches `regex` and user presses Enter. The `regex` should end
|
|
115
|
+
* with `$`.
|
|
116
|
+
*
|
|
117
|
+
* @param options
|
|
118
|
+
*
|
|
119
|
+
* @public
|
|
120
|
+
*/
|
|
121
|
+
export function defineEnterRule({
|
|
122
|
+
regex,
|
|
123
|
+
handler,
|
|
124
|
+
stop = false,
|
|
125
|
+
}: EnterRuleOptions): PlainExtension {
|
|
126
|
+
const rule: EnterRule = new EnterRule(regex, handler, stop)
|
|
127
|
+
return defineFacetPayload(enterRule, [rule]) as PlainExtension
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Defines an enter rule that replaces the matched text with a block node.
|
|
132
|
+
*
|
|
133
|
+
* See also {@link defineEnterRule}.
|
|
134
|
+
*
|
|
135
|
+
* @param options
|
|
136
|
+
*
|
|
137
|
+
* @public
|
|
138
|
+
*/
|
|
139
|
+
export function defineTextBlockEnterRule({
|
|
140
|
+
regex,
|
|
141
|
+
type,
|
|
142
|
+
attrs,
|
|
143
|
+
stop = true,
|
|
144
|
+
}: TextBlockEnterRuleOptions): PlainExtension {
|
|
145
|
+
return defineEnterRule({
|
|
146
|
+
regex,
|
|
147
|
+
handler: ({ state, from, to, match }) => {
|
|
148
|
+
const nodeType = getNodeType(state.schema, type)
|
|
149
|
+
const $start = state.doc.resolve(from)
|
|
150
|
+
|
|
151
|
+
if (
|
|
152
|
+
!$start
|
|
153
|
+
.node(-1)
|
|
154
|
+
.canReplaceWith($start.index(-1), $start.indexAfter(-1), nodeType)
|
|
155
|
+
) {
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const nodeAttrs = maybeRun(attrs, match)
|
|
160
|
+
return state.tr
|
|
161
|
+
.delete(from, to)
|
|
162
|
+
.setBlockType(from, from, nodeType, nodeAttrs)
|
|
163
|
+
},
|
|
164
|
+
stop,
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @internal
|
|
170
|
+
*/
|
|
171
|
+
class EnterRule {
|
|
172
|
+
constructor(
|
|
173
|
+
readonly regex: RegExp,
|
|
174
|
+
readonly handler: EnterRuleHandler,
|
|
175
|
+
readonly stop: boolean,
|
|
176
|
+
) {}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const enterRule = defineFacet<EnterRule, PluginPayload>({
|
|
180
|
+
reduce: () => {
|
|
181
|
+
let rules: EnterRule[] = []
|
|
182
|
+
|
|
183
|
+
const command: Command = (state, dispatch, view) => {
|
|
184
|
+
if (!view) return false
|
|
185
|
+
return execRules(view, rules, dispatch)
|
|
186
|
+
}
|
|
187
|
+
const handler = keydownHandler({ Enter: command })
|
|
188
|
+
const plugin = new ProseMirrorPlugin({
|
|
189
|
+
key: new PluginKey('prosekit-enter-rule'),
|
|
190
|
+
props: { handleKeyDown: handler },
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
return function reducer(inputs) {
|
|
194
|
+
rules = inputs
|
|
195
|
+
return plugin
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
parent: pluginFacet,
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
function execRules(
|
|
203
|
+
view: EditorView,
|
|
204
|
+
rules: readonly EnterRule[],
|
|
205
|
+
dispatch?: (tr: Transaction) => void,
|
|
206
|
+
): boolean {
|
|
207
|
+
if (view.composing) return false
|
|
208
|
+
const state = view.state
|
|
209
|
+
const selection = state.selection
|
|
210
|
+
if (!isTextSelection(selection)) return false
|
|
211
|
+
const $cursor = selection.$cursor
|
|
212
|
+
if (!$cursor || $cursor.parent.type.spec.code) return false
|
|
213
|
+
|
|
214
|
+
const textBefore = $cursor.parent.textBetween(
|
|
215
|
+
Math.max(0, $cursor.parentOffset - MAX_MATCH),
|
|
216
|
+
$cursor.parentOffset,
|
|
217
|
+
null,
|
|
218
|
+
OBJECT_REPLACEMENT_CHARACTER,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
for (const rule of rules) {
|
|
222
|
+
rule.regex.lastIndex = 0
|
|
223
|
+
const match = rule.regex.exec(textBefore)
|
|
224
|
+
const tr = match
|
|
225
|
+
&& rule.handler({
|
|
226
|
+
state,
|
|
227
|
+
from: $cursor.pos - match[0].length,
|
|
228
|
+
to: $cursor.pos,
|
|
229
|
+
match,
|
|
230
|
+
})
|
|
231
|
+
if (!tr) continue
|
|
232
|
+
dispatch?.(tr)
|
|
233
|
+
|
|
234
|
+
if (rule.stop) {
|
|
235
|
+
return true
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const MAX_MATCH = 200
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineFacet,
|
|
3
|
+
defineFacetPayload,
|
|
4
|
+
editorEventFacet,
|
|
5
|
+
type DropHandler,
|
|
6
|
+
type EditorEventPayload,
|
|
7
|
+
type PlainExtension,
|
|
8
|
+
} from '@prosekit/core'
|
|
9
|
+
import type { EditorView } from '@prosekit/pm/view'
|
|
10
|
+
|
|
11
|
+
import { handleEvent } from './helpers'
|
|
12
|
+
|
|
13
|
+
export interface FileDropHandlerOptions {
|
|
14
|
+
/**
|
|
15
|
+
* The editor view.
|
|
16
|
+
*/
|
|
17
|
+
view: EditorView
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The event that triggered the drop.
|
|
21
|
+
*/
|
|
22
|
+
event: DragEvent
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The file that was dropped.
|
|
26
|
+
*/
|
|
27
|
+
file: File
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The position of the document where the file was dropped.
|
|
31
|
+
*/
|
|
32
|
+
pos: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A function that handles one of the files in a drop event.
|
|
37
|
+
*
|
|
38
|
+
* Returns `true` if the file was handled and thus should not be handled by
|
|
39
|
+
* other handlers.
|
|
40
|
+
*/
|
|
41
|
+
export type FileDropHandler = (
|
|
42
|
+
options: FileDropHandlerOptions,
|
|
43
|
+
) => boolean | void
|
|
44
|
+
|
|
45
|
+
export function defineFileDropHandler(
|
|
46
|
+
handler: FileDropHandler,
|
|
47
|
+
): PlainExtension {
|
|
48
|
+
return defineFacetPayload(facet, [handler]) as PlainExtension
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getFiles(event: DragEvent) {
|
|
52
|
+
return Array.from(event.dataTransfer?.files ?? [])
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const facet = defineFacet<FileDropHandler, EditorEventPayload>({
|
|
56
|
+
parent: editorEventFacet,
|
|
57
|
+
singleton: true,
|
|
58
|
+
reducer: (handlers: FileDropHandler[]): EditorEventPayload => {
|
|
59
|
+
const dropHandler: DropHandler = (view, event): boolean => {
|
|
60
|
+
const position = view.posAtCoords({ left: event.x, top: event.y })
|
|
61
|
+
if (!position) {
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
const pos = position.inside > 0 ? position.inside : position.pos
|
|
65
|
+
|
|
66
|
+
return handleEvent<DragEvent>(
|
|
67
|
+
view,
|
|
68
|
+
event,
|
|
69
|
+
handlers.map((handler) => (options) => handler({ ...options, pos })),
|
|
70
|
+
getFiles,
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
return ['drop', dropHandler]
|
|
74
|
+
},
|
|
75
|
+
})
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Priority,
|
|
3
|
+
union,
|
|
4
|
+
withPriority,
|
|
5
|
+
} from '@prosekit/core'
|
|
6
|
+
import {
|
|
7
|
+
beforeEach,
|
|
8
|
+
describe,
|
|
9
|
+
expect,
|
|
10
|
+
it,
|
|
11
|
+
vi,
|
|
12
|
+
} from 'vitest'
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
defineTestExtension,
|
|
16
|
+
setupTestFromExtension,
|
|
17
|
+
} from '../testing'
|
|
18
|
+
import { pasteFiles } from '../testing/clipboard'
|
|
19
|
+
|
|
20
|
+
import { defineFilePasteHandler } from './file-paste-handler'
|
|
21
|
+
|
|
22
|
+
function definePngPasteHandler(handler: VoidFunction) {
|
|
23
|
+
const extension = defineFilePasteHandler((options) => {
|
|
24
|
+
if (options.file.type === 'image/png') {
|
|
25
|
+
handler()
|
|
26
|
+
return true
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
return withPriority(extension, Priority.high)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function defineImagePasteHandler(handler: VoidFunction) {
|
|
33
|
+
const extension = defineFilePasteHandler((options) => {
|
|
34
|
+
if (options.file.type.startsWith('image/')) {
|
|
35
|
+
handler()
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
return withPriority(extension, Priority.default)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function defineFallbackPasteHandler(handler: VoidFunction) {
|
|
43
|
+
const extension = defineFilePasteHandler(() => {
|
|
44
|
+
handler()
|
|
45
|
+
return true
|
|
46
|
+
})
|
|
47
|
+
return withPriority(extension, Priority.low)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe('file paste handler', () => {
|
|
51
|
+
const pngHandler = vi.fn()
|
|
52
|
+
const imageHandler = vi.fn()
|
|
53
|
+
const fallbackHandler = vi.fn()
|
|
54
|
+
|
|
55
|
+
const extension = union(
|
|
56
|
+
defineTestExtension(),
|
|
57
|
+
defineImagePasteHandler(imageHandler),
|
|
58
|
+
definePngPasteHandler(pngHandler),
|
|
59
|
+
defineFallbackPasteHandler(fallbackHandler),
|
|
60
|
+
)
|
|
61
|
+
const { editor } = setupTestFromExtension(extension)
|
|
62
|
+
const paste = (files: File[]) => {
|
|
63
|
+
pasteFiles(editor.view, files)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
pngHandler.mockClear()
|
|
68
|
+
imageHandler.mockClear()
|
|
69
|
+
fallbackHandler.mockClear()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should handle file pasting', () => {
|
|
73
|
+
paste([new File([''], 'test.png', { type: 'image/png' })])
|
|
74
|
+
expect(pngHandler).toHaveBeenCalled()
|
|
75
|
+
expect(imageHandler).not.toHaveBeenCalled()
|
|
76
|
+
expect(fallbackHandler).not.toHaveBeenCalled()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should handle priority', () => {
|
|
80
|
+
paste([new File([''], 'test.jpg', { type: 'image/jpg' })])
|
|
81
|
+
expect(pngHandler).not.toHaveBeenCalled()
|
|
82
|
+
expect(imageHandler).toHaveBeenCalled()
|
|
83
|
+
expect(fallbackHandler).not.toHaveBeenCalled()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('should handle multiple files', () => {
|
|
87
|
+
paste([
|
|
88
|
+
new File([''], 'test.png', { type: 'image/png' }),
|
|
89
|
+
new File([''], 'test.pdf', { type: 'application/pdf' }),
|
|
90
|
+
])
|
|
91
|
+
expect(pngHandler).toHaveBeenCalled()
|
|
92
|
+
expect(imageHandler).not.toHaveBeenCalled()
|
|
93
|
+
expect(fallbackHandler).toHaveBeenCalled()
|
|
94
|
+
})
|
|
95
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineFacet,
|
|
3
|
+
defineFacetPayload,
|
|
4
|
+
editorEventFacet,
|
|
5
|
+
type EditorEventPayload,
|
|
6
|
+
type PasteHandler,
|
|
7
|
+
type PlainExtension,
|
|
8
|
+
} from '@prosekit/core'
|
|
9
|
+
import type { EditorView } from '@prosekit/pm/view'
|
|
10
|
+
|
|
11
|
+
import { handleEvent } from './helpers'
|
|
12
|
+
|
|
13
|
+
export interface FilePasteHandlerOptions {
|
|
14
|
+
/**
|
|
15
|
+
* The editor view.
|
|
16
|
+
*/
|
|
17
|
+
view: EditorView
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The event that triggered the paste.
|
|
21
|
+
*/
|
|
22
|
+
event: ClipboardEvent
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The file that was pasted.
|
|
26
|
+
*/
|
|
27
|
+
file: File
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A function that handles one of the files in a paste event.
|
|
32
|
+
*
|
|
33
|
+
* Returns `true` if the file was handled and thus should not be handled by
|
|
34
|
+
* other handlers.
|
|
35
|
+
*/
|
|
36
|
+
export type FilePasteHandler = (
|
|
37
|
+
options: FilePasteHandlerOptions,
|
|
38
|
+
) => boolean | void
|
|
39
|
+
|
|
40
|
+
export function defineFilePasteHandler(
|
|
41
|
+
handler: FilePasteHandler,
|
|
42
|
+
): PlainExtension {
|
|
43
|
+
return defineFacetPayload(facet, [handler]) as PlainExtension
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getFiles(event: ClipboardEvent) {
|
|
47
|
+
return Array.from(event.clipboardData?.files ?? [])
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const facet = defineFacet<FilePasteHandler, EditorEventPayload>({
|
|
51
|
+
parent: editorEventFacet,
|
|
52
|
+
singleton: true,
|
|
53
|
+
reducer: (handlers: FilePasteHandler[]): EditorEventPayload => {
|
|
54
|
+
const pasteHandler: PasteHandler = (view, event): boolean => {
|
|
55
|
+
return handleEvent<ClipboardEvent>(view, event, handlers, getFiles)
|
|
56
|
+
}
|
|
57
|
+
return ['paste', pasteHandler]
|
|
58
|
+
},
|
|
59
|
+
})
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { ProseKitError } from '@prosekit/core'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* An interface representing the upload progress.
|
|
5
|
+
*/
|
|
6
|
+
export interface UploadProgress {
|
|
7
|
+
// A number representing the amount of work already performed by the
|
|
8
|
+
// underlying process.
|
|
9
|
+
loaded: number
|
|
10
|
+
// A number representing the total amount of work that the underlying
|
|
11
|
+
// process is in the progress of performing.
|
|
12
|
+
total: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface UploaderOptions {
|
|
16
|
+
/**
|
|
17
|
+
* The file to be uploaded.
|
|
18
|
+
*/
|
|
19
|
+
file: File
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A callback function that should be called with the upload progress updates.
|
|
23
|
+
*/
|
|
24
|
+
onProgress: (progress: UploadProgress) => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The implementation of the actual upload function. You need to implement this
|
|
29
|
+
* function to upload files to your desired destination.
|
|
30
|
+
*/
|
|
31
|
+
export type Uploader<Result> = (options: UploaderOptions) => Promise<Result>
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A class that represents a upload task.
|
|
35
|
+
*/
|
|
36
|
+
export class UploadTask<Result> {
|
|
37
|
+
/**
|
|
38
|
+
* An [object URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL)
|
|
39
|
+
* representing the file to be uploaded. This URL will be revoked once the
|
|
40
|
+
* upload is complete successfully.
|
|
41
|
+
*/
|
|
42
|
+
readonly objectURL: string
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A boolean indicating whether the upload is complete (either successfully or with an error).
|
|
46
|
+
*/
|
|
47
|
+
protected done = false
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* If the upload is complete successfully, this will be the result of the upload.
|
|
51
|
+
*/
|
|
52
|
+
protected result: Result | undefined
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* If the upload is complete with an error, this will be the error that occurred.
|
|
56
|
+
*/
|
|
57
|
+
protected error: Error | undefined
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A promise that fulfills once the upload is complete, or rejects if an error occurs.
|
|
61
|
+
*/
|
|
62
|
+
readonly finished: Promise<Result>
|
|
63
|
+
|
|
64
|
+
private subscribers: ((progress: UploadProgress) => void)[] = []
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Creates a new upload task. You can find the upload task by its object URL
|
|
68
|
+
* later using `UploadTask.get()`.
|
|
69
|
+
*
|
|
70
|
+
* @param options - The options for the upload task.
|
|
71
|
+
*/
|
|
72
|
+
constructor({ file, uploader }: { file: File; uploader: Uploader<Result> }) {
|
|
73
|
+
this.objectURL = URL.createObjectURL(file)
|
|
74
|
+
this.finished = new Promise((resolve, reject) => {
|
|
75
|
+
const maybePromise = uploader({
|
|
76
|
+
file,
|
|
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
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
Promise.resolve(maybePromise).then(
|
|
92
|
+
(result) => {
|
|
93
|
+
this.done = true
|
|
94
|
+
URL.revokeObjectURL(this.objectURL)
|
|
95
|
+
this.result = result
|
|
96
|
+
resolve(result)
|
|
97
|
+
},
|
|
98
|
+
(err) => {
|
|
99
|
+
this.done = true
|
|
100
|
+
const error = new ProseKitError('[prosekit] Failed to upload file', { cause: err })
|
|
101
|
+
this.error = error
|
|
102
|
+
reject(error)
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
})
|
|
106
|
+
store.set(this.objectURL, this)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Subscribes to progress updates. Returns a function to unsubscribe.
|
|
111
|
+
*/
|
|
112
|
+
public subscribeProgress(
|
|
113
|
+
callback: (progress: UploadProgress) => void,
|
|
114
|
+
): VoidFunction {
|
|
115
|
+
this.subscribers.push(callback)
|
|
116
|
+
return () => {
|
|
117
|
+
this.subscribers = this.subscribers.filter(
|
|
118
|
+
(subscriber) => subscriber !== callback,
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Finds an upload task from the global store by its object URL.
|
|
125
|
+
*/
|
|
126
|
+
static get<Result = unknown>(
|
|
127
|
+
objectURL: string,
|
|
128
|
+
): UploadTask<Result> | undefined {
|
|
129
|
+
return store.get(objectURL) as UploadTask<Result> | undefined
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Deletes an upload task from the global store by its object URL.
|
|
134
|
+
*/
|
|
135
|
+
static delete(objectURL: string): void {
|
|
136
|
+
store.delete(objectURL)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const store = new Map<string, UploadTask<unknown>>()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { EditorView } from '@prosekit/pm/view'
|
|
2
|
+
|
|
3
|
+
type FileHandler<E extends Event> = (options: {
|
|
4
|
+
view: EditorView
|
|
5
|
+
event: E
|
|
6
|
+
file: File
|
|
7
|
+
}) => boolean | void
|
|
8
|
+
|
|
9
|
+
function handleFile<E extends Event>(
|
|
10
|
+
view: EditorView,
|
|
11
|
+
event: E,
|
|
12
|
+
file: File,
|
|
13
|
+
handlers: FileHandler<E>[],
|
|
14
|
+
) {
|
|
15
|
+
// The last item in `handlers` should has the highest priority.
|
|
16
|
+
for (let i = handlers.length - 1; i >= 0; i--) {
|
|
17
|
+
const handler = handlers[i]
|
|
18
|
+
if (handler({ view, event, file })) {
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function handleEvent<E extends Event>(
|
|
26
|
+
view: EditorView,
|
|
27
|
+
event: E,
|
|
28
|
+
handlers: FileHandler<E>[],
|
|
29
|
+
getFiles: (event: E) => File[],
|
|
30
|
+
): boolean {
|
|
31
|
+
const files = getFiles(event)
|
|
32
|
+
let handled = false
|
|
33
|
+
for (const file of files) {
|
|
34
|
+
if (handleFile(view, event, file, handlers)) {
|
|
35
|
+
handled = true
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return handled
|
|
39
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
defineFileDropHandler,
|
|
3
|
+
type FileDropHandler,
|
|
4
|
+
type FileDropHandlerOptions,
|
|
5
|
+
} from './file-drop-handler'
|
|
6
|
+
export {
|
|
7
|
+
defineFilePasteHandler,
|
|
8
|
+
type FilePasteHandler,
|
|
9
|
+
type FilePasteHandlerOptions,
|
|
10
|
+
} from './file-paste-handler'
|
|
11
|
+
export {
|
|
12
|
+
UploadTask,
|
|
13
|
+
type Uploader,
|
|
14
|
+
type UploaderOptions,
|
|
15
|
+
type UploadProgress,
|
|
16
|
+
} from './file-upload'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
definePlugin,
|
|
3
|
+
type PlainExtension,
|
|
4
|
+
} from '@prosekit/core'
|
|
5
|
+
import { gapCursor } from 'prosemirror-gapcursor'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export type GapCursorExtension = PlainExtension
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Capture clicks near and arrow-key-motion past places that don't have a
|
|
14
|
+
* normally selectable position nearby, and create a gap cursor selection for
|
|
15
|
+
* them. The cursor is drawn as an element with class `ProseMirror-gapcursor`.
|
|
16
|
+
*
|
|
17
|
+
* You can either include `prosekit/extensions/gap-cursor.css` or add your own
|
|
18
|
+
* styles to make it visible.
|
|
19
|
+
*
|
|
20
|
+
* See
|
|
21
|
+
* [prosemirror-gapcursor](https://github.com/ProseMirror/prosemirror-gapcursor)
|
|
22
|
+
* for more information.
|
|
23
|
+
*
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export function defineGapCursor(): GapCursorExtension {
|
|
27
|
+
return definePlugin(() => gapCursor())
|
|
28
|
+
}
|